From 3c9cb33109ead144ef55c326b259c0b561c0bb2e Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Tue, 17 Mar 2026 17:55:41 +0100 Subject: [PATCH 01/64] wip Signed-off-by: CHEN Roxane --- data/crac/crac-util/pom.xml | 6 + .../openrao/data/crac/util/IcsImporter.java | 252 +++--------------- data/ics-importer/pom.xml | 34 +++ .../com/powsybl/openrao/data/IcsData.java | 139 ++++++++++ .../powsybl/openrao/data/IcsDataImporter.java | 173 ++++++++++++ data/pom.xml | 1 + .../TimeCoupledConstraints.java | 4 + docs/input-data/specific-input-data/ics.md | 12 +- .../time-coupled-constraints.md | 2 +- 9 files changed, 411 insertions(+), 212 deletions(-) create mode 100644 data/ics-importer/pom.xml create mode 100644 data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java create mode 100644 data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java diff --git a/data/crac/crac-util/pom.xml b/data/crac/crac-util/pom.xml index ec7a2b36a7..4330e0d81d 100644 --- a/data/crac/crac-util/pom.xml +++ b/data/crac/crac-util/pom.xml @@ -94,6 +94,12 @@ commons-io runtime + + com.powsybl + ics-importer + 7.2.0-SNAPSHOT + compile + \ No newline at end of file diff --git a/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java b/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java index 66e28b473c..21e5ea445c 100644 --- a/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java +++ b/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java @@ -13,18 +13,15 @@ import com.powsybl.openrao.commons.OpenRaoException; import com.powsybl.openrao.commons.TemporalData; import com.powsybl.openrao.commons.TemporalDataImpl; +import com.powsybl.openrao.data.IcsData; 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.timecoupledconstraints.GeneratorConstraints; import com.powsybl.openrao.raoapi.RaoInputWithNetworkPaths; import com.powsybl.openrao.raoapi.TimeCoupledRaoInputWithNetworkPaths; -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.nio.file.Path; import java.time.OffsetDateTime; import java.util.*; @@ -45,7 +42,6 @@ public final class IcsImporter { private static double costDown; // Quality checks (or don't import the action at all): - // - check that P0s respect the min/max gradients // TODO in future versions: // - check other implemented constraints // - check consistency between ics files, particularly w.r.t gsk file @@ -53,19 +49,12 @@ public final class IcsImporter { // INFOS // Gradient constraints are defined for gsks at the action level and not per group : we translate it to the groups using the shift keys - public static final String MAXIMUM_POSITIVE_POWER_GRADIENT = "Maximum positive power gradient [MW/h]"; - public static final String MAXIMUM_NEGATIVE_POWER_GRADIENT = "Maximum negative power gradient [MW/h]"; - public static final String LEAD_TIME = "Lead time [h]"; - public static final String LAG_TIME = "Lag time [h]"; - public static final String STARTUP_ALLOWED = "Startup allowed"; - public static final String SHUTDOWN_ALLOWED = "Shutdown allowed"; public static final String P_MIN_RD = "Pmin_RD"; public static final String RA_RD_ID = "RA RD ID"; public static final String RDP_UP = "RDP+"; public static final String RDP_DOWN = "RDP-"; public static final String P0 = "P0"; public static final String UCT_NODE_OR_GSK_ID = "UCT Node or GSK ID"; - public static final String GSK_ID = "GSK ID"; public static final String PREVENTIVE = "Preventive"; public static final String CURATIVE = "Curative"; public static final String TRUE = "TRUE"; @@ -81,67 +70,45 @@ private IcsImporter() { } public static void populateInputWithICS(TimeCoupledRaoInputWithNetworkPaths timeCoupledRaoInput, - InputStream staticInputStream, - InputStream seriesInputStream, - InputStream gskInputStream, + IcsData icsData, double icsCostUp, - double icsCostDown) throws IOException { + double icsCostDown) { costUp = icsCostUp; costDown = icsCostDown; - TemporalData initialNetworks = new TemporalDataImpl<>(); + TemporalData modifiedInitialNetworks = new TemporalDataImpl<>(); timeCoupledRaoInput.getRaoInputs().getDataPerTimestamp().forEach((dateTime, raoInput) -> { Network network = Network.read(raoInput.getInitialNetworkPath()); - preProcessNetwork(network); - initialNetworks.put(dateTime, network); + updateNominalVoltage(network); + modifiedInitialNetworks.put(dateTime, network); }); - 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); - }); - - Map> weightPerNodePerGsk = new HashMap<>(); - if (gskInputStream != null) { - Iterable gskCsvRecords = csvFormat.parse(new InputStreamReader(gskInputStream)); - gskCsvRecords.forEach(record -> { - weightPerNodePerGsk.putIfAbsent(record.get(GSK_ID), new HashMap<>()); - weightPerNodePerGsk.get(record.get(GSK_ID)).put(record.get("Node"), parseDoubleWithPossibleCommas(record.get("Weight"))); - }); - } - staticCsvRecords.forEach(staticRecord -> { - if (shouldBeImported(staticRecord, weightPerNodePerGsk)) { - String raId = staticRecord.get(RA_RD_ID); - Map seriesPerType = seriesPerIdAndType.get(raId); - if (seriesPerType != null && - seriesPerType.containsKey(P0) && - seriesPerType.containsKey(RDP_DOWN) && - seriesPerType.containsKey(RDP_UP) && - rangeIsOkay(seriesPerType, timeCoupledRaoInput.getTimestampsToRun().stream().sorted().toList()) && - p0RespectsGradients(staticRecord, seriesPerType.get(P0), timeCoupledRaoInput.getTimestampsToRun().stream().sorted().toList())) { - if (staticRecord.get(RD_DESCRIPTION_MODE).equalsIgnoreCase(NODE)) { - importNodeRedispatchingAction(timeCoupledRaoInput, staticRecord, initialNetworks, seriesPerType, raId); - } else { - importGskRedispatchingAction(timeCoupledRaoInput, staticRecord, initialNetworks, seriesPerType, raId, weightPerNodePerGsk.get(staticRecord.get("UCT Node or GSK ID"))); - } - } + // Create redispatching actions in crac, generator in network + create generator constraint + + // For each redispatching action defined in static csv + icsData.getStaticConstraintPerId().forEach((raId, staticRecord) -> { + Map seriesPerType = icsData.getTimeseriesPerIdAndType().get(raId); + // If the remedial action is defined on a Node. + if (staticRecord.get(RD_DESCRIPTION_MODE).equalsIgnoreCase(NODE)) { + importNodeRedispatchingAction(timeCoupledRaoInput, staticRecord, modifiedInitialNetworks, seriesPerType, raId); + } else { // If the remedial action is defined on a GSK + Map weightPerNode = icsData.getWeightPerNodePerGsk().get(staticRecord.get("UCT Node or GSK ID")); + importGskRedispatchingAction(timeCoupledRaoInput, staticRecord, modifiedInitialNetworks, seriesPerType, raId, weightPerNode); } }); - initialNetworks.getDataPerTimestamp().forEach((dateTime, initialNetwork) -> - initialNetwork.write("JIIDM", new Properties(), Path.of(timeCoupledRaoInput.getRaoInputs().getData(dateTime).orElseThrow().getPostIcsImportNetworkPath()))); + // Save update networks (with corrected nominal voltage + newly created generator/load) + modifiedInitialNetworks.getDataPerTimestamp().forEach((dateTime, initialNetwork) -> + initialNetwork.write("JIIDM", new Properties(), Path.of(timeCoupledRaoInput.getRaoInputs().getData(dateTime).orElseThrow().getPostIcsImportNetworkPath())) + ); + } - private static void preProcessNetwork(Network network) { + + // By default, UCTE sets nominal voltage to 220 and 380kV for the voltage levels 6 and 7, + // whereas default values of Core countries are 225 and 400 kV instead. + // The preprocessor updates the nominal voltage levels to these values. + private static void updateNominalVoltage(Network network) { network.getVoltageLevelStream().forEach(voltageLevel -> { if (safeDoubleEquals(voltageLevel.getNominalV(), 380)) { voltageLevel.setNominalV(400); @@ -162,57 +129,25 @@ private static void importGskRedispatchingAction(TimeCoupledRaoInputWithNetworkP Map seriesPerType, String raId, Map weightPerNode) { + + // Create generator Map networkElementPerGskElement = new HashMap<>(); for (String nodeId : weightPerNode.keySet()) { - String networkElementId = processNetworks(nodeId, initialNetworks, seriesPerType, weightPerNode.get(nodeId)); + String networkElementId = createGeneratorAndLoadInNetworks(nodeId, initialNetworks, seriesPerType, weightPerNode.get(nodeId)); if (networkElementId == null) { return; } networkElementPerGskElement.put(nodeId, networkElementId); } + // Create redispatching actions in crac timeCoupledRaoInput.getRaoInputs().getDataPerTimestamp().forEach((dateTime, raoInput) -> { importGskRedispatchActionForOneTimestamp(staticRecord, seriesPerType, raId, weightPerNode, dateTime, raoInput, networkElementPerGskElement); }); - - for (Map.Entry entry : weightPerNode.entrySet()) { - String nodeId = entry.getKey(); - Double shiftKey = entry.getValue(); - String networkElementId = networkElementPerGskElement.get(nodeId); - // only create constraints if the network element is a generator - GeneratorConstraints.GeneratorConstraintsBuilder builder = GeneratorConstraints.create().withGeneratorId(networkElementId); - if (!staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT).isEmpty()) { - builder.withUpwardPowerGradient(shiftKey * parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT))); - } else { - builder.withUpwardPowerGradient(shiftKey * MAX_GRADIENT); - } - if (!staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT).isEmpty()) { - builder.withDownwardPowerGradient(-shiftKey * parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT))); - } else { - builder.withDownwardPowerGradient(-shiftKey * MAX_GRADIENT); - } - if (!staticRecord.get(LEAD_TIME).isEmpty()) { - builder.withLeadTime(parseDoubleWithPossibleCommas(staticRecord.get(LEAD_TIME))); - } - if (!staticRecord.get(LAG_TIME).isEmpty()) { - builder.withLagTime(parseDoubleWithPossibleCommas(staticRecord.get(LAG_TIME))); - } - if (staticRecord.get(SHUTDOWN_ALLOWED).isEmpty() || - !staticRecord.get(SHUTDOWN_ALLOWED).equalsIgnoreCase(TRUE) && !staticRecord.get(SHUTDOWN_ALLOWED).equalsIgnoreCase(FALSE)) { - throw new OpenRaoException("Could not parse shutDownAllowed value " + staticRecord.get(SHUTDOWN_ALLOWED) + " for nodeId " + nodeId); - } else { - builder.withShutDownAllowed(Boolean.parseBoolean(staticRecord.get(SHUTDOWN_ALLOWED))); - } - if (staticRecord.get(STARTUP_ALLOWED).isEmpty() || - !staticRecord.get(STARTUP_ALLOWED).equalsIgnoreCase(TRUE) && !staticRecord.get(STARTUP_ALLOWED).equalsIgnoreCase(FALSE)) { - throw new OpenRaoException("Could not parse startUpAllowed value " + staticRecord.get(STARTUP_ALLOWED) + " for nodeId " + nodeId); - } else { - builder.withStartUpAllowed(Boolean.parseBoolean(staticRecord.get(STARTUP_ALLOWED))); - } - timeCoupledRaoInput.getTimeCoupledConstraints().addGeneratorConstraints(builder.build()); - } } + + // TODO: put in common ? with importNodeRedispatchingAction private static void importGskRedispatchActionForOneTimestamp(CSVRecord staticRecord, Map seriesPerType, String raId, @@ -228,7 +163,6 @@ private static void importGskRedispatchActionForOneTimestamp(CSVRecord staticRec .withInitialSetpoint(p0) .withVariationCost(costUp, VariationDirection.UP) .withVariationCost(costDown, VariationDirection.DOWN) - //.withActivationCost(ACTIVATION_COST) .newRange() .withMin(p0 - parseDoubleWithPossibleCommas(seriesPerType.get(RDP_DOWN).get(dateTime.getHour() + OFFSET))) .withMax(p0 + parseDoubleWithPossibleCommas(seriesPerType.get(RDP_UP).get(dateTime.getHour() + OFFSET))) @@ -257,81 +191,24 @@ private static void importNodeRedispatchingAction(TimeCoupledRaoInputWithNetwork TemporalData initialNetworks, Map seriesPerType, String raId) { - String networkElementId = processNetworks(staticRecord.get(UCT_NODE_OR_GSK_ID), initialNetworks, seriesPerType, 1.); + + // Create generator + String networkElementId = createGeneratorAndLoadInNetworks(staticRecord.get(UCT_NODE_OR_GSK_ID), initialNetworks, seriesPerType, 1.); if (networkElementId == null) { return; } + + // Create injection range action in crac timeCoupledRaoInput.getRaoInputs().getDataPerTimestamp().forEach((dateTime, raoInput) -> { - importNodeRedispatchingActionForOneTimestamp(staticRecord, seriesPerType, raId, dateTime, raoInput, networkElementId); + + importGskRedispatchActionForOneTimestamp(staticRecord, seriesPerType, raId, Map.of(networkElementId, 1.0), dateTime, raoInput, Map.of(networkElementId, networkElementId)); }); - GeneratorConstraints.GeneratorConstraintsBuilder builder = GeneratorConstraints.create().withGeneratorId(networkElementId); - if (!staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT).isEmpty()) { - builder.withUpwardPowerGradient(parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT))); - } else { - builder.withUpwardPowerGradient(MAX_GRADIENT); - } - if (!staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT).isEmpty()) { - builder.withDownwardPowerGradient(-parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT))); - } else { - builder.withDownwardPowerGradient(-MAX_GRADIENT); - } - if (!staticRecord.get(LEAD_TIME).isEmpty()) { - builder.withLeadTime(parseDoubleWithPossibleCommas(staticRecord.get(LEAD_TIME))); - } - if (!staticRecord.get(LAG_TIME).isEmpty()) { - builder.withLagTime(parseDoubleWithPossibleCommas(staticRecord.get(LAG_TIME))); - } - if (staticRecord.get(SHUTDOWN_ALLOWED).isEmpty() || - !staticRecord.get(SHUTDOWN_ALLOWED).equalsIgnoreCase(TRUE) && !staticRecord.get(SHUTDOWN_ALLOWED).equalsIgnoreCase(FALSE)) { - throw new OpenRaoException("Could not parse shutDownAllowed value " + staticRecord.get(SHUTDOWN_ALLOWED) + " for raId " + raId); - } else { - builder.withShutDownAllowed(Boolean.parseBoolean(staticRecord.get(SHUTDOWN_ALLOWED))); - } - if (staticRecord.get(STARTUP_ALLOWED).isEmpty() || - !staticRecord.get(STARTUP_ALLOWED).equalsIgnoreCase(TRUE) && !staticRecord.get(STARTUP_ALLOWED).equalsIgnoreCase(FALSE)) { - throw new OpenRaoException("Could not parse startUpAllowed value " + staticRecord.get(STARTUP_ALLOWED) + " for raId " + raId); - } else { - builder.withStartUpAllowed(Boolean.parseBoolean(staticRecord.get(STARTUP_ALLOWED))); - } - timeCoupledRaoInput.getTimeCoupledConstraints().addGeneratorConstraints(builder.build()); } - private static void importNodeRedispatchingActionForOneTimestamp(CSVRecord staticRecord, - Map seriesPerType, - String raId, - OffsetDateTime dateTime, - RaoInputWithNetworkPaths raoInput, - String networkElementId) { - Crac crac = raoInput.getCrac(); - double p0 = parseDoubleWithPossibleCommas(seriesPerType.get(P0).get(dateTime.getHour() + OFFSET)); - InjectionRangeActionAdder injectionRangeActionAdder = crac.newInjectionRangeAction() - .withId(raId + RD_SUFFIX) - .withName(staticRecord.get(GENERATOR_NAME)) - .withNetworkElement(networkElementId) - .withInitialSetpoint(p0) - .withVariationCost(costUp, VariationDirection.UP) - .withVariationCost(costDown, VariationDirection.DOWN) - //.withActivationCost(ACTIVATION_COST) - .newRange() - .withMin(p0 - parseDoubleWithPossibleCommas(seriesPerType.get(RDP_DOWN).get(dateTime.getHour() + OFFSET))) - .withMax(p0 + parseDoubleWithPossibleCommas(seriesPerType.get(RDP_UP).get(dateTime.getHour() + OFFSET))) - .add(); - if (staticRecord.get(PREVENTIVE).equalsIgnoreCase(TRUE)) { - injectionRangeActionAdder.newOnInstantUsageRule() - .withInstant(crac.getPreventiveInstant().getId()) - .add(); - } - if (importCurative && staticRecord.get(CURATIVE).equalsIgnoreCase(TRUE)) { - injectionRangeActionAdder.newOnInstantUsageRule() - .withInstant(crac.getLastInstant().getId()) - .add(); - } - - injectionRangeActionAdder.add(); - } - private static String processNetworks(String nodeId, TemporalData initialNetworks, Map seriesPerType, double shiftKey) { + // modify bus, create generator + private static String createGeneratorAndLoadInNetworks(String nodeId, TemporalData initialNetworks, Map seriesPerType, double shiftKey) { String generatorId = seriesPerType.get(P0).get(RA_RD_ID) + "_" + nodeId + GENERATOR_SUFFIX; for (Map.Entry entry : initialNetworks.getDataPerTimestamp().entrySet()) { Bus bus = findBus(nodeId, entry.getValue()); @@ -398,51 +275,6 @@ private static void processBus(Bus bus, String generatorId, Double p0, double pM .add(); } - private static boolean shouldBeImported(CSVRecord staticRecord, Map> weightPerNodePerGsk) { - return (staticRecord.get(RD_DESCRIPTION_MODE).equalsIgnoreCase(NODE) || weightPerNodePerGsk.containsKey(staticRecord.get(UCT_NODE_OR_GSK_ID))) && - (staticRecord.get(PREVENTIVE).equalsIgnoreCase(TRUE) /*|| staticRecord.get(CURATIVE).equalsIgnoreCase(TRUE)*/); - } - - private static boolean p0RespectsGradients(CSVRecord staticRecord, CSVRecord p0record, List dateTimes) { - double maxGradient = staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT).isEmpty() ? - MAX_GRADIENT : parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT)); - double minGradient = staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT).isEmpty() ? - -MAX_GRADIENT : -parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT)); - - Iterator dateTimeIterator = dateTimes.iterator(); - OffsetDateTime currentDateTime = dateTimeIterator.next(); - while (dateTimeIterator.hasNext()) { - OffsetDateTime nextDateTime = dateTimeIterator.next(); - double diff = parseDoubleWithPossibleCommas(p0record.get(nextDateTime.getHour() + OFFSET)) - parseDoubleWithPossibleCommas(p0record.get(currentDateTime.getHour() + OFFSET)); - if (diff > maxGradient || diff < minGradient) { - BUSINESS_WARNS.warn( - "Redispatching action {} will not be imported because it does not respect power gradients : min/max/diff {} {} {}", - staticRecord.get(0), minGradient, maxGradient, diff - ); - return false; - } - currentDateTime = nextDateTime; - } - return true; - } - - private static boolean rangeIsOkay(Map seriesPerType, List dateTimes) { - double maxRange = 0.; - for (OffsetDateTime dateTime : dateTimes) { - double rdpPlus = parseDoubleWithPossibleCommas(seriesPerType.get(RDP_UP).get(dateTime.getHour() + OFFSET)); - double rdpMinus = parseDoubleWithPossibleCommas(seriesPerType.get(RDP_DOWN).get(dateTime.getHour() + OFFSET)); - maxRange = Math.max(maxRange, rdpPlus + rdpMinus); - if (rdpPlus < -1e-6 || rdpMinus < -1e-6) { - BUSINESS_WARNS.warn("Redispatching action {} will not be imported because of RDP+ {} or RDP- {} is negative", seriesPerType.get(P0).get(RA_RD_ID), rdpPlus, rdpMinus); - return false; - } - } - if (maxRange < 1) { - BUSINESS_WARNS.warn("Redispatching action {} will not be imported because max range in the day {} MW is too small", seriesPerType.get(P0).get(RA_RD_ID), maxRange); - return false; - } - return true; - } private static double parseDoubleWithPossibleCommas(String string) { return Double.parseDouble(string.replaceAll(",", ".")); diff --git a/data/ics-importer/pom.xml b/data/ics-importer/pom.xml new file mode 100644 index 0000000000..fe62f3dedc --- /dev/null +++ b/data/ics-importer/pom.xml @@ -0,0 +1,34 @@ + + + 4.0.0 + + com.powsybl + open-rao-data + 7.2.0-SNAPSHOT + + + ics-importer + + + 21 + 21 + UTF-8 + + + + com.powsybl + open-rao-rao-api + + + org.apache.commons + commons-csv + + + org.apache.commons + commons-csv + + + + \ No newline at end of file diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java new file mode 100644 index 0000000000..45f318c360 --- /dev/null +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2026, 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; + +import com.powsybl.openrao.commons.OpenRaoException; +import com.powsybl.openrao.data.timecoupledconstraints.GeneratorConstraints; +import org.apache.commons.csv.CSVRecord; + +import java.util.*; + + +/** + * @author Roxane Chen {@literal } + */ +public final class IcsData { + + // TODO: put value in common + + private static final double MAX_GRADIENT = 1000.0; + + public static final String MAXIMUM_POSITIVE_POWER_GRADIENT = "Maximum positive power gradient [MW/h]"; + public static final String MAXIMUM_NEGATIVE_POWER_GRADIENT = "Maximum negative power gradient [MW/h]"; + + public static final String P0 = "P0"; + public static final String UCT_NODE_OR_GSK_ID = "UCT Node or GSK ID"; + public static final String PREVENTIVE = "Preventive"; + public static final String TRUE = "TRUE"; + public static final String RD_DESCRIPTION_MODE = "RD description mode"; + public static final String NODE = "NODE"; + public static final String LEAD_TIME = "Lead time [h]"; + public static final String LAG_TIME = "Lag time [h]"; + public static final String STARTUP_ALLOWED = "Startup allowed"; + public static final String SHUTDOWN_ALLOWED = "Shutdown allowed"; + + public static final String GENERATOR_SUFFIX = "_GENERATOR"; + + + private static Map> timeseriesPerIdAndType; + private static Map> weightPerNodePerGsk; + private Map staticConstraintPerId; + + public IcsData(Map> timeseriesPerIdAndType, + Map> weightPerNodePerGsk, + Map staticConstraintPerId) { + this.staticConstraintPerId = staticConstraintPerId; + this.timeseriesPerIdAndType = timeseriesPerIdAndType; + this.weightPerNodePerGsk = weightPerNodePerGsk; + } + + // Define getters + + public Map getStaticConstraintPerId() { + return staticConstraintPerId; + } + + public Map> getTimeseriesPerIdAndType() { + return timeseriesPerIdAndType; + } + + public Map> getWeightPerNodePerGsk() { + return weightPerNodePerGsk; + } + + public String getGeneratorIdFromRaIdAndNodeId(String raId, String nodeId) { + return raId + "_" + nodeId + GENERATOR_SUFFIX; + } + + public Set getGeneratorConstraints() { + Set generatorConstraintsSet = new HashSet<>(); + staticConstraintPerId.forEach((raId, staticRecord) -> { + // If the remedial action is defined on a Node. + if (staticRecord.get(RD_DESCRIPTION_MODE).equalsIgnoreCase(NODE)) { + // create a generator constraint from staticRecord + String networkElementId = getGeneratorIdFromRaIdAndNodeId(raId, staticRecord.get(UCT_NODE_OR_GSK_ID)); + GeneratorConstraints generatorConstraints = createGeneratorConstraintFromStaticRecord(networkElementId, staticRecord, 1.0); + generatorConstraintsSet.add(generatorConstraints); + } else { // If the remedial action is defined on a GSK + // For a given GSK, create a generator constraint for each node of the GSK according to the shiftKey + Map weightPerNode = weightPerNodePerGsk.get(staticRecord.get(UCT_NODE_OR_GSK_ID)); + for (Map.Entry entry : weightPerNode.entrySet()) { + String nodeId = entry.getKey(); + Double shiftKey = entry.getValue(); + String networkElementId = getGeneratorIdFromRaIdAndNodeId(raId, nodeId); + GeneratorConstraints generatorConstraints = createGeneratorConstraintFromStaticRecord(networkElementId, staticRecord, shiftKey); + generatorConstraintsSet.add(generatorConstraints); + } + } + }); + + return generatorConstraintsSet; + } + + + private static GeneratorConstraints createGeneratorConstraintFromStaticRecord(String networkElementId, + CSVRecord staticRecord, + Double shiftKey) { + + GeneratorConstraints.GeneratorConstraintsBuilder builder = GeneratorConstraints.create().withGeneratorId(networkElementId); + if (!staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT).isEmpty()) { + builder.withUpwardPowerGradient(shiftKey * parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT))); + } else { + builder.withUpwardPowerGradient(shiftKey * MAX_GRADIENT); + } + if (!staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT).isEmpty()) { + builder.withDownwardPowerGradient(-shiftKey * parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT))); + } else { + builder.withDownwardPowerGradient(-shiftKey * MAX_GRADIENT); + } + if (!staticRecord.get(LEAD_TIME).isEmpty()) { + builder.withLeadTime(parseDoubleWithPossibleCommas(staticRecord.get(LEAD_TIME))); + } + if (!staticRecord.get(LAG_TIME).isEmpty()) { + builder.withLagTime(parseDoubleWithPossibleCommas(staticRecord.get(LAG_TIME))); + } + if (staticRecord.get(SHUTDOWN_ALLOWED).isEmpty() || + !staticRecord.get(SHUTDOWN_ALLOWED).equalsIgnoreCase(TRUE) && !staticRecord.get(SHUTDOWN_ALLOWED).equalsIgnoreCase(FALSE)) { + throw new OpenRaoException("Could not parse shutDownAllowed value " + staticRecord.get(SHUTDOWN_ALLOWED) + " for nodeId " + nodeId); + } else { + builder.withShutDownAllowed(Boolean.parseBoolean(staticRecord.get(SHUTDOWN_ALLOWED))); + } + if (staticRecord.get(STARTUP_ALLOWED).isEmpty() || + !staticRecord.get(STARTUP_ALLOWED).equalsIgnoreCase(TRUE) && !staticRecord.get(STARTUP_ALLOWED).equalsIgnoreCase(FALSE)) { + throw new OpenRaoException("Could not parse startUpAllowed value " + staticRecord.get(STARTUP_ALLOWED) + " for nodeId " + nodeId); + } else { + builder.withStartUpAllowed(Boolean.parseBoolean(staticRecord.get(STARTUP_ALLOWED))); + } + return builder.build(); + } + + private static double parseDoubleWithPossibleCommas(String string) { + return Double.parseDouble(string.replaceAll(",", ".")); + } + +} \ No newline at end of file diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java new file mode 100644 index 0000000000..d8a9ef4c64 --- /dev/null +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2026, 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; + +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.*; + +import static com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider.BUSINESS_WARNS; + +/** + * @author Roxane Chen {@literal } + */ +public class IcsDataImporter { + + private static final int OFFSET = 2; + private static final double MAX_GRADIENT = 1000.0; + + public static final String MAXIMUM_POSITIVE_POWER_GRADIENT = "Maximum positive power gradient [MW/h]"; + public static final String MAXIMUM_NEGATIVE_POWER_GRADIENT = "Maximum negative power gradient [MW/h]"; + public static final String RA_RD_ID = "RA RD ID"; + public static final String RDP_UP = "RDP+"; + public static final String RDP_DOWN = "RDP-"; + public static final String P_MIN_RD = "Pmin_RD"; + public static final String P0 = "P0"; + public static final String UCT_NODE_OR_GSK_ID = "UCT Node or GSK ID"; + public static final String GSK_ID = "GSK ID"; + public static final String PREVENTIVE = "Preventive"; + public static final String TRUE = "TRUE"; + public static final String RD_DESCRIPTION_MODE = "RD description mode"; + public static final String NODE = "NODE"; + + public IcsDataImporter() { + // only static use + } + + public IcsData read(InputStream staticInputStream, + InputStream seriesInputStream, + InputStream gskInputStream, + List sortedTimestampToRun) throws IOException { + + CSVFormat csvFormat = CSVFormat.DEFAULT.builder() + .setDelimiter(";") + .setHeader() + .setSkipHeaderRecord(true) + .get(); + + // Parse and sort per RA_ID and serie type (RDP-, RDP+, Pmin_RD or P0) + Map> timeseriesPerIdAndType = parseSeriesCsv(csvFormat, seriesInputStream); + // Parse GSK and get weight Per Node Per Gsk + Map> weightPerNodePerGsk = parseGskCsv(csvFormat, gskInputStream); + // Parse static CSV: remedial action’s generator’s static constraints. one line per RA_ID + Map staticConstraintPerId = parseAndFilterStaticCsv(csvFormat, staticInputStream, sortedTimestampToRun, weightPerNodePerGsk, timeseriesPerIdAndType); + + return new IcsData(timeseriesPerIdAndType, weightPerNodePerGsk, staticConstraintPerId); + + } + + Map parseAndFilterStaticCsv(CSVFormat csvFormat, InputStream staticInputStream, List sortedTimestampToRun, Map> weightPerNodePerGsk, Map> timeseriesPerIdAndType) throws IOException { + Iterable staticCsvRecords = csvFormat.parse(new InputStreamReader(staticInputStream)); + Map filteredStaticCsvRecords = new HashMap<>(); + staticCsvRecords.forEach(record -> { + if (shouldBeImported(record, sortedTimestampToRun, weightPerNodePerGsk, timeseriesPerIdAndType)) { + filteredStaticCsvRecords.put(record.get(RA_RD_ID), record); + } + }); + return filteredStaticCsvRecords; + } + + private static Map> parseSeriesCsv(CSVFormat csvFormat, InputStream seriesInputStream) throws IOException { + 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); + }); + + return seriesPerIdAndType; + } + + private static boolean shouldBeImported(CSVRecord staticRecord, List sortedTimestampToRun, Map> weightPerNodePerGsk, Map> timeseriesPerIdAndType) { + //TODO: add more consistency checks ? + // - check that P0s respect the min/max gradients + + // remedial action is defined on preventive instant + boolean isPreventive = staticRecord.get(PREVENTIVE).equalsIgnoreCase(TRUE); + + // remedial action is defined on a node or a gsk + boolean isDefinedOnANodeOrGsk = staticRecord.get(RD_DESCRIPTION_MODE).equalsIgnoreCase(NODE) || weightPerNodePerGsk.containsKey(staticRecord.get(UCT_NODE_OR_GSK_ID)); + + String raId = staticRecord.get(RA_RD_ID); + Map seriesPerType = timeseriesPerIdAndType.get(raId); + + // is correctly defined in series csv + boolean isDefinedInSeriesCsv = seriesPerType != null && + seriesPerType.containsKey(P0) && + seriesPerType.containsKey(RDP_DOWN) && + seriesPerType.containsKey(RDP_UP) && + seriesPerType.containsKey(P_MIN_RD); + + boolean rangeIsOkay = rangeIsOkay(seriesPerType, sortedTimestampToRun); + boolean p0RespectsGradients = p0RespectsGradients(staticRecord, seriesPerType.get(P0), sortedTimestampToRun); + + return isDefinedOnANodeOrGsk && isPreventive && isDefinedInSeriesCsv && rangeIsOkay && p0RespectsGradients; + } + + private static boolean p0RespectsGradients(CSVRecord staticRecord, CSVRecord p0record, List dateTimes) { + double maxGradient = staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT).isEmpty() ? + MAX_GRADIENT : parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT)); + double minGradient = staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT).isEmpty() ? + -MAX_GRADIENT : -parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT)); + + Iterator dateTimeIterator = dateTimes.iterator(); + OffsetDateTime currentDateTime = dateTimeIterator.next(); + while (dateTimeIterator.hasNext()) { + OffsetDateTime nextDateTime = dateTimeIterator.next(); + double diff = parseDoubleWithPossibleCommas(p0record.get(nextDateTime.getHour() + OFFSET)) - parseDoubleWithPossibleCommas(p0record.get(currentDateTime.getHour() + OFFSET)); + if (diff > maxGradient || diff < minGradient) { + BUSINESS_WARNS.warn( + "Redispatching action {} will not be imported because it does not respect power gradients : min/max/diff {} {} {}", + staticRecord.get(0), minGradient, maxGradient, diff + ); + return false; + } + currentDateTime = nextDateTime; + } + return true; + } + + private static boolean rangeIsOkay(Map seriesPerType, List dateTimes) { + double maxRange = 0.; + for (OffsetDateTime dateTime : dateTimes) { + double rdpPlus = parseDoubleWithPossibleCommas(seriesPerType.get(RDP_UP).get(dateTime.getHour() + OFFSET)); + double rdpMinus = parseDoubleWithPossibleCommas(seriesPerType.get(RDP_DOWN).get(dateTime.getHour() + OFFSET)); + maxRange = Math.max(maxRange, rdpPlus + rdpMinus); + if (rdpPlus < -1e-6 || rdpMinus < -1e-6) { + BUSINESS_WARNS.warn("Redispatching action {} will not be imported because of RDP+ {} or RDP- {} is negative", seriesPerType.get(P0).get(RA_RD_ID), rdpPlus, rdpMinus); + return false; + } + } + if (maxRange < 1) { + BUSINESS_WARNS.warn("Redispatching action {} will not be imported because max range in the day {} MW is too small", seriesPerType.get(P0).get(RA_RD_ID), maxRange); + return false; + } + return true; + } + + private static Map> parseGskCsv(CSVFormat csvFormat, InputStream gskInputStream) throws IOException { + Iterable gskCsvRecords = csvFormat.parse(new InputStreamReader(gskInputStream)); + Map> weightPerNodePerGsk = new HashMap<>(); + gskCsvRecords.forEach(record -> { + weightPerNodePerGsk.putIfAbsent(record.get(GSK_ID), new HashMap<>()); + weightPerNodePerGsk.get(record.get(GSK_ID)).put(record.get("Node"), parseDoubleWithPossibleCommas(record.get("Weight"))); + }); + + return weightPerNodePerGsk; + } + + private static double parseDoubleWithPossibleCommas(String string) { + return Double.parseDouble(string.replaceAll(",", ".")); + } +} \ No newline at end of file diff --git a/data/pom.xml b/data/pom.xml index ff6dde5117..39ea3b5748 100755 --- a/data/pom.xml +++ b/data/pom.xml @@ -23,5 +23,6 @@ time-coupled-constraints virtual-hubs crac/crac-io/crac-io-network + ics-importer diff --git a/data/time-coupled-constraints/src/main/java/com/powsybl/openrao/data/timecoupledconstraints/TimeCoupledConstraints.java b/data/time-coupled-constraints/src/main/java/com/powsybl/openrao/data/timecoupledconstraints/TimeCoupledConstraints.java index 6d94d9345b..df56f40966 100644 --- a/data/time-coupled-constraints/src/main/java/com/powsybl/openrao/data/timecoupledconstraints/TimeCoupledConstraints.java +++ b/data/time-coupled-constraints/src/main/java/com/powsybl/openrao/data/timecoupledconstraints/TimeCoupledConstraints.java @@ -20,6 +20,10 @@ public TimeCoupledConstraints() { this.generatorConstraints = new HashSet<>(); } + public TimeCoupledConstraints(Set generatorConstraints) { + this.generatorConstraints = generatorConstraints; + } + public void addGeneratorConstraints(GeneratorConstraints generatorConstraints) { this.generatorConstraints.add(generatorConstraints); } diff --git a/docs/input-data/specific-input-data/ics.md b/docs/input-data/specific-input-data/ics.md index 4af57d513f..f300e08c4a 100644 --- a/docs/input-data/specific-input-data/ics.md +++ b/docs/input-data/specific-input-data/ics.md @@ -42,4 +42,14 @@ RDP+, positive values), and the Pmin of redispatching (Pmin_RD). These values ar | Node | UCT code of the node described with 8 characters. | | Weight | Weight for GSK at respective node. Sum of weights for all nodes in one GSK should be 1. | -![ICS Importer](../../_static/img/ics-importer.png) \ No newline at end of file +![ICS Importer](../../_static/img/ics-importer.png) + +## Import TimeCoupledConstraints using ICS data + +```java +InputStream staticIcs = new FileInputStream("path/to/ics/static.csv"); +InputStream seriesIcs = new FileInputStream("path/to/ics/series.csv"); +InputStream gskIcs = new FileInputStream("path/to/ics/gsk.csv"); +IcsData icsData = new IcsDataImporter.read(staticIcs, seriesIcs, gskIcs); +TimeCoupledConstraints timeCoupledConstraints = new TimeCoupledConstraints(icsData.getGeneratorConstraints()); +``` \ No newline at end of file diff --git a/docs/input-data/specific-input-data/time-coupled-constraints.md b/docs/input-data/specific-input-data/time-coupled-constraints.md index 6a08ebc932..c27daf60d9 100644 --- a/docs/input-data/specific-input-data/time-coupled-constraints.md +++ b/docs/input-data/specific-input-data/time-coupled-constraints.md @@ -80,4 +80,4 @@ that generator can be started up. ```java TimeCoupledConstraints timeCoupledConstraints = JsonTimeCoupledConstraints.read(getClass().getResourceAsStream("/time-coupled-constraints.json")); -``` \ No newline at end of file +``` From e13e8ec27bdd146f72f51eb64dbf000ee5db4ae0 Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Wed, 18 Mar 2026 18:13:30 +0100 Subject: [PATCH 02/64] first version of the code Signed-off-by: CHEN Roxane --- .../openrao/data/crac/util/IcsImporter.java | 282 ----------------- .../com/powsybl/openrao/data/IcsData.java | 289 +++++++++++++++--- .../powsybl/openrao/data/IcsDataImporter.java | 173 ----------- .../com/powsybl/openrao/data/IcsUtil.java | 130 ++++++++ .../tests/steps/TimeCoupledRaoSteps.java | 61 +++- 5 files changed, 429 insertions(+), 506 deletions(-) delete mode 100644 data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java delete mode 100644 data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java create mode 100644 data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java diff --git a/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java b/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java deleted file mode 100644 index 21e5ea445c..0000000000 --- a/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright (c) 2025, 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.crac.util; - -import com.powsybl.iidm.network.Bus; -import com.powsybl.iidm.network.LoadType; -import com.powsybl.iidm.network.Network; -import com.powsybl.openrao.commons.OpenRaoException; -import com.powsybl.openrao.commons.TemporalData; -import com.powsybl.openrao.commons.TemporalDataImpl; -import com.powsybl.openrao.data.IcsData; -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.timecoupledconstraints.GeneratorConstraints; -import com.powsybl.openrao.raoapi.RaoInputWithNetworkPaths; -import com.powsybl.openrao.raoapi.TimeCoupledRaoInputWithNetworkPaths; -import org.apache.commons.csv.CSVRecord; - -import java.nio.file.Path; -import java.time.OffsetDateTime; -import java.util.*; - -import static com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider.BUSINESS_WARNS; - -/** - * @author Roxane Chen {@literal } - */ -public final class IcsImporter { - private static final int OFFSET = 2; - private static final double MAX_GRADIENT = 1000.0; - private static final double ON_POWER_THRESHOLD = 1.001; // TODO: mutualize with value from linear problem - - // TODO : either parametrize this or set it to true. May have to change the way it works to import for all curative instants instead of only the last one - public static boolean importCurative = false; - private static double costUp; - private static double costDown; - - // Quality checks (or don't import the action at all): - // TODO in future versions: - // - check other implemented constraints - // - check consistency between ics files, particularly w.r.t gsk file - - // INFOS - // Gradient constraints are defined for gsks at the action level and not per group : we translate it to the groups using the shift keys - - public static final String P_MIN_RD = "Pmin_RD"; - public static final String RA_RD_ID = "RA RD ID"; - public static final String RDP_UP = "RDP+"; - public static final String RDP_DOWN = "RDP-"; - public static final String P0 = "P0"; - public static final String UCT_NODE_OR_GSK_ID = "UCT Node or GSK ID"; - public static final String PREVENTIVE = "Preventive"; - public static final String CURATIVE = "Curative"; - public static final String TRUE = "TRUE"; - public static final String FALSE = "FALSE"; - public static final String RD_DESCRIPTION_MODE = "RD description mode"; - public static final String NODE = "NODE"; - public static final String GENERATOR_NAME = "Generator Name"; - public static final String RD_SUFFIX = "_RD"; - public static final String GENERATOR_SUFFIX = "_GENERATOR"; - - private IcsImporter() { - //should only be used statically - } - - public static void populateInputWithICS(TimeCoupledRaoInputWithNetworkPaths timeCoupledRaoInput, - IcsData icsData, - double icsCostUp, - double icsCostDown) { - costUp = icsCostUp; - costDown = icsCostDown; - - TemporalData modifiedInitialNetworks = new TemporalDataImpl<>(); - timeCoupledRaoInput.getRaoInputs().getDataPerTimestamp().forEach((dateTime, raoInput) -> { - Network network = Network.read(raoInput.getInitialNetworkPath()); - updateNominalVoltage(network); - modifiedInitialNetworks.put(dateTime, network); - }); - - // Create redispatching actions in crac, generator in network + create generator constraint - - // For each redispatching action defined in static csv - icsData.getStaticConstraintPerId().forEach((raId, staticRecord) -> { - Map seriesPerType = icsData.getTimeseriesPerIdAndType().get(raId); - // If the remedial action is defined on a Node. - if (staticRecord.get(RD_DESCRIPTION_MODE).equalsIgnoreCase(NODE)) { - importNodeRedispatchingAction(timeCoupledRaoInput, staticRecord, modifiedInitialNetworks, seriesPerType, raId); - } else { // If the remedial action is defined on a GSK - Map weightPerNode = icsData.getWeightPerNodePerGsk().get(staticRecord.get("UCT Node or GSK ID")); - importGskRedispatchingAction(timeCoupledRaoInput, staticRecord, modifiedInitialNetworks, seriesPerType, raId, weightPerNode); - } - }); - - // Save update networks (with corrected nominal voltage + newly created generator/load) - modifiedInitialNetworks.getDataPerTimestamp().forEach((dateTime, initialNetwork) -> - initialNetwork.write("JIIDM", new Properties(), Path.of(timeCoupledRaoInput.getRaoInputs().getData(dateTime).orElseThrow().getPostIcsImportNetworkPath())) - ); - - } - - - // By default, UCTE sets nominal voltage to 220 and 380kV for the voltage levels 6 and 7, - // whereas default values of Core countries are 225 and 400 kV instead. - // The preprocessor updates the nominal voltage levels to these values. - private static void updateNominalVoltage(Network network) { - network.getVoltageLevelStream().forEach(voltageLevel -> { - if (safeDoubleEquals(voltageLevel.getNominalV(), 380)) { - voltageLevel.setNominalV(400); - } else if (safeDoubleEquals(voltageLevel.getNominalV(), 220)) { - voltageLevel.setNominalV(225); - } - // Else, Should not be changed cause is not equal to the default nominal voltage of voltage levels 6 or 7 - }); - } - - private static boolean safeDoubleEquals(double a, double b) { - return Math.abs(a - b) < 1e-3; - } - - private static void importGskRedispatchingAction(TimeCoupledRaoInputWithNetworkPaths timeCoupledRaoInput, - CSVRecord staticRecord, - TemporalData initialNetworks, - Map seriesPerType, - String raId, - Map weightPerNode) { - - // Create generator - Map networkElementPerGskElement = new HashMap<>(); - for (String nodeId : weightPerNode.keySet()) { - String networkElementId = createGeneratorAndLoadInNetworks(nodeId, initialNetworks, seriesPerType, weightPerNode.get(nodeId)); - if (networkElementId == null) { - return; - } - networkElementPerGskElement.put(nodeId, networkElementId); - } - - // Create redispatching actions in crac - timeCoupledRaoInput.getRaoInputs().getDataPerTimestamp().forEach((dateTime, raoInput) -> { - importGskRedispatchActionForOneTimestamp(staticRecord, seriesPerType, raId, weightPerNode, dateTime, raoInput, networkElementPerGskElement); - }); - } - - - // TODO: put in common ? with importNodeRedispatchingAction - private static void importGskRedispatchActionForOneTimestamp(CSVRecord staticRecord, - Map seriesPerType, - String raId, - Map weightPerNode, - OffsetDateTime dateTime, - RaoInputWithNetworkPaths raoInput, - Map networkElementPerGskElement) { - Crac crac = raoInput.getCrac(); - double p0 = parseDoubleWithPossibleCommas(seriesPerType.get(P0).get(dateTime.getHour() + OFFSET)); - InjectionRangeActionAdder injectionRangeActionAdder = crac.newInjectionRangeAction() - .withId(raId + RD_SUFFIX) - .withName(staticRecord.get(GENERATOR_NAME)) - .withInitialSetpoint(p0) - .withVariationCost(costUp, VariationDirection.UP) - .withVariationCost(costDown, VariationDirection.DOWN) - .newRange() - .withMin(p0 - parseDoubleWithPossibleCommas(seriesPerType.get(RDP_DOWN).get(dateTime.getHour() + OFFSET))) - .withMax(p0 + parseDoubleWithPossibleCommas(seriesPerType.get(RDP_UP).get(dateTime.getHour() + OFFSET))) - .add(); - - weightPerNode.forEach((nodeId, shiftKey) -> { - injectionRangeActionAdder.withNetworkElementAndKey(shiftKey, networkElementPerGskElement.get(nodeId)); - }); - - if (staticRecord.get(PREVENTIVE).equalsIgnoreCase(TRUE)) { - injectionRangeActionAdder.newOnInstantUsageRule() - .withInstant(crac.getPreventiveInstant().getId()) - .add(); - } - if (importCurative && staticRecord.get(CURATIVE).equalsIgnoreCase(TRUE)) { - injectionRangeActionAdder.newOnInstantUsageRule() - .withInstant(crac.getLastInstant().getId()) - .add(); - } - - injectionRangeActionAdder.add(); - } - - private static void importNodeRedispatchingAction(TimeCoupledRaoInputWithNetworkPaths timeCoupledRaoInput, - CSVRecord staticRecord, - TemporalData initialNetworks, - Map seriesPerType, - String raId) { - - // Create generator - String networkElementId = createGeneratorAndLoadInNetworks(staticRecord.get(UCT_NODE_OR_GSK_ID), initialNetworks, seriesPerType, 1.); - if (networkElementId == null) { - return; - } - - // Create injection range action in crac - timeCoupledRaoInput.getRaoInputs().getDataPerTimestamp().forEach((dateTime, raoInput) -> { - - importGskRedispatchActionForOneTimestamp(staticRecord, seriesPerType, raId, Map.of(networkElementId, 1.0), dateTime, raoInput, Map.of(networkElementId, networkElementId)); - }); - - } - - - // modify bus, create generator - private static String createGeneratorAndLoadInNetworks(String nodeId, TemporalData initialNetworks, Map seriesPerType, double shiftKey) { - String generatorId = seriesPerType.get(P0).get(RA_RD_ID) + "_" + nodeId + GENERATOR_SUFFIX; - for (Map.Entry entry : initialNetworks.getDataPerTimestamp().entrySet()) { - Bus bus = findBus(nodeId, entry.getValue()); - if (bus == null) { - BUSINESS_WARNS.warn("Redispatching action {} cannot be imported because bus {} could not be found", seriesPerType.get(P0).get("RA RD ID"), nodeId); - return null; - } - Double p0 = parseDoubleWithPossibleCommas(seriesPerType.get(P0).get(entry.getKey().getHour() + OFFSET)) * shiftKey; - Optional pMinRd = parseValue(seriesPerType, P_MIN_RD, entry.getKey(), shiftKey); - processBus(bus, generatorId, p0, pMinRd.orElse(ON_POWER_THRESHOLD)); - } - return generatorId; - } - - private static Optional parseValue(Map seriesPerType, String key, OffsetDateTime timestamp, double shiftKey) { - if (seriesPerType.containsKey(key)) { - CSVRecord series = seriesPerType.get(key); - String value = series.get(timestamp.getHour() + OFFSET); - if (value != null) { - return Optional.of(parseDoubleWithPossibleCommas(value) * shiftKey); - } - } - return Optional.empty(); - } - - // TODO: make this more robust (and less UCTE dependent) - private static Bus findBus(String nodeId, Network network) { - // First try to get the bus in bus breaker view - Bus bus = network.getBusBreakerView().getBus(nodeId); - if (bus != null) { - return bus; - } - - // Then, if last char is *, remove it - String modifiedNodeId = nodeId; - if (nodeId.endsWith("*")) { - modifiedNodeId = nodeId.substring(0, nodeId.length() - 1); - } - // Try to find the bus using bus view - return network.getBusBreakerView().getBus(modifiedNodeId + " "); - } - - private static void processBus(Bus bus, String generatorId, Double p0, double pMinRd) { - bus.getVoltageLevel().newGenerator() - .setBus(bus.getId()) - .setEnsureIdUnicity(true) - .setId(generatorId) - .setMaxP(999999) - .setMinP(pMinRd) - .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 double parseDoubleWithPossibleCommas(String string) { - return Double.parseDouble(string.replaceAll(",", ".")); - } -} diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java index 45f318c360..40369b9f63 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java @@ -7,42 +7,39 @@ package com.powsybl.openrao.data; +import com.powsybl.iidm.network.Bus; +import com.powsybl.iidm.network.Network; import com.powsybl.openrao.commons.OpenRaoException; +import com.powsybl.openrao.commons.TemporalData; +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.timecoupledconstraints.GeneratorConstraints; +import com.powsybl.openrao.data.timecoupledconstraints.TimeCoupledConstraints; +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.*; +import static com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider.BUSINESS_WARNS; +import static com.powsybl.openrao.data.IcsUtil.*; + /** * @author Roxane Chen {@literal } */ public final class IcsData { - // TODO: put value in common - - private static final double MAX_GRADIENT = 1000.0; - - public static final String MAXIMUM_POSITIVE_POWER_GRADIENT = "Maximum positive power gradient [MW/h]"; - public static final String MAXIMUM_NEGATIVE_POWER_GRADIENT = "Maximum negative power gradient [MW/h]"; - - public static final String P0 = "P0"; - public static final String UCT_NODE_OR_GSK_ID = "UCT Node or GSK ID"; - public static final String PREVENTIVE = "Preventive"; - public static final String TRUE = "TRUE"; - public static final String RD_DESCRIPTION_MODE = "RD description mode"; - public static final String NODE = "NODE"; - public static final String LEAD_TIME = "Lead time [h]"; - public static final String LAG_TIME = "Lag time [h]"; - public static final String STARTUP_ALLOWED = "Startup allowed"; - public static final String SHUTDOWN_ALLOWED = "Shutdown allowed"; - - public static final String GENERATOR_SUFFIX = "_GENERATOR"; - - private static Map> timeseriesPerIdAndType; private static Map> weightPerNodePerGsk; - private Map staticConstraintPerId; + private static Map staticConstraintPerId; + + // TODO : either parametrize this or set it to true. May have to change the way it works to import for all curative instants instead of only the last one + public static boolean importCurative = false; public IcsData(Map> timeseriesPerIdAndType, Map> weightPerNodePerGsk, @@ -52,13 +49,12 @@ public IcsData(Map> timeseriesPerIdAndType, this.weightPerNodePerGsk = weightPerNodePerGsk; } - // Define getters - + // Define getters public Map getStaticConstraintPerId() { return staticConstraintPerId; } - public Map> getTimeseriesPerIdAndType() { + public static Map> getTimeseriesPerIdAndType() { return timeseriesPerIdAndType; } @@ -66,18 +62,26 @@ public Map> getWeightPerNodePerGsk() { return weightPerNodePerGsk; } - public String getGeneratorIdFromRaIdAndNodeId(String raId, String nodeId) { + public static String getGeneratorIdFromRaIdAndNodeId(String raId, String nodeId) { return raId + "_" + nodeId + GENERATOR_SUFFIX; } - public Set getGeneratorConstraints() { + public boolean isRaDefinedOnANode(String raId) { + return staticConstraintPerId.get(raId).get(RD_DESCRIPTION_MODE).equalsIgnoreCase(NODE); + } + + public String getNodeIdOrGskIdFromRaId(String raId) { + return staticConstraintPerId.get(raId).get("UCT Node or GSK ID"); + } + + public Set getAllGeneratorConstraints() { Set generatorConstraintsSet = new HashSet<>(); staticConstraintPerId.forEach((raId, staticRecord) -> { // If the remedial action is defined on a Node. if (staticRecord.get(RD_DESCRIPTION_MODE).equalsIgnoreCase(NODE)) { // create a generator constraint from staticRecord String networkElementId = getGeneratorIdFromRaIdAndNodeId(raId, staticRecord.get(UCT_NODE_OR_GSK_ID)); - GeneratorConstraints generatorConstraints = createGeneratorConstraintFromStaticRecord(networkElementId, staticRecord, 1.0); + GeneratorConstraints generatorConstraints = createGeneratorConstraintFromStaticRecord(networkElementId, staticRecord, staticRecord.get(UCT_NODE_OR_GSK_ID),1.0); generatorConstraintsSet.add(generatorConstraints); } else { // If the remedial action is defined on a GSK // For a given GSK, create a generator constraint for each node of the GSK according to the shiftKey @@ -86,7 +90,7 @@ public Set getGeneratorConstraints() { String nodeId = entry.getKey(); Double shiftKey = entry.getValue(); String networkElementId = getGeneratorIdFromRaIdAndNodeId(raId, nodeId); - GeneratorConstraints generatorConstraints = createGeneratorConstraintFromStaticRecord(networkElementId, staticRecord, shiftKey); + GeneratorConstraints generatorConstraints = createGeneratorConstraintFromStaticRecord(networkElementId, staticRecord, nodeId, shiftKey); generatorConstraintsSet.add(generatorConstraints); } } @@ -95,12 +99,108 @@ public Set getGeneratorConstraints() { return generatorConstraintsSet; } + public void createGeneratorConstraints(TimeCoupledConstraints getTimeCoupledConstraints, Map weightPerNode, String raId, Map networkElementPerGskElement) { + for (Map.Entry entry : weightPerNode.entrySet()) { + String nodeId = entry.getKey(); + Double shiftKey = entry.getValue(); + CSVRecord staticRecord = staticConstraintPerId.get(raId); + GeneratorConstraints generatorConstraints = createGeneratorConstraintFromStaticRecord(networkElementPerGskElement.get(nodeId), staticRecord, nodeId, shiftKey); + getTimeCoupledConstraints.addGeneratorConstraints(generatorConstraints); + } + } + + /** + * Find bus in network corresponding to the nodes of the GSK, create a generator and a load for each node of the GSK + * and return a map of the generatorId to the node id (if no bus is found, the generator is not created and not included in the returned map) . + * + * @param initialNetworksToModify + * @param raId + * @param weightPerNode + * @return + */ + public static Map createGeneratorAndLoadAndUpdateNetworks(TemporalData initialNetworksToModify, + String raId, + Map weightPerNode) { + + Map networkElementPerGskElement = new HashMap<>(); + Map seriesPerType = timeseriesPerIdAndType.get(raId); + + for (Map.Entry entry : weightPerNode.entrySet()) { + + String nodeId = entry.getKey(); + Double shiftKey = entry.getValue(); + String generatorId = getGeneratorIdFromRaIdAndNodeId(raId, nodeId); + + for (Map.Entry networkEntry : initialNetworksToModify.getDataPerTimestamp().entrySet()) { + OffsetDateTime dateTime = networkEntry.getKey(); + Network network = networkEntry.getValue(); + + Bus bus = findBus(nodeId, network); + if (bus == null) { + BUSINESS_WARNS.warn("Redispatching action {} cannot be imported because bus {} could not be found", raId, nodeId); + return Map.of(); + } + + int index = dateTime.getHour() + OFFSET; + Double p0 = parseDoubleWithPossibleCommas(seriesPerType.get(P0).get(index)) * shiftKey; + Optional pMinRd = IcsUtil.parseValue(seriesPerType, P_MIN_RD, dateTime, shiftKey); + processBus(bus, generatorId, p0, pMinRd.orElse(ON_POWER_THRESHOLD)); + } + + networkElementPerGskElement.put(nodeId, generatorId); + } + + return networkElementPerGskElement; + } + + public static void createInjectionRangeActionsAndUpdateCracs(TemporalData cracToModify, + String raId, + Map weightPerNode, + Map networkElementPerNode, + double costUp, + double costDown) { + + CSVRecord staticRecord = staticConstraintPerId.get(raId); + Map seriesPerType = timeseriesPerIdAndType.get(raId); + cracToModify.getDataPerTimestamp().forEach((dateTime, crac) -> { + double p0 = parseDoubleWithPossibleCommas(seriesPerType.get(P0).get(dateTime.getHour() + OFFSET)); + InjectionRangeActionAdder injectionRangeActionAdder = crac.newInjectionRangeAction() + .withId(raId + RD_SUFFIX) + .withName(staticRecord.get(GENERATOR_NAME)) + .withInitialSetpoint(p0) + .withVariationCost(costUp, VariationDirection.UP) + .withVariationCost(costDown, VariationDirection.DOWN) + .newRange() + .withMin(p0 - parseDoubleWithPossibleCommas(seriesPerType.get(RDP_DOWN).get(dateTime.getHour() + OFFSET))) + .withMax(p0 + parseDoubleWithPossibleCommas(seriesPerType.get(RDP_UP).get(dateTime.getHour() + OFFSET))) + .add(); + + weightPerNode.forEach((nodeId, shiftKey) -> { + injectionRangeActionAdder.withNetworkElementAndKey(shiftKey, networkElementPerNode.get(nodeId)); + }); + + if (staticRecord.get(PREVENTIVE).equalsIgnoreCase(TRUE)) { + injectionRangeActionAdder.newOnInstantUsageRule() + .withInstant(crac.getPreventiveInstant().getId()) + .add(); + } + if (importCurative && staticRecord.get(CURATIVE).equalsIgnoreCase(TRUE)) { + injectionRangeActionAdder.newOnInstantUsageRule() + .withInstant(crac.getLastInstant().getId()) + .add(); + } + + injectionRangeActionAdder.add(); + }); + } - private static GeneratorConstraints createGeneratorConstraintFromStaticRecord(String networkElementId, - CSVRecord staticRecord, - Double shiftKey) { - GeneratorConstraints.GeneratorConstraintsBuilder builder = GeneratorConstraints.create().withGeneratorId(networkElementId); + public static GeneratorConstraints createGeneratorConstraintFromStaticRecord(String generatorId, + CSVRecord staticRecord, + String nodeId, + Double shiftKey) { + + GeneratorConstraints.GeneratorConstraintsBuilder builder = GeneratorConstraints.create().withGeneratorId(generatorId); if (!staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT).isEmpty()) { builder.withUpwardPowerGradient(shiftKey * parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT))); } else { @@ -132,8 +232,127 @@ private static GeneratorConstraints createGeneratorConstraintFromStaticRecord(St return builder.build(); } - private static double parseDoubleWithPossibleCommas(String string) { - return Double.parseDouble(string.replaceAll(",", ".")); + public static IcsData read(InputStream staticInputStream, + InputStream seriesInputStream, + InputStream gskInputStream, + List sortedTimestampToRun) throws IOException { + + CSVFormat csvFormat = CSVFormat.DEFAULT.builder() + .setDelimiter(";") + .setHeader() + .setSkipHeaderRecord(true) + .get(); + + // Parse and sort per RA_ID and serie type (RDP-, RDP+, Pmin_RD or P0) + Map> timeseriesPerIdAndType = parseSeriesCsv(csvFormat, seriesInputStream); + // Parse GSK and get weight Per Node Per Gsk + Map> weightPerNodePerGsk = parseGskCsv(csvFormat, gskInputStream); + // Parse static CSV: remedial action’s generator’s static constraints. one line per RA_ID + Map staticConstraintPerId = parseAndFilterStaticCsv(csvFormat, staticInputStream, sortedTimestampToRun, weightPerNodePerGsk, timeseriesPerIdAndType); + + return new IcsData(timeseriesPerIdAndType, weightPerNodePerGsk, staticConstraintPerId); + + } + + static Map parseAndFilterStaticCsv(CSVFormat csvFormat, InputStream staticInputStream, List sortedTimestampToRun, Map> weightPerNodePerGsk, Map> timeseriesPerIdAndType) throws IOException { + Iterable staticCsvRecords = csvFormat.parse(new InputStreamReader(staticInputStream)); + Map filteredStaticCsvRecords = new HashMap<>(); + staticCsvRecords.forEach(record -> { + if (shouldBeImported(record, sortedTimestampToRun, weightPerNodePerGsk, timeseriesPerIdAndType)) { + filteredStaticCsvRecords.put(record.get(RA_RD_ID), record); + } + }); + return filteredStaticCsvRecords; + } + + private static Map> parseSeriesCsv(CSVFormat csvFormat, InputStream seriesInputStream) throws IOException { + 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); + }); + + return seriesPerIdAndType; + } + + private static boolean shouldBeImported(CSVRecord staticRecord, List sortedTimestampToRun, Map> weightPerNodePerGsk, Map> timeseriesPerIdAndType) { + //TODO: add more consistency checks ? + // - check that P0s respect the min/max gradients + // - import curative or no ? + + // remedial action should at least be defined on preventive instant + boolean isPreventive = staticRecord.get(PREVENTIVE).equalsIgnoreCase(TRUE); + + // remedial action is defined on a node or a gsk + boolean isDefinedOnANodeOrGsk = staticRecord.get(RD_DESCRIPTION_MODE).equalsIgnoreCase(NODE) || weightPerNodePerGsk.containsKey(staticRecord.get(UCT_NODE_OR_GSK_ID)); + + String raId = staticRecord.get(RA_RD_ID); + Map seriesPerType = timeseriesPerIdAndType.get(raId); + + // is correctly defined in series csv + boolean isDefinedInSeriesCsv = seriesPerType != null && + seriesPerType.containsKey(P0) && + seriesPerType.containsKey(RDP_DOWN) && + seriesPerType.containsKey(RDP_UP) && + seriesPerType.containsKey(P_MIN_RD); + + boolean rangeIsOkay = rangeIsOkay(seriesPerType, sortedTimestampToRun); + boolean p0RespectsGradients = p0RespectsGradients(staticRecord, seriesPerType.get(P0), sortedTimestampToRun); + + return isDefinedOnANodeOrGsk && isPreventive && isDefinedInSeriesCsv && rangeIsOkay && p0RespectsGradients; + } + + private static boolean p0RespectsGradients(CSVRecord staticRecord, CSVRecord p0record, List dateTimes) { + double maxGradient = staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT).isEmpty() ? + MAX_GRADIENT : parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT)); + double minGradient = staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT).isEmpty() ? + -MAX_GRADIENT : -parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT)); + + Iterator dateTimeIterator = dateTimes.iterator(); + OffsetDateTime currentDateTime = dateTimeIterator.next(); + while (dateTimeIterator.hasNext()) { + OffsetDateTime nextDateTime = dateTimeIterator.next(); + double diff = parseDoubleWithPossibleCommas(p0record.get(nextDateTime.getHour() + OFFSET)) - parseDoubleWithPossibleCommas(p0record.get(currentDateTime.getHour() + OFFSET)); + if (diff > maxGradient || diff < minGradient) { + BUSINESS_WARNS.warn( + "Redispatching action {} will not be imported because it does not respect power gradients : min/max/diff {} {} {}", + staticRecord.get(0), minGradient, maxGradient, diff + ); + return false; + } + currentDateTime = nextDateTime; + } + return true; + } + + private static boolean rangeIsOkay(Map seriesPerType, List dateTimes) { + double maxRange = 0.; + for (OffsetDateTime dateTime : dateTimes) { + double rdpPlus = parseDoubleWithPossibleCommas(seriesPerType.get(RDP_UP).get(dateTime.getHour() + OFFSET)); + double rdpMinus = parseDoubleWithPossibleCommas(seriesPerType.get(RDP_DOWN).get(dateTime.getHour() + OFFSET)); + maxRange = Math.max(maxRange, rdpPlus + rdpMinus); + if (rdpPlus < -1e-6 || rdpMinus < -1e-6) { + BUSINESS_WARNS.warn("Redispatching action {} will not be imported because of RDP+ {} or RDP- {} is negative", seriesPerType.get(P0).get(RA_RD_ID), rdpPlus, rdpMinus); + return false; + } + } + if (maxRange < 1) { + BUSINESS_WARNS.warn("Redispatching action {} will not be imported because max range in the day {} MW is too small", seriesPerType.get(P0).get(RA_RD_ID), maxRange); + return false; + } + return true; } + private static Map> parseGskCsv(CSVFormat csvFormat, InputStream gskInputStream) throws IOException { + Iterable gskCsvRecords = csvFormat.parse(new InputStreamReader(gskInputStream)); + Map> weightPerNodePerGsk = new HashMap<>(); + gskCsvRecords.forEach(record -> { + weightPerNodePerGsk.putIfAbsent(record.get(GSK_ID), new HashMap<>()); + weightPerNodePerGsk.get(record.get(GSK_ID)).put(record.get("Node"), parseDoubleWithPossibleCommas(record.get("Weight"))); + }); + + return weightPerNodePerGsk; + } } \ No newline at end of file diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java deleted file mode 100644 index d8a9ef4c64..0000000000 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (c) 2026, 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; - -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.*; - -import static com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider.BUSINESS_WARNS; - -/** - * @author Roxane Chen {@literal } - */ -public class IcsDataImporter { - - private static final int OFFSET = 2; - private static final double MAX_GRADIENT = 1000.0; - - public static final String MAXIMUM_POSITIVE_POWER_GRADIENT = "Maximum positive power gradient [MW/h]"; - public static final String MAXIMUM_NEGATIVE_POWER_GRADIENT = "Maximum negative power gradient [MW/h]"; - public static final String RA_RD_ID = "RA RD ID"; - public static final String RDP_UP = "RDP+"; - public static final String RDP_DOWN = "RDP-"; - public static final String P_MIN_RD = "Pmin_RD"; - public static final String P0 = "P0"; - public static final String UCT_NODE_OR_GSK_ID = "UCT Node or GSK ID"; - public static final String GSK_ID = "GSK ID"; - public static final String PREVENTIVE = "Preventive"; - public static final String TRUE = "TRUE"; - public static final String RD_DESCRIPTION_MODE = "RD description mode"; - public static final String NODE = "NODE"; - - public IcsDataImporter() { - // only static use - } - - public IcsData read(InputStream staticInputStream, - InputStream seriesInputStream, - InputStream gskInputStream, - List sortedTimestampToRun) throws IOException { - - CSVFormat csvFormat = CSVFormat.DEFAULT.builder() - .setDelimiter(";") - .setHeader() - .setSkipHeaderRecord(true) - .get(); - - // Parse and sort per RA_ID and serie type (RDP-, RDP+, Pmin_RD or P0) - Map> timeseriesPerIdAndType = parseSeriesCsv(csvFormat, seriesInputStream); - // Parse GSK and get weight Per Node Per Gsk - Map> weightPerNodePerGsk = parseGskCsv(csvFormat, gskInputStream); - // Parse static CSV: remedial action’s generator’s static constraints. one line per RA_ID - Map staticConstraintPerId = parseAndFilterStaticCsv(csvFormat, staticInputStream, sortedTimestampToRun, weightPerNodePerGsk, timeseriesPerIdAndType); - - return new IcsData(timeseriesPerIdAndType, weightPerNodePerGsk, staticConstraintPerId); - - } - - Map parseAndFilterStaticCsv(CSVFormat csvFormat, InputStream staticInputStream, List sortedTimestampToRun, Map> weightPerNodePerGsk, Map> timeseriesPerIdAndType) throws IOException { - Iterable staticCsvRecords = csvFormat.parse(new InputStreamReader(staticInputStream)); - Map filteredStaticCsvRecords = new HashMap<>(); - staticCsvRecords.forEach(record -> { - if (shouldBeImported(record, sortedTimestampToRun, weightPerNodePerGsk, timeseriesPerIdAndType)) { - filteredStaticCsvRecords.put(record.get(RA_RD_ID), record); - } - }); - return filteredStaticCsvRecords; - } - - private static Map> parseSeriesCsv(CSVFormat csvFormat, InputStream seriesInputStream) throws IOException { - 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); - }); - - return seriesPerIdAndType; - } - - private static boolean shouldBeImported(CSVRecord staticRecord, List sortedTimestampToRun, Map> weightPerNodePerGsk, Map> timeseriesPerIdAndType) { - //TODO: add more consistency checks ? - // - check that P0s respect the min/max gradients - - // remedial action is defined on preventive instant - boolean isPreventive = staticRecord.get(PREVENTIVE).equalsIgnoreCase(TRUE); - - // remedial action is defined on a node or a gsk - boolean isDefinedOnANodeOrGsk = staticRecord.get(RD_DESCRIPTION_MODE).equalsIgnoreCase(NODE) || weightPerNodePerGsk.containsKey(staticRecord.get(UCT_NODE_OR_GSK_ID)); - - String raId = staticRecord.get(RA_RD_ID); - Map seriesPerType = timeseriesPerIdAndType.get(raId); - - // is correctly defined in series csv - boolean isDefinedInSeriesCsv = seriesPerType != null && - seriesPerType.containsKey(P0) && - seriesPerType.containsKey(RDP_DOWN) && - seriesPerType.containsKey(RDP_UP) && - seriesPerType.containsKey(P_MIN_RD); - - boolean rangeIsOkay = rangeIsOkay(seriesPerType, sortedTimestampToRun); - boolean p0RespectsGradients = p0RespectsGradients(staticRecord, seriesPerType.get(P0), sortedTimestampToRun); - - return isDefinedOnANodeOrGsk && isPreventive && isDefinedInSeriesCsv && rangeIsOkay && p0RespectsGradients; - } - - private static boolean p0RespectsGradients(CSVRecord staticRecord, CSVRecord p0record, List dateTimes) { - double maxGradient = staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT).isEmpty() ? - MAX_GRADIENT : parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT)); - double minGradient = staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT).isEmpty() ? - -MAX_GRADIENT : -parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT)); - - Iterator dateTimeIterator = dateTimes.iterator(); - OffsetDateTime currentDateTime = dateTimeIterator.next(); - while (dateTimeIterator.hasNext()) { - OffsetDateTime nextDateTime = dateTimeIterator.next(); - double diff = parseDoubleWithPossibleCommas(p0record.get(nextDateTime.getHour() + OFFSET)) - parseDoubleWithPossibleCommas(p0record.get(currentDateTime.getHour() + OFFSET)); - if (diff > maxGradient || diff < minGradient) { - BUSINESS_WARNS.warn( - "Redispatching action {} will not be imported because it does not respect power gradients : min/max/diff {} {} {}", - staticRecord.get(0), minGradient, maxGradient, diff - ); - return false; - } - currentDateTime = nextDateTime; - } - return true; - } - - private static boolean rangeIsOkay(Map seriesPerType, List dateTimes) { - double maxRange = 0.; - for (OffsetDateTime dateTime : dateTimes) { - double rdpPlus = parseDoubleWithPossibleCommas(seriesPerType.get(RDP_UP).get(dateTime.getHour() + OFFSET)); - double rdpMinus = parseDoubleWithPossibleCommas(seriesPerType.get(RDP_DOWN).get(dateTime.getHour() + OFFSET)); - maxRange = Math.max(maxRange, rdpPlus + rdpMinus); - if (rdpPlus < -1e-6 || rdpMinus < -1e-6) { - BUSINESS_WARNS.warn("Redispatching action {} will not be imported because of RDP+ {} or RDP- {} is negative", seriesPerType.get(P0).get(RA_RD_ID), rdpPlus, rdpMinus); - return false; - } - } - if (maxRange < 1) { - BUSINESS_WARNS.warn("Redispatching action {} will not be imported because max range in the day {} MW is too small", seriesPerType.get(P0).get(RA_RD_ID), maxRange); - return false; - } - return true; - } - - private static Map> parseGskCsv(CSVFormat csvFormat, InputStream gskInputStream) throws IOException { - Iterable gskCsvRecords = csvFormat.parse(new InputStreamReader(gskInputStream)); - Map> weightPerNodePerGsk = new HashMap<>(); - gskCsvRecords.forEach(record -> { - weightPerNodePerGsk.putIfAbsent(record.get(GSK_ID), new HashMap<>()); - weightPerNodePerGsk.get(record.get(GSK_ID)).put(record.get("Node"), parseDoubleWithPossibleCommas(record.get("Weight"))); - }); - - return weightPerNodePerGsk; - } - - private static double parseDoubleWithPossibleCommas(String string) { - return Double.parseDouble(string.replaceAll(",", ".")); - } -} \ No newline at end of file diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java new file mode 100644 index 0000000000..3bd88eaea6 --- /dev/null +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2026, 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; + +import com.powsybl.iidm.network.Bus; +import com.powsybl.iidm.network.LoadType; +import com.powsybl.iidm.network.Network; +import org.apache.commons.csv.CSVRecord; + +import java.time.OffsetDateTime; +import java.util.Map; +import java.util.Optional; + +/** + * @author Roxane Chen {@literal } + */ +public class IcsUtil { + + static final double MAX_GRADIENT = 1000.0; + + public static final String MAXIMUM_POSITIVE_POWER_GRADIENT = "Maximum positive power gradient [MW/h]"; + public static final String MAXIMUM_NEGATIVE_POWER_GRADIENT = "Maximum negative power gradient [MW/h]"; + + public static final String RA_RD_ID = "RA RD ID"; + public static final String RDP_UP = "RDP+"; + public static final String RDP_DOWN = "RDP-"; + public static final String P_MIN_RD = "Pmin_RD"; + public static final String P0 = "P0"; + public static final String UCT_NODE_OR_GSK_ID = "UCT Node or GSK ID"; + public static final String PREVENTIVE = "Preventive"; + public static final String TRUE = "TRUE"; + public static final String RD_DESCRIPTION_MODE = "RD description mode"; + public static final String NODE = "NODE"; + public static final String LEAD_TIME = "Lead time [h]"; + public static final String LAG_TIME = "Lag time [h]"; + public static final String STARTUP_ALLOWED = "Startup allowed"; + public static final String SHUTDOWN_ALLOWED = "Shutdown allowed"; + public static final String GSK_ID = "GSK ID"; + + public static final String GENERATOR_SUFFIX = "_GENERATOR"; + public static final int OFFSET = 2; + public static final double ON_POWER_THRESHOLD = 1.001; // TODO: mutualize with value from linear problem + + public static final String CURATIVE = "Curative"; + public static final String FALSE = "FALSE"; + public static final String GENERATOR_NAME = "Generator Name"; + public static final String RD_SUFFIX = "_RD"; + + static Optional parseValue(Map seriesPerType, String key, OffsetDateTime timestamp, double shiftKey) { + if (seriesPerType.containsKey(key)) { + CSVRecord series = seriesPerType.get(key); + String value = series.get(timestamp.getHour() + OFFSET); + if (value != null) { + return Optional.of(parseDoubleWithPossibleCommas(value) * shiftKey); + } + } + return Optional.empty(); + } + + static double parseDoubleWithPossibleCommas(String string) { + return Double.parseDouble(string.replaceAll(",", ".")); + } + + // TODO: make this more robust (and less UCTE dependent) + static Bus findBus(String nodeId, Network network) { + // First try to get the bus in bus breaker view + Bus bus = network.getBusBreakerView().getBus(nodeId); + if (bus != null) { + return bus; + } + + // Then, if last char is *, remove it + String modifiedNodeId = nodeId; + if (nodeId.endsWith("*")) { + modifiedNodeId = nodeId.substring(0, nodeId.length() - 1); + } + // Try to find the bus using bus view + return network.getBusBreakerView().getBus(modifiedNodeId + " "); + } + + + static void processBus(Bus bus, String generatorId, Double p0, double pMinRd) { + bus.getVoltageLevel().newGenerator() + .setBus(bus.getId()) + .setEnsureIdUnicity(true) + .setId(generatorId) + .setMaxP(999999) + .setMinP(pMinRd) + .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(); + } + + // TODO: Move elsewhere + // By default, UCTE sets nominal voltage to 220 and 380kV for the voltage levels 6 and 7, + // whereas default values of Core countries are 225 and 400 kV instead. + // The preprocessor updates the nominal voltage levels to these values. + public static void updateNominalVoltage(Network network) { + network.getVoltageLevelStream().forEach(voltageLevel -> { + if (safeDoubleEquals(voltageLevel.getNominalV(), 380)) { + voltageLevel.setNominalV(400); + } else if (safeDoubleEquals(voltageLevel.getNominalV(), 220)) { + voltageLevel.setNominalV(225); + } + // Else, Should not be changed cause is not equal to the default nominal voltage of voltage levels 6 or 7 + }); + } + + private static boolean safeDoubleEquals(double a, double b) { + return Math.abs(a - b) < 1e-3; + } + +} \ No newline at end of file diff --git a/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java b/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java index 0b89e2b18f..d09cff7c87 100644 --- a/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java +++ b/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java @@ -14,6 +14,7 @@ import com.powsybl.openrao.commons.TemporalData; import com.powsybl.openrao.commons.TemporalDataImpl; import com.powsybl.openrao.commons.Unit; +import com.powsybl.openrao.data.IcsData; import com.powsybl.openrao.data.crac.api.Crac; import com.powsybl.openrao.data.crac.api.CracCreationContext; import com.powsybl.openrao.data.crac.api.Instant; @@ -28,7 +29,6 @@ import com.powsybl.openrao.data.crac.api.rangeaction.RangeAction; import com.powsybl.openrao.data.crac.io.fbconstraint.FbConstraintCreationContext; import com.powsybl.openrao.data.crac.io.fbconstraint.parameters.FbConstraintCracCreationParameters; -import com.powsybl.openrao.data.crac.util.IcsImporter; import com.powsybl.openrao.data.raoresult.api.RaoResult; import com.powsybl.openrao.data.raoresult.api.TimeCoupledRaoResult; import com.powsybl.openrao.data.raoresult.io.idcc.core.F711Utils; @@ -67,19 +67,14 @@ import java.nio.file.StandardCopyOption; import java.time.OffsetDateTime; import java.time.format.DateTimeFormatter; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Properties; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import static com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider.TECHNICAL_LOGS; +import static com.powsybl.openrao.data.IcsUtil.updateNominalVoltage; import static com.powsybl.openrao.tests.steps.CommonTestData.buildConfig; import static com.powsybl.openrao.tests.steps.CommonTestData.cracPath; import static com.powsybl.openrao.tests.steps.CommonTestData.getRaoParameters; @@ -258,16 +253,50 @@ public static void loadDataForCoreTimeCoupledRao(DataTable arg1) throws IOExcept TECHNICAL_LOGS.warn("No FB Constraint CRAC creation parameters found. Default parameters will be used."); fbConstraintParameters = new FbConstraintCracCreationParameters(); } - IcsImporter.populateInputWithICS( - timeCoupledRaoInputWithNetworkPaths, - new FileInputStream(getFile(icsStaticPath)), - new FileInputStream(getFile(icsSeriesPath)), - gskInputStream, - fbConstraintParameters.getIcsCostUp(), - fbConstraintParameters.getIcsCostDown() - ); + + // Update voltage monitoring + TemporalData modifiedInitialNetworks = new TemporalDataImpl<>(); + timeCoupledRaoInputWithNetworkPaths.getRaoInputs().getDataPerTimestamp().forEach((dateTime, raoInput) -> { + Network network = Network.read(raoInput.getInitialNetworkPath()); + updateNominalVoltage(network); + modifiedInitialNetworks.put(dateTime, network); + }); + + + // Read ICS Data + IcsData icsData = IcsData.read(new FileInputStream(getFile(icsStaticPath)),new FileInputStream(getFile(icsSeriesPath)),gskInputStream, timeCoupledRaoInputWithNetworkPaths.getTimestampsToRun().stream().sorted().toList()); + + TemporalData cracToModify = new TemporalDataImpl<>(); + timeCoupledRaoInputWithNetworkPaths.getRaoInputs().getDataPerTimestamp().forEach((dateTime, raoInput) -> { + cracToModify.put(dateTime, raoInput.getCrac()); + }); + + + FbConstraintCracCreationParameters finalFbConstraintParameters = fbConstraintParameters; + // For each redispatching actions defined in static csv update networks and update cracs + icsData.getStaticConstraintPerId().forEach((raId, staticRecord) -> { + Map weightPerNode; + // If the remedial action is defined on a Node. + if (icsData.isRaDefinedOnANode(raId)) { + weightPerNode = Map.of(icsData.getNodeIdOrGskIdFromRaId(raId), 1.0); + } else { // If the remedial action is defined on a GSK + weightPerNode = icsData.getWeightPerNodePerGsk().get(icsData.getNodeIdOrGskIdFromRaId(raId)); + } + + // Create generator and load in networks + Map generatorIdPerNode = icsData.createGeneratorAndLoadAndUpdateNetworks(modifiedInitialNetworks, raId, weightPerNode); + // One of the node could not be find no need to create injection range actions and generator constraint. + if (generatorIdPerNode.isEmpty()) { + return; + } + // Create Injection Range Actions in CRACs + icsData.createInjectionRangeActionsAndUpdateCracs(cracToModify, raId, weightPerNode, generatorIdPerNode, finalFbConstraintParameters.getIcsCostUp(), finalFbConstraintParameters.getIcsCostDown()); + // Create generator constraints and them to time coupled rao input + icsData.createGeneratorConstraints(timeCoupledRaoInputWithNetworkPaths.getTimeCoupledConstraints(), weightPerNode, raId, generatorIdPerNode); + }); } + @When("I launch marmot") public static void iLaunchMarmot() { timeCoupledRaoResult = TimeCoupledRao.run(timeCoupledRaoInputWithNetworkPaths, getRaoParameters()); From 4bb418cc00ac10b321adb675378d111a9c066ceb Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Thu, 19 Mar 2026 13:33:00 +0100 Subject: [PATCH 03/64] clean up the code Signed-off-by: CHEN Roxane --- .../com/powsybl/openrao/data/IcsData.java | 192 ++++++++++-------- .../com/powsybl/openrao/data/IcsUtil.java | 6 +- .../tests/steps/TimeCoupledRaoSteps.java | 5 +- 3 files changed, 115 insertions(+), 88 deletions(-) diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java index 40369b9f63..c8fea09200 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java @@ -49,7 +49,6 @@ public IcsData(Map> timeseriesPerIdAndType, this.weightPerNodePerGsk = weightPerNodePerGsk; } - // Define getters public Map getStaticConstraintPerId() { return staticConstraintPerId; } @@ -71,52 +70,68 @@ public boolean isRaDefinedOnANode(String raId) { } public String getNodeIdOrGskIdFromRaId(String raId) { - return staticConstraintPerId.get(raId).get("UCT Node or GSK ID"); + return staticConstraintPerId.get(raId).get(UCT_NODE_OR_GSK_ID); } - public Set getAllGeneratorConstraints() { + /** + * Generates a set of generator constraints based on the provided remedial action ID. + * + * @param raId The identifier of the remedial action for which the generator constraints are being created. + * @param weightPerNode A map linking node identifiers to their respective generation shift key weights. + * @param networkElementIdPerNodeId A map linking nodeId to their respective network elements id. + * @return A set of {@code GeneratorConstraints} generated for the specified parameters. + * @throws OpenRaoException if data related to shutdown or startup allowances cannot be parsed. + */ + public Set createGeneratorConstraints(String raId, Map weightPerNode, Map networkElementIdPerNodeId) { Set generatorConstraintsSet = new HashSet<>(); - staticConstraintPerId.forEach((raId, staticRecord) -> { - // If the remedial action is defined on a Node. - if (staticRecord.get(RD_DESCRIPTION_MODE).equalsIgnoreCase(NODE)) { - // create a generator constraint from staticRecord - String networkElementId = getGeneratorIdFromRaIdAndNodeId(raId, staticRecord.get(UCT_NODE_OR_GSK_ID)); - GeneratorConstraints generatorConstraints = createGeneratorConstraintFromStaticRecord(networkElementId, staticRecord, staticRecord.get(UCT_NODE_OR_GSK_ID),1.0); - generatorConstraintsSet.add(generatorConstraints); - } else { // If the remedial action is defined on a GSK - // For a given GSK, create a generator constraint for each node of the GSK according to the shiftKey - Map weightPerNode = weightPerNodePerGsk.get(staticRecord.get(UCT_NODE_OR_GSK_ID)); - for (Map.Entry entry : weightPerNode.entrySet()) { - String nodeId = entry.getKey(); - Double shiftKey = entry.getValue(); - String networkElementId = getGeneratorIdFromRaIdAndNodeId(raId, nodeId); - GeneratorConstraints generatorConstraints = createGeneratorConstraintFromStaticRecord(networkElementId, staticRecord, nodeId, shiftKey); - generatorConstraintsSet.add(generatorConstraints); - } - } - }); - - return generatorConstraintsSet; - } - - public void createGeneratorConstraints(TimeCoupledConstraints getTimeCoupledConstraints, Map weightPerNode, String raId, Map networkElementPerGskElement) { for (Map.Entry entry : weightPerNode.entrySet()) { String nodeId = entry.getKey(); Double shiftKey = entry.getValue(); CSVRecord staticRecord = staticConstraintPerId.get(raId); - GeneratorConstraints generatorConstraints = createGeneratorConstraintFromStaticRecord(networkElementPerGskElement.get(nodeId), staticRecord, nodeId, shiftKey); - getTimeCoupledConstraints.addGeneratorConstraints(generatorConstraints); + GeneratorConstraints.GeneratorConstraintsBuilder builder = GeneratorConstraints.create().withGeneratorId(networkElementIdPerNodeId.get(nodeId)); + if (!staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT).isEmpty()) { + builder.withUpwardPowerGradient(shiftKey * parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT))); + } else { + builder.withUpwardPowerGradient(shiftKey * MAX_GRADIENT); + } + if (!staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT).isEmpty()) { + builder.withDownwardPowerGradient(-shiftKey * parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT))); + } else { + builder.withDownwardPowerGradient(-shiftKey * MAX_GRADIENT); + } + if (!staticRecord.get(LEAD_TIME).isEmpty()) { + builder.withLeadTime(parseDoubleWithPossibleCommas(staticRecord.get(LEAD_TIME))); + } + if (!staticRecord.get(LAG_TIME).isEmpty()) { + builder.withLagTime(parseDoubleWithPossibleCommas(staticRecord.get(LAG_TIME))); + } + if (staticRecord.get(SHUTDOWN_ALLOWED).isEmpty() || + !staticRecord.get(SHUTDOWN_ALLOWED).equalsIgnoreCase(TRUE) && !staticRecord.get(SHUTDOWN_ALLOWED).equalsIgnoreCase(FALSE)) { + throw new OpenRaoException("Could not parse shutDownAllowed value " + staticRecord.get(SHUTDOWN_ALLOWED) + " for nodeId " + nodeId); + } else { + builder.withShutDownAllowed(Boolean.parseBoolean(staticRecord.get(SHUTDOWN_ALLOWED))); + } + if (staticRecord.get(STARTUP_ALLOWED).isEmpty() || + !staticRecord.get(STARTUP_ALLOWED).equalsIgnoreCase(TRUE) && !staticRecord.get(STARTUP_ALLOWED).equalsIgnoreCase(FALSE)) { + throw new OpenRaoException("Could not parse startUpAllowed value " + staticRecord.get(STARTUP_ALLOWED) + " for nodeId " + nodeId); + } else { + builder.withStartUpAllowed(Boolean.parseBoolean(staticRecord.get(STARTUP_ALLOWED))); + } + GeneratorConstraints generatorConstraints = builder.build(); + generatorConstraintsSet.add(generatorConstraints); } + return generatorConstraintsSet; } /** - * Find bus in network corresponding to the nodes of the GSK, create a generator and a load for each node of the GSK - * and return a map of the generatorId to the node id (if no bus is found, the generator is not created and not included in the returned map) . + * Creates generator/load elements and updates provided networks given the remedial action's ID and its shift key mapping * - * @param initialNetworksToModify - * @param raId - * @param weightPerNode - * @return + * @param initialNetworksToModify Temporal data representing the networks that will be modified. + * Contains network configurations per timestamp. + * @param raId The identifier of the remedial action for which generators and network modifications are being applied. + * @param weightPerNode A map linking node identifiers to their corresponding generation shift key weights. + * @return A map associating each node identifier to its corresponding generator identifier. + * Returns an empty map if the process is aborted due to missing network components. */ public static Map createGeneratorAndLoadAndUpdateNetworks(TemporalData initialNetworksToModify, String raId, @@ -153,6 +168,16 @@ public static Map createGeneratorAndLoadAndUpdateNetworks(Tempor return networkElementPerGskElement; } + /** + * Creates injection range actions and updates CRACs for all timestamps. + * + * @param cracToModify Temporal data containing CRACs to be modified and timestamps to consider. + * @param raId The identifier of the remedial action for which injection range actions are created. + * @param weightPerNode A map linking node identifiers to their associated generation shift key weights. + * @param networkElementPerNode A map linking each node identifier to its corresponding network element/generator id. + * @param costUp The cost associated with increasing the generation (VariationDirection.UP). + * @param costDown The cost associated with decreasing the generation (VariationDirection.DOWN). + */ public static void createInjectionRangeActionsAndUpdateCracs(TemporalData cracToModify, String raId, Map weightPerNode, @@ -195,43 +220,20 @@ public static void createInjectionRangeActionsAndUpdateCracs(TemporalData } - public static GeneratorConstraints createGeneratorConstraintFromStaticRecord(String generatorId, - CSVRecord staticRecord, - String nodeId, - Double shiftKey) { - - GeneratorConstraints.GeneratorConstraintsBuilder builder = GeneratorConstraints.create().withGeneratorId(generatorId); - if (!staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT).isEmpty()) { - builder.withUpwardPowerGradient(shiftKey * parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT))); - } else { - builder.withUpwardPowerGradient(shiftKey * MAX_GRADIENT); - } - if (!staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT).isEmpty()) { - builder.withDownwardPowerGradient(-shiftKey * parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT))); - } else { - builder.withDownwardPowerGradient(-shiftKey * MAX_GRADIENT); - } - if (!staticRecord.get(LEAD_TIME).isEmpty()) { - builder.withLeadTime(parseDoubleWithPossibleCommas(staticRecord.get(LEAD_TIME))); - } - if (!staticRecord.get(LAG_TIME).isEmpty()) { - builder.withLagTime(parseDoubleWithPossibleCommas(staticRecord.get(LAG_TIME))); - } - if (staticRecord.get(SHUTDOWN_ALLOWED).isEmpty() || - !staticRecord.get(SHUTDOWN_ALLOWED).equalsIgnoreCase(TRUE) && !staticRecord.get(SHUTDOWN_ALLOWED).equalsIgnoreCase(FALSE)) { - throw new OpenRaoException("Could not parse shutDownAllowed value " + staticRecord.get(SHUTDOWN_ALLOWED) + " for nodeId " + nodeId); - } else { - builder.withShutDownAllowed(Boolean.parseBoolean(staticRecord.get(SHUTDOWN_ALLOWED))); - } - if (staticRecord.get(STARTUP_ALLOWED).isEmpty() || - !staticRecord.get(STARTUP_ALLOWED).equalsIgnoreCase(TRUE) && !staticRecord.get(STARTUP_ALLOWED).equalsIgnoreCase(FALSE)) { - throw new OpenRaoException("Could not parse startUpAllowed value " + staticRecord.get(STARTUP_ALLOWED) + " for nodeId " + nodeId); - } else { - builder.withStartUpAllowed(Boolean.parseBoolean(staticRecord.get(STARTUP_ALLOWED))); - } - return builder.build(); - } + // READER // + /** + * Reads and processes inputs to generate Ics Data Object + * + * @param staticInputStream the input stream for static constraints data, defining generator constraints + * associated with remedial actions. + * @param seriesInputStream the input stream for time series data, mapped per RA_ID and series type + * (e.g., RDP-, RDP+, Pmin_RD, or P0). + * @param gskInputStream the input stream for GSK data, mapping nodes to their generation shift key weights. + * @param sortedTimestampToRun the list of timestamps to consider + * @return an {@code IcsData} instance + * @throws IOException if an issue occurs while reading or processing the input streams. + */ public static IcsData read(InputStream staticInputStream, InputStream seriesInputStream, InputStream gskInputStream, @@ -277,10 +279,19 @@ private static Map> parseSeriesCsv(CSVFormat csvF return seriesPerIdAndType; } + private static Map> parseGskCsv(CSVFormat csvFormat, InputStream gskInputStream) throws IOException { + Iterable gskCsvRecords = csvFormat.parse(new InputStreamReader(gskInputStream)); + Map> weightPerNodePerGsk = new HashMap<>(); + gskCsvRecords.forEach(record -> { + weightPerNodePerGsk.putIfAbsent(record.get(GSK_ID), new HashMap<>()); + weightPerNodePerGsk.get(record.get(GSK_ID)).put(record.get("Node"), parseDoubleWithPossibleCommas(record.get("Weight"))); + }); + + return weightPerNodePerGsk; + } + + // Consistency check functions private static boolean shouldBeImported(CSVRecord staticRecord, List sortedTimestampToRun, Map> weightPerNodePerGsk, Map> timeseriesPerIdAndType) { - //TODO: add more consistency checks ? - // - check that P0s respect the min/max gradients - // - import curative or no ? // remedial action should at least be defined on preventive instant boolean isPreventive = staticRecord.get(PREVENTIVE).equalsIgnoreCase(TRUE); @@ -304,6 +315,18 @@ private static boolean shouldBeImported(CSVRecord staticRecord, List dateTimes) { double maxGradient = staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT).isEmpty() ? MAX_GRADIENT : parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT)); @@ -327,6 +350,16 @@ private static boolean p0RespectsGradients(CSVRecord staticRecord, CSVRecord p0r return true; } + /** + * Verifies whether the range of redispatching parameters is valid for the input time series, + * ensuring that redispatching values are non-negative and exceed a minimum threshold. + * + * @param seriesPerType A map where keys are series types (e.g., RDP+ or RDP-) and values are time-series data (CSVRecord) + * corresponding to these types. + * @param dateTimes A list of timestamps to evaluate the redispatching parameters at specific hours within a day. + * @return {@code true} if the range of redispatching values is valid and meets the defined constraints; + * {@code false} otherwise. + */ private static boolean rangeIsOkay(Map seriesPerType, List dateTimes) { double maxRange = 0.; for (OffsetDateTime dateTime : dateTimes) { @@ -344,15 +377,4 @@ private static boolean rangeIsOkay(Map seriesPerType, List> parseGskCsv(CSVFormat csvFormat, InputStream gskInputStream) throws IOException { - Iterable gskCsvRecords = csvFormat.parse(new InputStreamReader(gskInputStream)); - Map> weightPerNodePerGsk = new HashMap<>(); - gskCsvRecords.forEach(record -> { - weightPerNodePerGsk.putIfAbsent(record.get(GSK_ID), new HashMap<>()); - weightPerNodePerGsk.get(record.get(GSK_ID)).put(record.get("Node"), parseDoubleWithPossibleCommas(record.get("Weight"))); - }); - - return weightPerNodePerGsk; - } } \ No newline at end of file diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java index 3bd88eaea6..487433caa9 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java @@ -43,6 +43,7 @@ public class IcsUtil { public static final String GSK_ID = "GSK ID"; public static final String GENERATOR_SUFFIX = "_GENERATOR"; + public static final String LOAD_SUFFIX = "_LOAD"; public static final int OFFSET = 2; public static final double ON_POWER_THRESHOLD = 1.001; // TODO: mutualize with value from linear problem @@ -57,6 +58,9 @@ static Optional parseValue(Map seriesPerType, String String value = series.get(timestamp.getHour() + OFFSET); if (value != null) { return Optional.of(parseDoubleWithPossibleCommas(value) * shiftKey); + } else { + // TODO: make sure to add a test for this + return Optional.empty(); } } return Optional.empty(); @@ -101,7 +105,7 @@ static void processBus(Bus bus, String generatorId, Double p0, double pMinRd) { bus.getVoltageLevel().newLoad() .setBus(bus.getId()) .setEnsureIdUnicity(true) - .setId(bus.getId() + "_LOAD") + .setId(bus.getId() + LOAD_SUFFIX) .setP0(p0) .setQ0(0) .setLoadType(LoadType.FICTITIOUS) diff --git a/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java b/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java index d09cff7c87..42960e7bac 100644 --- a/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java +++ b/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java @@ -33,6 +33,7 @@ import com.powsybl.openrao.data.raoresult.api.TimeCoupledRaoResult; import com.powsybl.openrao.data.raoresult.io.idcc.core.F711Utils; import com.powsybl.openrao.data.refprog.refprogxmlimporter.TimeCoupledRefProg; +import com.powsybl.openrao.data.timecoupledconstraints.GeneratorConstraints; import com.powsybl.openrao.data.timecoupledconstraints.TimeCoupledConstraints; import com.powsybl.openrao.data.timecoupledconstraints.io.JsonTimeCoupledConstraints; import com.powsybl.openrao.raoapi.RaoInputWithNetworkPaths; @@ -282,7 +283,6 @@ public static void loadDataForCoreTimeCoupledRao(DataTable arg1) throws IOExcept } else { // If the remedial action is defined on a GSK weightPerNode = icsData.getWeightPerNodePerGsk().get(icsData.getNodeIdOrGskIdFromRaId(raId)); } - // Create generator and load in networks Map generatorIdPerNode = icsData.createGeneratorAndLoadAndUpdateNetworks(modifiedInitialNetworks, raId, weightPerNode); // One of the node could not be find no need to create injection range actions and generator constraint. @@ -292,7 +292,8 @@ public static void loadDataForCoreTimeCoupledRao(DataTable arg1) throws IOExcept // Create Injection Range Actions in CRACs icsData.createInjectionRangeActionsAndUpdateCracs(cracToModify, raId, weightPerNode, generatorIdPerNode, finalFbConstraintParameters.getIcsCostUp(), finalFbConstraintParameters.getIcsCostDown()); // Create generator constraints and them to time coupled rao input - icsData.createGeneratorConstraints(timeCoupledRaoInputWithNetworkPaths.getTimeCoupledConstraints(), weightPerNode, raId, generatorIdPerNode); + Set generatorConstraintsSet = icsData.createGeneratorConstraints(raId, weightPerNode, generatorIdPerNode); + generatorConstraintsSet.forEach(generatorConstraints -> timeCoupledRaoInputWithNetworkPaths.getTimeCoupledConstraints().addGeneratorConstraints(generatorConstraints)); }); } From 839ae633d64f7f250600d4505dd5ee14dc1cb998 Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Thu, 19 Mar 2026 17:14:38 +0100 Subject: [PATCH 04/64] start test Signed-off-by: CHEN Roxane --- .../data/crac/util/IcsImporterTest.java | 135 -------- data/ics-importer/pom.xml | 56 +++- .../com/powsybl/openrao/data/IcsData.java | 6 +- .../com/powsybl/openrao/data/IcsDataTest.java | 296 ++++++++++++++++++ .../src/test/resources/crac/crac-0030.json | 0 .../src/test/resources/crac/crac-0130.json | 0 .../src/test/resources/glsk/gsk.csv | 0 .../src/test/resources/ics/series.csv | 0 .../resources/ics/series_gradient_not_ok.csv | 0 .../test/resources/ics/series_no_pmin_rd.csv | 0 .../src/test/resources/ics/static.csv | 2 +- .../ics/static_no_gradient_no_lead_no_lag.csv | 0 .../test/resources/ics/static_no_shutdown.csv | 2 +- .../test/resources/ics/static_no_startup.csv | 0 .../ics/static_shutdown_startup_true.csv | 2 +- .../test/resources/ics/static_with_gsk.csv | 2 +- .../ics/static_with_gsk_no_shutdown.csv | 0 .../ics/static_with_gsk_no_startup.csv | 0 .../ics/static_with_gsk_wrong_shutdown.csv | 0 .../ics/static_with_gsk_wrong_startup.csv | 0 .../resources/ics/static_wrong_shutdown.csv | 2 +- .../resources/ics/static_wrong_startup.csv | 0 .../network/12Nodes_with_Xnodes.xiidm | 283 +++++++++++++++++ .../network/2Nodes2ParallelLinesPST_0030.uct | 12 + .../network/2Nodes2ParallelLinesPST_0130.uct | 12 + .../tests/steps/TimeCoupledRaoSteps.java | 1 + 26 files changed, 667 insertions(+), 144 deletions(-) create mode 100644 data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java rename data/{crac/crac-util => ics-importer}/src/test/resources/crac/crac-0030.json (100%) rename data/{crac/crac-util => ics-importer}/src/test/resources/crac/crac-0130.json (100%) rename data/{crac/crac-util => ics-importer}/src/test/resources/glsk/gsk.csv (100%) rename data/{crac/crac-util => ics-importer}/src/test/resources/ics/series.csv (100%) rename data/{crac/crac-util => ics-importer}/src/test/resources/ics/series_gradient_not_ok.csv (100%) rename data/{crac/crac-util => ics-importer}/src/test/resources/ics/series_no_pmin_rd.csv (100%) rename data/{crac/crac-util => ics-importer}/src/test/resources/ics/static.csv (89%) rename data/{crac/crac-util => ics-importer}/src/test/resources/ics/static_no_gradient_no_lead_no_lag.csv (100%) rename data/{crac/crac-util => ics-importer}/src/test/resources/ics/static_no_shutdown.csv (90%) rename data/{crac/crac-util => ics-importer}/src/test/resources/ics/static_no_startup.csv (100%) rename data/{crac/crac-util => ics-importer}/src/test/resources/ics/static_shutdown_startup_true.csv (90%) rename data/{crac/crac-util => ics-importer}/src/test/resources/ics/static_with_gsk.csv (89%) rename data/{crac/crac-util => ics-importer}/src/test/resources/ics/static_with_gsk_no_shutdown.csv (100%) rename data/{crac/crac-util => ics-importer}/src/test/resources/ics/static_with_gsk_no_startup.csv (100%) rename data/{crac/crac-util => ics-importer}/src/test/resources/ics/static_with_gsk_wrong_shutdown.csv (100%) rename data/{crac/crac-util => ics-importer}/src/test/resources/ics/static_with_gsk_wrong_startup.csv (100%) rename data/{crac/crac-util => ics-importer}/src/test/resources/ics/static_wrong_shutdown.csv (88%) rename data/{crac/crac-util => ics-importer}/src/test/resources/ics/static_wrong_startup.csv (100%) create mode 100644 data/ics-importer/src/test/resources/network/12Nodes_with_Xnodes.xiidm create mode 100644 data/ics-importer/src/test/resources/network/2Nodes2ParallelLinesPST_0030.uct create mode 100644 data/ics-importer/src/test/resources/network/2Nodes2ParallelLinesPST_0130.uct diff --git a/data/crac/crac-util/src/test/java/com/powsybl/openrao/data/crac/util/IcsImporterTest.java b/data/crac/crac-util/src/test/java/com/powsybl/openrao/data/crac/util/IcsImporterTest.java index f38030ec0b..46c6219def 100644 --- a/data/crac/crac-util/src/test/java/com/powsybl/openrao/data/crac/util/IcsImporterTest.java +++ b/data/crac/crac-util/src/test/java/com/powsybl/openrao/data/crac/util/IcsImporterTest.java @@ -92,92 +92,7 @@ void tearDown() { } } - @Test - void testIcsImporterOneAction() throws IOException { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - IcsImporter.populateInputWithICS(timeCoupledRaoInputWithNetworkPaths, staticInputStream, seriesInputStream, gskInputStream, cost, cost); - - assertEquals(1, timeCoupledRaoInputWithNetworkPaths.getTimeCoupledConstraints().getGeneratorConstraints().size()); - GeneratorConstraints generatorConstraints = timeCoupledRaoInputWithNetworkPaths.getTimeCoupledConstraints().getGeneratorConstraints().iterator().next(); - assertEquals("Redispatching_RA_BBE1AA1_GENERATOR", generatorConstraints.getGeneratorId()); - assertTrue(generatorConstraints.getDownwardPowerGradient().isPresent()); - assertEquals(-10., generatorConstraints.getDownwardPowerGradient().get(), DOUBLE_EPSILON); - assertTrue(generatorConstraints.getUpwardPowerGradient().isPresent()); - assertEquals(10., generatorConstraints.getUpwardPowerGradient().get(), DOUBLE_EPSILON); - assertTrue(generatorConstraints.getLeadTime().isPresent()); - assertEquals(1.0, generatorConstraints.getLeadTime().get(), DOUBLE_EPSILON); - assertTrue(generatorConstraints.getLagTime().isPresent()); - assertEquals(1.0, generatorConstraints.getLagTime().get(), DOUBLE_EPSILON); - assertFalse(generatorConstraints.isShutDownAllowed()); - assertFalse(generatorConstraints.isStartUpAllowed()); - - assertEquals(1, crac1.getInjectionRangeActions().size()); - InjectionRangeAction ra1 = crac1.getInjectionRangeActions().iterator().next(); - assertEquals("Redispatching_RA_RD", ra1.getId()); - assertEquals(116., ra1.getInitialSetpoint(), DOUBLE_EPSILON); - assertTrue(ra1.getVariationCost(VariationDirection.UP).isPresent()); - assertEquals(5., ra1.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); - assertTrue(ra1.getVariationCost(VariationDirection.DOWN).isPresent()); - assertEquals(5., ra1.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); - Network network1 = Network.read(networkFilePathPostIcsImport1); - Generator generator1 = network1.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); - assertEquals(116., generator1.getTargetP(), DOUBLE_EPSILON); - assertEquals(10.0, generator1.getMinP(), DOUBLE_EPSILON); - assertEquals(1, crac2.getInjectionRangeActions().size()); - InjectionRangeAction ra2 = crac2.getInjectionRangeActions().iterator().next(); - assertEquals("Redispatching_RA_RD", ra2.getId()); - assertEquals(120., ra2.getInitialSetpoint(), DOUBLE_EPSILON); - assertTrue(ra2.getVariationCost(VariationDirection.UP).isPresent()); - assertEquals(5., ra2.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); - assertTrue(ra2.getVariationCost(VariationDirection.DOWN).isPresent()); - assertEquals(5., ra2.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); - Network network2 = Network.read(networkFilePathPostIcsImport2); - Generator generator2 = network2.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); - assertEquals(120., generator2.getTargetP(), DOUBLE_EPSILON); - assertEquals(15.0, generator2.getMinP(), DOUBLE_EPSILON); - } - - @Test - void testIcsImporterShutDownAndStartUpTrue() throws IOException { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_shutdown_startup_true.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - IcsImporter.populateInputWithICS(timeCoupledRaoInputWithNetworkPaths, staticInputStream, seriesInputStream, gskInputStream, cost, cost); - - assertEquals(1, timeCoupledRaoInputWithNetworkPaths.getTimeCoupledConstraints().getGeneratorConstraints().size()); - GeneratorConstraints generatorConstraints = timeCoupledRaoInputWithNetworkPaths.getTimeCoupledConstraints().getGeneratorConstraints().iterator().next(); - assertTrue(generatorConstraints.isShutDownAllowed()); - assertTrue(generatorConstraints.isStartUpAllowed()); - } - - @Test - void testIcsImporterNoShutDown() { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_no_shutdown.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - - Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInputWithNetworkPaths, staticInputStream, seriesInputStream, gskInputStream, cost, cost)) - .withMessage("Could not parse shutDownAllowed value for raId Redispatching_RA"); - } - - @Test - void testIcsImporterWrongShutDown() { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_wrong_shutdown.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - - Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInputWithNetworkPaths, staticInputStream, seriesInputStream, gskInputStream, cost, cost)) - .withMessage("Could not parse shutDownAllowed value wrongValue for raId Redispatching_RA"); - } @Test void testIcsImporterNoStartUp() { @@ -360,55 +275,5 @@ void testIcsImporterGradientNotOk() throws IOException { assertEquals(0, crac2.getInjectionRangeActions().size()); } - @Test - void testIcsImporterWithGSK() throws IOException { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_with_gsk.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - IcsImporter.populateInputWithICS(timeCoupledRaoInputWithNetworkPaths, staticInputStream, seriesInputStream, gskInputStream, cost, cost); - - assertEquals(2, timeCoupledRaoInputWithNetworkPaths.getTimeCoupledConstraints().getGeneratorConstraints().size()); - GeneratorConstraints generatorConstraintsBE = timeCoupledRaoInputWithNetworkPaths.getTimeCoupledConstraints().getGeneratorConstraints().stream() - .filter(gc -> gc.getGeneratorId().contains("BE")) - .findFirst().orElseThrow(); - assertEquals("Redispatching_RA_BBE1AA1_GENERATOR", generatorConstraintsBE.getGeneratorId()); - assertEquals(-6., generatorConstraintsBE.getDownwardPowerGradient().orElseThrow(), DOUBLE_EPSILON); - assertEquals(6., generatorConstraintsBE.getUpwardPowerGradient().orElseThrow(), DOUBLE_EPSILON); - assertTrue(generatorConstraintsBE.getLeadTime().isPresent()); - assertEquals(1.0, generatorConstraintsBE.getLeadTime().get(), DOUBLE_EPSILON); - assertTrue(generatorConstraintsBE.getLagTime().isPresent()); - assertEquals(1.0, generatorConstraintsBE.getLagTime().get(), DOUBLE_EPSILON); - assertFalse(generatorConstraintsBE.isShutDownAllowed()); - assertFalse(generatorConstraintsBE.isStartUpAllowed()); - GeneratorConstraints generatorConstraintsFR = timeCoupledRaoInputWithNetworkPaths.getTimeCoupledConstraints().getGeneratorConstraints().stream() - .filter(gc -> gc.getGeneratorId().contains("FR")) - .findFirst().orElseThrow(); - assertEquals("Redispatching_RA_FFR1AA1_GENERATOR", generatorConstraintsFR.getGeneratorId()); - assertEquals(-4., generatorConstraintsFR.getDownwardPowerGradient().orElseThrow(), DOUBLE_EPSILON); - assertEquals(4., generatorConstraintsFR.getUpwardPowerGradient().orElseThrow(), DOUBLE_EPSILON); - assertTrue(generatorConstraintsFR.getLeadTime().isPresent()); - assertEquals(1.0, generatorConstraintsFR.getLeadTime().get(), DOUBLE_EPSILON); - assertTrue(generatorConstraintsFR.getLagTime().isPresent()); - assertEquals(1.0, generatorConstraintsFR.getLagTime().get(), DOUBLE_EPSILON); - assertFalse(generatorConstraintsFR.isShutDownAllowed()); - assertFalse(generatorConstraintsFR.isStartUpAllowed()); - assertEquals(1, crac1.getInjectionRangeActions().size()); - InjectionRangeAction ra1 = crac1.getInjectionRangeActions().iterator().next(); - assertEquals("Redispatching_RA_RD", ra1.getId()); - assertEquals(116., ra1.getInitialSetpoint(), DOUBLE_EPSILON); - assertTrue(ra1.getVariationCost(VariationDirection.UP).isPresent()); - assertEquals(5., ra1.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); - assertTrue(ra1.getVariationCost(VariationDirection.DOWN).isPresent()); - assertEquals(5., ra1.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); - - Network network1 = Network.read(networkFilePathPostIcsImport1); - Generator generatorBE = network1.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); - assertEquals(116. * 0.6, generatorBE.getTargetP(), DOUBLE_EPSILON); - assertEquals(10.0 * 0.6, generatorBE.getMinP(), DOUBLE_EPSILON); - Generator generatorFR = network1.getGenerator("Redispatching_RA_FFR1AA1_GENERATOR"); - assertEquals(116. * 0.4, generatorFR.getTargetP(), DOUBLE_EPSILON); - assertEquals(10.0 * 0.4, generatorFR.getMinP(), DOUBLE_EPSILON); - } } diff --git a/data/ics-importer/pom.xml b/data/ics-importer/pom.xml index fe62f3dedc..727b15bf84 100644 --- a/data/ics-importer/pom.xml +++ b/data/ics-importer/pom.xml @@ -25,9 +25,61 @@ org.apache.commons commons-csv + + + - org.apache.commons - commons-csv + org.junit.jupiter + junit-jupiter + test + + + ${project.groupId} + open-rao-crac-impl + ${project.version} + test-jar + test + + + ${project.groupId} + open-rao-crac-impl + test + + + ${project.groupId} + open-rao-crac-io-json + test + + + com.powsybl + powsybl-config-test + test + + + com.powsybl + powsybl-iidm-impl + test + + + com.powsybl + powsybl-ucte-converter + test + + + org.assertj + assertj-core + test + + + org.junit.jupiter + junit-jupiter + test + + + + commons-io + commons-io + runtime diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java index c8fea09200..221345d380 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java @@ -16,6 +16,7 @@ import com.powsybl.openrao.data.crac.api.rangeaction.VariationDirection; import com.powsybl.openrao.data.timecoupledconstraints.GeneratorConstraints; import com.powsybl.openrao.data.timecoupledconstraints.TimeCoupledConstraints; +import com.powsybl.openrao.raoapi.TimeCoupledRaoInputWithNetworkPaths; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVRecord; @@ -107,13 +108,13 @@ public Set createGeneratorConstraints(String raId, Map> parseGskCsv(CSVFormat csvFormat, // Consistency check functions private static boolean shouldBeImported(CSVRecord staticRecord, List sortedTimestampToRun, Map> weightPerNodePerGsk, Map> timeseriesPerIdAndType) { + //TODO: check that sum of GSK if defined on one equal to 1 // remedial action should at least be defined on preventive instant boolean isPreventive = staticRecord.get(PREVENTIVE).equalsIgnoreCase(TRUE); diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java new file mode 100644 index 0000000000..d2abc997f2 --- /dev/null +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2026, 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; + +import com.powsybl.iidm.network.Generator; +import com.powsybl.iidm.network.Network; +import com.powsybl.openrao.commons.OpenRaoException; +import com.powsybl.openrao.commons.TemporalData; +import com.powsybl.openrao.commons.TemporalDataImpl; +import com.powsybl.openrao.data.crac.api.Crac; +import com.powsybl.openrao.data.crac.api.rangeaction.InjectionRangeAction; +import com.powsybl.openrao.data.crac.api.rangeaction.VariationDirection; +import com.powsybl.openrao.data.timecoupledconstraints.GeneratorConstraints; +import com.powsybl.openrao.raoapi.TimeCoupledRaoInputWithNetworkPaths; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.*; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Roxane Chen {@literal } + */ +public class IcsDataTest { + private static final double DOUBLE_EPSILON = 1e-6; + private static final String TMP_DIR = System.getProperty("java.io.tmpdir") + File.separator; + private String networkFilePathPostIcsImport1; + private String networkFilePathPostIcsImport2; + private Crac crac1; + private Crac crac2; + private Network network1; + private Network network2; + private TimeCoupledRaoInputWithNetworkPaths timeCoupledRaoInputWithNetworkPaths; + private TemporalData networkTemporalData; + private TemporalData cracTemporalData; + + @BeforeEach + void setUp() throws IOException { + // we need to import twice the network to avoid variant names conflicts on the same network object + String networkFilePath1 = "2Nodes2ParallelLinesPST_0030.uct"; + String networkFilePath2 = "2Nodes2ParallelLinesPST_0130.uct"; + network1 = Network.read(networkFilePath1, IcsDataTest.class.getResourceAsStream("/network/" + networkFilePath1)); + network2 = Network.read(networkFilePath2, IcsDataTest.class.getResourceAsStream("/network/" + networkFilePath2)); + + crac1 = Crac.read("/crac/crac-0030.json", getClass().getResourceAsStream("/crac/crac-0030.json"), network1); + crac2 = Crac.read("/crac/crac-0130.json", getClass().getResourceAsStream("/crac/crac-0130.json"), network2); + + OffsetDateTime timestamp1 = OffsetDateTime.of(2025, 2, 13, 0, 30, 0, 0, ZoneOffset.UTC); + OffsetDateTime timestamp2 = OffsetDateTime.of(2025, 2, 13, 1, 30, 0, 0, ZoneOffset.UTC); + + networkTemporalData = new TemporalDataImpl<>( + Map.of( + timestamp1, network1, + timestamp2, network2 + )); + + cracTemporalData = new TemporalDataImpl<>( + Map.of( + timestamp1, crac1, + timestamp2, crac2 + )); + } + + public static List generateOffsetDateTimeList() { + List dateTimes = new ArrayList<>(); + + OffsetDateTime start = OffsetDateTime.of(2025, 2, 13, 0, 30, 0, 0, ZoneOffset.UTC); + OffsetDateTime end = OffsetDateTime.of(2025, 2, 13, 23, 30, 0, 0, ZoneOffset.UTC); + + OffsetDateTime current = start; + while (!current.isAfter(end)) { + dateTimes.add(current); + current = current.plusHours(1); + } + + return dateTimes; + } + + @Test + void testIcsDataReadOkNode() throws IOException { + // Test generic case without any error + // one remedial action "Redispatching_RA" defined on the node "BBE1AA1" + + // Read ICS Data + IcsData icsData = IcsData.read( + getClass().getResourceAsStream("/ics/static.csv"), + getClass().getResourceAsStream("/ics/series.csv"), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList()); + + assertEquals(1, icsData.getStaticConstraintPerId().size()); + assertEquals(4, icsData.getTimeseriesPerIdAndType().get("Redispatching_RA").size()); + + // Check generator constraint creation + Set generatorConstraintsSet = icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", 1.0), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1")) ); + + assertEquals(1, generatorConstraintsSet.size()); + GeneratorConstraints generatorConstraints = generatorConstraintsSet.iterator().next(); + assertEquals("Redispatching_RA_BBE1AA1_GENERATOR", generatorConstraints.getGeneratorId()); + assertTrue(generatorConstraints.getDownwardPowerGradient().isPresent()); + assertEquals(-20., generatorConstraints.getDownwardPowerGradient().get(), DOUBLE_EPSILON); + assertTrue(generatorConstraints.getUpwardPowerGradient().isPresent()); + assertEquals(20., generatorConstraints.getUpwardPowerGradient().get(), DOUBLE_EPSILON); + assertTrue(generatorConstraints.getLeadTime().isPresent()); + assertEquals(1.0, generatorConstraints.getLeadTime().get(), DOUBLE_EPSILON); + assertTrue(generatorConstraints.getLagTime().isPresent()); + assertEquals(1.0, generatorConstraints.getLagTime().get(), DOUBLE_EPSILON); + assertFalse(generatorConstraints.isShutDownAllowed()); + assertFalse(generatorConstraints.isStartUpAllowed()); + + // Test generator creation in network + Map generatorIdPerNodeId = icsData.createGeneratorAndLoadAndUpdateNetworks(networkTemporalData, "Redispatching_RA", Map.of("BBE1AA1", 1.0)); + Generator generator1 = network1.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); + assertEquals(116., generator1.getTargetP(), DOUBLE_EPSILON); + assertEquals(10.0, generator1.getMinP(), DOUBLE_EPSILON); + Generator generator2 = network2.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); + assertEquals(120., generator2.getTargetP(), DOUBLE_EPSILON); + assertEquals(15.0, generator2.getMinP(), DOUBLE_EPSILON); + + // Test injection range action creation in crac + icsData.createInjectionRangeActionsAndUpdateCracs(cracTemporalData, "Redispatching_RA", Map.of("BBE1AA1", 1.0), generatorIdPerNodeId, 5., 5.); + assertEquals(1, crac1.getInjectionRangeActions().size()); + InjectionRangeAction ra1 = crac1.getInjectionRangeActions().iterator().next(); + assertEquals("Redispatching_RA_RD", ra1.getId()); + assertEquals(116., ra1.getInitialSetpoint(), DOUBLE_EPSILON); + assertTrue(ra1.getVariationCost(VariationDirection.UP).isPresent()); + assertEquals(5., ra1.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); + assertTrue(ra1.getVariationCost(VariationDirection.DOWN).isPresent()); + assertEquals(5., ra1.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); + + assertEquals(1, crac2.getInjectionRangeActions().size()); + InjectionRangeAction ra2 = crac2.getInjectionRangeActions().iterator().next(); + assertEquals("Redispatching_RA_RD", ra2.getId()); + assertEquals(120., ra2.getInitialSetpoint(), DOUBLE_EPSILON); + assertTrue(ra2.getVariationCost(VariationDirection.UP).isPresent()); + assertEquals(5., ra2.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); + assertTrue(ra2.getVariationCost(VariationDirection.DOWN).isPresent()); + assertEquals(5., ra2.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); + } + + @Test + void testIcsImporterWithGSK() throws IOException { + // Test generic case without any error + // one remedial action "Redispatching_RA" defined on a GSK "GSK_NAME" + + // Read ICS Data + IcsData icsData = IcsData.read( + getClass().getResourceAsStream("/ics/static_with_gsk.csv"), + getClass().getResourceAsStream("/ics/series.csv"), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList()); + + assertEquals(1, icsData.getStaticConstraintPerId().size()); + assertEquals(4, icsData.getTimeseriesPerIdAndType().get("Redispatching_RA").size()); + assertEquals(1, icsData.getWeightPerNodePerGsk().size()); + assertEquals(2, icsData.getWeightPerNodePerGsk().get("GSK_NAME").size()); + assertEquals(Map.of("BBE1AA1", 0.6, "FFR1AA1", 0.4), icsData.getWeightPerNodePerGsk().get("GSK_NAME")); + + Map generatorIdPerNodeId = icsData.createGeneratorAndLoadAndUpdateNetworks(networkTemporalData, "Redispatching_RA", icsData.getWeightPerNodePerGsk().get("GSK_NAME")); + assertEquals(Map.of("BBE1AA1", "Redispatching_RA_BBE1AA1_GENERATOR", "FFR1AA1", "Redispatching_RA_FFR1AA1_GENERATOR"), generatorIdPerNodeId); + + Set generatorConstraintsSet = icsData.createGeneratorConstraints("Redispatching_RA", icsData.getWeightPerNodePerGsk().get("GSK_NAME"), generatorIdPerNodeId); + + icsData.createInjectionRangeActionsAndUpdateCracs(cracTemporalData, "Redispatching_RA", icsData.getWeightPerNodePerGsk().get("GSK_NAME"), generatorIdPerNodeId, 5., 5.); + + assertEquals(2, generatorConstraintsSet.size()); + GeneratorConstraints generatorConstraintsBE = generatorConstraintsSet.stream() + .filter(gc -> gc.getGeneratorId().contains("BE")) + .findFirst().orElseThrow(); + assertEquals("Redispatching_RA_BBE1AA1_GENERATOR", generatorConstraintsBE.getGeneratorId()); + assertEquals(-12., generatorConstraintsBE.getDownwardPowerGradient().orElseThrow(), DOUBLE_EPSILON); // 20*0.6 + assertEquals(12., generatorConstraintsBE.getUpwardPowerGradient().orElseThrow(), DOUBLE_EPSILON); + assertTrue(generatorConstraintsBE.getLeadTime().isPresent()); + assertEquals(1.0, generatorConstraintsBE.getLeadTime().get(), DOUBLE_EPSILON); + assertTrue(generatorConstraintsBE.getLagTime().isPresent()); + assertEquals(1.0, generatorConstraintsBE.getLagTime().get(), DOUBLE_EPSILON); + assertFalse(generatorConstraintsBE.isShutDownAllowed()); + assertFalse(generatorConstraintsBE.isStartUpAllowed()); + GeneratorConstraints generatorConstraintsFR = generatorConstraintsSet.stream() + .filter(gc -> gc.getGeneratorId().contains("FR")) + .findFirst().orElseThrow(); + assertEquals("Redispatching_RA_FFR1AA1_GENERATOR", generatorConstraintsFR.getGeneratorId()); + assertEquals(-8., generatorConstraintsFR.getDownwardPowerGradient().orElseThrow(), DOUBLE_EPSILON); //20*0.4 + assertEquals(8., generatorConstraintsFR.getUpwardPowerGradient().orElseThrow(), DOUBLE_EPSILON); + assertTrue(generatorConstraintsFR.getLeadTime().isPresent()); + assertEquals(1.0, generatorConstraintsFR.getLeadTime().get(), DOUBLE_EPSILON); + assertTrue(generatorConstraintsFR.getLagTime().isPresent()); + assertEquals(1.0, generatorConstraintsFR.getLagTime().get(), DOUBLE_EPSILON); + assertFalse(generatorConstraintsFR.isShutDownAllowed()); + assertFalse(generatorConstraintsFR.isStartUpAllowed()); + + assertEquals(1, crac1.getInjectionRangeActions().size()); + InjectionRangeAction ra1 = crac1.getInjectionRangeActions().iterator().next(); + assertEquals("Redispatching_RA_RD", ra1.getId()); + assertEquals(116., ra1.getInitialSetpoint(), DOUBLE_EPSILON); + assertTrue(ra1.getVariationCost(VariationDirection.UP).isPresent()); + assertEquals(5., ra1.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); + assertTrue(ra1.getVariationCost(VariationDirection.DOWN).isPresent()); + assertEquals(5., ra1.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); + + Generator generatorBE = network1.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); + assertEquals(116. * 0.6, generatorBE.getTargetP(), DOUBLE_EPSILON); + assertEquals(10.0 * 0.6, generatorBE.getMinP(), DOUBLE_EPSILON); + Generator generatorFR = network1.getGenerator("Redispatching_RA_FFR1AA1_GENERATOR"); + assertEquals(116. * 0.4, generatorFR.getTargetP(), DOUBLE_EPSILON); + assertEquals(10.0 * 0.4, generatorFR.getMinP(), DOUBLE_EPSILON); + } + + @Test + void testIcsDataReadNok() { + // Test reader, where some remedial action need to be filtered out + } + + @Test + void testIcsImporterShutDownAndStartUpTrue() throws IOException { + // Check that we take into account the shutDownAllowed and startUpAllowed flag in the ICS file + IcsData icsData = IcsData.read( + getClass().getResourceAsStream("/ics/static_shutdown_startup_true.csv"), + getClass().getResourceAsStream("/ics/series.csv"), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList()); + + Set generatorConstraintsSet = icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", 1.0), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1")) ); + + assertEquals(1, generatorConstraintsSet.size()); + GeneratorConstraints generatorConstraints = generatorConstraintsSet.iterator().next(); + assertTrue(generatorConstraints.isShutDownAllowed()); + assertTrue(generatorConstraints.isStartUpAllowed()); + } + + @Test + void testIcsImporterNoShutDown() throws IOException { + // Check that an error is thrown if the shutDownAllowed flag is not present + + IcsData icsData = IcsData.read( + getClass().getResourceAsStream("/ics/static_no_shutdown.csv"), + getClass().getResourceAsStream("/ics/series.csv"), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList()); + + Assertions.assertThatExceptionOfType(OpenRaoException.class) + .isThrownBy(() -> icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", 1.0), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1")))) + .withMessage("Could not parse shutDownAllowed value for raId Redispatching_RA: "); + } + + @Test + void testIcsImporterWrongShutDown() throws IOException { + IcsData icsData = IcsData.read( + getClass().getResourceAsStream("/ics/static_wrong_shutdown.csv"), + getClass().getResourceAsStream("/ics/series.csv"), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList()); + + Assertions.assertThatExceptionOfType(OpenRaoException.class) + .isThrownBy(() -> icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", 1.0), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1")))) + .withMessage("Could not parse shutDownAllowed value for raId Redispatching_RA: wrongValue"); + } + + + @Test + void testIcsImporterNoStartUp() { + double cost = 5.; + InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_no_startup.csv"); + InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); + InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); + + Assertions.assertThatExceptionOfType(OpenRaoException.class) + .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInputWithNetworkPaths, staticInputStream, seriesInputStream, gskInputStream, cost, cost)) + .withMessage("Could not parse startUpAllowed value for raId Redispatching_RA"); + } + + @Test + void testIcsImporterWrongStartUp() { + double cost = 5.; + InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_wrong_startup.csv"); + InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); + InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); + + Assertions.assertThatExceptionOfType(OpenRaoException.class) + .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInputWithNetworkPaths, staticInputStream, seriesInputStream, gskInputStream, cost, cost)) + .withMessage("Could not parse startUpAllowed value wrongValue for raId Redispatching_RA"); + } +} \ No newline at end of file diff --git a/data/crac/crac-util/src/test/resources/crac/crac-0030.json b/data/ics-importer/src/test/resources/crac/crac-0030.json similarity index 100% rename from data/crac/crac-util/src/test/resources/crac/crac-0030.json rename to data/ics-importer/src/test/resources/crac/crac-0030.json diff --git a/data/crac/crac-util/src/test/resources/crac/crac-0130.json b/data/ics-importer/src/test/resources/crac/crac-0130.json similarity index 100% rename from data/crac/crac-util/src/test/resources/crac/crac-0130.json rename to data/ics-importer/src/test/resources/crac/crac-0130.json diff --git a/data/crac/crac-util/src/test/resources/glsk/gsk.csv b/data/ics-importer/src/test/resources/glsk/gsk.csv similarity index 100% rename from data/crac/crac-util/src/test/resources/glsk/gsk.csv rename to data/ics-importer/src/test/resources/glsk/gsk.csv diff --git a/data/crac/crac-util/src/test/resources/ics/series.csv b/data/ics-importer/src/test/resources/ics/series.csv similarity index 100% rename from data/crac/crac-util/src/test/resources/ics/series.csv rename to data/ics-importer/src/test/resources/ics/series.csv diff --git a/data/crac/crac-util/src/test/resources/ics/series_gradient_not_ok.csv b/data/ics-importer/src/test/resources/ics/series_gradient_not_ok.csv similarity index 100% rename from data/crac/crac-util/src/test/resources/ics/series_gradient_not_ok.csv rename to data/ics-importer/src/test/resources/ics/series_gradient_not_ok.csv diff --git a/data/crac/crac-util/src/test/resources/ics/series_no_pmin_rd.csv b/data/ics-importer/src/test/resources/ics/series_no_pmin_rd.csv similarity index 100% rename from data/crac/crac-util/src/test/resources/ics/series_no_pmin_rd.csv rename to data/ics-importer/src/test/resources/ics/series_no_pmin_rd.csv diff --git a/data/crac/crac-util/src/test/resources/ics/static.csv b/data/ics-importer/src/test/resources/ics/static.csv similarity index 89% rename from data/crac/crac-util/src/test/resources/ics/static.csv rename to data/ics-importer/src/test/resources/ics/static.csv index 39f5ab7ee0..4ec750bbc7 100644 --- a/data/crac/crac-util/src/test/resources/ics/static.csv +++ b/data/ics-importer/src/test/resources/ics/static.csv @@ -1,2 +1,2 @@ RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed -Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;10;10;1;1;FALSE;FALSE \ No newline at end of file +Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;20;20;1;1;FALSE;FALSE \ No newline at end of file diff --git a/data/crac/crac-util/src/test/resources/ics/static_no_gradient_no_lead_no_lag.csv b/data/ics-importer/src/test/resources/ics/static_no_gradient_no_lead_no_lag.csv similarity index 100% rename from data/crac/crac-util/src/test/resources/ics/static_no_gradient_no_lead_no_lag.csv rename to data/ics-importer/src/test/resources/ics/static_no_gradient_no_lead_no_lag.csv diff --git a/data/crac/crac-util/src/test/resources/ics/static_no_shutdown.csv b/data/ics-importer/src/test/resources/ics/static_no_shutdown.csv similarity index 90% rename from data/crac/crac-util/src/test/resources/ics/static_no_shutdown.csv rename to data/ics-importer/src/test/resources/ics/static_no_shutdown.csv index a8dbae4fdd..65e2c4c016 100644 --- a/data/crac/crac-util/src/test/resources/ics/static_no_shutdown.csv +++ b/data/ics-importer/src/test/resources/ics/static_no_shutdown.csv @@ -1,2 +1,2 @@ RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed -Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;10;10;1;1;FALSE;; \ No newline at end of file +Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;20;20;1;1;FALSE;; \ No newline at end of file diff --git a/data/crac/crac-util/src/test/resources/ics/static_no_startup.csv b/data/ics-importer/src/test/resources/ics/static_no_startup.csv similarity index 100% rename from data/crac/crac-util/src/test/resources/ics/static_no_startup.csv rename to data/ics-importer/src/test/resources/ics/static_no_startup.csv diff --git a/data/crac/crac-util/src/test/resources/ics/static_shutdown_startup_true.csv b/data/ics-importer/src/test/resources/ics/static_shutdown_startup_true.csv similarity index 90% rename from data/crac/crac-util/src/test/resources/ics/static_shutdown_startup_true.csv rename to data/ics-importer/src/test/resources/ics/static_shutdown_startup_true.csv index 654d8b7a6c..129c323405 100644 --- a/data/crac/crac-util/src/test/resources/ics/static_shutdown_startup_true.csv +++ b/data/ics-importer/src/test/resources/ics/static_shutdown_startup_true.csv @@ -1,2 +1,2 @@ RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed -Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;10;10;1;1;TRUE;TRUE \ No newline at end of file +Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;20;20;1;1;TRUE;TRUE \ No newline at end of file diff --git a/data/crac/crac-util/src/test/resources/ics/static_with_gsk.csv b/data/ics-importer/src/test/resources/ics/static_with_gsk.csv similarity index 89% rename from data/crac/crac-util/src/test/resources/ics/static_with_gsk.csv rename to data/ics-importer/src/test/resources/ics/static_with_gsk.csv index 3e1e6354ce..e4325e596e 100644 --- a/data/crac/crac-util/src/test/resources/ics/static_with_gsk.csv +++ b/data/ics-importer/src/test/resources/ics/static_with_gsk.csv @@ -1,2 +1,2 @@ RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed -Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;GSK;GSK_NAME;50;Coal;2;2;10;10;1;1;FALSE;FALSE \ No newline at end of file +Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;GSK;GSK_NAME;50;Coal;2;2;20;20;1;1;FALSE;FALSE \ No newline at end of file diff --git a/data/crac/crac-util/src/test/resources/ics/static_with_gsk_no_shutdown.csv b/data/ics-importer/src/test/resources/ics/static_with_gsk_no_shutdown.csv similarity index 100% rename from data/crac/crac-util/src/test/resources/ics/static_with_gsk_no_shutdown.csv rename to data/ics-importer/src/test/resources/ics/static_with_gsk_no_shutdown.csv diff --git a/data/crac/crac-util/src/test/resources/ics/static_with_gsk_no_startup.csv b/data/ics-importer/src/test/resources/ics/static_with_gsk_no_startup.csv similarity index 100% rename from data/crac/crac-util/src/test/resources/ics/static_with_gsk_no_startup.csv rename to data/ics-importer/src/test/resources/ics/static_with_gsk_no_startup.csv diff --git a/data/crac/crac-util/src/test/resources/ics/static_with_gsk_wrong_shutdown.csv b/data/ics-importer/src/test/resources/ics/static_with_gsk_wrong_shutdown.csv similarity index 100% rename from data/crac/crac-util/src/test/resources/ics/static_with_gsk_wrong_shutdown.csv rename to data/ics-importer/src/test/resources/ics/static_with_gsk_wrong_shutdown.csv diff --git a/data/crac/crac-util/src/test/resources/ics/static_with_gsk_wrong_startup.csv b/data/ics-importer/src/test/resources/ics/static_with_gsk_wrong_startup.csv similarity index 100% rename from data/crac/crac-util/src/test/resources/ics/static_with_gsk_wrong_startup.csv rename to data/ics-importer/src/test/resources/ics/static_with_gsk_wrong_startup.csv diff --git a/data/crac/crac-util/src/test/resources/ics/static_wrong_shutdown.csv b/data/ics-importer/src/test/resources/ics/static_wrong_shutdown.csv similarity index 88% rename from data/crac/crac-util/src/test/resources/ics/static_wrong_shutdown.csv rename to data/ics-importer/src/test/resources/ics/static_wrong_shutdown.csv index 4a469f871c..59bf39e939 100644 --- a/data/crac/crac-util/src/test/resources/ics/static_wrong_shutdown.csv +++ b/data/ics-importer/src/test/resources/ics/static_wrong_shutdown.csv @@ -1,2 +1,2 @@ RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed -Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;10;10;1;1;FALSE;wrongValue \ No newline at end of file +Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;20;20;1;1;FALSE;wrongValue \ No newline at end of file diff --git a/data/crac/crac-util/src/test/resources/ics/static_wrong_startup.csv b/data/ics-importer/src/test/resources/ics/static_wrong_startup.csv similarity index 100% rename from data/crac/crac-util/src/test/resources/ics/static_wrong_startup.csv rename to data/ics-importer/src/test/resources/ics/static_wrong_startup.csv diff --git a/data/ics-importer/src/test/resources/network/12Nodes_with_Xnodes.xiidm b/data/ics-importer/src/test/resources/network/12Nodes_with_Xnodes.xiidm new file mode 100644 index 0000000000..acdf6ceced --- /dev/null +++ b/data/ics-importer/src/test/resources/network/12Nodes_with_Xnodes.xiidm @@ -0,0 +1,283 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/ics-importer/src/test/resources/network/2Nodes2ParallelLinesPST_0030.uct b/data/ics-importer/src/test/resources/network/2Nodes2ParallelLinesPST_0030.uct new file mode 100644 index 0000000000..c4b652e668 --- /dev/null +++ b/data/ics-importer/src/test/resources/network/2Nodes2ParallelLinesPST_0030.uct @@ -0,0 +1,12 @@ +##C 2007.05.01 +##N +##ZBE +BBE1AA1 BE1 0 2 400.00 0.00000 0.00000 -1000.0 0.00000 9000.00 -9000.0 9000.00 -9000.0 +##ZFR +FFR1AA1 FR1 0 2 400.00 1000.00 0.00000 00000.0 0.00000 9000.00 -9000.0 9000.00 -9000.0 +##L +BBE1AA1 FFR1AA1 1 0 0.0000 10.000 0.000000 5000 +##T +BBE1AA1 FFR1AA1 2 0 400.0 400.0 1000. 0.0000 10.000 0.000000 0.0 5000 PST +##R +BBE1AA1 FFR1AA1 2 -0.68 90.00 16 0 SYMM diff --git a/data/ics-importer/src/test/resources/network/2Nodes2ParallelLinesPST_0130.uct b/data/ics-importer/src/test/resources/network/2Nodes2ParallelLinesPST_0130.uct new file mode 100644 index 0000000000..c4b652e668 --- /dev/null +++ b/data/ics-importer/src/test/resources/network/2Nodes2ParallelLinesPST_0130.uct @@ -0,0 +1,12 @@ +##C 2007.05.01 +##N +##ZBE +BBE1AA1 BE1 0 2 400.00 0.00000 0.00000 -1000.0 0.00000 9000.00 -9000.0 9000.00 -9000.0 +##ZFR +FFR1AA1 FR1 0 2 400.00 1000.00 0.00000 00000.0 0.00000 9000.00 -9000.0 9000.00 -9000.0 +##L +BBE1AA1 FFR1AA1 1 0 0.0000 10.000 0.000000 5000 +##T +BBE1AA1 FFR1AA1 2 0 400.0 400.0 1000. 0.0000 10.000 0.000000 0.0 5000 PST +##R +BBE1AA1 FFR1AA1 2 -0.68 90.00 16 0 SYMM diff --git a/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java b/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java index 42960e7bac..3cae1f00af 100644 --- a/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java +++ b/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java @@ -291,6 +291,7 @@ public static void loadDataForCoreTimeCoupledRao(DataTable arg1) throws IOExcept } // Create Injection Range Actions in CRACs icsData.createInjectionRangeActionsAndUpdateCracs(cracToModify, raId, weightPerNode, generatorIdPerNode, finalFbConstraintParameters.getIcsCostUp(), finalFbConstraintParameters.getIcsCostDown()); + // Create generator constraints and them to time coupled rao input Set generatorConstraintsSet = icsData.createGeneratorConstraints(raId, weightPerNode, generatorIdPerNode); generatorConstraintsSet.forEach(generatorConstraints -> timeCoupledRaoInputWithNetworkPaths.getTimeCoupledConstraints().addGeneratorConstraints(generatorConstraints)); From eeb5fe7f8161c6e9393585bd6b7b8ef8e45fb11b Mon Sep 17 00:00:00 2001 From: Godelaine de Montmorillon Date: Mon, 30 Mar 2026 13:43:47 +0200 Subject: [PATCH 05/64] WIP - initiate p0RespectsGradients function Signed-off-by: Godelaine de Montmorillon --- .../openrao/data/crac/util/IcsImporter.java | 83 +- .../linearproblem/LinearProblem.java | 14 +- .../features/4_time_coupled/US93_5.feature | 865 ++++++++++++++++++ tests/src/test/resources/logback.xml | 2 +- 4 files changed, 950 insertions(+), 14 deletions(-) create mode 100644 tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature diff --git a/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java b/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java index 66e28b473c..3681048da8 100644 --- a/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java +++ b/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java @@ -122,6 +122,14 @@ public static void populateInputWithICS(TimeCoupledRaoInputWithNetworkPaths time if (shouldBeImported(staticRecord, weightPerNodePerGsk)) { String raId = staticRecord.get(RA_RD_ID); Map seriesPerType = seriesPerIdAndType.get(raId); + if (seriesPerType != null && + seriesPerType.containsKey(P0) && + seriesPerType.containsKey(RDP_DOWN) && + seriesPerType.containsKey(RDP_UP) && + rangeIsOkay(seriesPerType, timeCoupledRaoInput.getTimestampsToRun().stream().sorted().toList()) && + p0RespectsGradients(staticRecord, seriesPerType.get(P0), timeCoupledRaoInput.getTimestampsToRun().stream().sorted().toList())) { + p0RespectsConstraints(staticRecord, seriesPerType, timeCoupledRaoInput.getTimestampsToRun().stream().sorted().toList()); + } if (seriesPerType != null && seriesPerType.containsKey(P0) && seriesPerType.containsKey(RDP_DOWN) && @@ -214,12 +222,12 @@ private static void importGskRedispatchingAction(TimeCoupledRaoInputWithNetworkP } private static void importGskRedispatchActionForOneTimestamp(CSVRecord staticRecord, - Map seriesPerType, - String raId, - Map weightPerNode, - OffsetDateTime dateTime, - RaoInputWithNetworkPaths raoInput, - Map networkElementPerGskElement) { + Map seriesPerType, + String raId, + Map weightPerNode, + OffsetDateTime dateTime, + RaoInputWithNetworkPaths raoInput, + Map networkElementPerGskElement) { Crac crac = raoInput.getCrac(); double p0 = parseDoubleWithPossibleCommas(seriesPerType.get(P0).get(dateTime.getHour() + OFFSET)); InjectionRangeActionAdder injectionRangeActionAdder = crac.newInjectionRangeAction() @@ -298,11 +306,11 @@ private static void importNodeRedispatchingAction(TimeCoupledRaoInputWithNetwork } private static void importNodeRedispatchingActionForOneTimestamp(CSVRecord staticRecord, - Map seriesPerType, - String raId, - OffsetDateTime dateTime, - RaoInputWithNetworkPaths raoInput, - String networkElementId) { + Map seriesPerType, + String raId, + OffsetDateTime dateTime, + RaoInputWithNetworkPaths raoInput, + String networkElementId) { Crac crac = raoInput.getCrac(); double p0 = parseDoubleWithPossibleCommas(seriesPerType.get(P0).get(dateTime.getHour() + OFFSET)); InjectionRangeActionAdder injectionRangeActionAdder = crac.newInjectionRangeAction() @@ -350,7 +358,7 @@ private static Optional parseValue(Map seriesPerType, if (seriesPerType.containsKey(key)) { CSVRecord series = seriesPerType.get(key); String value = series.get(timestamp.getHour() + OFFSET); - if (value != null) { + if (value != null && !"".equals(value)) { return Optional.of(parseDoubleWithPossibleCommas(value) * shiftKey); } } @@ -403,6 +411,52 @@ private static boolean shouldBeImported(CSVRecord staticRecord, Map seriesRecord, List dateTimes) { + // 1) check that P0 > Pmin or P0 < 1 + // 2) check that if shutDown not allowed, no switch to 0 + // 3) check that if startUp not allowed, no switch from P0 < 1 to P0 > Pmin + CSVRecord p0 = seriesRecord.get(P0); + Boolean shutDownAllowed = Boolean.parseBoolean(staticRecord.get(SHUTDOWN_ALLOWED)); + Boolean startUpAllowed = Boolean.parseBoolean(staticRecord.get(STARTUP_ALLOWED)); + + Iterator dateTimeIterator = dateTimes.iterator(); + OffsetDateTime currentDateTime = dateTimeIterator.next(); + while (dateTimeIterator.hasNext()) { + OffsetDateTime nextDateTime = dateTimeIterator.next(); + double next_p0 = parseDoubleWithPossibleCommas(p0.get(nextDateTime.getHour() + OFFSET)); + double current_p0 = parseDoubleWithPossibleCommas(p0.get(currentDateTime.getHour() + OFFSET)); + Optional pMinRD = parseValue(seriesRecord, P_MIN_RD, currentDateTime, 1); + double pMin = pMinRD.orElse(ON_POWER_THRESHOLD); + + if (current_p0 < pMin && current_p0 > ON_POWER_THRESHOLD) { + BUSINESS_WARNS.warn("RA {} has P0 at {} and Pmin at {}", staticRecord.get(0), current_p0, pMin); + } + if (current_p0 < pMin && next_p0 > pMin) { + if (!startUpAllowed) { + BUSINESS_WARNS.warn("RA {} starting up even though it's prohibited", staticRecord.get(0)); + } + // TODO : integerer la notion d'arrondi comme dans le filler : lead de 1 => + // TODO : forcer le passage par Pmin pour le lead et le lag => une modification de P0 + if (!staticRecord.get(LEAD_TIME).isEmpty()) { + double lead = parseDoubleWithPossibleCommas(staticRecord.get(LEAD_TIME)); + BUSINESS_WARNS.warn("RA {} starting up at {}. TODO : check lead ({}) is respected", staticRecord.get(0), currentDateTime.getHour(), lead); + } + } + if (current_p0 > pMin && next_p0 < pMin) { + if (!shutDownAllowed) { + BUSINESS_WARNS.warn("RA {} shutting down even though it's prohibited", staticRecord.get(0)); + } + if (!staticRecord.get(LAG_TIME).isEmpty()) { + double lag = parseDoubleWithPossibleCommas(staticRecord.get(LAG_TIME)); + double lead = parseDoubleWithPossibleCommas(staticRecord.get(LEAD_TIME)); + BUSINESS_WARNS.warn("RA {} shutting down at {}. TODO : check lead ({}) + lag ({}) is respected", staticRecord.get(0), currentDateTime.getHour(), lead, lag); + + } + } + + } + } + private static boolean p0RespectsGradients(CSVRecord staticRecord, CSVRecord p0record, List dateTimes) { double maxGradient = staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT).isEmpty() ? MAX_GRADIENT : parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT)); @@ -445,6 +499,11 @@ private static boolean rangeIsOkay(Map seriesPerType, List - + From 1094f0b6bd316643a7c6c52af9f3590285d60334 Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Mon, 30 Mar 2026 14:30:38 +0200 Subject: [PATCH 06/64] add ics data importer Signed-off-by: CHEN Roxane --- .../powsybl/openrao/data/IcsDataImporter.java | 244 +++++++++++++ .../openrao/data/IcsDataImporterTest.java | 340 ++++++++++++++++++ 2 files changed, 584 insertions(+) create mode 100644 data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java create mode 100644 data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataImporterTest.java diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java new file mode 100644 index 0000000000..6120136cdc --- /dev/null +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2026, 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; + +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.*; + +import static com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider.BUSINESS_WARNS; +import static com.powsybl.openrao.data.IcsUtil.*; +import static com.powsybl.openrao.data.IcsUtil.MAXIMUM_NEGATIVE_POWER_GRADIENT; +import static com.powsybl.openrao.data.IcsUtil.MAXIMUM_POSITIVE_POWER_GRADIENT; +import static com.powsybl.openrao.data.IcsUtil.MAX_GRADIENT; +import static com.powsybl.openrao.data.IcsUtil.NODE; +import static com.powsybl.openrao.data.IcsUtil.OFFSET; +import static com.powsybl.openrao.data.IcsUtil.P0; +import static com.powsybl.openrao.data.IcsUtil.PREVENTIVE; +import static com.powsybl.openrao.data.IcsUtil.P_MIN_RD; +import static com.powsybl.openrao.data.IcsUtil.RA_RD_ID; +import static com.powsybl.openrao.data.IcsUtil.RDP_DOWN; +import static com.powsybl.openrao.data.IcsUtil.RDP_UP; +import static com.powsybl.openrao.data.IcsUtil.RD_DESCRIPTION_MODE; +import static com.powsybl.openrao.data.IcsUtil.TRUE; +import static com.powsybl.openrao.data.IcsUtil.UCT_NODE_OR_GSK_ID; +import static com.powsybl.openrao.data.IcsUtil.parseDoubleWithPossibleCommas; + +/** + * @author Roxane Chen {@literal } + */ +public class IcsDataImporter { + + private static CSVFormat csvFormat = CSVFormat.DEFAULT.builder() + .setDelimiter(";") + .setHeader() + .setSkipHeaderRecord(true) + .get(); + + /** + * Reads and processes inputs to generate Ics Data Object + * + * @param staticInputStream the input stream for static constraints data, defining generator constraints + * associated with remedial actions. + * @param seriesInputStream the input stream for time series data, mapped per RA_ID and series type + * (e.g., RDP-, RDP+, Pmin_RD, or P0). + * @param gskInputStream the input stream for GSK data, mapping nodes to their generation shift key weights. + * @param sortedTimestampToRun the list of timestamps to consider + * @return an {@code IcsData} instance + * @throws IOException if an issue occurs while reading or processing the input streams. + */ + public static IcsData read(InputStream staticInputStream, + InputStream seriesInputStream, + InputStream gskInputStream, + List sortedTimestampToRun) throws IOException { + + // Parse and sort per RA_ID and serie type (RDP-, RDP+, Pmin_RD or P0) + Map> timeseriesPerIdAndType = parseSeriesCsv(seriesInputStream); + // Parse GSK and get weight Per Node Per Gsk + Map> weightPerNodePerGsk = parseGskCsv(gskInputStream); + // Parse static CSV: remedial action’s generator’s static constraints. one line per RA_ID + Map staticConstraintPerId = parseStaticCsv(staticInputStream); + + Set consistentRAs = filterOutInconsistentRedispatchingActions(staticConstraintPerId, timeseriesPerIdAndType, weightPerNodePerGsk, sortedTimestampToRun); + + return new IcsData(consistentRAs, timeseriesPerIdAndType, weightPerNodePerGsk, staticConstraintPerId); + + } + + static Set filterOutInconsistentRedispatchingActions(Map staticConstraintPerId, + Map> timeseriesPerIdAndType, + Map> weightPerNodePerGsk, + List sortedTimestampToRun) { + // Get set of consistent redispatching action ID. + Set consistentRAs = new HashSet<>(); + staticConstraintPerId.forEach((raId, record) -> { + if (shouldBeImported(record, sortedTimestampToRun, weightPerNodePerGsk, timeseriesPerIdAndType)) { + consistentRAs.add(raId); + } + }); + // Remove inconsistent RAs from the data structures + staticConstraintPerId.entrySet().removeIf(entry -> !consistentRAs.contains(entry.getKey())); + timeseriesPerIdAndType.entrySet().removeIf(entry -> !consistentRAs.contains(entry.getKey())); + weightPerNodePerGsk.entrySet().removeIf(entry -> !consistentRAs.contains(entry.getKey())); + return consistentRAs; + } + + static Map parseStaticCsv(InputStream staticInputStream) throws IOException { + Iterable staticCsvRecords = csvFormat.parse(new InputStreamReader(staticInputStream)); + Map filteredStaticCsvRecords = new HashMap<>(); + staticCsvRecords.forEach(record -> { + filteredStaticCsvRecords.put(record.get(RA_RD_ID), record); + }); + return filteredStaticCsvRecords; + } + + static Map> parseSeriesCsv(InputStream seriesInputStream) throws IOException { + 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); + }); + + return seriesPerIdAndType; + } + + static Map> parseGskCsv(InputStream gskInputStream) throws IOException { + Iterable gskCsvRecords = csvFormat.parse(new InputStreamReader(gskInputStream)); + Map> weightPerNodePerGsk = new HashMap<>(); + gskCsvRecords.forEach(record -> { + weightPerNodePerGsk.putIfAbsent(record.get(GSK_ID), new HashMap<>()); + weightPerNodePerGsk.get(record.get(GSK_ID)).put(record.get("Node"), parseDoubleWithPossibleCommas(record.get("Weight"))); + }); + + return weightPerNodePerGsk; + } + + // Consistency check functions + private static boolean shouldBeImported(CSVRecord staticRecord, List sortedTimestampToRun, Map> weightPerNodePerGsk, Map> timeseriesPerIdAndType) { + //TODO: check that sum of GSK if defined on one equal to 1 + String raId = staticRecord.get(RA_RD_ID); + + // Check that remedial action is defined in series csv and gsk (if defined on a gsk) + if (!timeseriesPerIdAndType.containsKey(raId)) { + BUSINESS_WARNS.warn("Redispatching action {} is not defined in the time series csv", raId); + return false; + } + // Check that if remedial action is defined on a gsk, the gsk is defined in the gsk csv + if (staticRecord.get(RD_DESCRIPTION_MODE).equalsIgnoreCase(GSK) && !weightPerNodePerGsk.containsKey(staticRecord.get(UCT_NODE_OR_GSK_ID))) { + BUSINESS_WARNS.warn("Redispatching action {} is defined on a gsk {} but the gsk is not defined in the gsk csv", raId, staticRecord.get(UCT_NODE_OR_GSK_ID)); + return false; + } + + // Check that remedial action should at least be defined on preventive instant + if(!staticRecord.get(PREVENTIVE).equalsIgnoreCase(TRUE)) { + BUSINESS_WARNS.warn("Redispatching action {} is not defined on preventive instant", raId); + return false; + } + + // Check that the remedial action is defined on a node or a gsk + if (!staticRecord.get(RD_DESCRIPTION_MODE).equalsIgnoreCase(NODE) && !staticRecord.get(RD_DESCRIPTION_MODE).equalsIgnoreCase(GSK)) { + BUSINESS_WARNS.warn("Redispatching action {} is not defined on a node or a gsk but on a {}", raId, staticRecord.get(RD_DESCRIPTION_MODE)); + return false; + } + + // Check that the RA is correctly defined in series csv + Map seriesPerType = timeseriesPerIdAndType.get(raId); + boolean isDefinedInSeriesCsv = seriesPerType.containsKey(P0) && + seriesPerType.containsKey(RDP_DOWN) && + seriesPerType.containsKey(RDP_UP) && + seriesPerType.containsKey(P_MIN_RD); + + if (!isDefinedInSeriesCsv) { + BUSINESS_WARNS.warn("Redispatching action {} is not defined in the time series csv. Missing one or several timeseries type (P0, RDP_DOWN, RDP_UP or P_MIN_RD).", raId); + return false; + } + + // Check that the range of redispatching parameters is valid + if (!rangeIsOkay(seriesPerType, sortedTimestampToRun)) { + return false; + } + + // Check that the P0 record respects the specified power gradients + if (!p0RespectsGradients(staticRecord, seriesPerType.get(P0), sortedTimestampToRun)) { + return false; + } + + return true; + } + + /** + * Determines whether the P0 record values respect the specified power gradients for each time interval. + * It checks the difference in values between consecutive timestamps and ensures that the differences + * fall within the acceptable gradient range defined by the static record. + * + * @param staticRecord The static record containing gradient constraints, including the maximum positive + * and minimum negative power gradients. + * @param p0record The P0 record containing time-series data representing power values at specific timestamps. + * @param dateTimes A list of timestamps to evaluate the gradient between consecutive entries in the P0 record. + * @return {@code true} if the P0 record respects the specified power gradients for all timestamps; + * {@code false} otherwise. + */ + private static boolean p0RespectsGradients(CSVRecord staticRecord, CSVRecord p0record, List dateTimes) { + double maxGradient = staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT).isEmpty() ? + MAX_GRADIENT : parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT)); + double minGradient = staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT).isEmpty() ? + -MAX_GRADIENT : -parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT)); + + Iterator dateTimeIterator = dateTimes.iterator(); + OffsetDateTime currentDateTime = dateTimeIterator.next(); + while (dateTimeIterator.hasNext()) { + OffsetDateTime nextDateTime = dateTimeIterator.next(); + double diff = parseDoubleWithPossibleCommas(p0record.get(nextDateTime.getHour() + OFFSET)) - parseDoubleWithPossibleCommas(p0record.get(currentDateTime.getHour() + OFFSET)); + if (diff > maxGradient || diff < minGradient) { + BUSINESS_WARNS.warn( + "Redispatching action {} will not be imported because it does not respect power gradients : min/max/diff = {} / {} / {}", + staticRecord.get(0), minGradient, maxGradient, diff + ); + return false; + } + currentDateTime = nextDateTime; + } + return true; + } + + /** + * Verifies whether the range of redispatching parameters is valid for the input time series, + * ensuring that redispatching values are non-negative and exceed a minimum threshold. + * + * @param seriesPerType A map where keys are series types (e.g., RDP+ or RDP-) and values are time-series data (CSVRecord) + * corresponding to these types. + * @param dateTimes A list of timestamps to evaluate the redispatching parameters at specific hours within a day. + * @return {@code true} if the range of redispatching values is valid and meets the defined constraints; + * {@code false} otherwise. + */ + private static boolean rangeIsOkay(Map seriesPerType, List dateTimes) { + double maxRange = 0.; + for (OffsetDateTime dateTime : dateTimes) { + double rdpPlus = parseDoubleWithPossibleCommas(seriesPerType.get(RDP_UP).get(dateTime.getHour() + OFFSET)); + double rdpMinus = parseDoubleWithPossibleCommas(seriesPerType.get(RDP_DOWN).get(dateTime.getHour() + OFFSET)); + maxRange = Math.max(maxRange, rdpPlus + rdpMinus); + if (rdpPlus < -1e-6 || rdpMinus < -1e-6) { + BUSINESS_WARNS.warn("Redispatching action {} will not be imported because of RDP+ {} or RDP- {} is negative for datetime {}", seriesPerType.get(P0).get(RA_RD_ID), rdpPlus, rdpMinus, dateTime); + return false; + } + } + if (maxRange < 1) { + BUSINESS_WARNS.warn("Redispatching action {} will not be imported because max range in the day {} MW is too small", seriesPerType.get(P0).get(RA_RD_ID), maxRange); + return false; + } + return true; + } + +} \ No newline at end of file diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataImporterTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataImporterTest.java new file mode 100644 index 0000000000..2b3aefe4f6 --- /dev/null +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataImporterTest.java @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2026, 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; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; + +import com.powsybl.openrao.commons.logs.RaoBusinessWarns; +import org.apache.commons.csv.CSVRecord; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.slf4j.LoggerFactory; + + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Roxane Chen {@literal } + */ +public class IcsDataImporterTest { + + List logsList; + + public static List generateOffsetDateTimeList(int numberOfHours) { + List dateTimes = new ArrayList<>(); + + OffsetDateTime start = OffsetDateTime.of(2025, 2, 13, 0, 30, 0, 0, ZoneOffset.UTC); + + int current = 0; + while (current < numberOfHours) { + dateTimes.add(start.plusHours(current)); + current++; + } + + return dateTimes; + } + + @BeforeEach + void setUp() { + Logger logger = (Logger) LoggerFactory.getLogger(RaoBusinessWarns.class); + ListAppender listAppender = new ListAppender<>(); + listAppender.start(); + logger.addAppender(listAppender); + logsList = listAppender.list; + } + + @Test + void testParseSeriesCsv() throws IOException { + Map> result = IcsDataImporter.parseSeriesCsv(getClass().getResourceAsStream("/ics/series.csv")); + + assertEquals(1, result.size()); + assertTrue(result.containsKey("Redispatching_RA")); + + Map redispatchingRaSeries = result.get("Redispatching_RA"); + assertEquals(4, redispatchingRaSeries.size()); + assertTrue(redispatchingRaSeries.containsKey("RDP-")); + assertTrue(redispatchingRaSeries.containsKey("RDP+")); + assertTrue(redispatchingRaSeries.containsKey("P0")); + assertTrue(redispatchingRaSeries.containsKey("Pmin_RD")); + + assertEquals("35", redispatchingRaSeries.get("RDP-").get("00:30")); + assertEquals("43", redispatchingRaSeries.get("RDP+").get("00:30")); + assertEquals("116", redispatchingRaSeries.get("P0").get("00:30")); + assertEquals("10", redispatchingRaSeries.get("Pmin_RD").get("00:30")); + + assertEquals("39", redispatchingRaSeries.get("RDP-").get("23:30")); + assertEquals("39", redispatchingRaSeries.get("RDP+").get("23:30")); + assertEquals("120", redispatchingRaSeries.get("P0").get("23:30")); + assertEquals("30", redispatchingRaSeries.get("Pmin_RD").get("23:30")); + } + + @Test + void testParseStaticCsv() throws IOException { + Map result = IcsDataImporter.parseStaticCsv(getClass().getResourceAsStream("/ics/static.csv")); + + assertEquals(1, result.size()); + assertTrue(result.containsKey("Redispatching_RA")); + + CSVRecord record = result.get("Redispatching_RA"); + assertEquals("FR", record.get("TSO")); + assertEquals("TRUE", record.get("Preventive")); + assertEquals("FALSE", record.get("Curative")); + assertEquals("Node", record.get("RD description mode")); + assertEquals("BBE1AA1", record.get("UCT Node or GSK ID")); + assertEquals("50", record.get("Minimum Redispatch [MW]")); + assertEquals("FALSE", record.get("Startup allowed")); + assertEquals("FALSE", record.get("Shutdown allowed")); + } + + @Test + void testParseGskCsv() throws IOException { + Map> result = IcsDataImporter.parseGskCsv(getClass().getResourceAsStream("/glsk/gsk.csv")); + + assertEquals(1, result.size()); + assertTrue(result.containsKey("GSK_NAME")); + + Map gskWeights = result.get("GSK_NAME"); + assertEquals(2, gskWeights.size()); + assertEquals(0.6, gskWeights.get("BBE1AA1"), 1e-6); + assertEquals(0.4, gskWeights.get("FFR1AA1"), 1e-6); + } + + @Test + void testStandardIcsDataImporterRead() throws IOException { + IcsData icsData = IcsDataImporter.read( + getClass().getResourceAsStream("/ics/static.csv"), + getClass().getResourceAsStream("/ics/series.csv"), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList(24)); + assertEquals(1, icsData.getRedispatchingActions().size()); + assertTrue(icsData.getRedispatchingActions().contains("Redispatching_RA")); + } + + @Test + void testRaNotAvailableInPreventive() throws IOException { + String staticCsv = """ + RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed + Redispatching_RA;FR;FALSE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;20;20;1;1;FALSE;FALSE + """; + IcsData icsData = IcsDataImporter.read( + new ByteArrayInputStream(staticCsv.getBytes(StandardCharsets.UTF_8)), + getClass().getResourceAsStream("/ics/series.csv"), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList(24)); + assertEquals(0, icsData.getRedispatchingActions().size()); + assertEquals("Redispatching action Redispatching_RA is not defined on preventive instant", logsList.get(0).getFormattedMessage()); + } + + @Test + void testRaDefinedInStaticButNotInSeries() throws IOException { + String staticCsv = """ + RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed + Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;20;20;1;1;FALSE;FALSE + Redispatching_RA_not_defined_in_series_csv;FR;FALSE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;20;20;1;1;FALSE;FALSE + """; + IcsData icsData = IcsDataImporter.read( + new ByteArrayInputStream(staticCsv.getBytes(StandardCharsets.UTF_8)), + getClass().getResourceAsStream("/ics/series.csv"), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList(24)); + assertEquals(1, icsData.getRedispatchingActions().size()); + assertTrue(icsData.getRedispatchingActions().contains("Redispatching_RA")); + assertFalse(icsData.getRedispatchingActions().contains("Redispatching_RA_not_defined_in_series_csv")); + assertEquals("Redispatching action Redispatching_RA_not_defined_in_series_csv is not defined in the time series csv", logsList.get(0).getFormattedMessage()); + } + + @Test + void testRaDefinedOnGskButNotInGskCsv() throws IOException { + String staticCsv = """ + RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed + Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;GSK;GSK_NAME_NOT_IN_CSV;50;Coal;2;2;20;20;1;1;FALSE;FALSE + """; + IcsData icsData = IcsDataImporter.read( + new ByteArrayInputStream(staticCsv.getBytes(StandardCharsets.UTF_8)), + getClass().getResourceAsStream("/ics/series.csv"), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList(24)); + assertEquals(0, icsData.getRedispatchingActions().size()); + assertFalse(icsData.getRedispatchingActions().contains("Redispatching_RA")); + assertEquals("Redispatching action Redispatching_RA is defined on a gsk GSK_NAME_NOT_IN_CSV but the gsk is not defined in the gsk csv", logsList.get(0).getFormattedMessage()); + } + + @Test + void testRaNotDefinedOnANodeOrAGsk() throws IOException { + String staticCsv = """ + RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed + Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;OTHER;GSK_NAME_NOT_IN_CSV;50;Coal;2;2;20;20;1;1;FALSE;FALSE + """; + IcsData icsData = IcsDataImporter.read( + new ByteArrayInputStream(staticCsv.getBytes(StandardCharsets.UTF_8)), + getClass().getResourceAsStream("/ics/series.csv"), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList(24)); + assertEquals(0, icsData.getRedispatchingActions().size()); + assertEquals("Redispatching action Redispatching_RA is not defined on a node or a gsk but on a OTHER", logsList.get(0).getFormattedMessage()); + } + + private static Stream seriesCsvWithMissingSeriesTypeCases() { + String header = """ + RA RD ID;Type of timeseries;00:30;01:30 + """; + + String missingP0Csv = header + """ + Redispatching_RA;RDP-;35;35 + Redispatching_RA;RDP+;43;43 + Redispatching_RA;Pmin_RD;10;15 + """; + + String missingRdpDownCsv = header + """ + Redispatching_RA;RDP+;43;43 + Redispatching_RA;P0;116;120 + Redispatching_RA;Pmin_RD;10;15 + """; + + String missingRdpUpCsv = header + """ + Redispatching_RA;RDP-;35;35 + Redispatching_RA;P0;116;120 + Redispatching_RA;Pmin_RD;10;15 + """; + + String missingPminRdCsv = header + """ + Redispatching_RA;RDP-;35;35 + Redispatching_RA;RDP+;43;43 + Redispatching_RA;P0;116;120 + """; + + return Stream.of( + Arguments.of(missingP0Csv, "Redispatching action Redispatching_RA is not defined in the time series csv. Missing one or several timeseries type (P0, RDP_DOWN, RDP_UP or P_MIN_RD)."), + Arguments.of(missingRdpDownCsv, "Redispatching action Redispatching_RA is not defined in the time series csv. Missing one or several timeseries type (P0, RDP_DOWN, RDP_UP or P_MIN_RD)."), + Arguments.of(missingRdpUpCsv, "Redispatching action Redispatching_RA is not defined in the time series csv. Missing one or several timeseries type (P0, RDP_DOWN, RDP_UP or P_MIN_RD)."), + Arguments.of(missingPminRdCsv, "Redispatching action Redispatching_RA is not defined in the time series csv. Missing one or several timeseries type (P0, RDP_DOWN, RDP_UP or P_MIN_RD).") + ); + } + + private static Stream rangeIsNotOkayCases() { + String baseHeader = """ + RA RD ID;Type of timeseries;00:30;01:30;02:30 + """; + + String negativeRdpPlusCsv = baseHeader + """ + Redispatching_RA;RDP-;35;35;35 + Redispatching_RA;RDP+;43;-1;43 + Redispatching_RA;P0;116;120;117 + Redispatching_RA;Pmin_RD;10;15;20 + """; + String negativeRdpMinusCsv = baseHeader + """ + Redispatching_RA;RDP-;35;-35;35 + Redispatching_RA;RDP+;43;1;43 + Redispatching_RA;P0;116;120;117 + Redispatching_RA;Pmin_RD;10;15;20 + """; + + String tooSmallRangeCsv = baseHeader + """ + Redispatching_RA;RDP-;0;0;0 + Redispatching_RA;RDP+;0;0;0 + Redispatching_RA;P0;116;120;117 + Redispatching_RA;Pmin_RD;10;15;20 + """; + + return Stream.of( + Arguments.of(negativeRdpPlusCsv, + "Redispatching action Redispatching_RA will not be imported because of RDP+ -1.0 or RDP- 35.0 is negative for datetime 2025-02-13T01:30Z"), + Arguments.of(negativeRdpMinusCsv, + "Redispatching action Redispatching_RA will not be imported because of RDP+ 1.0 or RDP- -35.0 is negative for datetime 2025-02-13T01:30Z"), + Arguments.of(tooSmallRangeCsv, + "Redispatching action Redispatching_RA will not be imported because max range in the day 0.0 MW is too small") + ); + } + + private static Stream gradientNotRespectCsvCases() { + String header = """ + RA RD ID;Type of timeseries;00:30;01:30;02:30 + """; + + // diff = 150 - 116 = 34 > 20 => rejected + String tooHighGradientCsv = header + """ + Redispatching_RA;RDP-;35;35;35 + Redispatching_RA;RDP+;43;43;43 + Redispatching_RA;P0;116;150;117 + Redispatching_RA;Pmin_RD;10;15;20 + """; + + // diff = 80 - 116 = -36 < -20 => rejected + String tooLowGradientCsv = header + """ + Redispatching_RA;RDP-;35;35;35 + Redispatching_RA;RDP+;43;43;43 + Redispatching_RA;P0;116;80;117 + Redispatching_RA;Pmin_RD;10;15;20 + """; + + return Stream.of( + Arguments.of( + tooHighGradientCsv, + "Redispatching action Redispatching_RA will not be imported because it does not respect power gradients : min/max/diff = -20.0 / 20.0 / 34.0" + ), + Arguments.of( + tooLowGradientCsv, + "Redispatching action Redispatching_RA will not be imported because it does not respect power gradients : min/max/diff = -20.0 / 20.0 / -36.0" + ) + ); + } + + @ParameterizedTest + @MethodSource("gradientNotRespectCsvCases") + @MethodSource("rangeIsNotOkayCases") + @MethodSource("seriesCsvWithMissingSeriesTypeCases") + void testP0RespectsGradients(String seriesCsv, String expectedLogMessage) throws IOException { + IcsData icsData = IcsDataImporter.read( + getClass().getResourceAsStream("/ics/static.csv"), + new ByteArrayInputStream(seriesCsv.getBytes(StandardCharsets.UTF_8)), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList(3)); + + assertEquals(0, icsData.getRedispatchingActions().size()); + assertEquals(expectedLogMessage, logsList.get(0).getFormattedMessage()); + } + + @Test + void testMissingGradientInStaticCsv() throws IOException { + // Missing gradient in static csv -> we should use the default MAX_GRADIENT + String staticCsv = """ + RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed + Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;;;1;1;FALSE;FALSE + """; + String seriesCsv = """ + RA RD ID;Type of timeseries;00:30;01:30;02:30 + Redispatching_RA;RDP-;35;35;35 + Redispatching_RA;RDP+;43;43;43 + Redispatching_RA;P0;116;2080;117 + Redispatching_RA;Pmin_RD;10;15;20 + """; + IcsData icsData = IcsDataImporter.read( + new ByteArrayInputStream(staticCsv.getBytes(StandardCharsets.UTF_8)), + new ByteArrayInputStream(seriesCsv.getBytes(StandardCharsets.UTF_8)), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList(3)); + + assertEquals(0, icsData.getRedispatchingActions().size()); + assertEquals("Redispatching action Redispatching_RA will not be imported because it does not respect power gradients : min/max/diff = -1000.0 / 1000.0 / 1964.0", logsList.get(0).getFormattedMessage()); + } +} From 574e73321f08a64d0f408dacd7cecbfe5ffcb22b Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Mon, 30 Mar 2026 14:46:09 +0200 Subject: [PATCH 07/64] complete Signed-off-by: CHEN Roxane --- .../powsybl/openrao/data/IcsDataImporter.java | 36 ++++++++++++++++--- .../openrao/data/IcsDataImporterTest.java | 17 +++++++++ 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java index 6120136cdc..a1bbeedda2 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java @@ -127,7 +127,6 @@ static Map> parseGskCsv(InputStream gskInputStream) // Consistency check functions private static boolean shouldBeImported(CSVRecord staticRecord, List sortedTimestampToRun, Map> weightPerNodePerGsk, Map> timeseriesPerIdAndType) { - //TODO: check that sum of GSK if defined on one equal to 1 String raId = staticRecord.get(RA_RD_ID); // Check that remedial action is defined in series csv and gsk (if defined on a gsk) @@ -135,10 +134,21 @@ private static boolean shouldBeImported(CSVRecord staticRecord, List> weightPerNodePerGsk) { + double sumOfGsk = 0.; + for (Map.Entry entry : weightPerNodePerGsk.get(gskId).entrySet()) { + sumOfGsk += entry.getValue(); + } + return Math.abs(sumOfGsk - 1.) < 1e-6; + } + /** * Determines whether the P0 record values respect the specified power gradients for each time interval. * It checks the difference in values between consecutive timestamps and ensures that the differences diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataImporterTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataImporterTest.java index 2b3aefe4f6..69b88bbfbc 100644 --- a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataImporterTest.java +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataImporterTest.java @@ -337,4 +337,21 @@ void testMissingGradientInStaticCsv() throws IOException { assertEquals(0, icsData.getRedispatchingActions().size()); assertEquals("Redispatching action Redispatching_RA will not be imported because it does not respect power gradients : min/max/diff = -1000.0 / 1000.0 / 1964.0", logsList.get(0).getFormattedMessage()); } + + @Test + void testGskWeightSumNotEqualToOne() throws IOException { + String gsk = """ + GSK ID;Node;Weight + GSK_NAME;BBE1AA1;0.6 + GSK_NAME;FFR1AA1;0.5 + """; + IcsData icsData = IcsDataImporter.read( + getClass().getResourceAsStream("/ics/static_with_gsk.csv"), + getClass().getResourceAsStream("/ics/series.csv"), + new ByteArrayInputStream(gsk.getBytes(StandardCharsets.UTF_8)), + generateOffsetDateTimeList(24)); + + assertEquals(0, icsData.getRedispatchingActions().size()); + assertEquals("Redispatching action Redispatching_RA is ignored but it is defined on a GSK but sum of weights is not equal to 1", logsList.get(0).getFormattedMessage()); + } } From 47f8037c176ac0323b2f5cfe8c5d5bf621c09ee8 Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Mon, 30 Mar 2026 15:54:52 +0200 Subject: [PATCH 08/64] wip icsdatatest Signed-off-by: CHEN Roxane --- .../com/powsybl/openrao/data/IcsData.java | 175 +-------- .../powsybl/openrao/data/IcsDataImporter.java | 1 - .../com/powsybl/openrao/data/IcsUtil.java | 3 +- .../com/powsybl/openrao/data/IcsDataTest.java | 343 +++++++++++------- .../test/resources/ics/static_no_shutdown.csv | 2 - .../test/resources/ics/static_no_startup.csv | 2 - .../ics/static_shutdown_startup_true.csv | 2 - .../ics/static_with_gsk_no_shutdown.csv | 2 - .../ics/static_with_gsk_no_startup.csv | 2 - .../ics/static_with_gsk_wrong_shutdown.csv | 2 - .../ics/static_with_gsk_wrong_startup.csv | 2 - .../resources/ics/static_wrong_shutdown.csv | 2 - .../resources/ics/static_wrong_startup.csv | 2 - 13 files changed, 220 insertions(+), 320 deletions(-) delete mode 100644 data/ics-importer/src/test/resources/ics/static_no_shutdown.csv delete mode 100644 data/ics-importer/src/test/resources/ics/static_no_startup.csv delete mode 100644 data/ics-importer/src/test/resources/ics/static_shutdown_startup_true.csv delete mode 100644 data/ics-importer/src/test/resources/ics/static_with_gsk_no_shutdown.csv delete mode 100644 data/ics-importer/src/test/resources/ics/static_with_gsk_no_startup.csv delete mode 100644 data/ics-importer/src/test/resources/ics/static_with_gsk_wrong_shutdown.csv delete mode 100644 data/ics-importer/src/test/resources/ics/static_with_gsk_wrong_startup.csv delete mode 100644 data/ics-importer/src/test/resources/ics/static_wrong_shutdown.csv delete mode 100644 data/ics-importer/src/test/resources/ics/static_wrong_startup.csv diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java index 221345d380..cb7e1b759e 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java @@ -15,14 +15,8 @@ import com.powsybl.openrao.data.crac.api.rangeaction.InjectionRangeActionAdder; import com.powsybl.openrao.data.crac.api.rangeaction.VariationDirection; import com.powsybl.openrao.data.timecoupledconstraints.GeneratorConstraints; -import com.powsybl.openrao.data.timecoupledconstraints.TimeCoupledConstraints; -import com.powsybl.openrao.raoapi.TimeCoupledRaoInputWithNetworkPaths; -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.*; @@ -38,13 +32,16 @@ public final class IcsData { private static Map> timeseriesPerIdAndType; private static Map> weightPerNodePerGsk; private static Map staticConstraintPerId; + private static Set consistentRedispatchingActions; // TODO : either parametrize this or set it to true. May have to change the way it works to import for all curative instants instead of only the last one public static boolean importCurative = false; - public IcsData(Map> timeseriesPerIdAndType, + public IcsData(Set consistentRedispatchingActions, + Map> timeseriesPerIdAndType, Map> weightPerNodePerGsk, Map staticConstraintPerId) { + this.consistentRedispatchingActions = consistentRedispatchingActions; this.staticConstraintPerId = staticConstraintPerId; this.timeseriesPerIdAndType = timeseriesPerIdAndType; this.weightPerNodePerGsk = weightPerNodePerGsk; @@ -62,6 +59,10 @@ public Map> getWeightPerNodePerGsk() { return weightPerNodePerGsk; } + public Set getRedispatchingActions() { + return consistentRedispatchingActions; + } + public static String getGeneratorIdFromRaIdAndNodeId(String raId, String nodeId) { return raId + "_" + nodeId + GENERATOR_SUFFIX; } @@ -219,164 +220,4 @@ public static void createInjectionRangeActionsAndUpdateCracs(TemporalData injectionRangeActionAdder.add(); }); } - - - // READER // - - /** - * Reads and processes inputs to generate Ics Data Object - * - * @param staticInputStream the input stream for static constraints data, defining generator constraints - * associated with remedial actions. - * @param seriesInputStream the input stream for time series data, mapped per RA_ID and series type - * (e.g., RDP-, RDP+, Pmin_RD, or P0). - * @param gskInputStream the input stream for GSK data, mapping nodes to their generation shift key weights. - * @param sortedTimestampToRun the list of timestamps to consider - * @return an {@code IcsData} instance - * @throws IOException if an issue occurs while reading or processing the input streams. - */ - public static IcsData read(InputStream staticInputStream, - InputStream seriesInputStream, - InputStream gskInputStream, - List sortedTimestampToRun) throws IOException { - - CSVFormat csvFormat = CSVFormat.DEFAULT.builder() - .setDelimiter(";") - .setHeader() - .setSkipHeaderRecord(true) - .get(); - - // Parse and sort per RA_ID and serie type (RDP-, RDP+, Pmin_RD or P0) - Map> timeseriesPerIdAndType = parseSeriesCsv(csvFormat, seriesInputStream); - // Parse GSK and get weight Per Node Per Gsk - Map> weightPerNodePerGsk = parseGskCsv(csvFormat, gskInputStream); - // Parse static CSV: remedial action’s generator’s static constraints. one line per RA_ID - Map staticConstraintPerId = parseAndFilterStaticCsv(csvFormat, staticInputStream, sortedTimestampToRun, weightPerNodePerGsk, timeseriesPerIdAndType); - - return new IcsData(timeseriesPerIdAndType, weightPerNodePerGsk, staticConstraintPerId); - - } - - static Map parseAndFilterStaticCsv(CSVFormat csvFormat, InputStream staticInputStream, List sortedTimestampToRun, Map> weightPerNodePerGsk, Map> timeseriesPerIdAndType) throws IOException { - Iterable staticCsvRecords = csvFormat.parse(new InputStreamReader(staticInputStream)); - Map filteredStaticCsvRecords = new HashMap<>(); - staticCsvRecords.forEach(record -> { - if (shouldBeImported(record, sortedTimestampToRun, weightPerNodePerGsk, timeseriesPerIdAndType)) { - filteredStaticCsvRecords.put(record.get(RA_RD_ID), record); - } - }); - return filteredStaticCsvRecords; - } - - private static Map> parseSeriesCsv(CSVFormat csvFormat, InputStream seriesInputStream) throws IOException { - 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); - }); - - return seriesPerIdAndType; - } - - private static Map> parseGskCsv(CSVFormat csvFormat, InputStream gskInputStream) throws IOException { - Iterable gskCsvRecords = csvFormat.parse(new InputStreamReader(gskInputStream)); - Map> weightPerNodePerGsk = new HashMap<>(); - gskCsvRecords.forEach(record -> { - weightPerNodePerGsk.putIfAbsent(record.get(GSK_ID), new HashMap<>()); - weightPerNodePerGsk.get(record.get(GSK_ID)).put(record.get("Node"), parseDoubleWithPossibleCommas(record.get("Weight"))); - }); - - return weightPerNodePerGsk; - } - - // Consistency check functions - private static boolean shouldBeImported(CSVRecord staticRecord, List sortedTimestampToRun, Map> weightPerNodePerGsk, Map> timeseriesPerIdAndType) { - //TODO: check that sum of GSK if defined on one equal to 1 - - // remedial action should at least be defined on preventive instant - boolean isPreventive = staticRecord.get(PREVENTIVE).equalsIgnoreCase(TRUE); - - // remedial action is defined on a node or a gsk - boolean isDefinedOnANodeOrGsk = staticRecord.get(RD_DESCRIPTION_MODE).equalsIgnoreCase(NODE) || weightPerNodePerGsk.containsKey(staticRecord.get(UCT_NODE_OR_GSK_ID)); - - String raId = staticRecord.get(RA_RD_ID); - Map seriesPerType = timeseriesPerIdAndType.get(raId); - - // is correctly defined in series csv - boolean isDefinedInSeriesCsv = seriesPerType != null && - seriesPerType.containsKey(P0) && - seriesPerType.containsKey(RDP_DOWN) && - seriesPerType.containsKey(RDP_UP) && - seriesPerType.containsKey(P_MIN_RD); - - boolean rangeIsOkay = rangeIsOkay(seriesPerType, sortedTimestampToRun); - boolean p0RespectsGradients = p0RespectsGradients(staticRecord, seriesPerType.get(P0), sortedTimestampToRun); - - return isDefinedOnANodeOrGsk && isPreventive && isDefinedInSeriesCsv && rangeIsOkay && p0RespectsGradients; - } - - /** - * Determines whether the P0 record values respect the specified power gradients for each time interval. - * It checks the difference in values between consecutive timestamps and ensures that the differences - * fall within the acceptable gradient range defined by the static record. - * - * @param staticRecord The static record containing gradient constraints, including the maximum positive - * and minimum negative power gradients. - * @param p0record The P0 record containing time-series data representing power values at specific timestamps. - * @param dateTimes A list of timestamps to evaluate the gradient between consecutive entries in the P0 record. - * @return {@code true} if the P0 record respects the specified power gradients for all timestamps; - * {@code false} otherwise. - */ - private static boolean p0RespectsGradients(CSVRecord staticRecord, CSVRecord p0record, List dateTimes) { - double maxGradient = staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT).isEmpty() ? - MAX_GRADIENT : parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT)); - double minGradient = staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT).isEmpty() ? - -MAX_GRADIENT : -parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT)); - - Iterator dateTimeIterator = dateTimes.iterator(); - OffsetDateTime currentDateTime = dateTimeIterator.next(); - while (dateTimeIterator.hasNext()) { - OffsetDateTime nextDateTime = dateTimeIterator.next(); - double diff = parseDoubleWithPossibleCommas(p0record.get(nextDateTime.getHour() + OFFSET)) - parseDoubleWithPossibleCommas(p0record.get(currentDateTime.getHour() + OFFSET)); - if (diff > maxGradient || diff < minGradient) { - BUSINESS_WARNS.warn( - "Redispatching action {} will not be imported because it does not respect power gradients : min/max/diff {} {} {}", - staticRecord.get(0), minGradient, maxGradient, diff - ); - return false; - } - currentDateTime = nextDateTime; - } - return true; - } - - /** - * Verifies whether the range of redispatching parameters is valid for the input time series, - * ensuring that redispatching values are non-negative and exceed a minimum threshold. - * - * @param seriesPerType A map where keys are series types (e.g., RDP+ or RDP-) and values are time-series data (CSVRecord) - * corresponding to these types. - * @param dateTimes A list of timestamps to evaluate the redispatching parameters at specific hours within a day. - * @return {@code true} if the range of redispatching values is valid and meets the defined constraints; - * {@code false} otherwise. - */ - private static boolean rangeIsOkay(Map seriesPerType, List dateTimes) { - double maxRange = 0.; - for (OffsetDateTime dateTime : dateTimes) { - double rdpPlus = parseDoubleWithPossibleCommas(seriesPerType.get(RDP_UP).get(dateTime.getHour() + OFFSET)); - double rdpMinus = parseDoubleWithPossibleCommas(seriesPerType.get(RDP_DOWN).get(dateTime.getHour() + OFFSET)); - maxRange = Math.max(maxRange, rdpPlus + rdpMinus); - if (rdpPlus < -1e-6 || rdpMinus < -1e-6) { - BUSINESS_WARNS.warn("Redispatching action {} will not be imported because of RDP+ {} or RDP- {} is negative", seriesPerType.get(P0).get(RA_RD_ID), rdpPlus, rdpMinus); - return false; - } - } - if (maxRange < 1) { - BUSINESS_WARNS.warn("Redispatching action {} will not be imported because max range in the day {} MW is too small", seriesPerType.get(P0).get(RA_RD_ID), maxRange); - return false; - } - return true; - } } \ No newline at end of file diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java index a1bbeedda2..a764039e98 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java @@ -89,7 +89,6 @@ static Set filterOutInconsistentRedispatchingActions(Map !consistentRAs.contains(entry.getKey())); timeseriesPerIdAndType.entrySet().removeIf(entry -> !consistentRAs.contains(entry.getKey())); - weightPerNodePerGsk.entrySet().removeIf(entry -> !consistentRAs.contains(entry.getKey())); return consistentRAs; } diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java index 487433caa9..0e1bc89eaa 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java @@ -21,11 +21,11 @@ */ public class IcsUtil { + // TODO: move to a configuration file ? static final double MAX_GRADIENT = 1000.0; public static final String MAXIMUM_POSITIVE_POWER_GRADIENT = "Maximum positive power gradient [MW/h]"; public static final String MAXIMUM_NEGATIVE_POWER_GRADIENT = "Maximum negative power gradient [MW/h]"; - public static final String RA_RD_ID = "RA RD ID"; public static final String RDP_UP = "RDP+"; public static final String RDP_DOWN = "RDP-"; @@ -36,6 +36,7 @@ public class IcsUtil { public static final String TRUE = "TRUE"; public static final String RD_DESCRIPTION_MODE = "RD description mode"; public static final String NODE = "NODE"; + public static final String GSK = "GSK"; public static final String LEAD_TIME = "Lead time [h]"; public static final String LAG_TIME = "Lag time [h]"; public static final String STARTUP_ALLOWED = "Startup allowed"; diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java index d2abc997f2..80d0c881b6 100644 --- a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java @@ -16,19 +16,23 @@ import com.powsybl.openrao.data.crac.api.rangeaction.InjectionRangeAction; import com.powsybl.openrao.data.crac.api.rangeaction.VariationDirection; import com.powsybl.openrao.data.timecoupledconstraints.GeneratorConstraints; -import com.powsybl.openrao.raoapi.TimeCoupledRaoInputWithNetworkPaths; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import java.io.*; +import java.nio.charset.StandardCharsets; import java.time.OffsetDateTime; import java.time.ZoneOffset; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Stream; +import static com.powsybl.openrao.data.IcsDataImporterTest.generateOffsetDateTimeList; +import static com.powsybl.openrao.data.IcsUtil.MAX_GRADIENT; import static org.junit.jupiter.api.Assertions.*; /** @@ -36,14 +40,10 @@ */ public class IcsDataTest { private static final double DOUBLE_EPSILON = 1e-6; - private static final String TMP_DIR = System.getProperty("java.io.tmpdir") + File.separator; - private String networkFilePathPostIcsImport1; - private String networkFilePathPostIcsImport2; private Crac crac1; private Crac crac2; private Network network1; private Network network2; - private TimeCoupledRaoInputWithNetworkPaths timeCoupledRaoInputWithNetworkPaths; private TemporalData networkTemporalData; private TemporalData cracTemporalData; @@ -74,35 +74,19 @@ void setUp() throws IOException { )); } - public static List generateOffsetDateTimeList() { - List dateTimes = new ArrayList<>(); - - OffsetDateTime start = OffsetDateTime.of(2025, 2, 13, 0, 30, 0, 0, ZoneOffset.UTC); - OffsetDateTime end = OffsetDateTime.of(2025, 2, 13, 23, 30, 0, 0, ZoneOffset.UTC); - - OffsetDateTime current = start; - while (!current.isAfter(end)) { - dateTimes.add(current); - current = current.plusHours(1); - } - - return dateTimes; - } + // Test Create Generator Constraint @Test - void testIcsDataReadOkNode() throws IOException { + void testCreateGeneratorConstraintRaDefinedOnANode() throws IOException { // Test generic case without any error // one remedial action "Redispatching_RA" defined on the node "BBE1AA1" // Read ICS Data - IcsData icsData = IcsData.read( + IcsData icsData = IcsDataImporter.read( getClass().getResourceAsStream("/ics/static.csv"), getClass().getResourceAsStream("/ics/series.csv"), getClass().getResourceAsStream("/glsk/gsk.csv"), - generateOffsetDateTimeList()); - - assertEquals(1, icsData.getStaticConstraintPerId().size()); - assertEquals(4, icsData.getTimeseriesPerIdAndType().get("Redispatching_RA").size()); + generateOffsetDateTimeList(24)); // Check generator constraint creation Set generatorConstraintsSet = icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", 1.0), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1")) ); @@ -120,61 +104,20 @@ void testIcsDataReadOkNode() throws IOException { assertEquals(1.0, generatorConstraints.getLagTime().get(), DOUBLE_EPSILON); assertFalse(generatorConstraints.isShutDownAllowed()); assertFalse(generatorConstraints.isStartUpAllowed()); - - // Test generator creation in network - Map generatorIdPerNodeId = icsData.createGeneratorAndLoadAndUpdateNetworks(networkTemporalData, "Redispatching_RA", Map.of("BBE1AA1", 1.0)); - Generator generator1 = network1.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); - assertEquals(116., generator1.getTargetP(), DOUBLE_EPSILON); - assertEquals(10.0, generator1.getMinP(), DOUBLE_EPSILON); - Generator generator2 = network2.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); - assertEquals(120., generator2.getTargetP(), DOUBLE_EPSILON); - assertEquals(15.0, generator2.getMinP(), DOUBLE_EPSILON); - - // Test injection range action creation in crac - icsData.createInjectionRangeActionsAndUpdateCracs(cracTemporalData, "Redispatching_RA", Map.of("BBE1AA1", 1.0), generatorIdPerNodeId, 5., 5.); - assertEquals(1, crac1.getInjectionRangeActions().size()); - InjectionRangeAction ra1 = crac1.getInjectionRangeActions().iterator().next(); - assertEquals("Redispatching_RA_RD", ra1.getId()); - assertEquals(116., ra1.getInitialSetpoint(), DOUBLE_EPSILON); - assertTrue(ra1.getVariationCost(VariationDirection.UP).isPresent()); - assertEquals(5., ra1.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); - assertTrue(ra1.getVariationCost(VariationDirection.DOWN).isPresent()); - assertEquals(5., ra1.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); - - assertEquals(1, crac2.getInjectionRangeActions().size()); - InjectionRangeAction ra2 = crac2.getInjectionRangeActions().iterator().next(); - assertEquals("Redispatching_RA_RD", ra2.getId()); - assertEquals(120., ra2.getInitialSetpoint(), DOUBLE_EPSILON); - assertTrue(ra2.getVariationCost(VariationDirection.UP).isPresent()); - assertEquals(5., ra2.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); - assertTrue(ra2.getVariationCost(VariationDirection.DOWN).isPresent()); - assertEquals(5., ra2.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); } @Test - void testIcsImporterWithGSK() throws IOException { - // Test generic case without any error + void testCreateGeneratorConstraintRaDefinedOnAGSK() throws IOException { // one remedial action "Redispatching_RA" defined on a GSK "GSK_NAME" // Read ICS Data - IcsData icsData = IcsData.read( + IcsData icsData = IcsDataImporter.read( getClass().getResourceAsStream("/ics/static_with_gsk.csv"), getClass().getResourceAsStream("/ics/series.csv"), getClass().getResourceAsStream("/glsk/gsk.csv"), - generateOffsetDateTimeList()); - - assertEquals(1, icsData.getStaticConstraintPerId().size()); - assertEquals(4, icsData.getTimeseriesPerIdAndType().get("Redispatching_RA").size()); - assertEquals(1, icsData.getWeightPerNodePerGsk().size()); - assertEquals(2, icsData.getWeightPerNodePerGsk().get("GSK_NAME").size()); - assertEquals(Map.of("BBE1AA1", 0.6, "FFR1AA1", 0.4), icsData.getWeightPerNodePerGsk().get("GSK_NAME")); + generateOffsetDateTimeList(24)); - Map generatorIdPerNodeId = icsData.createGeneratorAndLoadAndUpdateNetworks(networkTemporalData, "Redispatching_RA", icsData.getWeightPerNodePerGsk().get("GSK_NAME")); - assertEquals(Map.of("BBE1AA1", "Redispatching_RA_BBE1AA1_GENERATOR", "FFR1AA1", "Redispatching_RA_FFR1AA1_GENERATOR"), generatorIdPerNodeId); - - Set generatorConstraintsSet = icsData.createGeneratorConstraints("Redispatching_RA", icsData.getWeightPerNodePerGsk().get("GSK_NAME"), generatorIdPerNodeId); - - icsData.createInjectionRangeActionsAndUpdateCracs(cracTemporalData, "Redispatching_RA", icsData.getWeightPerNodePerGsk().get("GSK_NAME"), generatorIdPerNodeId, 5., 5.); + Set generatorConstraintsSet = icsData.createGeneratorConstraints("Redispatching_RA", icsData.getWeightPerNodePerGsk().get("GSK_NAME"), Map.of("BBE1AA1", "Redispatching_RA_BBE1AA1_GENERATOR", "FFR1AA1", "Redispatching_RA_FFR1AA1_GENERATOR")); assertEquals(2, generatorConstraintsSet.size()); GeneratorConstraints generatorConstraintsBE = generatorConstraintsSet.stream() @@ -201,16 +144,133 @@ void testIcsImporterWithGSK() throws IOException { assertEquals(1.0, generatorConstraintsFR.getLagTime().get(), DOUBLE_EPSILON); assertFalse(generatorConstraintsFR.isShutDownAllowed()); assertFalse(generatorConstraintsFR.isStartUpAllowed()); + } - assertEquals(1, crac1.getInjectionRangeActions().size()); - InjectionRangeAction ra1 = crac1.getInjectionRangeActions().iterator().next(); - assertEquals("Redispatching_RA_RD", ra1.getId()); - assertEquals(116., ra1.getInitialSetpoint(), DOUBLE_EPSILON); - assertTrue(ra1.getVariationCost(VariationDirection.UP).isPresent()); - assertEquals(5., ra1.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); - assertTrue(ra1.getVariationCost(VariationDirection.DOWN).isPresent()); - assertEquals(5., ra1.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); + @Test + void testCreateGeneratorConstraintRaDefinedOnANodeMissingGradientLeadTimeAndLagTime() throws IOException { + String staticCsv = """ + RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed + Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;;;;;FALSE;FALSE + """; + IcsData icsData = IcsDataImporter.read( + new ByteArrayInputStream(staticCsv.getBytes(StandardCharsets.UTF_8)), + getClass().getResourceAsStream("/ics/series.csv"), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList(24)); + Set generatorConstraintsSet = icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", 1.0), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1")) ); + assertEquals(1, generatorConstraintsSet.size()); + GeneratorConstraints generatorConstraints = generatorConstraintsSet.iterator().next(); + assertEquals("Redispatching_RA_BBE1AA1_GENERATOR", generatorConstraints.getGeneratorId()); + + // Missing gradient value + assertTrue(generatorConstraints.getDownwardPowerGradient().isPresent()); + assertEquals(-MAX_GRADIENT, generatorConstraints.getDownwardPowerGradient().get(), DOUBLE_EPSILON); + assertTrue(generatorConstraints.getUpwardPowerGradient().isPresent()); + assertEquals(MAX_GRADIENT, generatorConstraints.getUpwardPowerGradient().get(), DOUBLE_EPSILON); + + // Missing lead time + assertFalse(generatorConstraints.getLeadTime().isPresent()); + // Missing lag time + assertFalse(generatorConstraints.getLagTime().isPresent()); + } + + @ParameterizedTest + @MethodSource("startUpAndShutDownAllowedCsvCases") + void testCreateGeneratorConstraintShutDownAndStartUpAllowed(String staticCsv, String expectedLogMessage) throws IOException { + IcsData icsData = IcsDataImporter.read( + new ByteArrayInputStream(staticCsv.getBytes(StandardCharsets.UTF_8)), + getClass().getResourceAsStream("/ics/series.csv"), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList(24)); + + if (expectedLogMessage == null) { + Set generatorConstraintsSet = icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", 1.0), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1")) ); + assertEquals(1, generatorConstraintsSet.size()); + GeneratorConstraints generatorConstraints = generatorConstraintsSet.iterator().next(); + assertTrue(generatorConstraints.isShutDownAllowed()); + assertTrue(generatorConstraints.isStartUpAllowed()); + } else { + Assertions.assertThatExceptionOfType(OpenRaoException.class) + .isThrownBy(() -> icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", 0.6, "FFR1AA1", 0.4), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1"), "FFR1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "FFR1AA1")))) + .withMessage(expectedLogMessage); + } + } + + + private static Stream startUpAndShutDownAllowedCsvCases() { + String baseHeader = """ + RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed + """; + + String shutDownAndStartUpTrue = baseHeader + """ + Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;GSK;GSK_NAME;50;Coal;2;2;20;20;1;1;TRUE;TRUE + """; + + String noShutDown = baseHeader + """ + Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;GSK;GSK_NAME;50;Coal;2;2;20;20;1;1;FALSE;; + """; + + String shutDownWrongValue = baseHeader + """ + Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;GSK;GSK_NAME;50;Coal;2;2;20;20;1;1;FALSE;wrongValue + """; + + String noStartUp = baseHeader + """ + Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;GSK;GSK_NAME;50;Coal;2;2;20;20;1;1;;FALSE; + """; + + String startUpWrongValue = baseHeader + """ + Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;GSK;GSK_NAME;50;Coal;2;2;20;20;1;1;wrongValue;FALSE + """; + + return Stream.of( + Arguments.of(shutDownAndStartUpTrue, + null), + Arguments.of(noShutDown, + "Could not parse shutDownAllowed value for raId Redispatching_RA: "), + Arguments.of(shutDownWrongValue, + "Could not parse shutDownAllowed value for raId Redispatching_RA: wrongValue"), + Arguments.of(noStartUp, + "Could not parse startUpAllowed value for raId Redispatching_RA: "), + Arguments.of(startUpWrongValue, + "Could not parse startUpAllowed value for raId Redispatching_RA: wrongValue") + ); + } + // Test createGeneratorAndLoadAndUpdateNetworks + + @Test + void testCreateGeneratorAndLoadAndUpdateNetworksOnANode() throws IOException { + // Test generic case without any error + // one remedial action "Redispatching_RA" defined on the node "BBE1AA1" + + // Read ICS Data + IcsData icsData = IcsDataImporter.read( + getClass().getResourceAsStream("/ics/static.csv"), + getClass().getResourceAsStream("/ics/series.csv"), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList(2)); + + icsData.createGeneratorAndLoadAndUpdateNetworks(networkTemporalData, "Redispatching_RA", Map.of("BBE1AA1", 1.0)); + Generator generator1 = network1.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); + assertEquals(116., generator1.getTargetP(), DOUBLE_EPSILON); + assertEquals(10.0, generator1.getMinP(), DOUBLE_EPSILON); + Generator generator2 = network2.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); + assertEquals(120., generator2.getTargetP(), DOUBLE_EPSILON); + assertEquals(15.0, generator2.getMinP(), DOUBLE_EPSILON); + } + @Test + void testCreateGeneratorAndLoadAndUpdateNetworksOnAGsk() throws IOException { + // Test generic case without any error + // one remedial action "Redispatching_RA" defined on a GSK "GSK_NAME" + + // Read ICS Data + IcsData icsData = IcsDataImporter.read( + getClass().getResourceAsStream("/ics/static_with_gsk.csv"), + getClass().getResourceAsStream("/ics/series.csv"), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList(2)); + Map generatorIdPerNodeId = icsData.createGeneratorAndLoadAndUpdateNetworks(networkTemporalData, "Redispatching_RA", icsData.getWeightPerNodePerGsk().get("GSK_NAME")); + assertEquals(Map.of("BBE1AA1", "Redispatching_RA_BBE1AA1_GENERATOR", "FFR1AA1", "Redispatching_RA_FFR1AA1_GENERATOR"), generatorIdPerNodeId); Generator generatorBE = network1.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); assertEquals(116. * 0.6, generatorBE.getTargetP(), DOUBLE_EPSILON); assertEquals(10.0 * 0.6, generatorBE.getMinP(), DOUBLE_EPSILON); @@ -219,78 +279,95 @@ void testIcsImporterWithGSK() throws IOException { assertEquals(10.0 * 0.4, generatorFR.getMinP(), DOUBLE_EPSILON); } + // Test createInjectionRangeActionsAndUpdateCracs @Test - void testIcsDataReadNok() { - // Test reader, where some remedial action need to be filtered out - } - - @Test - void testIcsImporterShutDownAndStartUpTrue() throws IOException { - // Check that we take into account the shutDownAllowed and startUpAllowed flag in the ICS file - IcsData icsData = IcsData.read( - getClass().getResourceAsStream("/ics/static_shutdown_startup_true.csv"), + void testCreateInjectionRangeActionsAndUpdateCracsOnANode() throws IOException { + // Read ICS Data + IcsData icsData = IcsDataImporter.read( + getClass().getResourceAsStream("/ics/static.csv"), getClass().getResourceAsStream("/ics/series.csv"), getClass().getResourceAsStream("/glsk/gsk.csv"), - generateOffsetDateTimeList()); + generateOffsetDateTimeList(2)); - Set generatorConstraintsSet = icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", 1.0), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1")) ); - assertEquals(1, generatorConstraintsSet.size()); - GeneratorConstraints generatorConstraints = generatorConstraintsSet.iterator().next(); - assertTrue(generatorConstraints.isShutDownAllowed()); - assertTrue(generatorConstraints.isStartUpAllowed()); + // Test injection range action creation in crac + icsData.createInjectionRangeActionsAndUpdateCracs(cracTemporalData, "Redispatching_RA", Map.of("BBE1AA1", 1.0), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1")), 5., 5.); + assertEquals(1, crac1.getInjectionRangeActions().size()); + InjectionRangeAction ra1 = crac1.getInjectionRangeActions().iterator().next(); + assertEquals("Redispatching_RA_RD", ra1.getId()); + assertEquals(116., ra1.getInitialSetpoint(), DOUBLE_EPSILON); + assertTrue(ra1.getVariationCost(VariationDirection.UP).isPresent()); + assertEquals(5., ra1.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); + assertTrue(ra1.getVariationCost(VariationDirection.DOWN).isPresent()); + assertEquals(5., ra1.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); + + assertEquals(1, crac2.getInjectionRangeActions().size()); + InjectionRangeAction ra2 = crac2.getInjectionRangeActions().iterator().next(); + assertEquals("Redispatching_RA_RD", ra2.getId()); + assertEquals(120., ra2.getInitialSetpoint(), DOUBLE_EPSILON); + assertTrue(ra2.getVariationCost(VariationDirection.UP).isPresent()); + assertEquals(5., ra2.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); + assertTrue(ra2.getVariationCost(VariationDirection.DOWN).isPresent()); + assertEquals(5., ra2.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); } + @Test + void testCreateInjectionRangeActionsAndUpdateCracsOnAGsk() throws IOException { + + // Read ICS Data + IcsData icsData = IcsDataImporter.read( + getClass().getResourceAsStream("/ics/static_with_gsk.csv"), + getClass().getResourceAsStream("/ics/series.csv"), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList(2)); + + icsData.createInjectionRangeActionsAndUpdateCracs(cracTemporalData, "Redispatching_RA", icsData.getWeightPerNodePerGsk().get("GSK_NAME"), Map.of("BBE1AA1", "Redispatching_RA_BBE1AA1_GENERATOR", "FFR1AA1", "Redispatching_RA_FFR1AA1_GENERATOR"), 5., 5.); + + assertEquals(1, crac1.getInjectionRangeActions().size()); + InjectionRangeAction ra1 = crac1.getInjectionRangeActions().iterator().next(); + assertEquals("Redispatching_RA_RD", ra1.getId()); + assertEquals(116., ra1.getInitialSetpoint(), DOUBLE_EPSILON); + assertTrue(ra1.getVariationCost(VariationDirection.UP).isPresent()); + assertEquals(5., ra1.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); + assertTrue(ra1.getVariationCost(VariationDirection.DOWN).isPresent()); + assertEquals(5., ra1.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); + } + + // Test Getter and Setter @Test - void testIcsImporterNoShutDown() throws IOException { - // Check that an error is thrown if the shutDownAllowed flag is not present + void testIcsDataReadOkNode() throws IOException { + // Test generic case without any error + // one remedial action "Redispatching_RA" defined on the node "BBE1AA1" - IcsData icsData = IcsData.read( - getClass().getResourceAsStream("/ics/static_no_shutdown.csv"), + // Read ICS Data + IcsData icsData = IcsDataImporter.read( + getClass().getResourceAsStream("/ics/static.csv"), getClass().getResourceAsStream("/ics/series.csv"), getClass().getResourceAsStream("/glsk/gsk.csv"), - generateOffsetDateTimeList()); + generateOffsetDateTimeList(24)); - Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", 1.0), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1")))) - .withMessage("Could not parse shutDownAllowed value for raId Redispatching_RA: "); + assertEquals(1, icsData.getStaticConstraintPerId().size()); + assertEquals(4, icsData.getTimeseriesPerIdAndType().get("Redispatching_RA").size()); } @Test - void testIcsImporterWrongShutDown() throws IOException { - IcsData icsData = IcsData.read( - getClass().getResourceAsStream("/ics/static_wrong_shutdown.csv"), + void testIcsImporterWithGSK() throws IOException { + // Test generic case without any error + // one remedial action "Redispatching_RA" defined on a GSK "GSK_NAME" + + // Read ICS Data + IcsData icsData = IcsDataImporter.read( + getClass().getResourceAsStream("/ics/static_with_gsk.csv"), getClass().getResourceAsStream("/ics/series.csv"), getClass().getResourceAsStream("/glsk/gsk.csv"), - generateOffsetDateTimeList()); - - Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", 1.0), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1")))) - .withMessage("Could not parse shutDownAllowed value for raId Redispatching_RA: wrongValue"); - } + generateOffsetDateTimeList(24)); + assertEquals(1, icsData.getStaticConstraintPerId().size()); + assertEquals(4, icsData.getTimeseriesPerIdAndType().get("Redispatching_RA").size()); + assertEquals(1, icsData.getWeightPerNodePerGsk().size()); + assertEquals(2, icsData.getWeightPerNodePerGsk().get("GSK_NAME").size()); + assertEquals(Map.of("BBE1AA1", 0.6, "FFR1AA1", 0.4), icsData.getWeightPerNodePerGsk().get("GSK_NAME")); - @Test - void testIcsImporterNoStartUp() { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_no_startup.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - - Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInputWithNetworkPaths, staticInputStream, seriesInputStream, gskInputStream, cost, cost)) - .withMessage("Could not parse startUpAllowed value for raId Redispatching_RA"); } - @Test - void testIcsImporterWrongStartUp() { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_wrong_startup.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - - Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInputWithNetworkPaths, staticInputStream, seriesInputStream, gskInputStream, cost, cost)) - .withMessage("Could not parse startUpAllowed value wrongValue for raId Redispatching_RA"); - } } \ No newline at end of file diff --git a/data/ics-importer/src/test/resources/ics/static_no_shutdown.csv b/data/ics-importer/src/test/resources/ics/static_no_shutdown.csv deleted file mode 100644 index 65e2c4c016..0000000000 --- a/data/ics-importer/src/test/resources/ics/static_no_shutdown.csv +++ /dev/null @@ -1,2 +0,0 @@ -RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed -Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;20;20;1;1;FALSE;; \ No newline at end of file diff --git a/data/ics-importer/src/test/resources/ics/static_no_startup.csv b/data/ics-importer/src/test/resources/ics/static_no_startup.csv deleted file mode 100644 index c48b07b47e..0000000000 --- a/data/ics-importer/src/test/resources/ics/static_no_startup.csv +++ /dev/null @@ -1,2 +0,0 @@ -RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed -Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;10;10;1;1;;FALSE \ No newline at end of file diff --git a/data/ics-importer/src/test/resources/ics/static_shutdown_startup_true.csv b/data/ics-importer/src/test/resources/ics/static_shutdown_startup_true.csv deleted file mode 100644 index 129c323405..0000000000 --- a/data/ics-importer/src/test/resources/ics/static_shutdown_startup_true.csv +++ /dev/null @@ -1,2 +0,0 @@ -RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed -Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;20;20;1;1;TRUE;TRUE \ No newline at end of file diff --git a/data/ics-importer/src/test/resources/ics/static_with_gsk_no_shutdown.csv b/data/ics-importer/src/test/resources/ics/static_with_gsk_no_shutdown.csv deleted file mode 100644 index 4f983be0e6..0000000000 --- a/data/ics-importer/src/test/resources/ics/static_with_gsk_no_shutdown.csv +++ /dev/null @@ -1,2 +0,0 @@ -RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed -Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;GSK;GSK_NAME;50;Coal;2;2;10;10;1;1;FALSE;; \ No newline at end of file diff --git a/data/ics-importer/src/test/resources/ics/static_with_gsk_no_startup.csv b/data/ics-importer/src/test/resources/ics/static_with_gsk_no_startup.csv deleted file mode 100644 index 29243b1a50..0000000000 --- a/data/ics-importer/src/test/resources/ics/static_with_gsk_no_startup.csv +++ /dev/null @@ -1,2 +0,0 @@ -RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed -Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;GSK;GSK_NAME;50;Coal;2;2;10;10;1;1;;FALSE \ No newline at end of file diff --git a/data/ics-importer/src/test/resources/ics/static_with_gsk_wrong_shutdown.csv b/data/ics-importer/src/test/resources/ics/static_with_gsk_wrong_shutdown.csv deleted file mode 100644 index f3264e21bd..0000000000 --- a/data/ics-importer/src/test/resources/ics/static_with_gsk_wrong_shutdown.csv +++ /dev/null @@ -1,2 +0,0 @@ -RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed -Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;GSK;GSK_NAME;50;Coal;2;2;10;10;1;1;FALSE;wrongValue \ No newline at end of file diff --git a/data/ics-importer/src/test/resources/ics/static_with_gsk_wrong_startup.csv b/data/ics-importer/src/test/resources/ics/static_with_gsk_wrong_startup.csv deleted file mode 100644 index 784fe8d20b..0000000000 --- a/data/ics-importer/src/test/resources/ics/static_with_gsk_wrong_startup.csv +++ /dev/null @@ -1,2 +0,0 @@ -RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed -Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;GSK;GSK_NAME;50;Coal;2;2;10;10;1;1;wrongValue;FALSE \ No newline at end of file diff --git a/data/ics-importer/src/test/resources/ics/static_wrong_shutdown.csv b/data/ics-importer/src/test/resources/ics/static_wrong_shutdown.csv deleted file mode 100644 index 59bf39e939..0000000000 --- a/data/ics-importer/src/test/resources/ics/static_wrong_shutdown.csv +++ /dev/null @@ -1,2 +0,0 @@ -RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed -Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;20;20;1;1;FALSE;wrongValue \ No newline at end of file diff --git a/data/ics-importer/src/test/resources/ics/static_wrong_startup.csv b/data/ics-importer/src/test/resources/ics/static_wrong_startup.csv deleted file mode 100644 index f923debd2f..0000000000 --- a/data/ics-importer/src/test/resources/ics/static_wrong_startup.csv +++ /dev/null @@ -1,2 +0,0 @@ -RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed -Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;10;10;1;1;wrongValue;FALSE \ No newline at end of file From 1ee0c184c1ccd746083fba5edb36b3cf41fd390e Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Mon, 30 Mar 2026 15:55:37 +0200 Subject: [PATCH 09/64] remove unecessary test Signed-off-by: CHEN Roxane --- .../data/crac/util/IcsImporterTest.java | 74 ------------------- 1 file changed, 74 deletions(-) diff --git a/data/crac/crac-util/src/test/java/com/powsybl/openrao/data/crac/util/IcsImporterTest.java b/data/crac/crac-util/src/test/java/com/powsybl/openrao/data/crac/util/IcsImporterTest.java index 46c6219def..09e85846dd 100644 --- a/data/crac/crac-util/src/test/java/com/powsybl/openrao/data/crac/util/IcsImporterTest.java +++ b/data/crac/crac-util/src/test/java/com/powsybl/openrao/data/crac/util/IcsImporterTest.java @@ -92,80 +92,6 @@ void tearDown() { } } - - - @Test - void testIcsImporterNoStartUp() { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_no_startup.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - - Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInputWithNetworkPaths, staticInputStream, seriesInputStream, gskInputStream, cost, cost)) - .withMessage("Could not parse startUpAllowed value for raId Redispatching_RA"); - } - - @Test - void testIcsImporterWrongStartUp() { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_wrong_startup.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - - Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInputWithNetworkPaths, staticInputStream, seriesInputStream, gskInputStream, cost, cost)) - .withMessage("Could not parse startUpAllowed value wrongValue for raId Redispatching_RA"); - } - - @Test - void testIcsImporterWithGskNoShutDown() { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_with_gsk_no_shutdown.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - - Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInputWithNetworkPaths, staticInputStream, seriesInputStream, gskInputStream, cost, cost)) - .withMessage("Could not parse shutDownAllowed value for nodeId FFR1AA1"); - } - - @Test - void testIcsImporterWithGskWrongShutDown() { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_with_gsk_wrong_shutdown.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - - Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInputWithNetworkPaths, staticInputStream, seriesInputStream, gskInputStream, cost, cost)) - .withMessage("Could not parse shutDownAllowed value wrongValue for nodeId FFR1AA1"); - } - - @Test - void testIcsImporterWithGskNoStartUp() { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_with_gsk_no_startup.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - - Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInputWithNetworkPaths, staticInputStream, seriesInputStream, gskInputStream, cost, cost)) - .withMessage("Could not parse startUpAllowed value for nodeId FFR1AA1"); - } - - @Test - void testIcsImporterWithGskWrongStartUp() { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_with_gsk_wrong_startup.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - - Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInputWithNetworkPaths, staticInputStream, seriesInputStream, gskInputStream, cost, cost)) - .withMessage("Could not parse startUpAllowed value wrongValue for nodeId FFR1AA1"); - } - @Test void testIcsImporterOneActionNoPminRd() throws IOException { double cost = 5.; From 58394c54c624a961c44dc2bad64123b383bc67dd Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Mon, 30 Mar 2026 15:55:49 +0200 Subject: [PATCH 10/64] test Signed-off-by: CHEN Roxane --- .../com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java b/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java index 3cae1f00af..a4112059f1 100644 --- a/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java +++ b/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java @@ -15,6 +15,7 @@ import com.powsybl.openrao.commons.TemporalDataImpl; import com.powsybl.openrao.commons.Unit; import com.powsybl.openrao.data.IcsData; +import com.powsybl.openrao.data.IcsDataImporter; import com.powsybl.openrao.data.crac.api.Crac; import com.powsybl.openrao.data.crac.api.CracCreationContext; import com.powsybl.openrao.data.crac.api.Instant; @@ -265,7 +266,7 @@ public static void loadDataForCoreTimeCoupledRao(DataTable arg1) throws IOExcept // Read ICS Data - IcsData icsData = IcsData.read(new FileInputStream(getFile(icsStaticPath)),new FileInputStream(getFile(icsSeriesPath)),gskInputStream, timeCoupledRaoInputWithNetworkPaths.getTimestampsToRun().stream().sorted().toList()); + IcsData icsData = IcsDataImporter.read(new FileInputStream(getFile(icsStaticPath)),new FileInputStream(getFile(icsSeriesPath)),gskInputStream, timeCoupledRaoInputWithNetworkPaths.getTimestampsToRun().stream().sorted().toList()); TemporalData cracToModify = new TemporalDataImpl<>(); timeCoupledRaoInputWithNetworkPaths.getRaoInputs().getDataPerTimestamp().forEach((dateTime, raoInput) -> { From b4d71c7dc0570bac52c5d9238572d71d5ba09d77 Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Mon, 30 Mar 2026 17:23:02 +0200 Subject: [PATCH 11/64] update test Signed-off-by: CHEN Roxane --- .../com/powsybl/openrao/data/IcsData.java | 11 ++- .../com/powsybl/openrao/data/IcsDataTest.java | 97 +++++++++++++++++-- 2 files changed, 97 insertions(+), 11 deletions(-) diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java index cb7e1b759e..1fa44ea912 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java @@ -159,7 +159,9 @@ public static Map createGeneratorAndLoadAndUpdateNetworks(Tempor } int index = dateTime.getHour() + OFFSET; + // p0 values checked during IcsData import Double p0 = parseDoubleWithPossibleCommas(seriesPerType.get(P0).get(index)) * shiftKey; + // pMin can be undefined Optional pMinRd = IcsUtil.parseValue(seriesPerType, P_MIN_RD, dateTime, shiftKey); processBus(bus, generatorId, p0, pMinRd.orElse(ON_POWER_THRESHOLD)); } @@ -206,11 +208,10 @@ public static void createInjectionRangeActionsAndUpdateCracs(TemporalData injectionRangeActionAdder.withNetworkElementAndKey(shiftKey, networkElementPerNode.get(nodeId)); }); - if (staticRecord.get(PREVENTIVE).equalsIgnoreCase(TRUE)) { - injectionRangeActionAdder.newOnInstantUsageRule() - .withInstant(crac.getPreventiveInstant().getId()) - .add(); - } + injectionRangeActionAdder.newOnInstantUsageRule() + .withInstant(crac.getPreventiveInstant().getId()) + .add(); + if (importCurative && staticRecord.get(CURATIVE).equalsIgnoreCase(TRUE)) { injectionRangeActionAdder.newOnInstantUsageRule() .withInstant(crac.getLastInstant().getId()) diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java index 80d0c881b6..466219a253 100644 --- a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java @@ -7,14 +7,19 @@ package com.powsybl.openrao.data; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; import com.powsybl.iidm.network.Generator; import com.powsybl.iidm.network.Network; import com.powsybl.openrao.commons.OpenRaoException; import com.powsybl.openrao.commons.TemporalData; import com.powsybl.openrao.commons.TemporalDataImpl; +import com.powsybl.openrao.commons.logs.RaoBusinessWarns; import com.powsybl.openrao.data.crac.api.Crac; import com.powsybl.openrao.data.crac.api.rangeaction.InjectionRangeAction; import com.powsybl.openrao.data.crac.api.rangeaction.VariationDirection; +import com.powsybl.openrao.data.crac.api.usagerule.UsageRule; import com.powsybl.openrao.data.timecoupledconstraints.GeneratorConstraints; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -22,17 +27,20 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.slf4j.LoggerFactory; import java.io.*; import java.nio.charset.StandardCharsets; import java.time.OffsetDateTime; import java.time.ZoneOffset; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Stream; import static com.powsybl.openrao.data.IcsDataImporterTest.generateOffsetDateTimeList; import static com.powsybl.openrao.data.IcsUtil.MAX_GRADIENT; +import static com.powsybl.openrao.data.IcsUtil.ON_POWER_THRESHOLD; import static org.junit.jupiter.api.Assertions.*; /** @@ -46,6 +54,7 @@ public class IcsDataTest { private Network network2; private TemporalData networkTemporalData; private TemporalData cracTemporalData; + List logsList; @BeforeEach void setUp() throws IOException { @@ -74,6 +83,15 @@ void setUp() throws IOException { )); } + @BeforeEach + void setUpLogger() { + Logger logger = (Logger) LoggerFactory.getLogger(RaoBusinessWarns.class); + ListAppender listAppender = new ListAppender<>(); + listAppender.start(); + logger.addAppender(listAppender); + logsList = listAppender.list; + } + // Test Create Generator Constraint @Test @@ -279,6 +297,58 @@ void testCreateGeneratorAndLoadAndUpdateNetworksOnAGsk() throws IOException { assertEquals(10.0 * 0.4, generatorFR.getMinP(), DOUBLE_EPSILON); } + @Test + void testCreateGeneratorAndLoadBusNotFound() throws IOException { + String gsk = """ + GSK ID;Node;Weight + GSK_NAME;undefined_node;0.6 + GSK_NAME;FFR1AA1;0.4 + """; + + // Read ICS Data + IcsData icsData = IcsDataImporter.read( + getClass().getResourceAsStream("/ics/static_with_gsk.csv"), + getClass().getResourceAsStream("/ics/series.csv"), + new ByteArrayInputStream(gsk.getBytes(StandardCharsets.UTF_8)), + generateOffsetDateTimeList(2)); + Map generatorIdPerNodeId = icsData.createGeneratorAndLoadAndUpdateNetworks(networkTemporalData, "Redispatching_RA", icsData.getWeightPerNodePerGsk().get("GSK_NAME")); + assertEquals(Map.of(), generatorIdPerNodeId); + assertEquals("Redispatching action Redispatching_RA cannot be imported because bus undefined_node could not be found", logsList.get(0).getFormattedMessage()); + } + + @Test + void testCreateGeneratorAndLoadPMinNotDefined() throws IOException { + String seriesCsv = """ + RA RD ID;Type of timeseries;00:30;01:30 + Redispatching_RA;RDP-;35;35 + Redispatching_RA;RDP+;43;43 + Redispatching_RA;P0;116;116 + Redispatching_RA;Pmin_RD;10; + """; + // Read ICS Data + IcsData icsData = IcsDataImporter.read( + getClass().getResourceAsStream("/ics/static_with_gsk.csv"), + new ByteArrayInputStream(seriesCsv.getBytes(StandardCharsets.UTF_8)), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList(2)); + Map generatorIdPerNodeId = icsData.createGeneratorAndLoadAndUpdateNetworks(networkTemporalData, "Redispatching_RA", icsData.getWeightPerNodePerGsk().get("GSK_NAME")); + assertEquals(Map.of("BBE1AA1", "Redispatching_RA_BBE1AA1_GENERATOR", "FFR1AA1", "Redispatching_RA_FFR1AA1_GENERATOR"), generatorIdPerNodeId); + Generator generatorBE = network1.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); + assertEquals(116. * 0.6, generatorBE.getTargetP(), DOUBLE_EPSILON); + assertEquals(10. * 0.6, generatorBE.getMinP(), DOUBLE_EPSILON); + Generator generatorFR = network1.getGenerator("Redispatching_RA_FFR1AA1_GENERATOR"); + assertEquals(116. * 0.4, generatorFR.getTargetP(), DOUBLE_EPSILON); + assertEquals(10. * 0.4, generatorFR.getMinP(), DOUBLE_EPSILON); + + Generator generatorBE2 = network2.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); + assertEquals(116. * 0.6, generatorBE2.getTargetP(), DOUBLE_EPSILON); + assertEquals(ON_POWER_THRESHOLD, generatorBE2.getMinP(), DOUBLE_EPSILON); + Generator generatorFR2 = network2.getGenerator("Redispatching_RA_FFR1AA1_GENERATOR"); + assertEquals(116. * 0.4, generatorFR2.getTargetP(), DOUBLE_EPSILON); + assertEquals(ON_POWER_THRESHOLD, generatorFR2.getMinP(), DOUBLE_EPSILON); + assertEquals("Redispatching action Redispatching_RA is missing Pmin_RD value for datetime 2025-02-13T01:30Z", logsList.get(0).getFormattedMessage()); + } + // Test createInjectionRangeActionsAndUpdateCracs @Test void testCreateInjectionRangeActionsAndUpdateCracsOnANode() throws IOException { @@ -313,8 +383,6 @@ void testCreateInjectionRangeActionsAndUpdateCracsOnANode() throws IOException { @Test void testCreateInjectionRangeActionsAndUpdateCracsOnAGsk() throws IOException { - - // Read ICS Data IcsData icsData = IcsDataImporter.read( getClass().getResourceAsStream("/ics/static_with_gsk.csv"), getClass().getResourceAsStream("/ics/series.csv"), @@ -333,12 +401,27 @@ void testCreateInjectionRangeActionsAndUpdateCracsOnAGsk() throws IOException { assertEquals(5., ra1.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); } + @Test + void testCreateInjectionRangeActionsAndUpdateCracsCurativeRedispatchingAction() throws IOException { + String staticCsv = """ + RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed + Redispatching_RA;FR;TRUE;TRUE;00:00;24:00:00;Generator_Name;GSK;GSK_NAME;50;Coal;2;2;20;20;1;1;FALSE;FALSE + """; + IcsData icsData = IcsDataImporter.read( + new ByteArrayInputStream(staticCsv.getBytes(StandardCharsets.UTF_8)), + getClass().getResourceAsStream("/ics/series.csv"), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList(2)); + icsData.createInjectionRangeActionsAndUpdateCracs(cracTemporalData, "Redispatching_RA", icsData.getWeightPerNodePerGsk().get("GSK_NAME"), Map.of("BBE1AA1", "Redispatching_RA_BBE1AA1_GENERATOR", "FFR1AA1", "Redispatching_RA_FFR1AA1_GENERATOR"), 5., 5.); + assertEquals(1, crac1.getInjectionRangeActions().size()); + InjectionRangeAction ra1 = crac1.getInjectionRangeActions().iterator().next(); + assertEquals(1, ra1.getUsageRules().size()); + assertEquals(crac1.getInstant("preventive"), ra1.getUsageRules().iterator().next().getInstant()); + } + // Test Getter and Setter @Test void testIcsDataReadOkNode() throws IOException { - // Test generic case without any error - // one remedial action "Redispatching_RA" defined on the node "BBE1AA1" - // Read ICS Data IcsData icsData = IcsDataImporter.read( getClass().getResourceAsStream("/ics/static.csv"), @@ -348,6 +431,8 @@ void testIcsDataReadOkNode() throws IOException { assertEquals(1, icsData.getStaticConstraintPerId().size()); assertEquals(4, icsData.getTimeseriesPerIdAndType().get("Redispatching_RA").size()); + assertEquals(1, icsData.getWeightPerNodePerGsk().size()); + assertEquals(Set.of("Redispatching_RA"), icsData.getRedispatchingActions()); } @Test @@ -367,7 +452,7 @@ void testIcsImporterWithGSK() throws IOException { assertEquals(1, icsData.getWeightPerNodePerGsk().size()); assertEquals(2, icsData.getWeightPerNodePerGsk().get("GSK_NAME").size()); assertEquals(Map.of("BBE1AA1", 0.6, "FFR1AA1", 0.4), icsData.getWeightPerNodePerGsk().get("GSK_NAME")); - + assertEquals(Set.of("Redispatching_RA"), icsData.getRedispatchingActions()); } } \ No newline at end of file From 18bdbb9a43512b1884013e695df36c5ac68e237f Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Mon, 30 Mar 2026 17:23:34 +0200 Subject: [PATCH 12/64] update log Signed-off-by: CHEN Roxane --- .../src/main/java/com/powsybl/openrao/data/IcsUtil.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java index 0e1bc89eaa..16807a07c7 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java @@ -16,6 +16,8 @@ import java.util.Map; import java.util.Optional; +import static com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider.BUSINESS_WARNS; + /** * @author Roxane Chen {@literal } */ @@ -57,10 +59,10 @@ static Optional parseValue(Map seriesPerType, String if (seriesPerType.containsKey(key)) { CSVRecord series = seriesPerType.get(key); String value = series.get(timestamp.getHour() + OFFSET); - if (value != null) { + if (value != null && !value.isEmpty()) { return Optional.of(parseDoubleWithPossibleCommas(value) * shiftKey); } else { - // TODO: make sure to add a test for this + BUSINESS_WARNS.warn("Redispatching action {} is missing {} value for datetime {}", series.get(RA_RD_ID), key, timestamp); return Optional.empty(); } } From 368e40e7ba86de828c893be5e2c8f7541771680a Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Mon, 30 Mar 2026 17:23:44 +0200 Subject: [PATCH 13/64] update pom Signed-off-by: CHEN Roxane --- data/ics-importer/pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data/ics-importer/pom.xml b/data/ics-importer/pom.xml index 727b15bf84..f52b355ba8 100644 --- a/data/ics-importer/pom.xml +++ b/data/ics-importer/pom.xml @@ -81,6 +81,10 @@ commons-io runtime + + ch.qos.logback + logback-classic + \ No newline at end of file From 0b54b58ac66170929b8e2888b5140056bf2b1ca1 Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Mon, 30 Mar 2026 17:31:40 +0200 Subject: [PATCH 14/64] remove icsImporterTest Signed-off-by: CHEN Roxane --- .../data/crac/util/IcsImporterTest.java | 205 ------------------ 1 file changed, 205 deletions(-) delete mode 100644 data/crac/crac-util/src/test/java/com/powsybl/openrao/data/crac/util/IcsImporterTest.java diff --git a/data/crac/crac-util/src/test/java/com/powsybl/openrao/data/crac/util/IcsImporterTest.java b/data/crac/crac-util/src/test/java/com/powsybl/openrao/data/crac/util/IcsImporterTest.java deleted file mode 100644 index 09e85846dd..0000000000 --- a/data/crac/crac-util/src/test/java/com/powsybl/openrao/data/crac/util/IcsImporterTest.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright (c) 2025, 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.crac.util; - -import com.powsybl.iidm.network.Generator; -import com.powsybl.iidm.network.Network; -import com.powsybl.openrao.commons.OpenRaoException; -import com.powsybl.openrao.commons.TemporalData; -import com.powsybl.openrao.commons.TemporalDataImpl; -import com.powsybl.openrao.data.crac.api.Crac; -import com.powsybl.openrao.data.crac.api.rangeaction.InjectionRangeAction; -import com.powsybl.openrao.data.crac.api.rangeaction.VariationDirection; -import com.powsybl.openrao.data.timecoupledconstraints.GeneratorConstraints; -import com.powsybl.openrao.data.timecoupledconstraints.TimeCoupledConstraints; -import com.powsybl.openrao.raoapi.RaoInputWithNetworkPaths; -import com.powsybl.openrao.raoapi.TimeCoupledRaoInputWithNetworkPaths; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Path; -import java.time.OffsetDateTime; -import java.time.ZoneOffset; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertFalse; -import org.assertj.core.api.Assertions; - -/** - * @author Philippe Edwards {@literal } - */ -class IcsImporterTest { - private static final double DOUBLE_EPSILON = 1e-6; - private static final String TMP_DIR = System.getProperty("java.io.tmpdir") + File.separator; - private String networkFilePathPostIcsImport1; - private String networkFilePathPostIcsImport2; - private TimeCoupledRaoInputWithNetworkPaths timeCoupledRaoInputWithNetworkPaths; - private Crac crac1; - private Crac crac2; - - @BeforeEach - void setUp() throws IOException { - // we need to import twice the network to avoid variant names conflicts on the same network object - String networkFilePath1 = "2Nodes2ParallelLinesPST_0030.uct"; - String networkFilePath2 = "2Nodes2ParallelLinesPST_0130.uct"; - Network network1 = Network.read(networkFilePath1, IcsImporterTest.class.getResourceAsStream("/network/" + networkFilePath1)); - Network network2 = Network.read(networkFilePath2, IcsImporterTest.class.getResourceAsStream("/network/" + networkFilePath2)); - - // Create postIcsNetwork: - networkFilePathPostIcsImport1 = TMP_DIR + networkFilePath1.split(".uct")[0].concat("_modified.jiidm"); - networkFilePathPostIcsImport2 = TMP_DIR + networkFilePath2.split(".uct")[0].concat("_modified.jiidm"); - - crac1 = Crac.read("/crac/crac-0030.json", IcsImporterTest.class.getResourceAsStream("/crac/crac-0030.json"), network1); - crac2 = Crac.read("/crac/crac-0130.json", IcsImporterTest.class.getResourceAsStream("/crac/crac-0130.json"), network2); - - OffsetDateTime timestamp1 = OffsetDateTime.of(2025, 2, 13, 0, 30, 0, 0, ZoneOffset.UTC); - OffsetDateTime timestamp2 = OffsetDateTime.of(2025, 2, 13, 1, 30, 0, 0, ZoneOffset.UTC); - - TemporalData raoInputs = new TemporalDataImpl<>( - Map.of( - timestamp1, RaoInputWithNetworkPaths.build(getResourcePath("network/" + networkFilePath1), networkFilePathPostIcsImport1, crac1).build(), - timestamp2, RaoInputWithNetworkPaths.build(getResourcePath("network/" + networkFilePath2), networkFilePathPostIcsImport2, crac2).build() - )); - - timeCoupledRaoInputWithNetworkPaths = new TimeCoupledRaoInputWithNetworkPaths(raoInputs, new TimeCoupledConstraints()); - } - - private String getResourcePath(String resourcePath) { - return "src/test/resources/" + resourcePath; - } - - @AfterEach - void tearDown() { - // Clean created networks - File file = new File(Path.of(networkFilePathPostIcsImport1).toUri()); - if (file.exists()) { - file.delete(); - } - file = new File(Path.of(networkFilePathPostIcsImport2).toUri()); - if (file.exists()) { - file.delete(); - } - } - - @Test - void testIcsImporterOneActionNoPminRd() throws IOException { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series_no_pmin_rd.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - IcsImporter.populateInputWithICS(timeCoupledRaoInputWithNetworkPaths, staticInputStream, seriesInputStream, gskInputStream, cost, cost); - - assertEquals(1, timeCoupledRaoInputWithNetworkPaths.getTimeCoupledConstraints().getGeneratorConstraints().size()); - GeneratorConstraints generatorConstraints = timeCoupledRaoInputWithNetworkPaths.getTimeCoupledConstraints().getGeneratorConstraints().iterator().next(); - assertEquals("Redispatching_RA_BBE1AA1_GENERATOR", generatorConstraints.getGeneratorId()); - assertTrue(generatorConstraints.getDownwardPowerGradient().isPresent()); - assertEquals(-10., generatorConstraints.getDownwardPowerGradient().get(), DOUBLE_EPSILON); - assertTrue(generatorConstraints.getUpwardPowerGradient().isPresent()); - assertEquals(10., generatorConstraints.getUpwardPowerGradient().get(), DOUBLE_EPSILON); - assertTrue(generatorConstraints.getLeadTime().isPresent()); - assertEquals(1.0, generatorConstraints.getLeadTime().get(), DOUBLE_EPSILON); - assertTrue(generatorConstraints.getLagTime().isPresent()); - assertEquals(1.0, generatorConstraints.getLagTime().get(), DOUBLE_EPSILON); - assertFalse(generatorConstraints.isShutDownAllowed()); - assertFalse(generatorConstraints.isStartUpAllowed()); - - assertEquals(1, crac1.getInjectionRangeActions().size()); - InjectionRangeAction ra1 = crac1.getInjectionRangeActions().iterator().next(); - assertEquals("Redispatching_RA_RD", ra1.getId()); - assertEquals(116., ra1.getInitialSetpoint(), DOUBLE_EPSILON); - assertTrue(ra1.getVariationCost(VariationDirection.UP).isPresent()); - assertEquals(5., ra1.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); - assertTrue(ra1.getVariationCost(VariationDirection.DOWN).isPresent()); - assertEquals(5., ra1.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); - Network network1 = Network.read(networkFilePathPostIcsImport1); - Generator generator1 = network1.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); - assertEquals(116., generator1.getTargetP(), DOUBLE_EPSILON); - assertEquals(1.001, generator1.getMinP(), DOUBLE_EPSILON); - - assertEquals(1, crac2.getInjectionRangeActions().size()); - InjectionRangeAction ra2 = crac2.getInjectionRangeActions().iterator().next(); - assertEquals("Redispatching_RA_RD", ra2.getId()); - assertEquals(120., ra2.getInitialSetpoint(), DOUBLE_EPSILON); - assertTrue(ra2.getVariationCost(VariationDirection.UP).isPresent()); - assertEquals(5., ra2.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); - assertTrue(ra2.getVariationCost(VariationDirection.DOWN).isPresent()); - assertEquals(5., ra2.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); - Network network2 = Network.read(networkFilePathPostIcsImport2); - Generator generator2 = network2.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); - assertEquals(120., generator2.getTargetP(), DOUBLE_EPSILON); - assertEquals(1.001, generator2.getMinP(), DOUBLE_EPSILON); - } - - @Test - void testIcsImporterOneActionNoGradientNoLeadNoLag() throws IOException { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_no_gradient_no_lead_no_lag.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - IcsImporter.populateInputWithICS(timeCoupledRaoInputWithNetworkPaths, staticInputStream, seriesInputStream, gskInputStream, cost, cost); - - assertEquals(1, timeCoupledRaoInputWithNetworkPaths.getTimeCoupledConstraints().getGeneratorConstraints().size()); - GeneratorConstraints generatorConstraints = timeCoupledRaoInputWithNetworkPaths.getTimeCoupledConstraints().getGeneratorConstraints().iterator().next(); - assertEquals("Redispatching_RA_BBE1AA1_GENERATOR", generatorConstraints.getGeneratorId()); - assertTrue(generatorConstraints.getDownwardPowerGradient().isPresent()); - assertEquals(-1000.0, generatorConstraints.getDownwardPowerGradient().get(), DOUBLE_EPSILON); - assertTrue(generatorConstraints.getUpwardPowerGradient().isPresent()); - assertEquals(1000.0, generatorConstraints.getUpwardPowerGradient().get(), DOUBLE_EPSILON); - assertTrue(generatorConstraints.getLeadTime().isEmpty()); - assertTrue(generatorConstraints.getLagTime().isEmpty()); - assertFalse(generatorConstraints.isShutDownAllowed()); - assertFalse(generatorConstraints.isStartUpAllowed()); - - assertEquals(1, crac1.getInjectionRangeActions().size()); - InjectionRangeAction ra1 = crac1.getInjectionRangeActions().iterator().next(); - assertEquals("Redispatching_RA_RD", ra1.getId()); - assertEquals(116., ra1.getInitialSetpoint(), DOUBLE_EPSILON); - assertTrue(ra1.getVariationCost(VariationDirection.UP).isPresent()); - assertEquals(5., ra1.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); - assertTrue(ra1.getVariationCost(VariationDirection.DOWN).isPresent()); - assertEquals(5., ra1.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); - Network network1 = Network.read(networkFilePathPostIcsImport1); - Generator generator1 = network1.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); - assertEquals(116., generator1.getTargetP(), DOUBLE_EPSILON); - assertEquals(10.0, generator1.getMinP(), DOUBLE_EPSILON); - - assertEquals(1, crac2.getInjectionRangeActions().size()); - InjectionRangeAction ra2 = crac2.getInjectionRangeActions().iterator().next(); - assertEquals("Redispatching_RA_RD", ra2.getId()); - assertEquals(120., ra2.getInitialSetpoint(), DOUBLE_EPSILON); - assertTrue(ra2.getVariationCost(VariationDirection.UP).isPresent()); - assertEquals(5., ra2.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); - assertTrue(ra2.getVariationCost(VariationDirection.DOWN).isPresent()); - assertEquals(5., ra2.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); - Network network2 = Network.read(networkFilePathPostIcsImport2); - Generator generator2 = network2.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); - assertEquals(120., generator2.getTargetP(), DOUBLE_EPSILON); - assertEquals(15.0, generator2.getMinP(), DOUBLE_EPSILON); - } - - @Test - void testIcsImporterGradientNotOk() throws IOException { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series_gradient_not_ok.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - IcsImporter.populateInputWithICS(timeCoupledRaoInputWithNetworkPaths, staticInputStream, seriesInputStream, gskInputStream, cost, cost); - - assertEquals(0, timeCoupledRaoInputWithNetworkPaths.getTimeCoupledConstraints().getGeneratorConstraints().size()); - assertEquals(0, crac1.getInjectionRangeActions().size()); - assertEquals(0, crac2.getInjectionRangeActions().size()); - } - - -} From e5f6ef10d55551793e774afdf289b91966981128 Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Mon, 30 Mar 2026 17:32:00 +0200 Subject: [PATCH 15/64] validate Signed-off-by: CHEN Roxane --- .../com/powsybl/openrao/data/IcsData.java | 5 +- .../powsybl/openrao/data/IcsDataImporter.java | 19 +++-- .../com/powsybl/openrao/data/IcsUtil.java | 10 ++- .../openrao/data/IcsDataImporterTest.java | 11 ++- .../com/powsybl/openrao/data/IcsDataTest.java | 73 +++++++++---------- 5 files changed, 59 insertions(+), 59 deletions(-) diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java index 1fa44ea912..e0ec18874d 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java @@ -23,7 +23,6 @@ import static com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider.BUSINESS_WARNS; import static com.powsybl.openrao.data.IcsUtil.*; - /** * @author Roxane Chen {@literal } */ @@ -115,7 +114,7 @@ public Set createGeneratorConstraints(String raId, Map injectionRangeActionAdder.add(); }); } -} \ No newline at end of file +} diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java index a764039e98..9e4465b904 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java @@ -37,7 +37,11 @@ /** * @author Roxane Chen {@literal } */ -public class IcsDataImporter { +public final class IcsDataImporter { + + private IcsDataImporter() { + + } private static CSVFormat csvFormat = CSVFormat.DEFAULT.builder() .setDelimiter(";") @@ -67,7 +71,7 @@ public static IcsData read(InputStream staticInputStream, // Parse GSK and get weight Per Node Per Gsk Map> weightPerNodePerGsk = parseGskCsv(gskInputStream); // Parse static CSV: remedial action’s generator’s static constraints. one line per RA_ID - Map staticConstraintPerId = parseStaticCsv(staticInputStream); + Map staticConstraintPerId = parseStaticCsv(staticInputStream); Set consistentRAs = filterOutInconsistentRedispatchingActions(staticConstraintPerId, timeseriesPerIdAndType, weightPerNodePerGsk, sortedTimestampToRun); @@ -75,7 +79,7 @@ public static IcsData read(InputStream staticInputStream, } - static Set filterOutInconsistentRedispatchingActions(Map staticConstraintPerId, + static Set filterOutInconsistentRedispatchingActions(Map staticConstraintPerId, Map> timeseriesPerIdAndType, Map> weightPerNodePerGsk, List sortedTimestampToRun) { @@ -125,7 +129,7 @@ static Map> parseGskCsv(InputStream gskInputStream) } // Consistency check functions - private static boolean shouldBeImported(CSVRecord staticRecord, List sortedTimestampToRun, Map> weightPerNodePerGsk, Map> timeseriesPerIdAndType) { + private static boolean shouldBeImported(CSVRecord staticRecord, List sortedTimestampToRun, Map> weightPerNodePerGsk, Map> timeseriesPerIdAndType) { String raId = staticRecord.get(RA_RD_ID); // Check that remedial action is defined in series csv and gsk (if defined on a gsk) @@ -134,7 +138,7 @@ private static boolean shouldBeImported(CSVRecord staticRecord, List seriesPerType, List} */ -public class IcsUtil { +public final class IcsUtil { + + private IcsUtil() { + + } // TODO: move to a configuration file ? static final double MAX_GRADIENT = 1000.0; @@ -90,7 +94,6 @@ static Bus findBus(String nodeId, Network network) { return network.getBusBreakerView().getBus(modifiedNodeId + " "); } - static void processBus(Bus bus, String generatorId, Double p0, double pMinRd) { bus.getVoltageLevel().newGenerator() .setBus(bus.getId()) @@ -133,5 +136,4 @@ public static void updateNominalVoltage(Network network) { private static boolean safeDoubleEquals(double a, double b) { return Math.abs(a - b) < 1e-3; } - -} \ No newline at end of file +} diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataImporterTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataImporterTest.java index 69b88bbfbc..94d02bd003 100644 --- a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataImporterTest.java +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataImporterTest.java @@ -20,7 +20,6 @@ import org.junit.jupiter.params.provider.MethodSource; import org.slf4j.LoggerFactory; - import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -121,7 +120,7 @@ void testParseGskCsv() throws IOException { @Test void testStandardIcsDataImporterRead() throws IOException { - IcsData icsData = IcsDataImporter.read( + IcsData icsData = IcsDataImporter.read( getClass().getResourceAsStream("/ics/static.csv"), getClass().getResourceAsStream("/ics/series.csv"), getClass().getResourceAsStream("/glsk/gsk.csv"), @@ -136,7 +135,7 @@ void testRaNotAvailableInPreventive() throws IOException { RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed Redispatching_RA;FR;FALSE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;20;20;1;1;FALSE;FALSE """; - IcsData icsData = IcsDataImporter.read( + IcsData icsData = IcsDataImporter.read( new ByteArrayInputStream(staticCsv.getBytes(StandardCharsets.UTF_8)), getClass().getResourceAsStream("/ics/series.csv"), getClass().getResourceAsStream("/glsk/gsk.csv"), @@ -152,7 +151,7 @@ void testRaDefinedInStaticButNotInSeries() throws IOException { Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;20;20;1;1;FALSE;FALSE Redispatching_RA_not_defined_in_series_csv;FR;FALSE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;20;20;1;1;FALSE;FALSE """; - IcsData icsData = IcsDataImporter.read( + IcsData icsData = IcsDataImporter.read( new ByteArrayInputStream(staticCsv.getBytes(StandardCharsets.UTF_8)), getClass().getResourceAsStream("/ics/series.csv"), getClass().getResourceAsStream("/glsk/gsk.csv"), @@ -169,7 +168,7 @@ void testRaDefinedOnGskButNotInGskCsv() throws IOException { RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;GSK;GSK_NAME_NOT_IN_CSV;50;Coal;2;2;20;20;1;1;FALSE;FALSE """; - IcsData icsData = IcsDataImporter.read( + IcsData icsData = IcsDataImporter.read( new ByteArrayInputStream(staticCsv.getBytes(StandardCharsets.UTF_8)), getClass().getResourceAsStream("/ics/series.csv"), getClass().getResourceAsStream("/glsk/gsk.csv"), @@ -185,7 +184,7 @@ void testRaNotDefinedOnANodeOrAGsk() throws IOException { RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;OTHER;GSK_NAME_NOT_IN_CSV;50;Coal;2;2;20;20;1;1;FALSE;FALSE """; - IcsData icsData = IcsDataImporter.read( + IcsData icsData = IcsDataImporter.read( new ByteArrayInputStream(staticCsv.getBytes(StandardCharsets.UTF_8)), getClass().getResourceAsStream("/ics/series.csv"), getClass().getResourceAsStream("/glsk/gsk.csv"), diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java index 466219a253..82678f7980 100644 --- a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java @@ -19,7 +19,6 @@ import com.powsybl.openrao.data.crac.api.Crac; import com.powsybl.openrao.data.crac.api.rangeaction.InjectionRangeAction; import com.powsybl.openrao.data.crac.api.rangeaction.VariationDirection; -import com.powsybl.openrao.data.crac.api.usagerule.UsageRule; import com.powsybl.openrao.data.timecoupledconstraints.GeneratorConstraints; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -107,7 +106,7 @@ void testCreateGeneratorConstraintRaDefinedOnANode() throws IOException { generateOffsetDateTimeList(24)); // Check generator constraint creation - Set generatorConstraintsSet = icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", 1.0), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1")) ); + Set generatorConstraintsSet = icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", 1.0), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1"))); assertEquals(1, generatorConstraintsSet.size()); GeneratorConstraints generatorConstraints = generatorConstraintsSet.iterator().next(); @@ -170,12 +169,12 @@ void testCreateGeneratorConstraintRaDefinedOnANodeMissingGradientLeadTimeAndLagT RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;;;;;FALSE;FALSE """; - IcsData icsData = IcsDataImporter.read( + IcsData icsData = IcsDataImporter.read( new ByteArrayInputStream(staticCsv.getBytes(StandardCharsets.UTF_8)), getClass().getResourceAsStream("/ics/series.csv"), getClass().getResourceAsStream("/glsk/gsk.csv"), generateOffsetDateTimeList(24)); - Set generatorConstraintsSet = icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", 1.0), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1")) ); + Set generatorConstraintsSet = icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", 1.0), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1"))); assertEquals(1, generatorConstraintsSet.size()); GeneratorConstraints generatorConstraints = generatorConstraintsSet.iterator().next(); assertEquals("Redispatching_RA_BBE1AA1_GENERATOR", generatorConstraints.getGeneratorId()); @@ -202,7 +201,7 @@ void testCreateGeneratorConstraintShutDownAndStartUpAllowed(String staticCsv, St generateOffsetDateTimeList(24)); if (expectedLogMessage == null) { - Set generatorConstraintsSet = icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", 1.0), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1")) ); + Set generatorConstraintsSet = icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", 1.0), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1"))); assertEquals(1, generatorConstraintsSet.size()); GeneratorConstraints generatorConstraints = generatorConstraintsSet.iterator().next(); assertTrue(generatorConstraints.isShutDownAllowed()); @@ -214,7 +213,6 @@ void testCreateGeneratorConstraintShutDownAndStartUpAllowed(String staticCsv, St } } - private static Stream startUpAndShutDownAllowedCsvCases() { String baseHeader = """ RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed @@ -276,6 +274,7 @@ void testCreateGeneratorAndLoadAndUpdateNetworksOnANode() throws IOException { assertEquals(120., generator2.getTargetP(), DOUBLE_EPSILON); assertEquals(15.0, generator2.getMinP(), DOUBLE_EPSILON); } + @Test void testCreateGeneratorAndLoadAndUpdateNetworksOnAGsk() throws IOException { // Test generic case without any error @@ -359,7 +358,6 @@ void testCreateInjectionRangeActionsAndUpdateCracsOnANode() throws IOException { getClass().getResourceAsStream("/glsk/gsk.csv"), generateOffsetDateTimeList(2)); - // Test injection range action creation in crac icsData.createInjectionRangeActionsAndUpdateCracs(cracTemporalData, "Redispatching_RA", Map.of("BBE1AA1", 1.0), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1")), 5., 5.); assertEquals(1, crac1.getInjectionRangeActions().size()); @@ -381,43 +379,43 @@ void testCreateInjectionRangeActionsAndUpdateCracsOnANode() throws IOException { assertEquals(5., ra2.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); } - @Test - void testCreateInjectionRangeActionsAndUpdateCracsOnAGsk() throws IOException { - IcsData icsData = IcsDataImporter.read( - getClass().getResourceAsStream("/ics/static_with_gsk.csv"), - getClass().getResourceAsStream("/ics/series.csv"), - getClass().getResourceAsStream("/glsk/gsk.csv"), - generateOffsetDateTimeList(2)); + @Test + void testCreateInjectionRangeActionsAndUpdateCracsOnAGsk() throws IOException { + IcsData icsData = IcsDataImporter.read( + getClass().getResourceAsStream("/ics/static_with_gsk.csv"), + getClass().getResourceAsStream("/ics/series.csv"), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList(2)); + + icsData.createInjectionRangeActionsAndUpdateCracs(cracTemporalData, "Redispatching_RA", icsData.getWeightPerNodePerGsk().get("GSK_NAME"), Map.of("BBE1AA1", "Redispatching_RA_BBE1AA1_GENERATOR", "FFR1AA1", "Redispatching_RA_FFR1AA1_GENERATOR"), 5., 5.); - icsData.createInjectionRangeActionsAndUpdateCracs(cracTemporalData, "Redispatching_RA", icsData.getWeightPerNodePerGsk().get("GSK_NAME"), Map.of("BBE1AA1", "Redispatching_RA_BBE1AA1_GENERATOR", "FFR1AA1", "Redispatching_RA_FFR1AA1_GENERATOR"), 5., 5.); - - assertEquals(1, crac1.getInjectionRangeActions().size()); - InjectionRangeAction ra1 = crac1.getInjectionRangeActions().iterator().next(); - assertEquals("Redispatching_RA_RD", ra1.getId()); - assertEquals(116., ra1.getInitialSetpoint(), DOUBLE_EPSILON); - assertTrue(ra1.getVariationCost(VariationDirection.UP).isPresent()); - assertEquals(5., ra1.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); - assertTrue(ra1.getVariationCost(VariationDirection.DOWN).isPresent()); - assertEquals(5., ra1.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); - } - - @Test - void testCreateInjectionRangeActionsAndUpdateCracsCurativeRedispatchingAction() throws IOException { - String staticCsv = """ + assertEquals(1, crac1.getInjectionRangeActions().size()); + InjectionRangeAction ra1 = crac1.getInjectionRangeActions().iterator().next(); + assertEquals("Redispatching_RA_RD", ra1.getId()); + assertEquals(116., ra1.getInitialSetpoint(), DOUBLE_EPSILON); + assertTrue(ra1.getVariationCost(VariationDirection.UP).isPresent()); + assertEquals(5., ra1.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); + assertTrue(ra1.getVariationCost(VariationDirection.DOWN).isPresent()); + assertEquals(5., ra1.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); + } + + @Test + void testCreateInjectionRangeActionsAndUpdateCracsCurativeRedispatchingAction() throws IOException { + String staticCsv = """ RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed Redispatching_RA;FR;TRUE;TRUE;00:00;24:00:00;Generator_Name;GSK;GSK_NAME;50;Coal;2;2;20;20;1;1;FALSE;FALSE """; - IcsData icsData = IcsDataImporter.read( + IcsData icsData = IcsDataImporter.read( new ByteArrayInputStream(staticCsv.getBytes(StandardCharsets.UTF_8)), getClass().getResourceAsStream("/ics/series.csv"), getClass().getResourceAsStream("/glsk/gsk.csv"), generateOffsetDateTimeList(2)); - icsData.createInjectionRangeActionsAndUpdateCracs(cracTemporalData, "Redispatching_RA", icsData.getWeightPerNodePerGsk().get("GSK_NAME"), Map.of("BBE1AA1", "Redispatching_RA_BBE1AA1_GENERATOR", "FFR1AA1", "Redispatching_RA_FFR1AA1_GENERATOR"), 5., 5.); - assertEquals(1, crac1.getInjectionRangeActions().size()); - InjectionRangeAction ra1 = crac1.getInjectionRangeActions().iterator().next(); - assertEquals(1, ra1.getUsageRules().size()); - assertEquals(crac1.getInstant("preventive"), ra1.getUsageRules().iterator().next().getInstant()); - } + icsData.createInjectionRangeActionsAndUpdateCracs(cracTemporalData, "Redispatching_RA", icsData.getWeightPerNodePerGsk().get("GSK_NAME"), Map.of("BBE1AA1", "Redispatching_RA_BBE1AA1_GENERATOR", "FFR1AA1", "Redispatching_RA_FFR1AA1_GENERATOR"), 5., 5.); + assertEquals(1, crac1.getInjectionRangeActions().size()); + InjectionRangeAction ra1 = crac1.getInjectionRangeActions().iterator().next(); + assertEquals(1, ra1.getUsageRules().size()); + assertEquals(crac1.getInstant("preventive"), ra1.getUsageRules().iterator().next().getInstant()); + } // Test Getter and Setter @Test @@ -454,5 +452,4 @@ void testIcsImporterWithGSK() throws IOException { assertEquals(Map.of("BBE1AA1", 0.6, "FFR1AA1", 0.4), icsData.getWeightPerNodePerGsk().get("GSK_NAME")); assertEquals(Set.of("Redispatching_RA"), icsData.getRedispatchingActions()); } - -} \ No newline at end of file +} From 5f26d8c993a5b6f979762a8ed22cdf9ec8a8e321 Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Mon, 30 Mar 2026 18:03:54 +0200 Subject: [PATCH 16/64] wip Signed-off-by: CHEN Roxane --- .../com/powsybl/openrao/data/IcsData.java | 53 +++++++++++++++++-- .../powsybl/openrao/data/IcsDataImporter.java | 3 ++ .../com/powsybl/openrao/data/IcsDataTest.java | 36 +++++++++++-- .../tests/steps/TimeCoupledRaoSteps.java | 40 +------------- 4 files changed, 87 insertions(+), 45 deletions(-) diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java index e0ec18874d..c90d49df5f 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java @@ -11,10 +11,13 @@ import com.powsybl.iidm.network.Network; import com.powsybl.openrao.commons.OpenRaoException; import com.powsybl.openrao.commons.TemporalData; +import com.powsybl.openrao.commons.TemporalDataImpl; 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.timecoupledconstraints.GeneratorConstraints; +import com.powsybl.openrao.raoapi.TimeCoupledRaoInput; +import com.powsybl.openrao.raoapi.TimeCoupledRaoInputWithNetworkPaths; import org.apache.commons.csv.CSVRecord; import java.time.OffsetDateTime; @@ -66,11 +69,11 @@ public static String getGeneratorIdFromRaIdAndNodeId(String raId, String nodeId) return raId + "_" + nodeId + GENERATOR_SUFFIX; } - public boolean isRaDefinedOnANode(String raId) { + public static boolean isRaDefinedOnANode(String raId) { return staticConstraintPerId.get(raId).get(RD_DESCRIPTION_MODE).equalsIgnoreCase(NODE); } - public String getNodeIdOrGskIdFromRaId(String raId) { + public static String getNodeIdOrGskIdFromRaId(String raId) { return staticConstraintPerId.get(raId).get(UCT_NODE_OR_GSK_ID); } @@ -83,7 +86,7 @@ public String getNodeIdOrGskIdFromRaId(String raId) { * @return A set of {@code GeneratorConstraints} generated for the specified parameters. * @throws OpenRaoException if data related to shutdown or startup allowances cannot be parsed. */ - public Set createGeneratorConstraints(String raId, Map weightPerNode, Map networkElementIdPerNodeId) { + public static Set createGeneratorConstraints(String raId, Map weightPerNode, Map networkElementIdPerNodeId) { Set generatorConstraintsSet = new HashSet<>(); for (Map.Entry entry : weightPerNode.entrySet()) { String nodeId = entry.getKey(); @@ -220,4 +223,48 @@ public static void createInjectionRangeActionsAndUpdateCracs(TemporalData injectionRangeActionAdder.add(); }); } + + public void processAllRedispatchingActions(TimeCoupledRaoInput timeCoupledRaoInput, + double costUp, + double costDown) { + + // Update voltage monitoring + TemporalData modifiedInitialNetworks = new TemporalDataImpl<>(); + timeCoupledRaoInput.getRaoInputs().getDataPerTimestamp().forEach((dateTime, raoInput) -> { + Network network = raoInput.getNetwork(); + updateNominalVoltage(network); + modifiedInitialNetworks.put(dateTime, network); + }); + + TemporalData cracToModify = new TemporalDataImpl<>(); + timeCoupledRaoInput.getRaoInputs().getDataPerTimestamp().forEach((dateTime, raoInput) -> { + cracToModify.put(dateTime, raoInput.getCrac()); + }); + + // For each redispatching actions defined in static csv update networks and update cracs + consistentRedispatchingActions.forEach(raId -> { + Map weightPerNode; + + // If the remedial action is defined on a Node. + if (isRaDefinedOnANode(raId)) { + weightPerNode = Map.of(getNodeIdOrGskIdFromRaId(raId), 1.0); + } else { // If the remedial action is defined on a GSK + weightPerNode = weightPerNodePerGsk.get(getNodeIdOrGskIdFromRaId(raId)); + } + + // Create generator and load in networks + Map generatorIdPerNode = createGeneratorAndLoadAndUpdateNetworks(modifiedInitialNetworks, raId, weightPerNode); + // One of the node could not be find no need to create injection range actions and generator constraint. + if (generatorIdPerNode.isEmpty()) { + return; + } + + // Create Injection Range Actions in CRACs + createInjectionRangeActionsAndUpdateCracs(cracToModify, raId, weightPerNode, generatorIdPerNode, costUp, costDown); + + // Create generator constraints and them to time coupled rao input + Set generatorConstraintsSet = createGeneratorConstraints(raId, weightPerNode, generatorIdPerNode); + generatorConstraintsSet.forEach(generatorConstraints -> timeCoupledRaoInput.getTimeCoupledConstraints().addGeneratorConstraints(generatorConstraints)); + }); + } } diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java index 9e4465b904..c7992d70c6 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java @@ -178,6 +178,9 @@ private static boolean shouldBeImported(CSVRecord staticRecord, List networkTemporalData; private TemporalData cracTemporalData; List logsList; + private final OffsetDateTime timestamp1 = OffsetDateTime.of(2025, 2, 13, 0, 30, 0, 0, ZoneOffset.UTC); + private final OffsetDateTime timestamp2 = OffsetDateTime.of(2025, 2, 13, 1, 30, 0, 0, ZoneOffset.UTC); @BeforeEach void setUp() throws IOException { @@ -66,9 +73,6 @@ void setUp() throws IOException { crac1 = Crac.read("/crac/crac-0030.json", getClass().getResourceAsStream("/crac/crac-0030.json"), network1); crac2 = Crac.read("/crac/crac-0130.json", getClass().getResourceAsStream("/crac/crac-0130.json"), network2); - OffsetDateTime timestamp1 = OffsetDateTime.of(2025, 2, 13, 0, 30, 0, 0, ZoneOffset.UTC); - OffsetDateTime timestamp2 = OffsetDateTime.of(2025, 2, 13, 1, 30, 0, 0, ZoneOffset.UTC); - networkTemporalData = new TemporalDataImpl<>( Map.of( timestamp1, network1, @@ -452,4 +456,30 @@ void testIcsImporterWithGSK() throws IOException { assertEquals(Map.of("BBE1AA1", 0.6, "FFR1AA1", 0.4), icsData.getWeightPerNodePerGsk().get("GSK_NAME")); assertEquals(Set.of("Redispatching_RA"), icsData.getRedispatchingActions()); } + + // Test Full Run + + @Test + void testProcessAllRedispatchingActions() throws IOException { + Network network1 = LazyNetwork.of(getResourcePath("/network/" + networkFilePath1)); + Network network2 = LazyNetwork.of(getResourcePath("/network/" + networkFilePath2)); + TemporalData raoInputs = new TemporalDataImpl<>( + Map.of( + timestamp1, RaoInput.build(network1, crac1).build(), + timestamp2, RaoInput.build(network2, crac2).build() + )); + + TimeCoupledRaoInput timeCoupledRaoInput = new TimeCoupledRaoInput(raoInputs, new TimeCoupledConstraints()); + IcsData icsData = IcsDataImporter.read( + getClass().getResourceAsStream("/ics/static_with_gsk.csv"), + getClass().getResourceAsStream("/ics/series.csv"), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList(24)); + + icsData.processAllRedispatchingActions(timeCoupledRaoInput, 5., 4.); + } + + private String getResourcePath(String resourcePath) { + return "src/test/resources/" + resourcePath; + } } diff --git a/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java b/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java index a4112059f1..1bb63180f2 100644 --- a/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java +++ b/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java @@ -34,7 +34,6 @@ import com.powsybl.openrao.data.raoresult.api.TimeCoupledRaoResult; import com.powsybl.openrao.data.raoresult.io.idcc.core.F711Utils; import com.powsybl.openrao.data.refprog.refprogxmlimporter.TimeCoupledRefProg; -import com.powsybl.openrao.data.timecoupledconstraints.GeneratorConstraints; import com.powsybl.openrao.data.timecoupledconstraints.TimeCoupledConstraints; import com.powsybl.openrao.data.timecoupledconstraints.io.JsonTimeCoupledConstraints; import com.powsybl.openrao.raoapi.RaoInputWithNetworkPaths; @@ -76,7 +75,6 @@ import java.util.zip.ZipOutputStream; import static com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider.TECHNICAL_LOGS; -import static com.powsybl.openrao.data.IcsUtil.updateNominalVoltage; import static com.powsybl.openrao.tests.steps.CommonTestData.buildConfig; import static com.powsybl.openrao.tests.steps.CommonTestData.cracPath; import static com.powsybl.openrao.tests.steps.CommonTestData.getRaoParameters; @@ -256,47 +254,11 @@ public static void loadDataForCoreTimeCoupledRao(DataTable arg1) throws IOExcept fbConstraintParameters = new FbConstraintCracCreationParameters(); } - // Update voltage monitoring - TemporalData modifiedInitialNetworks = new TemporalDataImpl<>(); - timeCoupledRaoInputWithNetworkPaths.getRaoInputs().getDataPerTimestamp().forEach((dateTime, raoInput) -> { - Network network = Network.read(raoInput.getInitialNetworkPath()); - updateNominalVoltage(network); - modifiedInitialNetworks.put(dateTime, network); - }); - - // Read ICS Data IcsData icsData = IcsDataImporter.read(new FileInputStream(getFile(icsStaticPath)),new FileInputStream(getFile(icsSeriesPath)),gskInputStream, timeCoupledRaoInputWithNetworkPaths.getTimestampsToRun().stream().sorted().toList()); - - TemporalData cracToModify = new TemporalDataImpl<>(); - timeCoupledRaoInputWithNetworkPaths.getRaoInputs().getDataPerTimestamp().forEach((dateTime, raoInput) -> { - cracToModify.put(dateTime, raoInput.getCrac()); - }); - - FbConstraintCracCreationParameters finalFbConstraintParameters = fbConstraintParameters; - // For each redispatching actions defined in static csv update networks and update cracs - icsData.getStaticConstraintPerId().forEach((raId, staticRecord) -> { - Map weightPerNode; - // If the remedial action is defined on a Node. - if (icsData.isRaDefinedOnANode(raId)) { - weightPerNode = Map.of(icsData.getNodeIdOrGskIdFromRaId(raId), 1.0); - } else { // If the remedial action is defined on a GSK - weightPerNode = icsData.getWeightPerNodePerGsk().get(icsData.getNodeIdOrGskIdFromRaId(raId)); - } - // Create generator and load in networks - Map generatorIdPerNode = icsData.createGeneratorAndLoadAndUpdateNetworks(modifiedInitialNetworks, raId, weightPerNode); - // One of the node could not be find no need to create injection range actions and generator constraint. - if (generatorIdPerNode.isEmpty()) { - return; - } - // Create Injection Range Actions in CRACs - icsData.createInjectionRangeActionsAndUpdateCracs(cracToModify, raId, weightPerNode, generatorIdPerNode, finalFbConstraintParameters.getIcsCostUp(), finalFbConstraintParameters.getIcsCostDown()); + icsData.processAllRedispatchingActions(timeCoupledRaoInputWithNetworkPaths, finalFbConstraintParameters.getIcsCostUp(), finalFbConstraintParameters.getIcsCostDown()); - // Create generator constraints and them to time coupled rao input - Set generatorConstraintsSet = icsData.createGeneratorConstraints(raId, weightPerNode, generatorIdPerNode); - generatorConstraintsSet.forEach(generatorConstraints -> timeCoupledRaoInputWithNetworkPaths.getTimeCoupledConstraints().addGeneratorConstraints(generatorConstraints)); - }); } From 42b0a3faaf857164732f65d27385a46dd71fb1b6 Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Tue, 31 Mar 2026 18:34:10 +0200 Subject: [PATCH 17/64] add ics util test Signed-off-by: CHEN Roxane --- .../com/powsybl/openrao/data/IcsUtilTest.java | 32 +++++++++++++++++++ .../2Nodes2ParallelLinesPST_voltagelevel.uct | 12 +++++++ 2 files changed, 44 insertions(+) create mode 100644 data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsUtilTest.java create mode 100644 data/ics-importer/src/test/resources/network/2Nodes2ParallelLinesPST_voltagelevel.uct diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsUtilTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsUtilTest.java new file mode 100644 index 0000000000..77fa58fd90 --- /dev/null +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsUtilTest.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2026, 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; + +import com.powsybl.iidm.network.Network; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Roxane Chen {@literal } + */ +public class IcsUtilTest { + + @Test + public void testUpdateNominalVoltage() { + String networkFilePath1 = "2Nodes2ParallelLinesPST_voltagelevel2.uct"; + Network network1 = Network.read(networkFilePath1, IcsDataTest.class.getResourceAsStream("/network/" + networkFilePath1)); + IcsUtil.updateNominalVoltage(network1); + assertEquals(network1.getVoltageLevel("BBE1AA2").getNominalV(), 225.); + + String networkFilePath2 = "2Nodes2ParallelLinesPST_0030.uct"; + Network network2 = Network.read(networkFilePath2, IcsDataTest.class.getResourceAsStream("/network/" + networkFilePath2)); + IcsUtil.updateNominalVoltage(network2); + assertEquals(network2.getVoltageLevel("BBE1AA1").getNominalV(), 400); + } +} diff --git a/data/ics-importer/src/test/resources/network/2Nodes2ParallelLinesPST_voltagelevel.uct b/data/ics-importer/src/test/resources/network/2Nodes2ParallelLinesPST_voltagelevel.uct new file mode 100644 index 0000000000..6a76705d04 --- /dev/null +++ b/data/ics-importer/src/test/resources/network/2Nodes2ParallelLinesPST_voltagelevel.uct @@ -0,0 +1,12 @@ +##C 2007.05.01 +##N +##ZBE +BBE1AA2 BE1 0 2 225.00 0.00000 0.00000 -1000.0 0.00000 9000.00 -9000.0 9000.00 -9000.0 +##ZFR +FFR1AA2 FR1 0 2 225.00 1000.00 0.00000 00000.0 0.00000 9000.00 -9000.0 9000.00 -9000.0 +##L +BBE1AA2 FFR1AA2 1 0 0.0000 10.000 0.000000 5000 +##T +BBE1AA2 FFR1AA2 2 0 225.0 225.0 1000. 0.0000 10.000 0.000000 0.0 5000 PST +##R +BBE1AA2 FFR1AA2 2 -0.68 90.00 16 0 SYMM From 1b36cead33a1600cd32c0a99f5d9f6ee9409355c Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Tue, 31 Mar 2026 18:34:30 +0200 Subject: [PATCH 18/64] add ics full process Signed-off-by: CHEN Roxane --- .../com/powsybl/openrao/data/IcsData.java | 37 +++++++++-- .../com/powsybl/openrao/data/IcsDataTest.java | 61 ++++++++++++++++--- .../test/resources/ics/series_with_two_ra.csv | 9 +++ .../test/resources/ics/static_with_two_ra.csv | 3 + 4 files changed, 99 insertions(+), 11 deletions(-) create mode 100644 data/ics-importer/src/test/resources/ics/series_with_two_ra.csv create mode 100644 data/ics-importer/src/test/resources/ics/static_with_two_ra.csv diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java index c90d49df5f..7ede08bbac 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java @@ -16,11 +16,14 @@ import com.powsybl.openrao.data.crac.api.rangeaction.InjectionRangeActionAdder; import com.powsybl.openrao.data.crac.api.rangeaction.VariationDirection; import com.powsybl.openrao.data.timecoupledconstraints.GeneratorConstraints; +import com.powsybl.openrao.raoapi.LazyNetwork; +import com.powsybl.openrao.raoapi.RaoInput; import com.powsybl.openrao.raoapi.TimeCoupledRaoInput; -import com.powsybl.openrao.raoapi.TimeCoupledRaoInputWithNetworkPaths; import org.apache.commons.csv.CSVRecord; +import java.nio.file.Path; import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; import java.util.*; import static com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider.BUSINESS_WARNS; @@ -109,12 +112,14 @@ public static Set createGeneratorConstraints(String raId, if (!staticRecord.get(LAG_TIME).isEmpty()) { builder.withLagTime(parseDoubleWithPossibleCommas(staticRecord.get(LAG_TIME))); } + // TODO: instead of throwing an error, just ignore the RA + move the check in the import if (staticRecord.get(SHUTDOWN_ALLOWED).isEmpty() || !staticRecord.get(SHUTDOWN_ALLOWED).equalsIgnoreCase(TRUE) && !staticRecord.get(SHUTDOWN_ALLOWED).equalsIgnoreCase(FALSE)) { throw new OpenRaoException("Could not parse shutDownAllowed value for raId " + raId + ": " + staticRecord.get(SHUTDOWN_ALLOWED)); } else { builder.withShutDownAllowed(Boolean.parseBoolean(staticRecord.get(SHUTDOWN_ALLOWED))); } + // TODO: instead of throwing an error, just ignore the RA + move the check in the import if (staticRecord.get(STARTUP_ALLOWED).isEmpty() || !staticRecord.get(STARTUP_ALLOWED).equalsIgnoreCase(TRUE) && !staticRecord.get(STARTUP_ALLOWED).equalsIgnoreCase(FALSE)) { throw new OpenRaoException("Could not parse startUpAllowed value for raId " + raId + ": " + staticRecord.get(STARTUP_ALLOWED)); @@ -224,9 +229,22 @@ public static void createInjectionRangeActionsAndUpdateCracs(TemporalData }); } - public void processAllRedispatchingActions(TimeCoupledRaoInput timeCoupledRaoInput, - double costUp, - double costDown) { + /** + * Processes all redispatching actions for the provided time-coupled input, updates networks + * and CRACs, and generates the required constraints for the specified costs. + * + * @param timeCoupledRaoInput The input data containing network and CRAC information for + * each timestamp. Includes all RAO-specific input required for + * redispatching action processing. + * @param costUp The cost associated with increasing the generation (VariationDirection.UP). + * @param costDown The cost associated with decreasing the generation (VariationDirection.DOWN). + * @param exportDirectory The directory where the exported networks will be saved. + * @return The updated time-coupled RAO input with processed redispatching actions and constraints. + */ + public TimeCoupledRaoInput processAllRedispatchingActions(TimeCoupledRaoInput timeCoupledRaoInput, + double costUp, + double costDown, + String exportDirectory) { // Update voltage monitoring TemporalData modifiedInitialNetworks = new TemporalDataImpl<>(); @@ -266,5 +284,16 @@ public void processAllRedispatchingActions(TimeCoupledRaoInput timeCoupledRaoInp Set generatorConstraintsSet = createGeneratorConstraints(raId, weightPerNode, generatorIdPerNode); generatorConstraintsSet.forEach(generatorConstraints -> timeCoupledRaoInput.getTimeCoupledConstraints().addGeneratorConstraints(generatorConstraints)); }); + + TemporalData postIcsRaoInputs = new TemporalDataImpl<>(); + + modifiedInitialNetworks.getDataPerTimestamp().forEach((dateTime, initialNetwork) -> { + String exportedNetworkPath = exportDirectory + dateTime.format(DateTimeFormatter.ofPattern("%y%m%d_%H%M%S")) + ".jiidm"; + initialNetwork.write("JIIDM", new Properties(), Path.of(exportedNetworkPath)); + postIcsRaoInputs.put(dateTime, RaoInput.build(new LazyNetwork(exportedNetworkPath), timeCoupledRaoInput.getRaoInputs().getData(dateTime).orElseThrow().getCrac()).build()); + }); + + return new TimeCoupledRaoInput(postIcsRaoInputs, timeCoupledRaoInput.getTimestampsToRun(), timeCoupledRaoInput.getTimeCoupledConstraints()); } + } diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java index f6506f8bd0..a5f359d83b 100644 --- a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java @@ -21,10 +21,9 @@ import com.powsybl.openrao.data.crac.api.rangeaction.VariationDirection; import com.powsybl.openrao.data.timecoupledconstraints.GeneratorConstraints; import com.powsybl.openrao.data.timecoupledconstraints.TimeCoupledConstraints; +import com.powsybl.openrao.raoapi.LazyNetwork; import com.powsybl.openrao.raoapi.RaoInput; -import com.powsybl.openrao.raoapi.RaoInputWithNetworkPaths; import com.powsybl.openrao.raoapi.TimeCoupledRaoInput; -import com.powsybl.openrao.raoapi.TimeCoupledRaoInputWithNetworkPaths; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -40,6 +39,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import java.util.stream.Stream; import static com.powsybl.openrao.data.IcsDataImporterTest.generateOffsetDateTimeList; @@ -460,7 +460,11 @@ void testIcsImporterWithGSK() throws IOException { // Test Full Run @Test - void testProcessAllRedispatchingActions() throws IOException { + void testProcessAllRedispatchingActionsWithLazyNetwork() throws IOException { + + String TMP_DIR = System.getProperty("java.io.tmpdir") + File.separator; + String networkFilePath1 = "2Nodes2ParallelLinesPST_0030.uct"; + String networkFilePath2 = "2Nodes2ParallelLinesPST_0130.uct"; Network network1 = LazyNetwork.of(getResourcePath("/network/" + networkFilePath1)); Network network2 = LazyNetwork.of(getResourcePath("/network/" + networkFilePath2)); TemporalData raoInputs = new TemporalDataImpl<>( @@ -471,12 +475,55 @@ void testProcessAllRedispatchingActions() throws IOException { TimeCoupledRaoInput timeCoupledRaoInput = new TimeCoupledRaoInput(raoInputs, new TimeCoupledConstraints()); IcsData icsData = IcsDataImporter.read( - getClass().getResourceAsStream("/ics/static_with_gsk.csv"), - getClass().getResourceAsStream("/ics/series.csv"), + getClass().getResourceAsStream("/ics/static_with_two_ra.csv"), + getClass().getResourceAsStream("/ics/series_with_two_ra.csv"), getClass().getResourceAsStream("/glsk/gsk.csv"), - generateOffsetDateTimeList(24)); + generateOffsetDateTimeList(2)); + + TimeCoupledRaoInput postIcsRaoInputs = icsData.processAllRedispatchingActions(timeCoupledRaoInput, 5., 4., TMP_DIR); + + assertEquals(3,postIcsRaoInputs.getTimeCoupledConstraints().getGeneratorConstraints().size()); + assertEquals(2 ,postIcsRaoInputs.getRaoInputs().getData(timestamp1).get().getCrac().getInjectionRangeActions().size()); + assertEquals(2 ,postIcsRaoInputs.getRaoInputs().getData(timestamp2).get().getCrac().getInjectionRangeActions().size()); + assertEquals( + Set.of("Redispatching_RA_1_RD", "Redispatching_RA_2_RD"), + postIcsRaoInputs.getRaoInputs().getData(timestamp1).get().getCrac().getInjectionRangeActions() + .stream() + .map(InjectionRangeAction::getId) + .collect(Collectors.toSet()) + ); + assertEquals( + Set.of("Redispatching_RA_1_RD", "Redispatching_RA_2_RD"), + postIcsRaoInputs.getRaoInputs().getData(timestamp2).get().getCrac().getInjectionRangeActions() + .stream() + .map(InjectionRangeAction::getId) + .collect(Collectors.toSet()) + ); + + Generator generatorBE = postIcsRaoInputs.getRaoInputs().getData(timestamp1).get().getNetwork().getGenerator("Redispatching_RA_1_BBE1AA1_GENERATOR"); + assertEquals(116. * 0.6, generatorBE.getTargetP(), DOUBLE_EPSILON); + assertEquals(10. * 0.6, generatorBE.getMinP(), DOUBLE_EPSILON); + Generator generatorFR = postIcsRaoInputs.getRaoInputs().getData(timestamp1).get().getNetwork().getGenerator("Redispatching_RA_1_FFR1AA1_GENERATOR"); + assertEquals(116. * 0.4, generatorFR.getTargetP(), DOUBLE_EPSILON); + assertEquals(10. * 0.4, generatorFR.getMinP(), DOUBLE_EPSILON); + + Generator generatorBE2 = postIcsRaoInputs.getRaoInputs().getData(timestamp2).get().getNetwork().getGenerator("Redispatching_RA_1_BBE1AA1_GENERATOR"); + assertEquals(120 * 0.6, generatorBE2.getTargetP(), DOUBLE_EPSILON); + assertEquals(15. * 0.6, generatorBE2.getMinP(), DOUBLE_EPSILON); + Generator generatorFR2 = postIcsRaoInputs.getRaoInputs().getData(timestamp2).get().getNetwork().getGenerator("Redispatching_RA_1_FFR1AA1_GENERATOR"); + assertEquals(120 * 0.4, generatorFR2.getTargetP(), DOUBLE_EPSILON); + assertEquals(15. * 0.4, generatorFR2.getMinP(), DOUBLE_EPSILON); + + Generator generatorNode1 = postIcsRaoInputs.getRaoInputs().getData(timestamp1).get().getNetwork().getGenerator("Redispatching_RA_2_BBE1AA1_GENERATOR"); + assertEquals(114., generatorNode1.getTargetP(), DOUBLE_EPSILON); + assertEquals(11., generatorNode1.getMinP(), DOUBLE_EPSILON); + Generator generatorNode2 = postIcsRaoInputs.getRaoInputs().getData(timestamp2).get().getNetwork().getGenerator("Redispatching_RA_2_BBE1AA1_GENERATOR"); + assertEquals(121., generatorNode2.getTargetP(), DOUBLE_EPSILON); + assertEquals(14., generatorNode2.getMinP(), DOUBLE_EPSILON); + + assertEquals(postIcsRaoInputs.getRaoInputs().getData(timestamp1).get().getNetwork().getVoltageLevel("BBE1AA1").getNominalV(), 400); + assertEquals(postIcsRaoInputs.getRaoInputs().getData(timestamp2).get().getNetwork().getVoltageLevel("BBE1AA1").getNominalV(), 400); - icsData.processAllRedispatchingActions(timeCoupledRaoInput, 5., 4.); } private String getResourcePath(String resourcePath) { diff --git a/data/ics-importer/src/test/resources/ics/series_with_two_ra.csv b/data/ics-importer/src/test/resources/ics/series_with_two_ra.csv new file mode 100644 index 0000000000..b2202a935f --- /dev/null +++ b/data/ics-importer/src/test/resources/ics/series_with_two_ra.csv @@ -0,0 +1,9 @@ +RA RD ID;Type of timeseries;00:30;01:30;02:30;03:30;04:30;05:30;06:30;07:30;08:30;09:30;10:30;11:30;12:30;13:30;14:30;15:30;16:30;17:30;18:30;19:30;20:30;21:30;22:30;23:30 +Redispatching_RA_1;RDP-;35;35;36;35;34;32;28;27;25;9;8;9;8;8;8;8;23;25;30;31;34;31;29;39 +Redispatching_RA_1;RDP+;43;43;41;42;43;45;50;51;53;68;70;69;70;70;70;69;54;52;48;47;44;46;49;39 +Redispatching_RA_1;P0;116;120;117;116;115;113;109;108;106;90;89;90;89;89;89;89;104;106;111;112;115;112;110;120 +Redispatching_RA_1;Pmin_RD;10;15;20;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30 +Redispatching_RA_2;RDP-;45;45;46;45;44;42;38;37;35;19;18;19;18;18;18;18;33;35;40;41;44;41;39;49 +Redispatching_RA_2;RDP+;53;53;51;52;53;55;60;61;63;78;80;79;80;80;80;79;64;62;58;57;54;56;59;49 +Redispatching_RA_2;P0;114;121;117;116;115;113;109;108;106;90;89;90;89;89;89;89;104;106;111;112;115;112;110;120 +Redispatching_RA_2;Pmin_RD;11;14;20;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30 \ No newline at end of file diff --git a/data/ics-importer/src/test/resources/ics/static_with_two_ra.csv b/data/ics-importer/src/test/resources/ics/static_with_two_ra.csv new file mode 100644 index 0000000000..2a95cbf88b --- /dev/null +++ b/data/ics-importer/src/test/resources/ics/static_with_two_ra.csv @@ -0,0 +1,3 @@ +RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed +Redispatching_RA_1;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;GSK;GSK_NAME;50;Coal;2;2;20;20;1;1;TRUE;TRUE +Redispatching_RA_2;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;30;40;1;1;TRUE;TRUE From 1626c79c534380e72d55f44a3a208ebf23ddafba Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Tue, 31 Mar 2026 18:34:49 +0200 Subject: [PATCH 19/64] gsk is optional ! Signed-off-by: CHEN Roxane --- .../powsybl/openrao/data/IcsDataImporter.java | 17 +++++++++-------- .../openrao/data/IcsDataImporterTest.java | 13 +++---------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java index c7992d70c6..463072dcfa 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java @@ -66,10 +66,13 @@ public static IcsData read(InputStream staticInputStream, InputStream gskInputStream, List sortedTimestampToRun) throws IOException { - // Parse and sort per RA_ID and serie type (RDP-, RDP+, Pmin_RD or P0) + // Parse and sort and serie type (RDP-, RDP+, Pmin_RD or P0) and per RA_ID Map> timeseriesPerIdAndType = parseSeriesCsv(seriesInputStream); // Parse GSK and get weight Per Node Per Gsk - Map> weightPerNodePerGsk = parseGskCsv(gskInputStream); + Map> weightPerNodePerGsk = new HashMap<>(); + if (gskInputStream != null) { + weightPerNodePerGsk = parseGskCsv(gskInputStream); + } // Parse static CSV: remedial action’s generator’s static constraints. one line per RA_ID Map staticConstraintPerId = parseStaticCsv(staticInputStream); @@ -83,7 +86,7 @@ static Set filterOutInconsistentRedispatchingActions(Map> timeseriesPerIdAndType, Map> weightPerNodePerGsk, List sortedTimestampToRun) { - // Get set of consistent redispatching action ID. + // Get a set of consistent redispatching action ID. Set consistentRAs = new HashSet<>(); staticConstraintPerId.forEach((raId, record) -> { if (shouldBeImported(record, sortedTimestampToRun, weightPerNodePerGsk, timeseriesPerIdAndType)) { @@ -132,6 +135,8 @@ static Map> parseGskCsv(InputStream gskInputStream) private static boolean shouldBeImported(CSVRecord staticRecord, List sortedTimestampToRun, Map> weightPerNodePerGsk, Map> timeseriesPerIdAndType) { String raId = staticRecord.get(RA_RD_ID); + // TODO: checks that all the mandatory fields are defined for all timestamps + // Check that remedial action is defined in series csv and gsk (if defined on a gsk) if (!timeseriesPerIdAndType.containsKey(raId)) { BUSINESS_WARNS.warn("Redispatching action {} is not defined in the time series csv", raId); @@ -170,17 +175,13 @@ private static boolean shouldBeImported(CSVRecord staticRecord, List seriesPerType = timeseriesPerIdAndType.get(raId); boolean isDefinedInSeriesCsv = seriesPerType.containsKey(P0) && seriesPerType.containsKey(RDP_DOWN) && - seriesPerType.containsKey(RDP_UP) && - seriesPerType.containsKey(P_MIN_RD); + seriesPerType.containsKey(RDP_UP); if (!isDefinedInSeriesCsv) { BUSINESS_WARNS.warn("Redispatching action {} is not defined in the time series csv. Missing one or several timeseries type (P0, RDP_DOWN, RDP_UP or P_MIN_RD).", raId); return false; } - // Check mandatory fields are defined for all timestamps - // TODO - // Check that the range of redispatching parameters is valid if (!rangeIsOkay(seriesPerType, sortedTimestampToRun)) { return false; diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataImporterTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataImporterTest.java index 94d02bd003..b12e16ebad 100644 --- a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataImporterTest.java +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataImporterTest.java @@ -138,7 +138,7 @@ void testRaNotAvailableInPreventive() throws IOException { IcsData icsData = IcsDataImporter.read( new ByteArrayInputStream(staticCsv.getBytes(StandardCharsets.UTF_8)), getClass().getResourceAsStream("/ics/series.csv"), - getClass().getResourceAsStream("/glsk/gsk.csv"), + null, generateOffsetDateTimeList(24)); assertEquals(0, icsData.getRedispatchingActions().size()); assertEquals("Redispatching action Redispatching_RA is not defined on preventive instant", logsList.get(0).getFormattedMessage()); @@ -154,7 +154,7 @@ void testRaDefinedInStaticButNotInSeries() throws IOException { IcsData icsData = IcsDataImporter.read( new ByteArrayInputStream(staticCsv.getBytes(StandardCharsets.UTF_8)), getClass().getResourceAsStream("/ics/series.csv"), - getClass().getResourceAsStream("/glsk/gsk.csv"), + null, generateOffsetDateTimeList(24)); assertEquals(1, icsData.getRedispatchingActions().size()); assertTrue(icsData.getRedispatchingActions().contains("Redispatching_RA")); @@ -216,17 +216,10 @@ private static Stream seriesCsvWithMissingSeriesTypeCases() { Redispatching_RA;Pmin_RD;10;15 """; - String missingPminRdCsv = header + """ - Redispatching_RA;RDP-;35;35 - Redispatching_RA;RDP+;43;43 - Redispatching_RA;P0;116;120 - """; - return Stream.of( Arguments.of(missingP0Csv, "Redispatching action Redispatching_RA is not defined in the time series csv. Missing one or several timeseries type (P0, RDP_DOWN, RDP_UP or P_MIN_RD)."), Arguments.of(missingRdpDownCsv, "Redispatching action Redispatching_RA is not defined in the time series csv. Missing one or several timeseries type (P0, RDP_DOWN, RDP_UP or P_MIN_RD)."), - Arguments.of(missingRdpUpCsv, "Redispatching action Redispatching_RA is not defined in the time series csv. Missing one or several timeseries type (P0, RDP_DOWN, RDP_UP or P_MIN_RD)."), - Arguments.of(missingPminRdCsv, "Redispatching action Redispatching_RA is not defined in the time series csv. Missing one or several timeseries type (P0, RDP_DOWN, RDP_UP or P_MIN_RD).") + Arguments.of(missingRdpUpCsv, "Redispatching action Redispatching_RA is not defined in the time series csv. Missing one or several timeseries type (P0, RDP_DOWN, RDP_UP or P_MIN_RD).") ); } From 726030fff230c340826a1cf26882f0362807e7ab Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Tue, 31 Mar 2026 18:35:23 +0200 Subject: [PATCH 20/64] update steps Signed-off-by: CHEN Roxane --- .../openrao/tests/steps/TimeCoupledRaoSteps.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java b/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java index 635954d176..3062d23ca7 100644 --- a/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java +++ b/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java @@ -14,6 +14,8 @@ import com.powsybl.openrao.commons.TemporalData; import com.powsybl.openrao.commons.TemporalDataImpl; import com.powsybl.openrao.commons.Unit; +import com.powsybl.openrao.data.IcsData; +import com.powsybl.openrao.data.IcsDataImporter; import com.powsybl.openrao.data.crac.api.Crac; import com.powsybl.openrao.data.crac.api.CracCreationContext; import com.powsybl.openrao.data.crac.api.Instant; @@ -257,15 +259,18 @@ public static void loadDataForCoreTimeCoupledRao(DataTable arg1) throws IOExcept TECHNICAL_LOGS.warn("No FB Constraint CRAC creation parameters found. Default parameters will be used."); fbConstraintParameters = new FbConstraintCracCreationParameters(); } - timeCoupledRaoInput = IcsImporter.populateInputWithICS( - new TimeCoupledRaoInput(raoInputs, new TimeCoupledConstraints()), + + IcsData icsData = IcsDataImporter.read( new FileInputStream(getFile(icsStaticPath)), new FileInputStream(getFile(icsSeriesPath)), gskInputStream, + raoInputs.getTimestamps().stream().sorted().toList()); + + timeCoupledRaoInput = icsData.processAllRedispatchingActions( + new TimeCoupledRaoInput(raoInputs, new TimeCoupledConstraints()), fbConstraintParameters.getIcsCostUp(), fbConstraintParameters.getIcsCostDown(), - networkFolderPathPostIcsImport.concat(inputs.getFirst().get("Network")).split(".uct")[0] - ); + networkFolderPathPostIcsImport.concat(inputs.getFirst().get("Network")).split(".uct")[0]); } @When("I launch marmot") From 6001ba504a8517574e34d9ea0e83ee45dbde2a11 Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Wed, 1 Apr 2026 11:26:47 +0200 Subject: [PATCH 21/64] remove gsk if ra only defined on a node Signed-off-by: CHEN Roxane --- .../java/com/powsybl/openrao/data/IcsDataTest.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java index a5f359d83b..688f15308e 100644 --- a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java @@ -106,7 +106,7 @@ void testCreateGeneratorConstraintRaDefinedOnANode() throws IOException { IcsData icsData = IcsDataImporter.read( getClass().getResourceAsStream("/ics/static.csv"), getClass().getResourceAsStream("/ics/series.csv"), - getClass().getResourceAsStream("/glsk/gsk.csv"), + null, generateOffsetDateTimeList(24)); // Check generator constraint creation @@ -176,7 +176,7 @@ void testCreateGeneratorConstraintRaDefinedOnANodeMissingGradientLeadTimeAndLagT IcsData icsData = IcsDataImporter.read( new ByteArrayInputStream(staticCsv.getBytes(StandardCharsets.UTF_8)), getClass().getResourceAsStream("/ics/series.csv"), - getClass().getResourceAsStream("/glsk/gsk.csv"), + null, generateOffsetDateTimeList(24)); Set generatorConstraintsSet = icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", 1.0), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1"))); assertEquals(1, generatorConstraintsSet.size()); @@ -267,7 +267,7 @@ void testCreateGeneratorAndLoadAndUpdateNetworksOnANode() throws IOException { IcsData icsData = IcsDataImporter.read( getClass().getResourceAsStream("/ics/static.csv"), getClass().getResourceAsStream("/ics/series.csv"), - getClass().getResourceAsStream("/glsk/gsk.csv"), + null, generateOffsetDateTimeList(2)); icsData.createGeneratorAndLoadAndUpdateNetworks(networkTemporalData, "Redispatching_RA", Map.of("BBE1AA1", 1.0)); @@ -359,7 +359,7 @@ void testCreateInjectionRangeActionsAndUpdateCracsOnANode() throws IOException { IcsData icsData = IcsDataImporter.read( getClass().getResourceAsStream("/ics/static.csv"), getClass().getResourceAsStream("/ics/series.csv"), - getClass().getResourceAsStream("/glsk/gsk.csv"), + null, generateOffsetDateTimeList(2)); // Test injection range action creation in crac @@ -423,17 +423,17 @@ void testCreateInjectionRangeActionsAndUpdateCracsCurativeRedispatchingAction() // Test Getter and Setter @Test - void testIcsDataReadOkNode() throws IOException { + void testIcsImporterWithNode() throws IOException { // Read ICS Data IcsData icsData = IcsDataImporter.read( getClass().getResourceAsStream("/ics/static.csv"), getClass().getResourceAsStream("/ics/series.csv"), - getClass().getResourceAsStream("/glsk/gsk.csv"), + null, generateOffsetDateTimeList(24)); assertEquals(1, icsData.getStaticConstraintPerId().size()); assertEquals(4, icsData.getTimeseriesPerIdAndType().get("Redispatching_RA").size()); - assertEquals(1, icsData.getWeightPerNodePerGsk().size()); + assertEquals(0, icsData.getWeightPerNodePerGsk().size()); assertEquals(Set.of("Redispatching_RA"), icsData.getRedispatchingActions()); } From f96a3b50db087262dce351d8837a0f5b5711a5d7 Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Wed, 1 Apr 2026 11:30:31 +0200 Subject: [PATCH 22/64] remove old code Signed-off-by: CHEN Roxane --- .../openrao/data/crac/util/IcsImporter.java | 469 ------------------ .../data/crac/util/IcsImporterTest.java | 393 --------------- 2 files changed, 862 deletions(-) delete mode 100644 data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java delete mode 100644 data/crac/crac-util/src/test/java/com/powsybl/openrao/data/crac/util/IcsImporterTest.java diff --git a/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java b/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java deleted file mode 100644 index d87b89e7fa..0000000000 --- a/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java +++ /dev/null @@ -1,469 +0,0 @@ -/* - * Copyright (c) 2025, 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.crac.util; - -import com.powsybl.iidm.network.Bus; -import com.powsybl.iidm.network.LoadType; -import com.powsybl.iidm.network.Network; -import com.powsybl.iidm.serde.NetworkSerDe; -import com.powsybl.openrao.commons.OpenRaoException; -import com.powsybl.openrao.commons.TemporalData; -import com.powsybl.openrao.commons.TemporalDataImpl; -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.timecoupledconstraints.GeneratorConstraints; -import com.powsybl.openrao.raoapi.LazyNetwork; -import com.powsybl.openrao.raoapi.RaoInput; -import com.powsybl.openrao.raoapi.TimeCoupledRaoInput; -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.nio.file.Path; -import java.time.OffsetDateTime; -import java.time.format.DateTimeFormatter; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Properties; - -import static com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider.BUSINESS_WARNS; - -/** - * @author Roxane Chen {@literal } - */ -public final class IcsImporter { - private static final int OFFSET = 2; - private static final double MAX_GRADIENT = 1000.0; - private static final double ON_POWER_THRESHOLD = 1.001; // TODO: mutualize with value from linear problem - - // TODO : either parametrize this or set it to true. May have to change the way it works to import for all curative instants instead of only the last one - public static boolean importCurative = false; - private static double costUp; - private static double costDown; - - // Quality checks (or don't import the action at all): - // - check that P0s respect the min/max gradients - // TODO in future versions: - // - check other implemented constraints - // - check consistency between ics files, particularly w.r.t gsk file - - // INFOS - // Gradient constraints are defined for gsks at the action level and not per group : we translate it to the groups using the shift keys - - public static final String MAXIMUM_POSITIVE_POWER_GRADIENT = "Maximum positive power gradient [MW/h]"; - public static final String MAXIMUM_NEGATIVE_POWER_GRADIENT = "Maximum negative power gradient [MW/h]"; - public static final String LEAD_TIME = "Lead time [h]"; - public static final String LAG_TIME = "Lag time [h]"; - public static final String STARTUP_ALLOWED = "Startup allowed"; - public static final String SHUTDOWN_ALLOWED = "Shutdown allowed"; - public static final String P_MIN_RD = "Pmin_RD"; - public static final String RA_RD_ID = "RA RD ID"; - public static final String RDP_UP = "RDP+"; - public static final String RDP_DOWN = "RDP-"; - public static final String P0 = "P0"; - public static final String UCT_NODE_OR_GSK_ID = "UCT Node or GSK ID"; - public static final String GSK_ID = "GSK ID"; - public static final String PREVENTIVE = "Preventive"; - public static final String CURATIVE = "Curative"; - public static final String TRUE = "TRUE"; - public static final String FALSE = "FALSE"; - public static final String RD_DESCRIPTION_MODE = "RD description mode"; - public static final String NODE = "NODE"; - public static final String GENERATOR_NAME = "Generator Name"; - public static final String RD_SUFFIX = "_RD"; - public static final String GENERATOR_SUFFIX = "_GENERATOR"; - - private IcsImporter() { - //should only be used statically - } - - public static TimeCoupledRaoInput populateInputWithICS(TimeCoupledRaoInput timeCoupledRaoInput, - InputStream staticInputStream, InputStream seriesInputStream, - InputStream gskInputStream, - double icsCostUp, - double icsCostDown, - String exportDirectory) throws IOException { - costUp = icsCostUp; - costDown = icsCostDown; - - TemporalData initialNetworks = new TemporalDataImpl<>(); - timeCoupledRaoInput.getRaoInputs().getDataPerTimestamp().forEach((dateTime, raoInput) -> { - Network network = raoInput.getNetwork(); - preProcessNetwork(network); - initialNetworks.put(dateTime, NetworkSerDe.copy(network)); // use a copy not to modify initial network - }); - 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); - }); - - Map> weightPerNodePerGsk = new HashMap<>(); - if (gskInputStream != null) { - Iterable gskCsvRecords = csvFormat.parse(new InputStreamReader(gskInputStream)); - gskCsvRecords.forEach(record -> { - weightPerNodePerGsk.putIfAbsent(record.get(GSK_ID), new HashMap<>()); - weightPerNodePerGsk.get(record.get(GSK_ID)).put(record.get("Node"), parseDoubleWithPossibleCommas(record.get("Weight"))); - }); - } - - staticCsvRecords.forEach(staticRecord -> { - if (shouldBeImported(staticRecord, weightPerNodePerGsk)) { - String raId = staticRecord.get(RA_RD_ID); - Map seriesPerType = seriesPerIdAndType.get(raId); - if (seriesPerType != null && - seriesPerType.containsKey(P0) && - seriesPerType.containsKey(RDP_DOWN) && - seriesPerType.containsKey(RDP_UP) && - rangeIsOkay(seriesPerType, timeCoupledRaoInput.getTimestampsToRun().stream().sorted().toList()) && - p0RespectsGradients(staticRecord, seriesPerType.get(P0), timeCoupledRaoInput.getTimestampsToRun().stream().sorted().toList())) { - if (staticRecord.get(RD_DESCRIPTION_MODE).equalsIgnoreCase(NODE)) { - importNodeRedispatchingAction(timeCoupledRaoInput, staticRecord, initialNetworks, seriesPerType, raId); - } else { - importGskRedispatchingAction(timeCoupledRaoInput, staticRecord, initialNetworks, seriesPerType, raId, weightPerNodePerGsk.get(staticRecord.get("UCT Node or GSK ID"))); - } - } - } - }); - - TemporalData postIcsRaoInputs = new TemporalDataImpl<>(); - - initialNetworks.getDataPerTimestamp().forEach((dateTime, initialNetwork) -> { - String exportedNetworkPath = exportDirectory + dateTime.format(DateTimeFormatter.ofPattern("%y%m%d_%H%M%S")) + ".jiidm"; - initialNetwork.write("JIIDM", new Properties(), Path.of(exportedNetworkPath)); - postIcsRaoInputs.put(dateTime, RaoInput.build(new LazyNetwork(exportedNetworkPath), timeCoupledRaoInput.getRaoInputs().getData(dateTime).orElseThrow().getCrac()).build()); - }); - - return new TimeCoupledRaoInput(postIcsRaoInputs, timeCoupledRaoInput.getTimestampsToRun(), timeCoupledRaoInput.getTimeCoupledConstraints()); - } - - private static void preProcessNetwork(Network network) { - network.getVoltageLevelStream().forEach(voltageLevel -> { - if (safeDoubleEquals(voltageLevel.getNominalV(), 380)) { - voltageLevel.setNominalV(400); - } else if (safeDoubleEquals(voltageLevel.getNominalV(), 220)) { - voltageLevel.setNominalV(225); - } - // Else, Should not be changed cause is not equal to the default nominal voltage of voltage levels 6 or 7 - }); - } - - private static boolean safeDoubleEquals(double a, double b) { - return Math.abs(a - b) < 1e-3; - } - - private static void importGskRedispatchingAction(TimeCoupledRaoInput timeCoupledRaoInput, - CSVRecord staticRecord, - TemporalData initialNetworks, - Map seriesPerType, - String raId, - Map weightPerNode) { - Map networkElementPerGskElement = new HashMap<>(); - for (String nodeId : weightPerNode.keySet()) { - String networkElementId = processNetworks(nodeId, initialNetworks, seriesPerType, weightPerNode.get(nodeId)); - if (networkElementId == null) { - return; - } - networkElementPerGskElement.put(nodeId, networkElementId); - } - - timeCoupledRaoInput.getRaoInputs().getDataPerTimestamp().forEach((dateTime, raoInput) -> { - importGskRedispatchActionForOneTimestamp(staticRecord, seriesPerType, raId, weightPerNode, dateTime, raoInput, networkElementPerGskElement); - }); - - for (Map.Entry entry : weightPerNode.entrySet()) { - String nodeId = entry.getKey(); - Double shiftKey = entry.getValue(); - String networkElementId = networkElementPerGskElement.get(nodeId); - // only create constraints if the network element is a generator - GeneratorConstraints.GeneratorConstraintsBuilder builder = GeneratorConstraints.create().withGeneratorId(networkElementId); - if (!staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT).isEmpty()) { - builder.withUpwardPowerGradient(shiftKey * parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT))); - } else { - builder.withUpwardPowerGradient(shiftKey * MAX_GRADIENT); - } - if (!staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT).isEmpty()) { - builder.withDownwardPowerGradient(-shiftKey * parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT))); - } else { - builder.withDownwardPowerGradient(-shiftKey * MAX_GRADIENT); - } - if (!staticRecord.get(LEAD_TIME).isEmpty()) { - builder.withLeadTime(parseDoubleWithPossibleCommas(staticRecord.get(LEAD_TIME))); - } - if (!staticRecord.get(LAG_TIME).isEmpty()) { - builder.withLagTime(parseDoubleWithPossibleCommas(staticRecord.get(LAG_TIME))); - } - if (staticRecord.get(SHUTDOWN_ALLOWED).isEmpty() || - !staticRecord.get(SHUTDOWN_ALLOWED).equalsIgnoreCase(TRUE) && !staticRecord.get(SHUTDOWN_ALLOWED).equalsIgnoreCase(FALSE)) { - throw new OpenRaoException("Could not parse shutDownAllowed value " + staticRecord.get(SHUTDOWN_ALLOWED) + " for nodeId " + nodeId); - } else { - builder.withShutDownAllowed(Boolean.parseBoolean(staticRecord.get(SHUTDOWN_ALLOWED))); - } - if (staticRecord.get(STARTUP_ALLOWED).isEmpty() || - !staticRecord.get(STARTUP_ALLOWED).equalsIgnoreCase(TRUE) && !staticRecord.get(STARTUP_ALLOWED).equalsIgnoreCase(FALSE)) { - throw new OpenRaoException("Could not parse startUpAllowed value " + staticRecord.get(STARTUP_ALLOWED) + " for nodeId " + nodeId); - } else { - builder.withStartUpAllowed(Boolean.parseBoolean(staticRecord.get(STARTUP_ALLOWED))); - } - timeCoupledRaoInput.getTimeCoupledConstraints().addGeneratorConstraints(builder.build()); - } - } - - private static void importGskRedispatchActionForOneTimestamp(CSVRecord staticRecord, - Map seriesPerType, - String raId, - Map weightPerNode, - OffsetDateTime dateTime, - RaoInput raoInput, - Map networkElementPerGskElement) { - Crac crac = raoInput.getCrac(); - double p0 = parseDoubleWithPossibleCommas(seriesPerType.get(P0).get(dateTime.getHour() + OFFSET)); - InjectionRangeActionAdder injectionRangeActionAdder = crac.newInjectionRangeAction() - .withId(raId + RD_SUFFIX) - .withName(staticRecord.get(GENERATOR_NAME)) - .withInitialSetpoint(p0) - .withVariationCost(costUp, VariationDirection.UP) - .withVariationCost(costDown, VariationDirection.DOWN) - //.withActivationCost(ACTIVATION_COST) - .newRange() - .withMin(p0 - parseDoubleWithPossibleCommas(seriesPerType.get(RDP_DOWN).get(dateTime.getHour() + OFFSET))) - .withMax(p0 + parseDoubleWithPossibleCommas(seriesPerType.get(RDP_UP).get(dateTime.getHour() + OFFSET))) - .add(); - - weightPerNode.forEach((nodeId, shiftKey) -> { - injectionRangeActionAdder.withNetworkElementAndKey(shiftKey, networkElementPerGskElement.get(nodeId)); - }); - - if (staticRecord.get(PREVENTIVE).equalsIgnoreCase(TRUE)) { - injectionRangeActionAdder.newOnInstantUsageRule() - .withInstant(crac.getPreventiveInstant().getId()) - .add(); - } - if (importCurative && staticRecord.get(CURATIVE).equalsIgnoreCase(TRUE)) { - injectionRangeActionAdder.newOnInstantUsageRule() - .withInstant(crac.getLastInstant().getId()) - .add(); - } - - injectionRangeActionAdder.add(); - } - - private static void importNodeRedispatchingAction(TimeCoupledRaoInput timeCoupledRaoInput, - CSVRecord staticRecord, - TemporalData initialNetworks, - Map seriesPerType, - String raId) { - String networkElementId = processNetworks(staticRecord.get(UCT_NODE_OR_GSK_ID), initialNetworks, seriesPerType, 1.); - if (networkElementId == null) { - return; - } - timeCoupledRaoInput.getRaoInputs().getDataPerTimestamp().forEach((dateTime, raoInput) -> { - importNodeRedispatchingActionForOneTimestamp(staticRecord, seriesPerType, raId, dateTime, raoInput, networkElementId); - }); - - GeneratorConstraints.GeneratorConstraintsBuilder builder = GeneratorConstraints.create().withGeneratorId(networkElementId); - if (!staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT).isEmpty()) { - builder.withUpwardPowerGradient(parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT))); - } else { - builder.withUpwardPowerGradient(MAX_GRADIENT); - } - if (!staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT).isEmpty()) { - builder.withDownwardPowerGradient(-parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT))); - } else { - builder.withDownwardPowerGradient(-MAX_GRADIENT); - } - if (!staticRecord.get(LEAD_TIME).isEmpty()) { - builder.withLeadTime(parseDoubleWithPossibleCommas(staticRecord.get(LEAD_TIME))); - } - if (!staticRecord.get(LAG_TIME).isEmpty()) { - builder.withLagTime(parseDoubleWithPossibleCommas(staticRecord.get(LAG_TIME))); - } - if (staticRecord.get(SHUTDOWN_ALLOWED).isEmpty() || - !staticRecord.get(SHUTDOWN_ALLOWED).equalsIgnoreCase(TRUE) && !staticRecord.get(SHUTDOWN_ALLOWED).equalsIgnoreCase(FALSE)) { - throw new OpenRaoException("Could not parse shutDownAllowed value " + staticRecord.get(SHUTDOWN_ALLOWED) + " for raId " + raId); - } else { - builder.withShutDownAllowed(Boolean.parseBoolean(staticRecord.get(SHUTDOWN_ALLOWED))); - } - if (staticRecord.get(STARTUP_ALLOWED).isEmpty() || - !staticRecord.get(STARTUP_ALLOWED).equalsIgnoreCase(TRUE) && !staticRecord.get(STARTUP_ALLOWED).equalsIgnoreCase(FALSE)) { - throw new OpenRaoException("Could not parse startUpAllowed value " + staticRecord.get(STARTUP_ALLOWED) + " for raId " + raId); - } else { - builder.withStartUpAllowed(Boolean.parseBoolean(staticRecord.get(STARTUP_ALLOWED))); - } - timeCoupledRaoInput.getTimeCoupledConstraints().addGeneratorConstraints(builder.build()); - } - - private static void importNodeRedispatchingActionForOneTimestamp(CSVRecord staticRecord, - Map seriesPerType, - String raId, - OffsetDateTime dateTime, - RaoInput raoInput, - String networkElementId) { - Crac crac = raoInput.getCrac(); - double p0 = parseDoubleWithPossibleCommas(seriesPerType.get(P0).get(dateTime.getHour() + OFFSET)); - InjectionRangeActionAdder injectionRangeActionAdder = crac.newInjectionRangeAction() - .withId(raId + RD_SUFFIX) - .withName(staticRecord.get(GENERATOR_NAME)) - .withNetworkElement(networkElementId) - .withInitialSetpoint(p0) - .withVariationCost(costUp, VariationDirection.UP) - .withVariationCost(costDown, VariationDirection.DOWN) - //.withActivationCost(ACTIVATION_COST) - .newRange() - .withMin(p0 - parseDoubleWithPossibleCommas(seriesPerType.get(RDP_DOWN).get(dateTime.getHour() + OFFSET))) - .withMax(p0 + parseDoubleWithPossibleCommas(seriesPerType.get(RDP_UP).get(dateTime.getHour() + OFFSET))) - .add(); - if (staticRecord.get(PREVENTIVE).equalsIgnoreCase(TRUE)) { - injectionRangeActionAdder.newOnInstantUsageRule() - .withInstant(crac.getPreventiveInstant().getId()) - .add(); - } - if (importCurative && staticRecord.get(CURATIVE).equalsIgnoreCase(TRUE)) { - injectionRangeActionAdder.newOnInstantUsageRule() - .withInstant(crac.getLastInstant().getId()) - .add(); - } - - injectionRangeActionAdder.add(); - } - - private static String processNetworks(String - nodeId, TemporalData initialNetworks, Map seriesPerType, double shiftKey) { - String generatorId = seriesPerType.get(P0).get(RA_RD_ID) + "_" + nodeId + GENERATOR_SUFFIX; - for (Map.Entry entry : initialNetworks.getDataPerTimestamp().entrySet()) { - Bus bus = findBus(nodeId, entry.getValue()); - if (bus == null) { - BUSINESS_WARNS.warn("Redispatching action {} cannot be imported because bus {} could not be found", seriesPerType.get(P0).get("RA RD ID"), nodeId); - return null; - } - Double p0 = parseDoubleWithPossibleCommas(seriesPerType.get(P0).get(entry.getKey().getHour() + OFFSET)) * shiftKey; - Optional pMinRd = parseValue(seriesPerType, P_MIN_RD, entry.getKey(), shiftKey); - processBus(bus, generatorId, p0, pMinRd.orElse(ON_POWER_THRESHOLD)); - } - return generatorId; - } - - private static Optional parseValue(Map seriesPerType, String key, OffsetDateTime - timestamp, double shiftKey) { - if (seriesPerType.containsKey(key)) { - CSVRecord series = seriesPerType.get(key); - String value = series.get(timestamp.getHour() + OFFSET); - if (value != null) { - return Optional.of(parseDoubleWithPossibleCommas(value) * shiftKey); - } - } - return Optional.empty(); - } - - // TODO: make this more robust (and less UCTE dependent) - private static Bus findBus(String nodeId, Network network) { - // First try to get the bus in bus breaker view - Bus bus = network.getBusBreakerView().getBus(nodeId); - if (bus != null) { - return bus; - } - - // Then, if last char is *, remove it - String modifiedNodeId = nodeId; - if (nodeId.endsWith("*")) { - modifiedNodeId = nodeId.substring(0, nodeId.length() - 1); - } - // Try to find the bus using bus view - return network.getBusBreakerView().getBus(modifiedNodeId + " "); - } - - private static void processBus(Bus bus, String generatorId, Double p0, double pMinRd) { - bus.getVoltageLevel().newGenerator() - .setBus(bus.getId()) - .setEnsureIdUnicity(true) - .setId(generatorId) - .setMaxP(999999) - .setMinP(pMinRd) - .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, Map> weightPerNodePerGsk) { - return (staticRecord.get(RD_DESCRIPTION_MODE).equalsIgnoreCase(NODE) || weightPerNodePerGsk.containsKey(staticRecord.get(UCT_NODE_OR_GSK_ID))) && - (staticRecord.get(PREVENTIVE).equalsIgnoreCase(TRUE) /*|| staticRecord.get(CURATIVE).equalsIgnoreCase(TRUE)*/); - } - - private static boolean p0RespectsGradients(CSVRecord staticRecord, CSVRecord - p0record, List dateTimes) { - double maxGradient = staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT).isEmpty() ? - MAX_GRADIENT : parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT)); - double minGradient = staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT).isEmpty() ? - -MAX_GRADIENT : -parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT)); - - Iterator dateTimeIterator = dateTimes.iterator(); - OffsetDateTime currentDateTime = dateTimeIterator.next(); - while (dateTimeIterator.hasNext()) { - OffsetDateTime nextDateTime = dateTimeIterator.next(); - double diff = parseDoubleWithPossibleCommas(p0record.get(nextDateTime.getHour() + OFFSET)) - parseDoubleWithPossibleCommas(p0record.get(currentDateTime.getHour() + OFFSET)); - if (diff > maxGradient || diff < minGradient) { - BUSINESS_WARNS.warn( - "Redispatching action {} will not be imported because it does not respect power gradients : min/max/diff {} {} {}", - staticRecord.get(0), minGradient, maxGradient, diff - ); - return false; - } - currentDateTime = nextDateTime; - } - return true; - } - - private static boolean rangeIsOkay(Map seriesPerType, List dateTimes) { - double maxRange = 0.; - for (OffsetDateTime dateTime : dateTimes) { - double rdpPlus = parseDoubleWithPossibleCommas(seriesPerType.get(RDP_UP).get(dateTime.getHour() + OFFSET)); - double rdpMinus = parseDoubleWithPossibleCommas(seriesPerType.get(RDP_DOWN).get(dateTime.getHour() + OFFSET)); - maxRange = Math.max(maxRange, rdpPlus + rdpMinus); - if (rdpPlus < -1e-6 || rdpMinus < -1e-6) { - BUSINESS_WARNS.warn("Redispatching action {} will not be imported because of RDP+ {} or RDP- {} is negative", seriesPerType.get(P0).get(RA_RD_ID), rdpPlus, rdpMinus); - return false; - } - } - if (maxRange < 1) { - BUSINESS_WARNS.warn("Redispatching action {} will not be imported because max range in the day {} MW is too small", seriesPerType.get(P0).get(RA_RD_ID), maxRange); - return false; - } - return true; - } - - private static double parseDoubleWithPossibleCommas(String string) { - return Double.parseDouble(string.replaceAll(",", ".")); - } -} diff --git a/data/crac/crac-util/src/test/java/com/powsybl/openrao/data/crac/util/IcsImporterTest.java b/data/crac/crac-util/src/test/java/com/powsybl/openrao/data/crac/util/IcsImporterTest.java deleted file mode 100644 index b5ff2e810a..0000000000 --- a/data/crac/crac-util/src/test/java/com/powsybl/openrao/data/crac/util/IcsImporterTest.java +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright (c) 2025, 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.crac.util; - -import com.powsybl.iidm.network.Generator; -import com.powsybl.iidm.network.Network; -import com.powsybl.openrao.commons.OpenRaoException; -import com.powsybl.openrao.commons.TemporalData; -import com.powsybl.openrao.commons.TemporalDataImpl; -import com.powsybl.openrao.data.crac.api.Crac; -import com.powsybl.openrao.data.crac.api.rangeaction.InjectionRangeAction; -import com.powsybl.openrao.data.crac.api.rangeaction.VariationDirection; -import com.powsybl.openrao.data.timecoupledconstraints.GeneratorConstraints; -import com.powsybl.openrao.data.timecoupledconstraints.TimeCoupledConstraints; -import com.powsybl.openrao.raoapi.LazyNetwork; -import com.powsybl.openrao.raoapi.RaoInput; -import com.powsybl.openrao.raoapi.TimeCoupledRaoInput; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.time.OffsetDateTime; -import java.time.ZoneOffset; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * @author Philippe Edwards {@literal } - */ -class IcsImporterTest { - private static final double DOUBLE_EPSILON = 1e-6; - private static final String TMP_DIR = System.getProperty("java.io.tmpdir") + File.separator; - private TimeCoupledRaoInput timeCoupledRaoInput; - private Crac crac1; - private Crac crac2; - private final OffsetDateTime timestamp1 = OffsetDateTime.of(2025, 2, 13, 0, 30, 0, 0, ZoneOffset.UTC); - private final OffsetDateTime timestamp2 = OffsetDateTime.of(2025, 2, 13, 1, 30, 0, 0, ZoneOffset.UTC); - - @BeforeEach - void setUp() throws IOException { - // we need to import twice the network to avoid variant names conflicts on the same network object - String networkFilePath1 = "2Nodes2ParallelLinesPST_0030.uct"; - String networkFilePath2 = "2Nodes2ParallelLinesPST_0130.uct"; - Network network1 = LazyNetwork.of(getResourcePath("/network/" + networkFilePath1)); - Network network2 = LazyNetwork.of(getResourcePath("/network/" + networkFilePath2)); - - crac1 = Crac.read("/crac/crac-0030.json", IcsImporterTest.class.getResourceAsStream("/crac/crac-0030.json"), network1); - crac2 = Crac.read("/crac/crac-0130.json", IcsImporterTest.class.getResourceAsStream("/crac/crac-0130.json"), network2); - - TemporalData raoInputs = new TemporalDataImpl<>( - Map.of( - timestamp1, RaoInput.build(network1, crac1).build(), - timestamp2, RaoInput.build(network2, crac2).build() - )); - - timeCoupledRaoInput = new TimeCoupledRaoInput(raoInputs, new TimeCoupledConstraints()); - } - - private String getResourcePath(String resourcePath) { - return "src/test/resources/" + resourcePath; - } - - @Test - void testIcsImporterOneAction() throws IOException { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - timeCoupledRaoInput = IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR); - - assertEquals(1, timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().size()); - GeneratorConstraints generatorConstraints = timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().iterator().next(); - assertEquals("Redispatching_RA_BBE1AA1_GENERATOR", generatorConstraints.getGeneratorId()); - assertTrue(generatorConstraints.getDownwardPowerGradient().isPresent()); - assertEquals(-10., generatorConstraints.getDownwardPowerGradient().get(), DOUBLE_EPSILON); - assertTrue(generatorConstraints.getUpwardPowerGradient().isPresent()); - assertEquals(10., generatorConstraints.getUpwardPowerGradient().get(), DOUBLE_EPSILON); - assertTrue(generatorConstraints.getLeadTime().isPresent()); - assertEquals(1.0, generatorConstraints.getLeadTime().get(), DOUBLE_EPSILON); - assertTrue(generatorConstraints.getLagTime().isPresent()); - assertEquals(1.0, generatorConstraints.getLagTime().get(), DOUBLE_EPSILON); - assertFalse(generatorConstraints.isShutDownAllowed()); - assertFalse(generatorConstraints.isStartUpAllowed()); - - assertEquals(1, crac1.getInjectionRangeActions().size()); - InjectionRangeAction ra1 = crac1.getInjectionRangeActions().iterator().next(); - assertEquals("Redispatching_RA_RD", ra1.getId()); - assertEquals(116., ra1.getInitialSetpoint(), DOUBLE_EPSILON); - assertTrue(ra1.getVariationCost(VariationDirection.UP).isPresent()); - assertEquals(5., ra1.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); - assertTrue(ra1.getVariationCost(VariationDirection.DOWN).isPresent()); - assertEquals(5., ra1.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); - Network network1 = timeCoupledRaoInput.getRaoInputs().getData(timestamp1).orElseThrow().getNetwork(); - Generator generator1 = network1.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); - assertEquals(116., generator1.getTargetP(), DOUBLE_EPSILON); - assertEquals(10.0, generator1.getMinP(), DOUBLE_EPSILON); - - assertEquals(1, crac2.getInjectionRangeActions().size()); - InjectionRangeAction ra2 = crac2.getInjectionRangeActions().iterator().next(); - assertEquals("Redispatching_RA_RD", ra2.getId()); - assertEquals(120., ra2.getInitialSetpoint(), DOUBLE_EPSILON); - assertTrue(ra2.getVariationCost(VariationDirection.UP).isPresent()); - assertEquals(5., ra2.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); - assertTrue(ra2.getVariationCost(VariationDirection.DOWN).isPresent()); - assertEquals(5., ra2.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); - Network network2 = timeCoupledRaoInput.getRaoInputs().getData(timestamp2).orElseThrow().getNetwork(); - Generator generator2 = network2.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); - assertEquals(120., generator2.getTargetP(), DOUBLE_EPSILON); - assertEquals(15.0, generator2.getMinP(), DOUBLE_EPSILON); - } - - @Test - void testIcsImporterShutDownAndStartUpTrue() throws IOException { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_shutdown_startup_true.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR); - - assertEquals(1, timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().size()); - GeneratorConstraints generatorConstraints = timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().iterator().next(); - assertTrue(generatorConstraints.isShutDownAllowed()); - assertTrue(generatorConstraints.isStartUpAllowed()); - } - - @Test - void testIcsImporterNoShutDown() { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_no_shutdown.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - - Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR)) - .withMessage("Could not parse shutDownAllowed value for raId Redispatching_RA"); - } - - @Test - void testIcsImporterWrongShutDown() { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_wrong_shutdown.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - - Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR)) - .withMessage("Could not parse shutDownAllowed value wrongValue for raId Redispatching_RA"); - } - - @Test - void testIcsImporterNoStartUp() { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_no_startup.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - - Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR)) - .withMessage("Could not parse startUpAllowed value for raId Redispatching_RA"); - } - - @Test - void testIcsImporterWrongStartUp() { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_wrong_startup.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - - Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR)) - .withMessage("Could not parse startUpAllowed value wrongValue for raId Redispatching_RA"); - } - - @Test - void testIcsImporterWithGskNoShutDown() { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_with_gsk_no_shutdown.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - - Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR)) - .withMessage("Could not parse shutDownAllowed value for nodeId FFR1AA1"); - } - - @Test - void testIcsImporterWithGskWrongShutDown() { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_with_gsk_wrong_shutdown.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - - Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR)) - .withMessage("Could not parse shutDownAllowed value wrongValue for nodeId FFR1AA1"); - } - - @Test - void testIcsImporterWithGskNoStartUp() { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_with_gsk_no_startup.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - - Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR)) - .withMessage("Could not parse startUpAllowed value for nodeId FFR1AA1"); - } - - @Test - void testIcsImporterWithGskWrongStartUp() { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_with_gsk_wrong_startup.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - - Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR)) - .withMessage("Could not parse startUpAllowed value wrongValue for nodeId FFR1AA1"); - } - - @Test - void testIcsImporterOneActionNoPminRd() throws IOException { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series_no_pmin_rd.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - timeCoupledRaoInput = IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR); - - assertEquals(1, timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().size()); - GeneratorConstraints generatorConstraints = timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().iterator().next(); - assertEquals("Redispatching_RA_BBE1AA1_GENERATOR", generatorConstraints.getGeneratorId()); - assertTrue(generatorConstraints.getDownwardPowerGradient().isPresent()); - assertEquals(-10., generatorConstraints.getDownwardPowerGradient().get(), DOUBLE_EPSILON); - assertTrue(generatorConstraints.getUpwardPowerGradient().isPresent()); - assertEquals(10., generatorConstraints.getUpwardPowerGradient().get(), DOUBLE_EPSILON); - assertTrue(generatorConstraints.getLeadTime().isPresent()); - assertEquals(1.0, generatorConstraints.getLeadTime().get(), DOUBLE_EPSILON); - assertTrue(generatorConstraints.getLagTime().isPresent()); - assertEquals(1.0, generatorConstraints.getLagTime().get(), DOUBLE_EPSILON); - assertFalse(generatorConstraints.isShutDownAllowed()); - assertFalse(generatorConstraints.isStartUpAllowed()); - - assertEquals(1, crac1.getInjectionRangeActions().size()); - InjectionRangeAction ra1 = crac1.getInjectionRangeActions().iterator().next(); - assertEquals("Redispatching_RA_RD", ra1.getId()); - assertEquals(116., ra1.getInitialSetpoint(), DOUBLE_EPSILON); - assertTrue(ra1.getVariationCost(VariationDirection.UP).isPresent()); - assertEquals(5., ra1.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); - assertTrue(ra1.getVariationCost(VariationDirection.DOWN).isPresent()); - assertEquals(5., ra1.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); - Network network1 = timeCoupledRaoInput.getRaoInputs().getData(timestamp1).orElseThrow().getNetwork(); - Generator generator1 = network1.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); - assertEquals(116., generator1.getTargetP(), DOUBLE_EPSILON); - assertEquals(1.001, generator1.getMinP(), DOUBLE_EPSILON); - - assertEquals(1, crac2.getInjectionRangeActions().size()); - InjectionRangeAction ra2 = crac2.getInjectionRangeActions().iterator().next(); - assertEquals("Redispatching_RA_RD", ra2.getId()); - assertEquals(120., ra2.getInitialSetpoint(), DOUBLE_EPSILON); - assertTrue(ra2.getVariationCost(VariationDirection.UP).isPresent()); - assertEquals(5., ra2.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); - assertTrue(ra2.getVariationCost(VariationDirection.DOWN).isPresent()); - assertEquals(5., ra2.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); - Network network2 = timeCoupledRaoInput.getRaoInputs().getData(timestamp2).orElseThrow().getNetwork(); - Generator generator2 = network2.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); - assertEquals(120., generator2.getTargetP(), DOUBLE_EPSILON); - assertEquals(1.001, generator2.getMinP(), DOUBLE_EPSILON); - } - - @Test - void testIcsImporterOneActionNoGradientNoLeadNoLag() throws IOException { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_no_gradient_no_lead_no_lag.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - timeCoupledRaoInput = IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR); - - assertEquals(1, timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().size()); - GeneratorConstraints generatorConstraints = timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().iterator().next(); - assertEquals("Redispatching_RA_BBE1AA1_GENERATOR", generatorConstraints.getGeneratorId()); - assertTrue(generatorConstraints.getDownwardPowerGradient().isPresent()); - assertEquals(-1000.0, generatorConstraints.getDownwardPowerGradient().get(), DOUBLE_EPSILON); - assertTrue(generatorConstraints.getUpwardPowerGradient().isPresent()); - assertEquals(1000.0, generatorConstraints.getUpwardPowerGradient().get(), DOUBLE_EPSILON); - assertTrue(generatorConstraints.getLeadTime().isEmpty()); - assertTrue(generatorConstraints.getLagTime().isEmpty()); - assertFalse(generatorConstraints.isShutDownAllowed()); - assertFalse(generatorConstraints.isStartUpAllowed()); - - assertEquals(1, crac1.getInjectionRangeActions().size()); - InjectionRangeAction ra1 = crac1.getInjectionRangeActions().iterator().next(); - assertEquals("Redispatching_RA_RD", ra1.getId()); - assertEquals(116., ra1.getInitialSetpoint(), DOUBLE_EPSILON); - assertTrue(ra1.getVariationCost(VariationDirection.UP).isPresent()); - assertEquals(5., ra1.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); - assertTrue(ra1.getVariationCost(VariationDirection.DOWN).isPresent()); - assertEquals(5., ra1.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); - Network network1 = timeCoupledRaoInput.getRaoInputs().getData(timestamp1).orElseThrow().getNetwork(); - Generator generator1 = network1.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); - assertEquals(116., generator1.getTargetP(), DOUBLE_EPSILON); - assertEquals(10.0, generator1.getMinP(), DOUBLE_EPSILON); - - assertEquals(1, crac2.getInjectionRangeActions().size()); - InjectionRangeAction ra2 = crac2.getInjectionRangeActions().iterator().next(); - assertEquals("Redispatching_RA_RD", ra2.getId()); - assertEquals(120., ra2.getInitialSetpoint(), DOUBLE_EPSILON); - assertTrue(ra2.getVariationCost(VariationDirection.UP).isPresent()); - assertEquals(5., ra2.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); - assertTrue(ra2.getVariationCost(VariationDirection.DOWN).isPresent()); - assertEquals(5., ra2.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); - Network network2 = timeCoupledRaoInput.getRaoInputs().getData(timestamp2).orElseThrow().getNetwork(); - Generator generator2 = network2.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); - assertEquals(120., generator2.getTargetP(), DOUBLE_EPSILON); - assertEquals(15.0, generator2.getMinP(), DOUBLE_EPSILON); - } - - @Test - void testIcsImporterGradientNotOk() throws IOException { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series_gradient_not_ok.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - timeCoupledRaoInput = IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR); - - assertEquals(0, timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().size()); - assertEquals(0, crac1.getInjectionRangeActions().size()); - assertEquals(0, crac2.getInjectionRangeActions().size()); - } - - @Test - void testIcsImporterWithGSK() throws IOException { - double cost = 5.; - InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_with_gsk.csv"); - InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); - InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - timeCoupledRaoInput = IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR); - - assertEquals(2, timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().size()); - GeneratorConstraints generatorConstraintsBE = timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().stream() - .filter(gc -> gc.getGeneratorId().contains("BE")) - .findFirst().orElseThrow(); - assertEquals("Redispatching_RA_BBE1AA1_GENERATOR", generatorConstraintsBE.getGeneratorId()); - assertEquals(-6., generatorConstraintsBE.getDownwardPowerGradient().orElseThrow(), DOUBLE_EPSILON); - assertEquals(6., generatorConstraintsBE.getUpwardPowerGradient().orElseThrow(), DOUBLE_EPSILON); - assertTrue(generatorConstraintsBE.getLeadTime().isPresent()); - assertEquals(1.0, generatorConstraintsBE.getLeadTime().get(), DOUBLE_EPSILON); - assertTrue(generatorConstraintsBE.getLagTime().isPresent()); - assertEquals(1.0, generatorConstraintsBE.getLagTime().get(), DOUBLE_EPSILON); - assertFalse(generatorConstraintsBE.isShutDownAllowed()); - assertFalse(generatorConstraintsBE.isStartUpAllowed()); - GeneratorConstraints generatorConstraintsFR = timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().stream() - .filter(gc -> gc.getGeneratorId().contains("FR")) - .findFirst().orElseThrow(); - assertEquals("Redispatching_RA_FFR1AA1_GENERATOR", generatorConstraintsFR.getGeneratorId()); - assertEquals(-4., generatorConstraintsFR.getDownwardPowerGradient().orElseThrow(), DOUBLE_EPSILON); - assertEquals(4., generatorConstraintsFR.getUpwardPowerGradient().orElseThrow(), DOUBLE_EPSILON); - assertTrue(generatorConstraintsFR.getLeadTime().isPresent()); - assertEquals(1.0, generatorConstraintsFR.getLeadTime().get(), DOUBLE_EPSILON); - assertTrue(generatorConstraintsFR.getLagTime().isPresent()); - assertEquals(1.0, generatorConstraintsFR.getLagTime().get(), DOUBLE_EPSILON); - assertFalse(generatorConstraintsFR.isShutDownAllowed()); - assertFalse(generatorConstraintsFR.isStartUpAllowed()); - - assertEquals(1, crac1.getInjectionRangeActions().size()); - InjectionRangeAction ra1 = crac1.getInjectionRangeActions().iterator().next(); - assertEquals("Redispatching_RA_RD", ra1.getId()); - assertEquals(116., ra1.getInitialSetpoint(), DOUBLE_EPSILON); - assertTrue(ra1.getVariationCost(VariationDirection.UP).isPresent()); - assertEquals(5., ra1.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); - assertTrue(ra1.getVariationCost(VariationDirection.DOWN).isPresent()); - assertEquals(5., ra1.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); - - Network network1 = timeCoupledRaoInput.getRaoInputs().getData(timestamp1).orElseThrow().getNetwork(); - Generator generatorBE = network1.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); - assertEquals(116. * 0.6, generatorBE.getTargetP(), DOUBLE_EPSILON); - assertEquals(10.0 * 0.6, generatorBE.getMinP(), DOUBLE_EPSILON); - Generator generatorFR = network1.getGenerator("Redispatching_RA_FFR1AA1_GENERATOR"); - assertEquals(116. * 0.4, generatorFR.getTargetP(), DOUBLE_EPSILON); - assertEquals(10.0 * 0.4, generatorFR.getMinP(), DOUBLE_EPSILON); - } -} From f63232b70b41928d9b79f44b8fefb68e31867f0b Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Wed, 1 Apr 2026 11:30:55 +0200 Subject: [PATCH 23/64] revert update to be moved in other PR Signed-off-by: CHEN Roxane --- .../com/powsybl/openrao/data/IcsUtil.java | 5 +-- .../com/powsybl/openrao/data/IcsDataTest.java | 33 ------------------- 2 files changed, 1 insertion(+), 37 deletions(-) diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java index daa0373305..ea49e96785 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java @@ -63,11 +63,8 @@ static Optional parseValue(Map seriesPerType, String if (seriesPerType.containsKey(key)) { CSVRecord series = seriesPerType.get(key); String value = series.get(timestamp.getHour() + OFFSET); - if (value != null && !value.isEmpty()) { + if (value != null) { return Optional.of(parseDoubleWithPossibleCommas(value) * shiftKey); - } else { - BUSINESS_WARNS.warn("Redispatching action {} is missing {} value for datetime {}", series.get(RA_RD_ID), key, timestamp); - return Optional.empty(); } } return Optional.empty(); diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java index 688f15308e..f1334ef844 100644 --- a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java @@ -319,39 +319,6 @@ void testCreateGeneratorAndLoadBusNotFound() throws IOException { assertEquals("Redispatching action Redispatching_RA cannot be imported because bus undefined_node could not be found", logsList.get(0).getFormattedMessage()); } - @Test - void testCreateGeneratorAndLoadPMinNotDefined() throws IOException { - String seriesCsv = """ - RA RD ID;Type of timeseries;00:30;01:30 - Redispatching_RA;RDP-;35;35 - Redispatching_RA;RDP+;43;43 - Redispatching_RA;P0;116;116 - Redispatching_RA;Pmin_RD;10; - """; - // Read ICS Data - IcsData icsData = IcsDataImporter.read( - getClass().getResourceAsStream("/ics/static_with_gsk.csv"), - new ByteArrayInputStream(seriesCsv.getBytes(StandardCharsets.UTF_8)), - getClass().getResourceAsStream("/glsk/gsk.csv"), - generateOffsetDateTimeList(2)); - Map generatorIdPerNodeId = icsData.createGeneratorAndLoadAndUpdateNetworks(networkTemporalData, "Redispatching_RA", icsData.getWeightPerNodePerGsk().get("GSK_NAME")); - assertEquals(Map.of("BBE1AA1", "Redispatching_RA_BBE1AA1_GENERATOR", "FFR1AA1", "Redispatching_RA_FFR1AA1_GENERATOR"), generatorIdPerNodeId); - Generator generatorBE = network1.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); - assertEquals(116. * 0.6, generatorBE.getTargetP(), DOUBLE_EPSILON); - assertEquals(10. * 0.6, generatorBE.getMinP(), DOUBLE_EPSILON); - Generator generatorFR = network1.getGenerator("Redispatching_RA_FFR1AA1_GENERATOR"); - assertEquals(116. * 0.4, generatorFR.getTargetP(), DOUBLE_EPSILON); - assertEquals(10. * 0.4, generatorFR.getMinP(), DOUBLE_EPSILON); - - Generator generatorBE2 = network2.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); - assertEquals(116. * 0.6, generatorBE2.getTargetP(), DOUBLE_EPSILON); - assertEquals(ON_POWER_THRESHOLD, generatorBE2.getMinP(), DOUBLE_EPSILON); - Generator generatorFR2 = network2.getGenerator("Redispatching_RA_FFR1AA1_GENERATOR"); - assertEquals(116. * 0.4, generatorFR2.getTargetP(), DOUBLE_EPSILON); - assertEquals(ON_POWER_THRESHOLD, generatorFR2.getMinP(), DOUBLE_EPSILON); - assertEquals("Redispatching action Redispatching_RA is missing Pmin_RD value for datetime 2025-02-13T01:30Z", logsList.get(0).getFormattedMessage()); - } - // Test createInjectionRangeActionsAndUpdateCracs @Test void testCreateInjectionRangeActionsAndUpdateCracsOnANode() throws IOException { From 8a05188a59f01063511b9a493d47ce5e6b9c7e88 Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Wed, 1 Apr 2026 13:56:08 +0200 Subject: [PATCH 24/64] update doc Signed-off-by: CHEN Roxane --- .../time-coupled-constraints.md | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/docs/input-data/specific-input-data/time-coupled-constraints.md b/docs/input-data/specific-input-data/time-coupled-constraints.md index c27daf60d9..67cbdb1911 100644 --- a/docs/input-data/specific-input-data/time-coupled-constraints.md +++ b/docs/input-data/specific-input-data/time-coupled-constraints.md @@ -81,3 +81,25 @@ that generator can be started up. ```java TimeCoupledConstraints timeCoupledConstraints = JsonTimeCoupledConstraints.read(getClass().getResourceAsStream("/time-coupled-constraints.json")); ``` + +#### Import Time-Coupled Constraints JSON from ICS data + +```java +// read ICS data +InputStream staticIcs = new FileInputStream("path/to/ics/static.csv"); +InputStream seriesIcs = new FileInputStream("path/to/ics/series.csv"); +InputStream gskIcs = new FileInputStream("path/to/ics/gsk.csv"); +IcsData icsData = new IcsDataImporter.read(staticIcs, seriesIcs, gskIcs); + +// Create Generator Constraints +Set generatorConstraintsSet = new HashSet<>(); +TimeCoupledConstraints timeCoupledConstraints = new TimeCoupledConstraints(); +// Warning: in exemple we use a default generator id per node +icsData.getRedispatchingActions().forEach(raId -> generatorConstraintsSet.addAll(icsData.createGeneratorConstraints(raId, getDefaultGeneratorIdPerNode(raId)))); +generatorConstraintsSet.forEach(constraint -> timeCoupledConstraints.addGeneratorConstraints(constraint)); + +// Write Time-Coupled Constraints JSON +OutputStream outputStream = Files.newOutputStream(Path.of("output.json")); +JsonTimeCoupledConstraints.write(timeCoupledConstraints, outputStream); +TimeCoupledConstraints timeCoupledConstraints = JsonTimeCoupledConstraints.read(icsData); +``` \ No newline at end of file From bc03c9d6bc860b390f4c80d10ab2fc048105b70d Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Wed, 1 Apr 2026 13:56:17 +0200 Subject: [PATCH 25/64] update doc Signed-off-by: CHEN Roxane --- docs/input-data/specific-input-data/ics.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/input-data/specific-input-data/ics.md b/docs/input-data/specific-input-data/ics.md index f300e08c4a..171e13f279 100644 --- a/docs/input-data/specific-input-data/ics.md +++ b/docs/input-data/specific-input-data/ics.md @@ -44,12 +44,11 @@ RDP+, positive values), and the Pmin of redispatching (Pmin_RD). These values ar ![ICS Importer](../../_static/img/ics-importer.png) -## Import TimeCoupledConstraints using ICS data +## Read ICS data ```java InputStream staticIcs = new FileInputStream("path/to/ics/static.csv"); InputStream seriesIcs = new FileInputStream("path/to/ics/series.csv"); InputStream gskIcs = new FileInputStream("path/to/ics/gsk.csv"); IcsData icsData = new IcsDataImporter.read(staticIcs, seriesIcs, gskIcs); -TimeCoupledConstraints timeCoupledConstraints = new TimeCoupledConstraints(icsData.getGeneratorConstraints()); -``` \ No newline at end of file +``` From d8d5594bf3a86a8cf94ab105211fab79c8edd77e Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Wed, 1 Apr 2026 13:56:34 +0200 Subject: [PATCH 26/64] use default functions Signed-off-by: CHEN Roxane --- .../com/powsybl/openrao/data/IcsData.java | 44 +++++++------ .../com/powsybl/openrao/data/IcsDataTest.java | 63 ++++++++++++++----- .../expected_time_coupled_constraints.json | 29 +++++++++ 3 files changed, 102 insertions(+), 34 deletions(-) create mode 100644 data/ics-importer/src/test/resources/expected_time_coupled_constraints.json diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java index 7ede08bbac..a9cdfadfb7 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java @@ -80,17 +80,34 @@ public static String getNodeIdOrGskIdFromRaId(String raId) { return staticConstraintPerId.get(raId).get(UCT_NODE_OR_GSK_ID); } + public static Map getWeightPerNode(String raId) { + if (isRaDefinedOnANode(raId)) { + return Map.of(getNodeIdOrGskIdFromRaId(raId), 1.0); + } else { + return weightPerNodePerGsk.get(getNodeIdOrGskIdFromRaId(raId)); + } + } + + public static Map getDefaultGeneratorIdPerNode(String raId) { + Map defaultGeneratorIdPerNode = new HashMap<>(); + Map weightPerNode = getWeightPerNode(raId); + for (Map.Entry entry : weightPerNode.entrySet()) { + defaultGeneratorIdPerNode.put(entry.getKey(), getGeneratorIdFromRaIdAndNodeId(raId, entry.getKey())); + } + return defaultGeneratorIdPerNode; + } + /** * Generates a set of generator constraints based on the provided remedial action ID. * * @param raId The identifier of the remedial action for which the generator constraints are being created. - * @param weightPerNode A map linking node identifiers to their respective generation shift key weights. * @param networkElementIdPerNodeId A map linking nodeId to their respective network elements id. * @return A set of {@code GeneratorConstraints} generated for the specified parameters. * @throws OpenRaoException if data related to shutdown or startup allowances cannot be parsed. */ - public static Set createGeneratorConstraints(String raId, Map weightPerNode, Map networkElementIdPerNodeId) { + public static Set createGeneratorConstraints(String raId, Map networkElementIdPerNodeId) { Set generatorConstraintsSet = new HashSet<>(); + Map weightPerNode = getWeightPerNode(raId); for (Map.Entry entry : weightPerNode.entrySet()) { String nodeId = entry.getKey(); Double shiftKey = entry.getValue(); @@ -138,16 +155,15 @@ public static Set createGeneratorConstraints(String raId, * @param initialNetworksToModify Temporal data representing the networks that will be modified. * Contains network configurations per timestamp. * @param raId The identifier of the remedial action for which generators and network modifications are being applied. - * @param weightPerNode A map linking node identifiers to their corresponding generation shift key weights. * @return A map associating each node identifier to its corresponding generator identifier. * Returns an empty map if the process is aborted due to missing network components. */ public static Map createGeneratorAndLoadAndUpdateNetworks(TemporalData initialNetworksToModify, - String raId, - Map weightPerNode) { + String raId) { Map networkElementPerGskElement = new HashMap<>(); Map seriesPerType = timeseriesPerIdAndType.get(raId); + Map weightPerNode = getWeightPerNode(raId); for (Map.Entry entry : weightPerNode.entrySet()) { @@ -184,20 +200,20 @@ public static Map createGeneratorAndLoadAndUpdateNetworks(Tempor * * @param cracToModify Temporal data containing CRACs to be modified and timestamps to consider. * @param raId The identifier of the remedial action for which injection range actions are created. - * @param weightPerNode A map linking node identifiers to their associated generation shift key weights. * @param networkElementPerNode A map linking each node identifier to its corresponding network element/generator id. * @param costUp The cost associated with increasing the generation (VariationDirection.UP). * @param costDown The cost associated with decreasing the generation (VariationDirection.DOWN). */ public static void createInjectionRangeActionsAndUpdateCracs(TemporalData cracToModify, String raId, - Map weightPerNode, Map networkElementPerNode, double costUp, double costDown) { CSVRecord staticRecord = staticConstraintPerId.get(raId); Map seriesPerType = timeseriesPerIdAndType.get(raId); + Map weightPerNode = getWeightPerNode(raId); + cracToModify.getDataPerTimestamp().forEach((dateTime, crac) -> { double p0 = parseDoubleWithPossibleCommas(seriesPerType.get(P0).get(dateTime.getHour() + OFFSET)); InjectionRangeActionAdder injectionRangeActionAdder = crac.newInjectionRangeAction() @@ -261,27 +277,19 @@ public TimeCoupledRaoInput processAllRedispatchingActions(TimeCoupledRaoInput ti // For each redispatching actions defined in static csv update networks and update cracs consistentRedispatchingActions.forEach(raId -> { - Map weightPerNode; - - // If the remedial action is defined on a Node. - if (isRaDefinedOnANode(raId)) { - weightPerNode = Map.of(getNodeIdOrGskIdFromRaId(raId), 1.0); - } else { // If the remedial action is defined on a GSK - weightPerNode = weightPerNodePerGsk.get(getNodeIdOrGskIdFromRaId(raId)); - } // Create generator and load in networks - Map generatorIdPerNode = createGeneratorAndLoadAndUpdateNetworks(modifiedInitialNetworks, raId, weightPerNode); + Map generatorIdPerNode = createGeneratorAndLoadAndUpdateNetworks(modifiedInitialNetworks, raId); // One of the node could not be find no need to create injection range actions and generator constraint. if (generatorIdPerNode.isEmpty()) { return; } // Create Injection Range Actions in CRACs - createInjectionRangeActionsAndUpdateCracs(cracToModify, raId, weightPerNode, generatorIdPerNode, costUp, costDown); + createInjectionRangeActionsAndUpdateCracs(cracToModify, raId, generatorIdPerNode, costUp, costDown); // Create generator constraints and them to time coupled rao input - Set generatorConstraintsSet = createGeneratorConstraints(raId, weightPerNode, generatorIdPerNode); + Set generatorConstraintsSet = createGeneratorConstraints(raId, generatorIdPerNode); generatorConstraintsSet.forEach(generatorConstraints -> timeCoupledRaoInput.getTimeCoupledConstraints().addGeneratorConstraints(generatorConstraints)); }); diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java index f1334ef844..98b0cfec83 100644 --- a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java @@ -10,6 +10,8 @@ import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.powsybl.iidm.network.Generator; import com.powsybl.iidm.network.Network; import com.powsybl.openrao.commons.OpenRaoException; @@ -21,6 +23,7 @@ import com.powsybl.openrao.data.crac.api.rangeaction.VariationDirection; import com.powsybl.openrao.data.timecoupledconstraints.GeneratorConstraints; import com.powsybl.openrao.data.timecoupledconstraints.TimeCoupledConstraints; +import com.powsybl.openrao.data.timecoupledconstraints.io.JsonTimeCoupledConstraints; import com.powsybl.openrao.raoapi.LazyNetwork; import com.powsybl.openrao.raoapi.RaoInput; import com.powsybl.openrao.raoapi.TimeCoupledRaoInput; @@ -36,15 +39,13 @@ import java.nio.charset.StandardCharsets; import java.time.OffsetDateTime; import java.time.ZoneOffset; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; +import static com.powsybl.openrao.data.IcsData.getDefaultGeneratorIdPerNode; import static com.powsybl.openrao.data.IcsDataImporterTest.generateOffsetDateTimeList; import static com.powsybl.openrao.data.IcsUtil.MAX_GRADIENT; -import static com.powsybl.openrao.data.IcsUtil.ON_POWER_THRESHOLD; import static org.junit.jupiter.api.Assertions.*; /** @@ -110,7 +111,7 @@ void testCreateGeneratorConstraintRaDefinedOnANode() throws IOException { generateOffsetDateTimeList(24)); // Check generator constraint creation - Set generatorConstraintsSet = icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", 1.0), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1"))); + Set generatorConstraintsSet = icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1"))); assertEquals(1, generatorConstraintsSet.size()); GeneratorConstraints generatorConstraints = generatorConstraintsSet.iterator().next(); @@ -138,7 +139,7 @@ void testCreateGeneratorConstraintRaDefinedOnAGSK() throws IOException { getClass().getResourceAsStream("/glsk/gsk.csv"), generateOffsetDateTimeList(24)); - Set generatorConstraintsSet = icsData.createGeneratorConstraints("Redispatching_RA", icsData.getWeightPerNodePerGsk().get("GSK_NAME"), Map.of("BBE1AA1", "Redispatching_RA_BBE1AA1_GENERATOR", "FFR1AA1", "Redispatching_RA_FFR1AA1_GENERATOR")); + Set generatorConstraintsSet = icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", "Redispatching_RA_BBE1AA1_GENERATOR", "FFR1AA1", "Redispatching_RA_FFR1AA1_GENERATOR")); assertEquals(2, generatorConstraintsSet.size()); GeneratorConstraints generatorConstraintsBE = generatorConstraintsSet.stream() @@ -178,7 +179,7 @@ void testCreateGeneratorConstraintRaDefinedOnANodeMissingGradientLeadTimeAndLagT getClass().getResourceAsStream("/ics/series.csv"), null, generateOffsetDateTimeList(24)); - Set generatorConstraintsSet = icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", 1.0), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1"))); + Set generatorConstraintsSet = icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1"))); assertEquals(1, generatorConstraintsSet.size()); GeneratorConstraints generatorConstraints = generatorConstraintsSet.iterator().next(); assertEquals("Redispatching_RA_BBE1AA1_GENERATOR", generatorConstraints.getGeneratorId()); @@ -205,14 +206,14 @@ void testCreateGeneratorConstraintShutDownAndStartUpAllowed(String staticCsv, St generateOffsetDateTimeList(24)); if (expectedLogMessage == null) { - Set generatorConstraintsSet = icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", 1.0), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1"))); - assertEquals(1, generatorConstraintsSet.size()); + Set generatorConstraintsSet = icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1"), "FFR1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "FFR1AA1"))); + assertEquals(2, generatorConstraintsSet.size()); GeneratorConstraints generatorConstraints = generatorConstraintsSet.iterator().next(); assertTrue(generatorConstraints.isShutDownAllowed()); assertTrue(generatorConstraints.isStartUpAllowed()); } else { Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", 0.6, "FFR1AA1", 0.4), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1"), "FFR1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "FFR1AA1")))) + .isThrownBy(() -> icsData.createGeneratorConstraints("Redispatching_RA", Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1"), "FFR1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "FFR1AA1")))) .withMessage(expectedLogMessage); } } @@ -270,7 +271,7 @@ void testCreateGeneratorAndLoadAndUpdateNetworksOnANode() throws IOException { null, generateOffsetDateTimeList(2)); - icsData.createGeneratorAndLoadAndUpdateNetworks(networkTemporalData, "Redispatching_RA", Map.of("BBE1AA1", 1.0)); + icsData.createGeneratorAndLoadAndUpdateNetworks(networkTemporalData, "Redispatching_RA"); Generator generator1 = network1.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); assertEquals(116., generator1.getTargetP(), DOUBLE_EPSILON); assertEquals(10.0, generator1.getMinP(), DOUBLE_EPSILON); @@ -290,7 +291,7 @@ void testCreateGeneratorAndLoadAndUpdateNetworksOnAGsk() throws IOException { getClass().getResourceAsStream("/ics/series.csv"), getClass().getResourceAsStream("/glsk/gsk.csv"), generateOffsetDateTimeList(2)); - Map generatorIdPerNodeId = icsData.createGeneratorAndLoadAndUpdateNetworks(networkTemporalData, "Redispatching_RA", icsData.getWeightPerNodePerGsk().get("GSK_NAME")); + Map generatorIdPerNodeId = icsData.createGeneratorAndLoadAndUpdateNetworks(networkTemporalData, "Redispatching_RA"); assertEquals(Map.of("BBE1AA1", "Redispatching_RA_BBE1AA1_GENERATOR", "FFR1AA1", "Redispatching_RA_FFR1AA1_GENERATOR"), generatorIdPerNodeId); Generator generatorBE = network1.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); assertEquals(116. * 0.6, generatorBE.getTargetP(), DOUBLE_EPSILON); @@ -314,7 +315,7 @@ void testCreateGeneratorAndLoadBusNotFound() throws IOException { getClass().getResourceAsStream("/ics/series.csv"), new ByteArrayInputStream(gsk.getBytes(StandardCharsets.UTF_8)), generateOffsetDateTimeList(2)); - Map generatorIdPerNodeId = icsData.createGeneratorAndLoadAndUpdateNetworks(networkTemporalData, "Redispatching_RA", icsData.getWeightPerNodePerGsk().get("GSK_NAME")); + Map generatorIdPerNodeId = icsData.createGeneratorAndLoadAndUpdateNetworks(networkTemporalData, "Redispatching_RA"); assertEquals(Map.of(), generatorIdPerNodeId); assertEquals("Redispatching action Redispatching_RA cannot be imported because bus undefined_node could not be found", logsList.get(0).getFormattedMessage()); } @@ -330,7 +331,7 @@ void testCreateInjectionRangeActionsAndUpdateCracsOnANode() throws IOException { generateOffsetDateTimeList(2)); // Test injection range action creation in crac - icsData.createInjectionRangeActionsAndUpdateCracs(cracTemporalData, "Redispatching_RA", Map.of("BBE1AA1", 1.0), Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1")), 5., 5.); + icsData.createInjectionRangeActionsAndUpdateCracs(cracTemporalData, "Redispatching_RA", Map.of("BBE1AA1", icsData.getGeneratorIdFromRaIdAndNodeId("Redispatching_RA", "BBE1AA1")), 5., 5.); assertEquals(1, crac1.getInjectionRangeActions().size()); InjectionRangeAction ra1 = crac1.getInjectionRangeActions().iterator().next(); assertEquals("Redispatching_RA_RD", ra1.getId()); @@ -358,7 +359,7 @@ void testCreateInjectionRangeActionsAndUpdateCracsOnAGsk() throws IOException { getClass().getResourceAsStream("/glsk/gsk.csv"), generateOffsetDateTimeList(2)); - icsData.createInjectionRangeActionsAndUpdateCracs(cracTemporalData, "Redispatching_RA", icsData.getWeightPerNodePerGsk().get("GSK_NAME"), Map.of("BBE1AA1", "Redispatching_RA_BBE1AA1_GENERATOR", "FFR1AA1", "Redispatching_RA_FFR1AA1_GENERATOR"), 5., 5.); + icsData.createInjectionRangeActionsAndUpdateCracs(cracTemporalData, "Redispatching_RA", Map.of("BBE1AA1", "Redispatching_RA_BBE1AA1_GENERATOR", "FFR1AA1", "Redispatching_RA_FFR1AA1_GENERATOR"), 5., 5.); assertEquals(1, crac1.getInjectionRangeActions().size()); InjectionRangeAction ra1 = crac1.getInjectionRangeActions().iterator().next(); @@ -381,7 +382,7 @@ void testCreateInjectionRangeActionsAndUpdateCracsCurativeRedispatchingAction() getClass().getResourceAsStream("/ics/series.csv"), getClass().getResourceAsStream("/glsk/gsk.csv"), generateOffsetDateTimeList(2)); - icsData.createInjectionRangeActionsAndUpdateCracs(cracTemporalData, "Redispatching_RA", icsData.getWeightPerNodePerGsk().get("GSK_NAME"), Map.of("BBE1AA1", "Redispatching_RA_BBE1AA1_GENERATOR", "FFR1AA1", "Redispatching_RA_FFR1AA1_GENERATOR"), 5., 5.); + icsData.createInjectionRangeActionsAndUpdateCracs(cracTemporalData, "Redispatching_RA", Map.of("BBE1AA1", "Redispatching_RA_BBE1AA1_GENERATOR", "FFR1AA1", "Redispatching_RA_FFR1AA1_GENERATOR"), 5., 5.); assertEquals(1, crac1.getInjectionRangeActions().size()); InjectionRangeAction ra1 = crac1.getInjectionRangeActions().iterator().next(); assertEquals(1, ra1.getUsageRules().size()); @@ -493,6 +494,36 @@ void testProcessAllRedispatchingActionsWithLazyNetwork() throws IOException { } + @Test + void testExportToTimeCoupledConstraintJson() throws IOException { + IcsData icsData = IcsDataImporter.read( + getClass().getResourceAsStream("/ics/static_with_two_ra.csv"), + getClass().getResourceAsStream("/ics/series_with_two_ra.csv"), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList(2)); + + // Create Generator Constraints + Set generatorConstraintsSet = new HashSet<>(); + TimeCoupledConstraints timeCoupledConstraints = new TimeCoupledConstraints(); + + // Warning: in exemple we use a default generator id per node + icsData.getRedispatchingActions().forEach(raId -> generatorConstraintsSet.addAll(icsData.createGeneratorConstraints(raId, getDefaultGeneratorIdPerNode(raId)))); + generatorConstraintsSet.forEach(constraint -> timeCoupledConstraints.addGeneratorConstraints(constraint)); + + ByteArrayOutputStream expectedOutputStream = new ByteArrayOutputStream(); + Objects.requireNonNull(getClass().getResourceAsStream("/expected_time_coupled_constraints.json")) + .transferTo(expectedOutputStream); + + ByteArrayOutputStream actualOutputStream = new ByteArrayOutputStream(); + JsonTimeCoupledConstraints.write(timeCoupledConstraints, actualOutputStream); + + ObjectMapper mapper = new ObjectMapper(); + JsonNode expectedJson = mapper.readTree(expectedOutputStream.toString()); + JsonNode actualJson = mapper.readTree(actualOutputStream.toString()); + + assertEquals(expectedJson, actualJson); + } + private String getResourcePath(String resourcePath) { return "src/test/resources/" + resourcePath; } diff --git a/data/ics-importer/src/test/resources/expected_time_coupled_constraints.json b/data/ics-importer/src/test/resources/expected_time_coupled_constraints.json new file mode 100644 index 0000000000..ce0cd3fdd6 --- /dev/null +++ b/data/ics-importer/src/test/resources/expected_time_coupled_constraints.json @@ -0,0 +1,29 @@ +{ + "type" : "OpenRAO Time-Coupled Constraints", + "version" : "1.0", + "generatorConstraints" : [ { + "generatorId" : "Redispatching_RA_1_BBE1AA1_GENERATOR", + "leadTime" : 1.0, + "lagTime" : 1.0, + "upwardPowerGradient" : 12.0, + "downwardPowerGradient" : -12.0, + "shutDownAllowed" : true, + "startUpAllowed" : true + }, { + "generatorId" : "Redispatching_RA_1_FFR1AA1_GENERATOR", + "leadTime" : 1.0, + "lagTime" : 1.0, + "upwardPowerGradient" : 8.0, + "downwardPowerGradient" : -8.0, + "shutDownAllowed" : true, + "startUpAllowed" : true + }, { + "generatorId" : "Redispatching_RA_2_BBE1AA1_GENERATOR", + "leadTime" : 1.0, + "lagTime" : 1.0, + "upwardPowerGradient" : 30.0, + "downwardPowerGradient" : -40.0, + "shutDownAllowed" : true, + "startUpAllowed" : true + } ] +} \ No newline at end of file From e22078507ddc93c5088b64dc53b1ba152ff4e952 Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Wed, 1 Apr 2026 14:32:53 +0200 Subject: [PATCH 27/64] validate Signed-off-by: CHEN Roxane --- data/ics-importer/pom.xml | 2 +- .../java/com/powsybl/openrao/data/IcsDataImporter.java | 1 - .../main/java/com/powsybl/openrao/data/IcsUtil.java | 2 -- .../java/com/powsybl/openrao/data/IcsDataTest.java | 10 +++++----- tests/pom.xml | 6 ++++++ .../openrao/tests/steps/TimeCoupledRaoSteps.java | 1 - 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/data/ics-importer/pom.xml b/data/ics-importer/pom.xml index f52b355ba8..1d0f7081cb 100644 --- a/data/ics-importer/pom.xml +++ b/data/ics-importer/pom.xml @@ -6,7 +6,7 @@ com.powsybl open-rao-data - 7.2.0-SNAPSHOT + 7.3.0-SNAPSHOT ics-importer diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java index 463072dcfa..2866d664c7 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java @@ -25,7 +25,6 @@ import static com.powsybl.openrao.data.IcsUtil.OFFSET; import static com.powsybl.openrao.data.IcsUtil.P0; import static com.powsybl.openrao.data.IcsUtil.PREVENTIVE; -import static com.powsybl.openrao.data.IcsUtil.P_MIN_RD; import static com.powsybl.openrao.data.IcsUtil.RA_RD_ID; import static com.powsybl.openrao.data.IcsUtil.RDP_DOWN; import static com.powsybl.openrao.data.IcsUtil.RDP_UP; diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java index ea49e96785..b166c73f84 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java @@ -16,8 +16,6 @@ import java.util.Map; import java.util.Optional; -import static com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider.BUSINESS_WARNS; - /** * @author Roxane Chen {@literal } */ diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java index 98b0cfec83..60f952a516 100644 --- a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java @@ -430,7 +430,7 @@ void testIcsImporterWithGSK() throws IOException { @Test void testProcessAllRedispatchingActionsWithLazyNetwork() throws IOException { - String TMP_DIR = System.getProperty("java.io.tmpdir") + File.separator; + String tmpDir = System.getProperty("java.io.tmpdir") + File.separator; String networkFilePath1 = "2Nodes2ParallelLinesPST_0030.uct"; String networkFilePath2 = "2Nodes2ParallelLinesPST_0130.uct"; Network network1 = LazyNetwork.of(getResourcePath("/network/" + networkFilePath1)); @@ -448,11 +448,11 @@ void testProcessAllRedispatchingActionsWithLazyNetwork() throws IOException { getClass().getResourceAsStream("/glsk/gsk.csv"), generateOffsetDateTimeList(2)); - TimeCoupledRaoInput postIcsRaoInputs = icsData.processAllRedispatchingActions(timeCoupledRaoInput, 5., 4., TMP_DIR); + TimeCoupledRaoInput postIcsRaoInputs = icsData.processAllRedispatchingActions(timeCoupledRaoInput, 5., 4., tmpDir); - assertEquals(3,postIcsRaoInputs.getTimeCoupledConstraints().getGeneratorConstraints().size()); - assertEquals(2 ,postIcsRaoInputs.getRaoInputs().getData(timestamp1).get().getCrac().getInjectionRangeActions().size()); - assertEquals(2 ,postIcsRaoInputs.getRaoInputs().getData(timestamp2).get().getCrac().getInjectionRangeActions().size()); + assertEquals(3, postIcsRaoInputs.getTimeCoupledConstraints().getGeneratorConstraints().size()); + assertEquals(2, postIcsRaoInputs.getRaoInputs().getData(timestamp1).get().getCrac().getInjectionRangeActions().size()); + assertEquals(2, postIcsRaoInputs.getRaoInputs().getData(timestamp2).get().getCrac().getInjectionRangeActions().size()); assertEquals( Set.of("Redispatching_RA_1_RD", "Redispatching_RA_2_RD"), postIcsRaoInputs.getRaoInputs().getData(timestamp1).get().getCrac().getInjectionRangeActions() diff --git a/tests/pom.xml b/tests/pom.xml index a3946eb8bb..8e352c074d 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -324,6 +324,12 @@ commons-io runtime + + com.powsybl + ics-importer + 7.3.0-SNAPSHOT + test + \ No newline at end of file diff --git a/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java b/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java index 3062d23ca7..88f2620126 100644 --- a/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java +++ b/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java @@ -30,7 +30,6 @@ import com.powsybl.openrao.data.crac.api.rangeaction.RangeAction; import com.powsybl.openrao.data.crac.io.fbconstraint.FbConstraintCreationContext; import com.powsybl.openrao.data.crac.io.fbconstraint.parameters.FbConstraintCracCreationParameters; -import com.powsybl.openrao.data.crac.util.IcsImporter; import com.powsybl.openrao.data.raoresult.api.RaoResult; import com.powsybl.openrao.data.raoresult.api.TimeCoupledRaoResult; import com.powsybl.openrao.data.raoresult.io.idcc.core.F711Utils; From 6172958de287149700a391a47d16921a70cc3caf Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Wed, 1 Apr 2026 14:57:32 +0200 Subject: [PATCH 28/64] update pom Signed-off-by: CHEN Roxane --- data/crac/crac-util/pom.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/data/crac/crac-util/pom.xml b/data/crac/crac-util/pom.xml index 4352409016..e8a4b9a3b2 100644 --- a/data/crac/crac-util/pom.xml +++ b/data/crac/crac-util/pom.xml @@ -86,8 +86,7 @@ com.powsybl ics-importer - 7.2.0-SNAPSHOT - compile + test \ No newline at end of file From 597a5933646e71f8d33fca9950d58f42edf6ac11 Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Wed, 1 Apr 2026 15:05:17 +0200 Subject: [PATCH 29/64] update pom Signed-off-by: CHEN Roxane --- data/crac/crac-util/pom.xml | 12 +++++++----- data/ics-importer/pom.xml | 8 +------- tests/pom.xml | 12 ++++++------ 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/data/crac/crac-util/pom.xml b/data/crac/crac-util/pom.xml index e8a4b9a3b2..00038cba74 100644 --- a/data/crac/crac-util/pom.xml +++ b/data/crac/crac-util/pom.xml @@ -63,6 +63,12 @@ open-rao-crac-io-json test + + ${project.groupId} + open-rao-ics-importer + ${project.version} + test + com.powsybl powsybl-config-test @@ -83,10 +89,6 @@ assertj-core test - - com.powsybl - ics-importer - test - + \ No newline at end of file diff --git a/data/ics-importer/pom.xml b/data/ics-importer/pom.xml index 1d0f7081cb..2f842185e2 100644 --- a/data/ics-importer/pom.xml +++ b/data/ics-importer/pom.xml @@ -9,7 +9,7 @@ 7.3.0-SNAPSHOT - ics-importer + open-rao-ics-importer 21 @@ -70,12 +70,6 @@ assertj-core test - - org.junit.jupiter - junit-jupiter - test - - commons-io commons-io diff --git a/tests/pom.xml b/tests/pom.xml index 8e352c074d..921b6421af 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -221,6 +221,12 @@ ${project.version} test + + ${project.groupId} + open-rao-ics-importer + ${project.version} + test + ch.qos.logback logback-classic @@ -324,12 +330,6 @@ commons-io runtime - - com.powsybl - ics-importer - 7.3.0-SNAPSHOT - test - \ No newline at end of file From ff8ebd85ecb39ab6f4a4d96f57c2067a950bf9b8 Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Wed, 1 Apr 2026 15:10:45 +0200 Subject: [PATCH 30/64] remove check not equal to one Signed-off-by: CHEN Roxane --- .../powsybl/openrao/data/IcsDataImporter.java | 24 ------------------- .../openrao/data/IcsDataImporterTest.java | 17 ------------- 2 files changed, 41 deletions(-) diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java index 2866d664c7..2fd9bfe28a 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java @@ -148,14 +148,6 @@ private static boolean shouldBeImported(CSVRecord staticRecord, List> weightPerNodePerGsk) { - double sumOfGsk = 0.; - for (Map.Entry entry : weightPerNodePerGsk.get(gskId).entrySet()) { - sumOfGsk += entry.getValue(); - } - return Math.abs(sumOfGsk - 1.) < 1e-6; - } - /** * Determines whether the P0 record values respect the specified power gradients for each time interval. * It checks the difference in values between consecutive timestamps and ensures that the differences diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataImporterTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataImporterTest.java index b12e16ebad..022298d575 100644 --- a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataImporterTest.java +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataImporterTest.java @@ -329,21 +329,4 @@ void testMissingGradientInStaticCsv() throws IOException { assertEquals(0, icsData.getRedispatchingActions().size()); assertEquals("Redispatching action Redispatching_RA will not be imported because it does not respect power gradients : min/max/diff = -1000.0 / 1000.0 / 1964.0", logsList.get(0).getFormattedMessage()); } - - @Test - void testGskWeightSumNotEqualToOne() throws IOException { - String gsk = """ - GSK ID;Node;Weight - GSK_NAME;BBE1AA1;0.6 - GSK_NAME;FFR1AA1;0.5 - """; - IcsData icsData = IcsDataImporter.read( - getClass().getResourceAsStream("/ics/static_with_gsk.csv"), - getClass().getResourceAsStream("/ics/series.csv"), - new ByteArrayInputStream(gsk.getBytes(StandardCharsets.UTF_8)), - generateOffsetDateTimeList(24)); - - assertEquals(0, icsData.getRedispatchingActions().size()); - assertEquals("Redispatching action Redispatching_RA is ignored but it is defined on a GSK but sum of weights is not equal to 1", logsList.get(0).getFormattedMessage()); - } } From 9a70a80cb47a1fe5c8f3f2047691faa0458e4fc8 Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Wed, 1 Apr 2026 15:12:21 +0200 Subject: [PATCH 31/64] update file name Signed-off-by: CHEN Roxane --- ...voltagelevel.uct => 2Nodes2ParallelLinesPST_voltagelevel2.uct} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename data/ics-importer/src/test/resources/network/{2Nodes2ParallelLinesPST_voltagelevel.uct => 2Nodes2ParallelLinesPST_voltagelevel2.uct} (100%) diff --git a/data/ics-importer/src/test/resources/network/2Nodes2ParallelLinesPST_voltagelevel.uct b/data/ics-importer/src/test/resources/network/2Nodes2ParallelLinesPST_voltagelevel2.uct similarity index 100% rename from data/ics-importer/src/test/resources/network/2Nodes2ParallelLinesPST_voltagelevel.uct rename to data/ics-importer/src/test/resources/network/2Nodes2ParallelLinesPST_voltagelevel2.uct From 759b624b1e1c65b7e6376127735ad2fe84fc9124 Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Wed, 1 Apr 2026 16:48:48 +0200 Subject: [PATCH 32/64] move package Signed-off-by: CHEN Roxane --- .../data/{ => icsimporter}/IcsData.java | 4 +-- .../{ => icsimporter}/IcsDataImporter.java | 32 +++++++++---------- .../data/{ => icsimporter}/IcsUtil.java | 2 +- .../IcsDataImporterTest.java | 2 +- .../data/{ => icsimporter}/IcsDataTest.java | 8 ++--- .../data/{ => icsimporter}/IcsUtilTest.java | 2 +- .../tests/steps/TimeCoupledRaoSteps.java | 4 +-- 7 files changed, 27 insertions(+), 27 deletions(-) rename data/ics-importer/src/main/java/com/powsybl/openrao/data/{ => icsimporter}/IcsData.java (99%) rename data/ics-importer/src/main/java/com/powsybl/openrao/data/{ => icsimporter}/IcsDataImporter.java (91%) rename data/ics-importer/src/main/java/com/powsybl/openrao/data/{ => icsimporter}/IcsUtil.java (99%) rename data/ics-importer/src/test/java/com/powsybl/openrao/data/{ => icsimporter}/IcsDataImporterTest.java (99%) rename data/ics-importer/src/test/java/com/powsybl/openrao/data/{ => icsimporter}/IcsDataTest.java (98%) rename data/ics-importer/src/test/java/com/powsybl/openrao/data/{ => icsimporter}/IcsUtilTest.java (96%) diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsData.java similarity index 99% rename from data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java rename to data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsData.java index a9cdfadfb7..1af5e8350a 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsData.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsData.java @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package com.powsybl.openrao.data; +package com.powsybl.openrao.data.icsimporter; import com.powsybl.iidm.network.Bus; import com.powsybl.iidm.network.Network; @@ -27,7 +27,7 @@ import java.util.*; import static com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider.BUSINESS_WARNS; -import static com.powsybl.openrao.data.IcsUtil.*; +import static com.powsybl.openrao.data.icsimporter.IcsUtil.*; /** * @author Roxane Chen {@literal } diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java similarity index 91% rename from data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java rename to data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java index 2fd9bfe28a..26d1a3879a 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsDataImporter.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package com.powsybl.openrao.data; +package com.powsybl.openrao.data.icsimporter; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVRecord; @@ -17,21 +17,21 @@ import java.util.*; import static com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider.BUSINESS_WARNS; -import static com.powsybl.openrao.data.IcsUtil.*; -import static com.powsybl.openrao.data.IcsUtil.MAXIMUM_NEGATIVE_POWER_GRADIENT; -import static com.powsybl.openrao.data.IcsUtil.MAXIMUM_POSITIVE_POWER_GRADIENT; -import static com.powsybl.openrao.data.IcsUtil.MAX_GRADIENT; -import static com.powsybl.openrao.data.IcsUtil.NODE; -import static com.powsybl.openrao.data.IcsUtil.OFFSET; -import static com.powsybl.openrao.data.IcsUtil.P0; -import static com.powsybl.openrao.data.IcsUtil.PREVENTIVE; -import static com.powsybl.openrao.data.IcsUtil.RA_RD_ID; -import static com.powsybl.openrao.data.IcsUtil.RDP_DOWN; -import static com.powsybl.openrao.data.IcsUtil.RDP_UP; -import static com.powsybl.openrao.data.IcsUtil.RD_DESCRIPTION_MODE; -import static com.powsybl.openrao.data.IcsUtil.TRUE; -import static com.powsybl.openrao.data.IcsUtil.UCT_NODE_OR_GSK_ID; -import static com.powsybl.openrao.data.IcsUtil.parseDoubleWithPossibleCommas; +import static com.powsybl.openrao.data.icsimporter.IcsUtil.*; +import static com.powsybl.openrao.data.icsimporter.IcsUtil.MAXIMUM_NEGATIVE_POWER_GRADIENT; +import static com.powsybl.openrao.data.icsimporter.IcsUtil.MAXIMUM_POSITIVE_POWER_GRADIENT; +import static com.powsybl.openrao.data.icsimporter.IcsUtil.MAX_GRADIENT; +import static com.powsybl.openrao.data.icsimporter.IcsUtil.NODE; +import static com.powsybl.openrao.data.icsimporter.IcsUtil.OFFSET; +import static com.powsybl.openrao.data.icsimporter.IcsUtil.P0; +import static com.powsybl.openrao.data.icsimporter.IcsUtil.PREVENTIVE; +import static com.powsybl.openrao.data.icsimporter.IcsUtil.RA_RD_ID; +import static com.powsybl.openrao.data.icsimporter.IcsUtil.RDP_DOWN; +import static com.powsybl.openrao.data.icsimporter.IcsUtil.RDP_UP; +import static com.powsybl.openrao.data.icsimporter.IcsUtil.RD_DESCRIPTION_MODE; +import static com.powsybl.openrao.data.icsimporter.IcsUtil.TRUE; +import static com.powsybl.openrao.data.icsimporter.IcsUtil.UCT_NODE_OR_GSK_ID; +import static com.powsybl.openrao.data.icsimporter.IcsUtil.parseDoubleWithPossibleCommas; /** * @author Roxane Chen {@literal } diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsUtil.java similarity index 99% rename from data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java rename to data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsUtil.java index b166c73f84..06235d45db 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/IcsUtil.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsUtil.java @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package com.powsybl.openrao.data; +package com.powsybl.openrao.data.icsimporter; import com.powsybl.iidm.network.Bus; import com.powsybl.iidm.network.LoadType; diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataImporterTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataImporterTest.java similarity index 99% rename from data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataImporterTest.java rename to data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataImporterTest.java index 022298d575..e6a579521e 100644 --- a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataImporterTest.java +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataImporterTest.java @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package com.powsybl.openrao.data; +package com.powsybl.openrao.data.icsimporter; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.spi.ILoggingEvent; diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataTest.java similarity index 98% rename from data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java rename to data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataTest.java index 60f952a516..74a8e55cb5 100644 --- a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsDataTest.java +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataTest.java @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package com.powsybl.openrao.data; +package com.powsybl.openrao.data.icsimporter; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.spi.ILoggingEvent; @@ -43,9 +43,9 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static com.powsybl.openrao.data.IcsData.getDefaultGeneratorIdPerNode; -import static com.powsybl.openrao.data.IcsDataImporterTest.generateOffsetDateTimeList; -import static com.powsybl.openrao.data.IcsUtil.MAX_GRADIENT; +import static com.powsybl.openrao.data.icsimporter.IcsData.getDefaultGeneratorIdPerNode; +import static com.powsybl.openrao.data.icsimporter.IcsDataImporterTest.generateOffsetDateTimeList; +import static com.powsybl.openrao.data.icsimporter.IcsUtil.MAX_GRADIENT; import static org.junit.jupiter.api.Assertions.*; /** diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsUtilTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsUtilTest.java similarity index 96% rename from data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsUtilTest.java rename to data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsUtilTest.java index 77fa58fd90..11f8e29a18 100644 --- a/data/ics-importer/src/test/java/com/powsybl/openrao/data/IcsUtilTest.java +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsUtilTest.java @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package com.powsybl.openrao.data; +package com.powsybl.openrao.data.icsimporter; import com.powsybl.iidm.network.Network; import org.junit.jupiter.api.Test; diff --git a/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java b/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java index 88f2620126..29ebff3866 100644 --- a/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java +++ b/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java @@ -14,8 +14,8 @@ import com.powsybl.openrao.commons.TemporalData; import com.powsybl.openrao.commons.TemporalDataImpl; import com.powsybl.openrao.commons.Unit; -import com.powsybl.openrao.data.IcsData; -import com.powsybl.openrao.data.IcsDataImporter; +import com.powsybl.openrao.data.icsimporter.IcsData; +import com.powsybl.openrao.data.icsimporter.IcsDataImporter; import com.powsybl.openrao.data.crac.api.Crac; import com.powsybl.openrao.data.crac.api.CracCreationContext; import com.powsybl.openrao.data.crac.api.Instant; From d59fbf9a6c64d6927dfe20ff7b4dde8339a5ad85 Mon Sep 17 00:00:00 2001 From: Godelaine de Montmorillon Date: Thu, 2 Apr 2026 09:33:07 +0200 Subject: [PATCH 33/64] Release LazyNetwork --- .../openrao/data/crac/util/IcsImporter.java | 105 ++++++++++++------ .../powsybl/openrao/raoapi/LazyNetwork.java | 16 +++ .../tests/steps/TimeCoupledRaoSteps.java | 3 + 3 files changed, 93 insertions(+), 31 deletions(-) diff --git a/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java b/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java index f0166497af..1063d1bc4c 100644 --- a/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java +++ b/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java @@ -94,14 +94,19 @@ public static TimeCoupledRaoInput populateInputWithICS(TimeCoupledRaoInput timeC double icsCostUp, double icsCostDown, String exportDirectory) throws IOException { + BUSINESS_WARNS.warn("Beginning of populateInputWithIcs"); costUp = icsCostUp; costDown = icsCostDown; - TemporalData initialNetworks = new TemporalDataImpl<>(); + TemporalData initialNetworks = new TemporalDataImpl<>(); + // TODO : initialNetwork : stocker en LazyNetwork non chargés timeCoupledRaoInput.getRaoInputs().getDataPerTimestamp().forEach((dateTime, raoInput) -> { Network network = raoInput.getNetwork(); preProcessNetwork(network); - initialNetworks.put(dateTime, NetworkSerDe.copy(network)); // use a copy not to modify initial network + initialNetworks.put(dateTime, new LazyNetwork(network)); // use a copy not to modify initial network + if (network instanceof LazyNetwork) { + ((LazyNetwork) network).release(); + } }); CSVFormat csvFormat = CSVFormat.DEFAULT.builder() .setDelimiter(";") @@ -130,14 +135,14 @@ public static TimeCoupledRaoInput populateInputWithICS(TimeCoupledRaoInput timeC if (shouldBeImported(staticRecord, weightPerNodePerGsk)) { String raId = staticRecord.get(RA_RD_ID); Map seriesPerType = seriesPerIdAndType.get(raId); - if (seriesPerType != null && - seriesPerType.containsKey(P0) && - seriesPerType.containsKey(RDP_DOWN) && - seriesPerType.containsKey(RDP_UP) && - rangeIsOkay(seriesPerType, timeCoupledRaoInput.getTimestampsToRun().stream().sorted().toList()) && - p0RespectsGradients(staticRecord, seriesPerType.get(P0), timeCoupledRaoInput.getTimestampsToRun().stream().sorted().toList())) { - p0RespectsConstraints(staticRecord, seriesPerType, timeCoupledRaoInput.getTimestampsToRun().stream().sorted().toList()); - } +// if (seriesPerType != null && +// seriesPerType.containsKey(P0) && +// seriesPerType.containsKey(RDP_DOWN) && +// seriesPerType.containsKey(RDP_UP) && +// rangeIsOkay(seriesPerType, timeCoupledRaoInput.getTimestampsToRun().stream().sorted().toList()) && +// p0RespectsGradients(staticRecord, seriesPerType.get(P0), timeCoupledRaoInput.getTimestampsToRun().stream().sorted().toList())) { +// p0RespectsConstraints(staticRecord, seriesPerType, timeCoupledRaoInput.getTimestampsToRun().stream().sorted().toList()); +// } if (seriesPerType != null && seriesPerType.containsKey(P0) && seriesPerType.containsKey(RDP_DOWN) && @@ -161,7 +166,11 @@ public static TimeCoupledRaoInput populateInputWithICS(TimeCoupledRaoInput timeC postIcsRaoInputs.put(dateTime, RaoInput.build(new LazyNetwork(exportedNetworkPath), timeCoupledRaoInput.getRaoInputs().getData(dateTime).orElseThrow().getCrac()).build()); }); - return new TimeCoupledRaoInput(postIcsRaoInputs, timeCoupledRaoInput.getTimestampsToRun(), timeCoupledRaoInput.getTimeCoupledConstraints()); + TimeCoupledRaoInput output = new TimeCoupledRaoInput(postIcsRaoInputs, timeCoupledRaoInput.getTimestampsToRun(), timeCoupledRaoInput.getTimeCoupledConstraints()); + + BUSINESS_WARNS.warn("End of populateInputWithIcs"); + + return output; } private static void preProcessNetwork(Network network) { @@ -181,7 +190,7 @@ private static boolean safeDoubleEquals(double a, double b) { private static void importGskRedispatchingAction(TimeCoupledRaoInput timeCoupledRaoInput, CSVRecord staticRecord, - TemporalData initialNetworks, + TemporalData initialNetworks, Map seriesPerType, String raId, Map weightPerNode) { @@ -275,9 +284,9 @@ private static void importGskRedispatchActionForOneTimestamp(CSVRecord staticRec injectionRangeActionAdder.add(); } - private static void importNodeRedispatchingAction(TimeCoupledRaoInputWithNetworkPaths timeCoupledRaoInput, + private static void importNodeRedispatchingAction(TimeCoupledRaoInput timeCoupledRaoInput, CSVRecord staticRecord, - TemporalData initialNetworks, + TemporalData initialNetworks, Map seriesPerType, String raId) { String networkElementId = processNetworks(staticRecord.get(UCT_NODE_OR_GSK_ID), initialNetworks, seriesPerType, 1.); @@ -355,9 +364,9 @@ private static void importNodeRedispatchingActionForOneTimestamp(CSVRecord stati } private static String processNetworks(String - nodeId, TemporalData initialNetworks, Map seriesPerType, double shiftKey) { + nodeId, TemporalData initialNetworks, Map seriesPerType, double shiftKey) { String generatorId = seriesPerType.get(P0).get(RA_RD_ID) + "_" + nodeId + GENERATOR_SUFFIX; - for (Map.Entry entry : initialNetworks.getDataPerTimestamp().entrySet()) { + for (Map.Entry entry : initialNetworks.getDataPerTimestamp().entrySet()) { Bus bus = findBus(nodeId, entry.getValue()); if (bus == null) { BUSINESS_WARNS.warn("Redispatching action {} cannot be imported because bus {} could not be found", seriesPerType.get(P0).get("RA RD ID"), nodeId); @@ -366,6 +375,8 @@ private static String processNetworks(String Double p0 = parseDoubleWithPossibleCommas(seriesPerType.get(P0).get(entry.getKey().getHour() + OFFSET)) * shiftKey; Optional pMinRd = parseValue(seriesPerType, P_MIN_RD, entry.getKey(), shiftKey); processBus(bus, generatorId, p0, pMinRd.orElse(ON_POWER_THRESHOLD)); + // TODO release initial network + entry.getValue().release(); } return generatorId; } @@ -430,15 +441,28 @@ private static boolean shouldBeImported(CSVRecord } private static void p0RespectsConstraints(CSVRecord staticRecord, Map seriesRecord, List dateTimes) { - // 1) check that P0 > Pmin or P0 < 1 - // 2) check that if shutDown not allowed, no switch to 0 + // 1) check that P0 > Pmin or P0 < 1d + // 2) check that if shutDown not allowe, no switch to 0 // 3) check that if startUp not allowed, no switch from P0 < 1 to P0 > Pmin CSVRecord p0 = seriesRecord.get(P0); Boolean shutDownAllowed = Boolean.parseBoolean(staticRecord.get(SHUTDOWN_ALLOWED)); Boolean startUpAllowed = Boolean.parseBoolean(staticRecord.get(STARTUP_ALLOWED)); + Optional lead = Optional.empty(); + Optional lagAndLead = Optional.empty(); + if (!staticRecord.get(LEAD_TIME).isEmpty()) { + lead = Optional.of(parseDoubleWithPossibleCommas(staticRecord.get(LEAD_TIME))); + } + if (!staticRecord.get(LAG_TIME).isEmpty()) { + double lag = parseDoubleWithPossibleCommas(staticRecord.get(LAG_TIME)); + lagAndLead = lead.map(aDouble -> lag + aDouble).or(() -> Optional.of(lag)); + } Iterator dateTimeIterator = dateTimes.iterator(); OffsetDateTime currentDateTime = dateTimeIterator.next(); + boolean count_lead = false; + int lead_count = 0; + boolean count_lag = false; + int lag_count = 0; while (dateTimeIterator.hasNext()) { OffsetDateTime nextDateTime = dateTimeIterator.next(); double next_p0 = parseDoubleWithPossibleCommas(p0.get(nextDateTime.getHour() + OFFSET)); @@ -446,6 +470,33 @@ private static void p0RespectsConstraints(CSVRecord staticRecord, Map pMinRD = parseValue(seriesRecord, P_MIN_RD, currentDateTime, 1); double pMin = pMinRD.orElse(ON_POWER_THRESHOLD); + if (count_lead) { + if (current_p0 < pMin) { + if (lead_count < lead.get()) { + // DO NOT IMPORT + BUSINESS_WARNS.warn("RA {} was ON after start up for only {} altough lead is {}", staticRecord.get(0), count_lead, lead.get()); + count_lead = false; + lead_count = 0; + } + } else { + lead_count += 1; + } + } + + if (count_lag) { + if (current_p0 >= pMin) { + if (lag_count < lagAndLead.get()) { + // DO NOT IMPORT + BUSINESS_WARNS.warn("RA {} was OFF after shutDown for only {} altough lagAndLead is {}", staticRecord.get(0), count_lag, lagAndLead.get()); + count_lag = false; + lag_count = 0; + } + } else { + lag_count += 1; + } + } + + if (current_p0 < pMin && current_p0 > ON_POWER_THRESHOLD) { BUSINESS_WARNS.warn("RA {} has P0 at {} and Pmin at {}", staticRecord.get(0), current_p0, pMin); } @@ -455,27 +506,24 @@ private static void p0RespectsConstraints(CSVRecord staticRecord, Map // TODO : forcer le passage par Pmin pour le lead et le lag => une modification de P0 - if (!staticRecord.get(LEAD_TIME).isEmpty()) { - double lead = parseDoubleWithPossibleCommas(staticRecord.get(LEAD_TIME)); + if (lead.isPresent()) { BUSINESS_WARNS.warn("RA {} starting up at {}. TODO : check lead ({}) is respected", staticRecord.get(0), currentDateTime.getHour(), lead); + count_lead = true; } } if (current_p0 > pMin && next_p0 < pMin) { if (!shutDownAllowed) { BUSINESS_WARNS.warn("RA {} shutting down even though it's prohibited", staticRecord.get(0)); } - if (!staticRecord.get(LAG_TIME).isEmpty()) { - double lag = parseDoubleWithPossibleCommas(staticRecord.get(LAG_TIME)); - double lead = parseDoubleWithPossibleCommas(staticRecord.get(LEAD_TIME)); - BUSINESS_WARNS.warn("RA {} shutting down at {}. TODO : check lead ({}) + lag ({}) is respected", staticRecord.get(0), currentDateTime.getHour(), lead, lag); - + if (lagAndLead.isPresent()) { + BUSINESS_WARNS.warn("RA {} shutting down at {}. TODO : check lagAndLead ({}) is respected", staticRecord.get(0), currentDateTime.getHour(), lagAndLead); + count_lag = true; } } } } - private static boolean p0RespectsGradients(CSVRecord staticRecord, CSVRecord p0record, List dateTimes) { private static boolean p0RespectsGradients(CSVRecord staticRecord, CSVRecord p0record, List dateTimes) { double maxGradient = staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT).isEmpty() ? @@ -519,11 +567,6 @@ private static boolean rangeIsOkay(Map seriesPerType, List} */ + public class LazyNetwork implements Network { + private static final String TEMP_DIR = System.getProperty("java.io.tmpdir") + File.separator; private final String networkPath; private boolean isLoaded; private Network network; @@ -95,6 +99,13 @@ public LazyNetwork(String networkPath) { this.isLoaded = false; } + public LazyNetwork(Network network) { + String networkName = TEMP_DIR + UUID.randomUUID().toString() + ".xiidm"; + network.write("XIIDM", new Properties(), Path.of(networkName)); + this.networkPath = networkName; + this.isLoaded = false; + } + private void load() { if (!isLoaded) { network = Network.read(networkPath); @@ -102,6 +113,11 @@ private void load() { } } + public void release() { + network = null; + isLoaded = false; + } + @Override public Collection getSubnetworks() { load(); diff --git a/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java b/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java index 635954d176..5b209233f1 100644 --- a/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java +++ b/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java @@ -243,6 +243,9 @@ public static void loadDataForCoreTimeCoupledRao(DataTable arg1) throws IOExcept } else { cracCreationParameters.getExtension(FbConstraintCracCreationParameters.class).setTimestamp(offsetDateTime); cracImportResult = importCrac(cracFile, network, cracCreationParameters); + if (network instanceof LazyNetwork) { + ((LazyNetwork) network).release(); + } } RaoInput raoInput = RaoInput .build(network, cracImportResult.getLeft()) From a3bf06049ec94e3dc2240470267ab54b6a2410d0 Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Thu, 2 Apr 2026 10:14:23 +0200 Subject: [PATCH 34/64] sonar warning fix Signed-off-by: CHEN Roxane --- .../openrao/data/icsimporter/IcsDataImporter.java | 10 ++++------ .../com/powsybl/openrao/data/icsimporter/IcsUtil.java | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java index 26d1a3879a..2125de36d2 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java @@ -101,9 +101,7 @@ static Set filterOutInconsistentRedispatchingActions(Map parseStaticCsv(InputStream staticInputStream) throws IOException { Iterable staticCsvRecords = csvFormat.parse(new InputStreamReader(staticInputStream)); Map filteredStaticCsvRecords = new HashMap<>(); - staticCsvRecords.forEach(record -> { - filteredStaticCsvRecords.put(record.get(RA_RD_ID), record); - }); + staticCsvRecords.forEach(record -> filteredStaticCsvRecords.put(record.get(RA_RD_ID), record)); return filteredStaticCsvRecords; } @@ -111,9 +109,9 @@ static Map> parseSeriesCsv(InputStream seriesInpu 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); + seriesCsvRecords.forEach(csvRecord -> { + seriesPerIdAndType.putIfAbsent(csvRecord.get(RA_RD_ID), new HashMap<>()); + seriesPerIdAndType.get(csvRecord.get(RA_RD_ID)).put(csvRecord.get("Type of timeseries"), csvRecord); }); return seriesPerIdAndType; diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsUtil.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsUtil.java index 06235d45db..bf8847e01a 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsUtil.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsUtil.java @@ -69,7 +69,7 @@ static Optional parseValue(Map seriesPerType, String } static double parseDoubleWithPossibleCommas(String string) { - return Double.parseDouble(string.replaceAll(",", ".")); + return Double.parseDouble(string.replace(",", ".")); } // TODO: make this more robust (and less UCTE dependent) From a2b8fd98c42cf2b13fa0450deac0d4aa7e131c84 Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Thu, 2 Apr 2026 10:26:36 +0200 Subject: [PATCH 35/64] update pom Signed-off-by: CHEN Roxane --- pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pom.xml b/pom.xml index c4e12769e6..0067002a4d 100644 --- a/pom.xml +++ b/pom.xml @@ -447,6 +447,11 @@ open-rao-time-coupled-constraints ${project.version} + + ${project.groupId} + open-rao-ics-importer + ${project.version} + ${project.groupId} open-rao-util From e21dd1989cd1848aff4e218667494190dad5ae6c Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Thu, 2 Apr 2026 10:43:26 +0200 Subject: [PATCH 36/64] update pom Signed-off-by: CHEN Roxane --- data/ics-importer/pom.xml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/data/ics-importer/pom.xml b/data/ics-importer/pom.xml index 2f842185e2..5d9b103db1 100644 --- a/data/ics-importer/pom.xml +++ b/data/ics-importer/pom.xml @@ -10,12 +10,10 @@ open-rao-ics-importer + jar + ICS Data + Module for ICS data - - 21 - 21 - UTF-8 - com.powsybl From 25daed4c03d85eba6e26c888b84c9bf2601b91bd Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Thu, 2 Apr 2026 10:43:43 +0200 Subject: [PATCH 37/64] rename field Signed-off-by: CHEN Roxane --- .../openrao/data/icsimporter/IcsData.java | 8 ++++---- .../data/icsimporter/IcsDataImporter.java | 20 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsData.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsData.java index 1af5e8350a..ab350ef385 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsData.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsData.java @@ -37,7 +37,7 @@ public final class IcsData { private static Map> timeseriesPerIdAndType; private static Map> weightPerNodePerGsk; private static Map staticConstraintPerId; - private static Set consistentRedispatchingActions; + private static Set redispatchingActions; // TODO : either parametrize this or set it to true. May have to change the way it works to import for all curative instants instead of only the last one public static boolean importCurative = false; @@ -46,7 +46,7 @@ public IcsData(Set consistentRedispatchingActions, Map> timeseriesPerIdAndType, Map> weightPerNodePerGsk, Map staticConstraintPerId) { - this.consistentRedispatchingActions = consistentRedispatchingActions; + this.redispatchingActions = consistentRedispatchingActions; this.staticConstraintPerId = staticConstraintPerId; this.timeseriesPerIdAndType = timeseriesPerIdAndType; this.weightPerNodePerGsk = weightPerNodePerGsk; @@ -65,7 +65,7 @@ public Map> getWeightPerNodePerGsk() { } public Set getRedispatchingActions() { - return consistentRedispatchingActions; + return redispatchingActions; } public static String getGeneratorIdFromRaIdAndNodeId(String raId, String nodeId) { @@ -276,7 +276,7 @@ public TimeCoupledRaoInput processAllRedispatchingActions(TimeCoupledRaoInput ti }); // For each redispatching actions defined in static csv update networks and update cracs - consistentRedispatchingActions.forEach(raId -> { + redispatchingActions.forEach(raId -> { // Create generator and load in networks Map generatorIdPerNode = createGeneratorAndLoadAndUpdateNetworks(modifiedInitialNetworks, raId); diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java index 2125de36d2..aa6a420b24 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java @@ -75,27 +75,27 @@ public static IcsData read(InputStream staticInputStream, // Parse static CSV: remedial action’s generator’s static constraints. one line per RA_ID Map staticConstraintPerId = parseStaticCsv(staticInputStream); - Set consistentRAs = filterOutInconsistentRedispatchingActions(staticConstraintPerId, timeseriesPerIdAndType, weightPerNodePerGsk, sortedTimestampToRun); + Set consistentRAs = filterRedispatchingActions(staticConstraintPerId, timeseriesPerIdAndType, weightPerNodePerGsk, sortedTimestampToRun); return new IcsData(consistentRAs, timeseriesPerIdAndType, weightPerNodePerGsk, staticConstraintPerId); } - static Set filterOutInconsistentRedispatchingActions(Map staticConstraintPerId, - Map> timeseriesPerIdAndType, - Map> weightPerNodePerGsk, - List sortedTimestampToRun) { + static Set filterRedispatchingActions(Map staticConstraintPerId, + Map> timeseriesPerIdAndType, + Map> weightPerNodePerGsk, + List sortedTimestampToRun) { // Get a set of consistent redispatching action ID. - Set consistentRAs = new HashSet<>(); + Set RaToImport = new HashSet<>(); staticConstraintPerId.forEach((raId, record) -> { if (shouldBeImported(record, sortedTimestampToRun, weightPerNodePerGsk, timeseriesPerIdAndType)) { - consistentRAs.add(raId); + RaToImport.add(raId); } }); // Remove inconsistent RAs from the data structures - staticConstraintPerId.entrySet().removeIf(entry -> !consistentRAs.contains(entry.getKey())); - timeseriesPerIdAndType.entrySet().removeIf(entry -> !consistentRAs.contains(entry.getKey())); - return consistentRAs; + staticConstraintPerId.entrySet().removeIf(entry -> !RaToImport.contains(entry.getKey())); + timeseriesPerIdAndType.entrySet().removeIf(entry -> !RaToImport.contains(entry.getKey())); + return RaToImport; } static Map parseStaticCsv(InputStream staticInputStream) throws IOException { From d03be8fe6ede99bf5c62fb168366b22c3cb1d567 Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Thu, 2 Apr 2026 10:47:42 +0200 Subject: [PATCH 38/64] rename field Signed-off-by: CHEN Roxane --- .../openrao/data/icsimporter/IcsDataImporter.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java index aa6a420b24..6c5c91078e 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java @@ -86,16 +86,16 @@ static Set filterRedispatchingActions(Map staticConst Map> weightPerNodePerGsk, List sortedTimestampToRun) { // Get a set of consistent redispatching action ID. - Set RaToImport = new HashSet<>(); + Set raToImport = new HashSet<>(); staticConstraintPerId.forEach((raId, record) -> { if (shouldBeImported(record, sortedTimestampToRun, weightPerNodePerGsk, timeseriesPerIdAndType)) { - RaToImport.add(raId); + raToImport.add(raId); } }); // Remove inconsistent RAs from the data structures - staticConstraintPerId.entrySet().removeIf(entry -> !RaToImport.contains(entry.getKey())); - timeseriesPerIdAndType.entrySet().removeIf(entry -> !RaToImport.contains(entry.getKey())); - return RaToImport; + staticConstraintPerId.entrySet().removeIf(entry -> !raToImport.contains(entry.getKey())); + timeseriesPerIdAndType.entrySet().removeIf(entry -> !raToImport.contains(entry.getKey())); + return raToImport; } static Map parseStaticCsv(InputStream staticInputStream) throws IOException { From 442681cb89cbd052e1956ce6e6916148dd362ac7 Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Thu, 2 Apr 2026 11:19:05 +0200 Subject: [PATCH 39/64] update pom ? Signed-off-by: CHEN Roxane --- data/ics-importer/pom.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/data/ics-importer/pom.xml b/data/ics-importer/pom.xml index 5d9b103db1..c1c9a0bb1e 100644 --- a/data/ics-importer/pom.xml +++ b/data/ics-importer/pom.xml @@ -24,7 +24,6 @@ commons-csv - org.junit.jupiter @@ -35,7 +34,6 @@ ${project.groupId} open-rao-crac-impl ${project.version} - test-jar test From 3dcb4aa8d1f8d5bccdf8b90951ea1c1954d526ec Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Thu, 2 Apr 2026 13:21:58 +0200 Subject: [PATCH 40/64] update pom ? Signed-off-by: CHEN Roxane --- distribution/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/distribution/pom.xml b/distribution/pom.xml index e15e978e2f..68a5a5cbbf 100755 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -72,6 +72,11 @@ open-rao-time-coupled-constraints ${project.version} + + ${project.groupId} + open-rao-ics-importer + ${project.version} + ${project.groupId} open-rao-loopflow-computation From 9271ef093806013d8e84634d9f91a0c9aea74ea8 Mon Sep 17 00:00:00 2001 From: Thomas Bouquet Date: Thu, 2 Apr 2026 14:37:42 +0200 Subject: [PATCH 41/64] fix MARMOT with lazy networls Signed-off-by: Thomas Bouquet --- .../openrao/data/crac/util/IcsImporter.java | 14 ++- .../powsybl/openrao/raoapi/LazyNetwork.java | 5 +- .../openrao/searchtreerao/marmot/Marmot.java | 86 ++++++++++++------- .../searchtreerao/marmot/MarmotUtils.java | 1 + .../powsybl/openrao/tests/utils/Helpers.java | 5 +- .../features/4_time_coupled/US93_5.feature | 8 +- 6 files changed, 71 insertions(+), 48 deletions(-) diff --git a/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java b/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java index 1063d1bc4c..5bd7da16cc 100644 --- a/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java +++ b/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java @@ -10,7 +10,6 @@ import com.powsybl.iidm.network.Bus; import com.powsybl.iidm.network.LoadType; import com.powsybl.iidm.network.Network; -import com.powsybl.iidm.serde.NetworkSerDe; import com.powsybl.openrao.commons.OpenRaoException; import com.powsybl.openrao.commons.TemporalData; import com.powsybl.openrao.commons.TemporalDataImpl; @@ -94,12 +93,10 @@ public static TimeCoupledRaoInput populateInputWithICS(TimeCoupledRaoInput timeC double icsCostUp, double icsCostDown, String exportDirectory) throws IOException { - BUSINESS_WARNS.warn("Beginning of populateInputWithIcs"); costUp = icsCostUp; costDown = icsCostDown; TemporalData initialNetworks = new TemporalDataImpl<>(); - // TODO : initialNetwork : stocker en LazyNetwork non chargés timeCoupledRaoInput.getRaoInputs().getDataPerTimestamp().forEach((dateTime, raoInput) -> { Network network = raoInput.getNetwork(); preProcessNetwork(network); @@ -108,6 +105,7 @@ public static TimeCoupledRaoInput populateInputWithICS(TimeCoupledRaoInput timeC ((LazyNetwork) network).release(); } }); + CSVFormat csvFormat = CSVFormat.DEFAULT.builder() .setDelimiter(";") .setHeader() @@ -131,6 +129,7 @@ public static TimeCoupledRaoInput populateInputWithICS(TimeCoupledRaoInput timeC }); } + List sortedTimestamps = timeCoupledRaoInput.getTimestampsToRun().stream().sorted().toList(); staticCsvRecords.forEach(staticRecord -> { if (shouldBeImported(staticRecord, weightPerNodePerGsk)) { String raId = staticRecord.get(RA_RD_ID); @@ -147,8 +146,8 @@ public static TimeCoupledRaoInput populateInputWithICS(TimeCoupledRaoInput timeC seriesPerType.containsKey(P0) && seriesPerType.containsKey(RDP_DOWN) && seriesPerType.containsKey(RDP_UP) && - rangeIsOkay(seriesPerType, timeCoupledRaoInput.getTimestampsToRun().stream().sorted().toList()) && - p0RespectsGradients(staticRecord, seriesPerType.get(P0), timeCoupledRaoInput.getTimestampsToRun().stream().sorted().toList())) { + rangeIsOkay(seriesPerType, sortedTimestamps) && + p0RespectsGradients(staticRecord, seriesPerType.get(P0), sortedTimestamps)) { if (staticRecord.get(RD_DESCRIPTION_MODE).equalsIgnoreCase(NODE)) { importNodeRedispatchingAction(timeCoupledRaoInput, staticRecord, initialNetworks, seriesPerType, raId); } else { @@ -164,12 +163,11 @@ public static TimeCoupledRaoInput populateInputWithICS(TimeCoupledRaoInput timeC String exportedNetworkPath = exportDirectory + dateTime.format(DateTimeFormatter.ofPattern("%y%m%d_%H%M%S")) + ".jiidm"; initialNetwork.write("JIIDM", new Properties(), Path.of(exportedNetworkPath)); postIcsRaoInputs.put(dateTime, RaoInput.build(new LazyNetwork(exportedNetworkPath), timeCoupledRaoInput.getRaoInputs().getData(dateTime).orElseThrow().getCrac()).build()); + initialNetwork.release(); }); TimeCoupledRaoInput output = new TimeCoupledRaoInput(postIcsRaoInputs, timeCoupledRaoInput.getTimestampsToRun(), timeCoupledRaoInput.getTimeCoupledConstraints()); - BUSINESS_WARNS.warn("End of populateInputWithIcs"); - return output; } @@ -375,8 +373,6 @@ private static String processNetworks(String Double p0 = parseDoubleWithPossibleCommas(seriesPerType.get(P0).get(entry.getKey().getHour() + OFFSET)) * shiftKey; Optional pMinRd = parseValue(seriesPerType, P_MIN_RD, entry.getKey(), shiftKey); processBus(bus, generatorId, p0, pMinRd.orElse(ON_POWER_THRESHOLD)); - // TODO release initial network - entry.getValue().release(); } return generatorId; } diff --git a/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/LazyNetwork.java b/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/LazyNetwork.java index 82b76ff517..ddce5706d0 100644 --- a/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/LazyNetwork.java +++ b/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/LazyNetwork.java @@ -16,14 +16,14 @@ import com.powsybl.iidm.network.Area; import com.powsybl.iidm.network.AreaAdder; import com.powsybl.iidm.network.Battery; +import com.powsybl.iidm.network.BoundaryLine; +import com.powsybl.iidm.network.BoundaryLineFilter; import com.powsybl.iidm.network.Branch; import com.powsybl.iidm.network.BusbarSection; import com.powsybl.iidm.network.Component; import com.powsybl.iidm.network.Connectable; import com.powsybl.iidm.network.ContainerType; import com.powsybl.iidm.network.Country; -import com.powsybl.iidm.network.BoundaryLine; -import com.powsybl.iidm.network.BoundaryLineFilter; import com.powsybl.iidm.network.DcBus; import com.powsybl.iidm.network.DcConnectable; import com.powsybl.iidm.network.DcGround; @@ -114,6 +114,7 @@ private void load() { } public void release() { + // TODO: currently modifications on the network will not be saved -> perhaps override networkPath with a UUID and write content to it network = null; isLoaded = false; } 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 4b4c469daf..235da9bb8a 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 @@ -8,6 +8,7 @@ package com.powsybl.openrao.searchtreerao.marmot; import com.google.auto.service.AutoService; +import com.powsybl.iidm.network.Network; import com.powsybl.openrao.commons.TemporalData; import com.powsybl.openrao.commons.TemporalDataImpl; import com.powsybl.openrao.commons.Unit; @@ -18,6 +19,7 @@ import com.powsybl.openrao.data.crac.api.rangeaction.RangeAction; import com.powsybl.openrao.data.raoresult.api.RaoResult; import com.powsybl.openrao.data.raoresult.api.TimeCoupledRaoResult; +import com.powsybl.openrao.raoapi.LazyNetwork; import com.powsybl.openrao.raoapi.RaoInput; import com.powsybl.openrao.raoapi.TimeCoupledRaoInput; import com.powsybl.openrao.raoapi.TimeCoupledRaoProvider; @@ -62,6 +64,7 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; import static com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider.TECHNICAL_LOGS; import static com.powsybl.openrao.raoapi.parameters.extensions.SearchTreeRaoRangeActionsOptimizationParameters.RaRangeShrinking.ENABLED; @@ -130,11 +133,20 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl timeCoupledRaoInput.getRaoInputs().map(RaoInput::getCrac), topologicalOptimizationResults ); - applyPreventiveTopologicalActionsOnNetworks(timeCoupledRaoInput.getRaoInputs(), preventiveTopologicalActions); + + TemporalData postTopologicalActionsInputs = applyPreventiveTopologicalActionsOnNetworks(timeCoupledRaoInput.getRaoInputs(), preventiveTopologicalActions); + TemporalData inputsForMip = new TemporalDataImpl<>( + postTopologicalActionsInputs.getDataPerTimestamp() + .entrySet() + .stream() + .collect( + Collectors.toMap(Map.Entry::getKey, entry -> RaoInput.build(new LazyNetwork(entry.getValue().getNetwork()), entry.getValue().getCrac()).build()) + ) + ); // 6. Create and iteratively solve MIP to find optimal range actions' set-points // Get the curative actions applied in the individual results to be able to apply them during sensitivity computations - TemporalData curativeRemedialActions = MarmotUtils.getAppliedRemedialActionsInCurative(timeCoupledRaoInput.getRaoInputs(), topologicalOptimizationResults); + TemporalData curativeRemedialActions = MarmotUtils.getAppliedRemedialActionsInCurative(inputsForMip, topologicalOptimizationResults); TECHNICAL_LOGS.info("[MARMOT] ----- Global range actions optimization [start]"); // make fast rao result lighter by keeping only initial flow result and filtered rao result for actions @@ -147,15 +159,15 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl int counter = 1; do { // Clone the PostTopoScenario variant to make sure we work on a clean variant every time - timeCoupledRaoInput.getRaoInputs().getDataPerTimestamp().values().forEach(raoInput -> { - raoInput.getNetwork().getVariantManager().cloneVariant(POST_TOPO_SCENARIO, MIP_SCENARIO, true); + inputsForMip.getDataPerTimestamp().values().forEach(raoInput -> { + raoInput.getNetwork().getVariantManager().cloneVariant(raoInput.getNetwork().getVariantManager().getWorkingVariantId(), MIP_SCENARIO, true); raoInput.getNetwork().getVariantManager().setWorkingVariant(MIP_SCENARIO); }); // Run post topo sensitivity analysis on all timestamps ON CONSIDERED CNECS ONLY (which is why we do it every loop) TECHNICAL_LOGS.info("[MARMOT] Systematic time-coupled sensitivity analysis [start]"); TemporalData postTopoResults = runAllSensitivityAnalysesBasedOnInitialResult( - timeCoupledRaoInput.getRaoInputs(), + inputsForMip, curativeRemedialActions, initialResults, raoParameters, @@ -174,7 +186,7 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl // Create and iteratively solve MIP to find optimal range actions' set-points FOR THE CONSIDERED CNECS TECHNICAL_LOGS.info("[MARMOT] ----- Global range actions optimization [start] for iteration {}", counter); linearOptimizationResults = optimizeLinearRemedialActions( - timeCoupledRaoInput, + new TimeCoupledRaoInput(inputsForMip, timeCoupledRaoInput.getTimestampsToRun(), timeCoupledRaoInput.getTimeCoupledConstraints()), initialResults, initialSetpointResults, postTopoResults, @@ -187,7 +199,7 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl TECHNICAL_LOGS.info("[MARMOT] ----- Global range actions optimization [end] for iteration {}", counter); // Compute the flows on ALL the cnecs to check if the worst cnecs have changed and were considered in the MIP or not - loadFlowResults = applyActionsAndRunFullLoadflow(timeCoupledRaoInput.getRaoInputs(), curativeRemedialActions, linearOptimizationResults, initialResults, raoParameters); + loadFlowResults = applyActionsAndRunFullLoadflow(postTopologicalActionsInputs, curativeRemedialActions, linearOptimizationResults, initialResults, raoParameters); // Create a global result with the flows on ALL cnecs and the actions applied during MIP TemporalData rangeActionActivationResultTemporalData = linearOptimizationResults.getRangeActionActivationResultTemporalData(); @@ -208,7 +220,7 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl // 7. Merge topological and linear result TECHNICAL_LOGS.info("[MARMOT] Merging topological and linear remedial action results"); TimeCoupledRaoResultImpl timeCoupledRaoResult = mergeTopologicalAndLinearOptimizationResults( - timeCoupledRaoInput.getRaoInputs(), + postTopologicalActionsInputs, initialResults, initialObjectiveFunctionResult, fullResults, @@ -364,7 +376,8 @@ private static void logCnecs(List addedCnecsForLogging) { TECHNICAL_LOGS.info(logMessage.toString()); } - record LoggingAddedCnecs(OffsetDateTime offsetDateTime, String vcName, List addedCnecs, Map margins) { + record LoggingAddedCnecs(OffsetDateTime offsetDateTime, String vcName, List addedCnecs, + Map margins) { private void addCnec(String cnec) { addedCnecs.add(cnec); } @@ -375,27 +388,25 @@ private void addCnec(String cnec, double margin) { } } - private static TemporalData applyActionsAndRunFullLoadflow(TemporalData raoInputs, + private static TemporalData applyActionsAndRunFullLoadflow(TemporalData postTopoInputs, TemporalData curativeRemedialActions, LinearOptimizationResult filteredResult, TemporalData initialResults, RaoParameters raoParameters) { TemporalData prePerimeterResults = new TemporalDataImpl<>(); - raoInputs.getDataPerTimestamp().forEach((timestamp, raoInput) -> { - // duplicate the postTopoScenario variant and switch to the new clone - raoInput.getNetwork().getVariantManager().cloneVariant(POST_TOPO_SCENARIO, "PostPreventiveScenario", true); - raoInput.getNetwork().getVariantManager().setWorkingVariant("PostPreventiveScenario"); + postTopoInputs.getDataPerTimestamp().forEach((timestamp, raoInput) -> { State preventiveState = raoInput.getCrac().getPreventiveState(); raoInput.getCrac().getRangeActions(preventiveState).forEach(rangeAction -> rangeAction.apply(raoInput.getNetwork(), filteredResult.getOptimizedSetpoint(rangeAction, preventiveState)) ); prePerimeterResults.put(timestamp, runInitialPrePerimeterSensitivityAnalysisWithoutRangeActions( - raoInputs.getData(timestamp).orElseThrow(), + postTopoInputs.getData(timestamp).orElseThrow(), curativeRemedialActions.getData(timestamp).orElseThrow(), initialResults.getData(timestamp).orElseThrow(), raoParameters)); - // switch back to the postTopoScenario to avoid keeping applied range actions when entering the MIP - raoInput.getNetwork().getVariantManager().setWorkingVariant(POST_TOPO_SCENARIO); + if (raoInput.getNetwork() instanceof LazyNetwork lazyNetwork) { + lazyNetwork.release(); + } }); return prePerimeterResults; } @@ -417,16 +428,27 @@ private static TemporalData runTopologicalOptimization(TemporalData raoInputs, TemporalData preventiveTopologicalActionsResults) { + private static TemporalData applyPreventiveTopologicalActionsOnNetworks(TemporalData raoInputs, TemporalData preventiveTopologicalActionsResults) { + Map postTopologicalActionsInputs = new HashMap<>(); raoInputs.getTimestamps().forEach(timestamp -> { RaoInput raoInput = raoInputs.getData(timestamp).orElseThrow(); NetworkActionsResult networkActionsResult = preventiveTopologicalActionsResults.getData(timestamp).orElseThrow(); MarmotUtils.applyPreventiveRemedialActions(raoInput, networkActionsResult, INITIAL_SCENARIO, POST_TOPO_SCENARIO); + postTopologicalActionsInputs.put(timestamp, RaoInput.build(raoInput.getNetwork(), raoInput.getCrac()).build()); + if (raoInput.getNetwork() instanceof LazyNetwork lazyNetwork) { + lazyNetwork.release(); + } }); + return new TemporalDataImpl<>(postTopologicalActionsInputs); } private TemporalData buildInitialResults(TemporalData topologicalOptimizationResults) { @@ -449,7 +471,7 @@ private static TemporalData runAllSensitivityAnalysesBasedOn initialFlowResults.getData(timestamp).orElseThrow(), raoParameters, consideredCnecs.getData(timestamp).orElseThrow() - ))); + ))); return prePerimeterResults; } @@ -485,19 +507,19 @@ private static GlobalLinearOptimizationResult optimizeLinearRemedialActions(Time Map linearOptimizerInputPerTimestamp = new HashMap<>(); raoInput.getRaoInputs().getTimestamps().forEach(timestamp -> linearOptimizerInputPerTimestamp.put(timestamp, IteratingLinearOptimizerInput.create() - .withNetwork(raoInput.getRaoInputs().getData(timestamp).orElseThrow().getNetwork()) - .withOptimizationPerimeter(optimizationPerimeterPerTimestamp.getData(timestamp).orElseThrow() - .copyWithFilteredAvailableHvdcRangeAction(raoInput.getRaoInputs().getData(timestamp).orElseThrow().getNetwork())) - .withInitialFlowResult(initialResults.getData(timestamp).orElseThrow()) - .withPrePerimeterFlowResult(initialResults.getData(timestamp).orElseThrow()) - .withPreOptimizationFlowResult(postTopologicalActionsResults.getData(timestamp).orElseThrow()) - .withPrePerimeterSetpoints(initialSetpoints.getData(timestamp).orElseThrow()) - .withPreOptimizationSensitivityResult(postTopologicalActionsResults.getData(timestamp).orElseThrow()) - .withPreOptimizationAppliedRemedialActions(curativeRemedialActions.getData(timestamp).orElseThrow()) - .withToolProvider(ToolProvider.buildFromRaoInputAndParameters(raoInput.getRaoInputs().getData(timestamp).orElseThrow(), parameters)) - .withOutageInstant(raoInput.getRaoInputs().getData(timestamp).orElseThrow().getCrac().getOutageInstant()) - .withAppliedNetworkActionsInPrimaryState(preventiveTopologicalActions.getData(timestamp).orElseThrow()) - .build())); + .withNetwork(raoInput.getRaoInputs().getData(timestamp).orElseThrow().getNetwork()) + .withOptimizationPerimeter(optimizationPerimeterPerTimestamp.getData(timestamp).orElseThrow() + .copyWithFilteredAvailableHvdcRangeAction(raoInput.getRaoInputs().getData(timestamp).orElseThrow().getNetwork())) + .withInitialFlowResult(initialResults.getData(timestamp).orElseThrow()) + .withPrePerimeterFlowResult(initialResults.getData(timestamp).orElseThrow()) + .withPreOptimizationFlowResult(postTopologicalActionsResults.getData(timestamp).orElseThrow()) + .withPrePerimeterSetpoints(initialSetpoints.getData(timestamp).orElseThrow()) + .withPreOptimizationSensitivityResult(postTopologicalActionsResults.getData(timestamp).orElseThrow()) + .withPreOptimizationAppliedRemedialActions(curativeRemedialActions.getData(timestamp).orElseThrow()) + .withToolProvider(ToolProvider.buildFromRaoInputAndParameters(raoInput.getRaoInputs().getData(timestamp).orElseThrow(), parameters)) + .withOutageInstant(raoInput.getRaoInputs().getData(timestamp).orElseThrow().getCrac().getOutageInstant()) + .withAppliedNetworkActionsInPrimaryState(preventiveTopologicalActions.getData(timestamp).orElseThrow()) + .build())); TimeCoupledIteratingLinearOptimizerInput timeCoupledLinearOptimizerInput = new TimeCoupledIteratingLinearOptimizerInput( new TemporalDataImpl<>(linearOptimizerInputPerTimestamp), objectiveFunction, raoInput.getTimeCoupledConstraints()); diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java index ad3a7e97d1..d31799e65f 100644 --- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java @@ -153,6 +153,7 @@ public static void applyPreventiveRemedialActions(RaoInput raoInput, NetworkActi Crac crac = raoInput.getCrac(); State preventiveState = crac.getPreventiveState(); + network.getVariantManager().cloneVariant(network.getVariantManager().getWorkingVariantId(), initialVariantId); network.getVariantManager().setWorkingVariant(initialVariantId); network.getVariantManager().cloneVariant(initialVariantId, newVariantId); network.getVariantManager().setWorkingVariant(newVariantId); diff --git a/tests/src/test/java/com/powsybl/openrao/tests/utils/Helpers.java b/tests/src/test/java/com/powsybl/openrao/tests/utils/Helpers.java index a2769bbd66..c82fcc75ac 100644 --- a/tests/src/test/java/com/powsybl/openrao/tests/utils/Helpers.java +++ b/tests/src/test/java/com/powsybl/openrao/tests/utils/Helpers.java @@ -26,6 +26,7 @@ import com.powsybl.openrao.data.raoresult.api.RaoResult; import com.powsybl.openrao.data.refprog.referenceprogram.ReferenceProgram; import com.powsybl.openrao.data.refprog.refprogxmlimporter.RefProgImporter; +import com.powsybl.openrao.raoapi.LazyNetwork; import com.powsybl.openrao.tests.steps.CommonTestData; import com.powsybl.openrao.tests.utils.roundtripcrac.RoundTripCimCracCreationContext; import com.powsybl.openrao.tests.utils.roundtripcrac.RoundTripCseCracCreationContext; @@ -68,7 +69,9 @@ public static Network importNetwork(File networkFile, boolean useRdfId) { if (useRdfId) { importParams.put("iidm.import.cgmes.source-for-iidm-id", "rdfID"); } - return Network.read(Paths.get(networkFile.toString()), LocalComputationManager.getDefault(), Suppliers.memoize(ImportConfig::load).get(), importParams); + Network network = Network.read(Paths.get(networkFile.toString()), LocalComputationManager.getDefault(), Suppliers.memoize(ImportConfig::load).get(), importParams); + LazyNetwork lazyNetwork = new LazyNetwork(network); + return lazyNetwork; } public static Pair importCrac(File cracFile, Network network, CracCreationParameters cracCreationParameters) throws IOException { diff --git a/tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature b/tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature index 0743fb1138..557a299df3 100644 --- a/tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature +++ b/tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature @@ -132,10 +132,10 @@ Feature: US 93.3: CORE IDCC data Scenario: US 93.3.4: 20240224 Given network files are in folder "idcc/20240224-FID2-734-v1-10V1001C--00264T-to-10V1001C--00085T" Given crac file is "idcc/20240224-FSC-ID2-CB-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given ics static file is "_110V1001C--00200I_CSA-COMRA-RDSTATIC-D_CORE-20240224-V001_.csv" - Given ics series file is "_110V1001C--00200I_CSA-COMRA-RDSERIES-D_CORE-20240224-V001_.csv" - Given ics gsk file is "_10V1001C--00200I_CSA-INDRA-GSK-D_D7-20240224-V001_.csv" - Given configuration file is "idcc/RaoParameters_minCost_megawatt_dc.json" + Given ics static file is "idcc/_110V1001C--00200I_CSA-COMRA-RDSTATIC-D_CORE-20240224-V001_.csv" + Given ics series file is "idcc/_110V1001C--00200I_CSA-COMRA-RDSERIES-D_CORE-20240224-V001_.csv" + Given ics gsk file is "idcc/_10V1001C--00200I_CSA-INDRA-GSK-D_D7-20240224-V001_.csv" + Given configuration file is "idcc/RAOParameters_minCost_megawatt_dc.json" Given time-coupled RefProg file is "idcc/20240224-FID2-632-v3-10V1001C--00264T-to-10V1001C--00085T.xml" Given time-coupled rao inputs for CORE are: | Timestamp | Network | From feb43c872c7c3af80c7b15d19a9d3c2cbc007b7c Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Thu, 2 Apr 2026 15:00:42 +0200 Subject: [PATCH 42/64] fix Signed-off-by: CHEN Roxane --- .../powsybl/openrao/data/icsimporter/IcsDataImporterTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataImporterTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataImporterTest.java index e6a579521e..363c6b5e13 100644 --- a/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataImporterTest.java +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataImporterTest.java @@ -295,7 +295,7 @@ private static Stream gradientNotRespectCsvCases() { @MethodSource("gradientNotRespectCsvCases") @MethodSource("rangeIsNotOkayCases") @MethodSource("seriesCsvWithMissingSeriesTypeCases") - void testP0RespectsGradients(String seriesCsv, String expectedLogMessage) throws IOException { + void testSeriesCsv(String seriesCsv, String expectedLogMessage) throws IOException { IcsData icsData = IcsDataImporter.read( getClass().getResourceAsStream("/ics/static.csv"), new ByteArrayInputStream(seriesCsv.getBytes(StandardCharsets.UTF_8)), From 5727f6eb9c5f11ec81328a1201579f4f59a7bfdf Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Thu, 2 Apr 2026 15:30:02 +0200 Subject: [PATCH 43/64] add a test Signed-off-by: CHEN Roxane --- .../openrao/data/icsimporter/IcsDataTest.java | 35 +++++++++++++++++++ ....csv => static_with_two_ra_wrong_node.csv} | 3 +- 2 files changed, 37 insertions(+), 1 deletion(-) rename data/ics-importer/src/test/resources/ics/{static_no_gradient_no_lead_no_lag.csv => static_with_two_ra_wrong_node.csv} (59%) diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataTest.java index 74a8e55cb5..3293ad4ebd 100644 --- a/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataTest.java +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataTest.java @@ -494,6 +494,41 @@ void testProcessAllRedispatchingActionsWithLazyNetwork() throws IOException { } + + @Test + void testProcessAllRedispatchingActionsMissingBusForOneRa() throws IOException { + + String tmpDir = System.getProperty("java.io.tmpdir") + File.separator; + String networkFilePath1 = "2Nodes2ParallelLinesPST_0030.uct"; + String networkFilePath2 = "2Nodes2ParallelLinesPST_0130.uct"; + Network network1 = LazyNetwork.of(getResourcePath("/network/" + networkFilePath1)); + Network network2 = LazyNetwork.of(getResourcePath("/network/" + networkFilePath2)); + TemporalData raoInputs = new TemporalDataImpl<>( + Map.of( + timestamp1, RaoInput.build(network1, crac1).build(), + timestamp2, RaoInput.build(network2, crac2).build() + )); + + TimeCoupledRaoInput timeCoupledRaoInput = new TimeCoupledRaoInput(raoInputs, new TimeCoupledConstraints()); + IcsData icsData = IcsDataImporter.read( + getClass().getResourceAsStream("/ics/static_with_two_ra_wrong_node.csv"), + getClass().getResourceAsStream("/ics/series_with_two_ra.csv"), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList(2)); + + TimeCoupledRaoInput postIcsRaoInputs = icsData.processAllRedispatchingActions(timeCoupledRaoInput, 5., 4., tmpDir); + assertEquals(2, postIcsRaoInputs.getTimeCoupledConstraints().getGeneratorConstraints().size()); + assertEquals(1, postIcsRaoInputs.getRaoInputs().getData(timestamp1).get().getCrac().getInjectionRangeActions().size()); + assertEquals(1, postIcsRaoInputs.getRaoInputs().getData(timestamp2).get().getCrac().getInjectionRangeActions().size()); + assertEquals( + Set.of("Redispatching_RA_1_RD"), + postIcsRaoInputs.getRaoInputs().getData(timestamp1).get().getCrac().getInjectionRangeActions() + .stream() + .map(InjectionRangeAction::getId) + .collect(Collectors.toSet()) + ); + } + @Test void testExportToTimeCoupledConstraintJson() throws IOException { IcsData icsData = IcsDataImporter.read( diff --git a/data/ics-importer/src/test/resources/ics/static_no_gradient_no_lead_no_lag.csv b/data/ics-importer/src/test/resources/ics/static_with_two_ra_wrong_node.csv similarity index 59% rename from data/ics-importer/src/test/resources/ics/static_no_gradient_no_lead_no_lag.csv rename to data/ics-importer/src/test/resources/ics/static_with_two_ra_wrong_node.csv index 64878d83f0..44f8c976bf 100644 --- a/data/ics-importer/src/test/resources/ics/static_no_gradient_no_lead_no_lag.csv +++ b/data/ics-importer/src/test/resources/ics/static_with_two_ra_wrong_node.csv @@ -1,2 +1,3 @@ RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed -Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;;;;;FALSE;FALSE \ No newline at end of file +Redispatching_RA_1;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;GSK;GSK_NAME;50;Coal;2;2;20;20;1;1;TRUE;TRUE +Redispatching_RA_2;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;WRONG_NODE;50;Coal;2;2;30;40;1;1;TRUE;TRUE From 557b51b8dce83196dc336c850ec885c9dc7cd516 Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Thu, 2 Apr 2026 15:30:12 +0200 Subject: [PATCH 44/64] remove file not used Signed-off-by: CHEN Roxane --- .../src/test/resources/ics/series_gradient_not_ok.csv | 4 ---- .../ics-importer/src/test/resources/ics/series_no_pmin_rd.csv | 4 ---- 2 files changed, 8 deletions(-) delete mode 100644 data/ics-importer/src/test/resources/ics/series_gradient_not_ok.csv delete mode 100644 data/ics-importer/src/test/resources/ics/series_no_pmin_rd.csv diff --git a/data/ics-importer/src/test/resources/ics/series_gradient_not_ok.csv b/data/ics-importer/src/test/resources/ics/series_gradient_not_ok.csv deleted file mode 100644 index 9b369bf679..0000000000 --- a/data/ics-importer/src/test/resources/ics/series_gradient_not_ok.csv +++ /dev/null @@ -1,4 +0,0 @@ -RA RD ID;Type of timeseries;00:30;01:30;02:30;03:30;04:30;05:30;06:30;07:30;08:30;09:30;10:30;11:30;12:30;13:30;14:30;15:30;16:30;17:30;18:30;19:30;20:30;21:30;22:30;23:30 -Redispatching_RA;RDP-;35;35;36;35;34;32;28;27;25;9;8;9;8;8;8;8;23;25;30;31;34;31;29;39 -Redispatching_RA;RDP+;43;43;41;42;43;45;50;51;53;68;70;69;70;70;70;69;54;52;48;47;44;46;49;39 -Redispatching_RA;P0;116;130;117;116;115;113;109;108;106;90;89;90;89;89;89;89;104;106;111;112;115;112;110;120 \ No newline at end of file diff --git a/data/ics-importer/src/test/resources/ics/series_no_pmin_rd.csv b/data/ics-importer/src/test/resources/ics/series_no_pmin_rd.csv deleted file mode 100644 index 07530afeba..0000000000 --- a/data/ics-importer/src/test/resources/ics/series_no_pmin_rd.csv +++ /dev/null @@ -1,4 +0,0 @@ -RA RD ID;Type of timeseries;00:30;01:30;02:30;03:30;04:30;05:30;06:30;07:30;08:30;09:30;10:30;11:30;12:30;13:30;14:30;15:30;16:30;17:30;18:30;19:30;20:30;21:30;22:30;23:30 -Redispatching_RA;RDP-;35;35;36;35;34;32;28;27;25;9;8;9;8;8;8;8;23;25;30;31;34;31;29;39 -Redispatching_RA;RDP+;43;43;41;42;43;45;50;51;53;68;70;69;70;70;70;69;54;52;48;47;44;46;49;39 -Redispatching_RA;P0;116;120;117;116;115;113;109;108;106;90;89;90;89;89;89;89;104;106;111;112;115;112;110;120 From 7c9e7e24077623ea3779c9e7f9862a006b3185b9 Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Thu, 2 Apr 2026 15:32:19 +0200 Subject: [PATCH 45/64] validate Signed-off-by: CHEN Roxane --- .../java/com/powsybl/openrao/data/icsimporter/IcsDataTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataTest.java index 3293ad4ebd..b19aa6d185 100644 --- a/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataTest.java +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataTest.java @@ -494,7 +494,6 @@ void testProcessAllRedispatchingActionsWithLazyNetwork() throws IOException { } - @Test void testProcessAllRedispatchingActionsMissingBusForOneRa() throws IOException { From 47a8a14a6f32b7656fc5d5e55f48e434d166a946 Mon Sep 17 00:00:00 2001 From: CHEN Roxane Date: Thu, 2 Apr 2026 15:56:56 +0200 Subject: [PATCH 46/64] update doc Signed-off-by: CHEN Roxane --- docs/_static/img/icsdata.png | Bin 0 -> 39042 bytes docs/input-data/specific-input-data/ics.md | 23 ++++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 docs/_static/img/icsdata.png diff --git a/docs/_static/img/icsdata.png b/docs/_static/img/icsdata.png new file mode 100644 index 0000000000000000000000000000000000000000..86aa78f64ceedafde777a77aa633b318619c93db GIT binary patch literal 39042 zcmeFY1z1(xx<4wg5R`6^1_|kIkPbmgy1QX5x?4(Vr9nVaX-R1Wqy(gqmhO_yJD1}A z+~4Qf=lt*a?{oIP&t3Sybj&%%c<0-{cf2c1QCL3eRP3U_ zm7JxWvm5Z&jfYF0i{k-sB?5uin5vr^$yz!=pJ(T0V&{TRk(E|?EJwvA23*@%TAKoY zq)d&i!O$sU77k!rU__FYgN=y=`U4C;F*GxDu>5ri2@~_be+EjWi>ZU7CD`uAo3ZgQ zu`zM~Am7p0(8lz~Fzb&I;AKrr9exY}JFro)OHi?h0Bb=1v5T2l8k##80x!>EY4YPC zadT;D7fmp`F{_LSgwb4C+C)_G$5M8N0KMOtsR*!%@%sk9uJ&_HTQ?~OLx{y=u!*S+ zu&{~S4M7}i08gOgo4EfN;pX7@F<|cSbN3snp#x7WJ$}B*cf#3N%$+SwOdW4l`!UlA z47PEyg#7)WG1$(|)c8h>H!B)CIDlRM{xLJK&CNUCJOcq<>&NOh8$t)3{Dq~^QFTib zCyO7?^03`7A382=YH4oqb7xjA-XB!j8vcCvW?@GQLldy;FMIu!k3YHT00sl={UWp< zH-7yMRd_K|8z{4X^6_RLKz07l=l+cw4u7+S|ITu1sv@io@{V9tBR3fnRs|1N8!j%! z8^*X8+Bn~^{0FBTo!oyaumjlH&cqZdA*k88T39-nK7kk-Lmzhq^a~iXaI&?zxov1; zX%4jq8&flYTcUs?un_~>fPW}HyBR0+2N-vBasXSK{&I)w`v*D$Z0GdLIQ#bxF#dxq zz~G!rf9mm%A&B80?D@{Ie=MV);slJt6tH_I2X{bPH!Cu-a@;WZ#xQ>V*YyuAV`IH} z#NrnXV`I4)G`!Jl^S`YPwX^?<^nqt?l;JP!Him6Bd#K@aExq=<6|9fJFO6kdsia@pEPjWV8HQ_Yn{+*n8xwzR4x&9{4 zzXXv$;u zTLE$z^Ozc${Z9#y{iiwo$6ocfR`@UIRd4d7U%cuQCm@G4{_lzLKQTljLt`Ej_TL#I z8#@OFClHx$qT@f=5ZV6=-t^}q|JvS^^DlNN^+ficD?=7jc2j8F`%C7-V#>q9!}Gr- zLsp*OIMaU*A>M$PUv1IU!4kkF|IDCh#A;$<1`U0Gks~V$H#;wo6#wto;%|fEzlR(F zH2>$^Mn>!j5Z}cOodDGTrwj+k4WYo|H;}^^KuJ)9EeZt*mH=85xxpf~mL?`pF!NjN z@&~$sO4R`jb?4s^JtHr4;O|}YcVhn8UY7I5B5$H4E8C5?`X$}@UCzpWGwpvRXZ|zIYur&lwD`2iapZQ6WsV&3_K!tzX z`p2R_$o*yb@3#Mwmj0nt{{_ta_d50$FlEMLZ2J2=+=z#h1JIVAF6W;Oj(@t7pNjdr z(D6IX{F(m!>u4q`ATFrHeu*7F1#9$M^#SUY&>99*aU9HzXgL9w1+69i@%O=ZDX>7j z6XQ)*4t@Bh3<4~EqlduNcePisv<1o^&Oo)&MA;B5V+V2mRoMX|eyZD_6zi||887>}+hT9PFH&99%rSe-b?v8<59CKi@U>yA}MC#m4=o zfc7&|{SwfALi%4}!_TpQA-8c;VE#L|@h1cQDKOU0lI?e8 zv;RAZMTKil`eZ`Q{4*XrBQ(lpNjl{1;Dym0>|8JEC3d9017B9Kr6)$hz+3XzmfC53~K+& zCxri8Q2S@^_!GYV5+#3oH1RW<{_EtXKYKdT(+m)M>d5Y%r5aW``Yq( zosU&iuK1!Pd2Xs#%bfdG5|*E*WkTfLAfPUH`rLgzGZuVeY_}<-*x+9qTIZ{ zBnS!b>K>=naLG)A=e5;r<7LTmZ_2gRM8z9WOY?mqvZhY<{fK(X>rS!#0XzOIV(wIz zyvr`QegAs0hn-jiAtppzI0&80`{7=cJDm^rqrEsxU7eo7T&Fdi!j^OEiRRroFZGzU z74SJEwl|gtw64O=*Q7&AR_}?Zs@#_;bwIX|nt0)o<85g^k@9_II13YX358bzVVdP; znT{)c>s|{VQN$ze;lSU;7S584_*|gQ9FETlnKD&BSsj2%>487+t)ey=}xNC4N$xiT;6AdOl)WGRB+?mYj$< zPS8?)BEcoM!P0diU4V?V6A7;1UDten`ooUyIQqD(I?rRf`_j?3CYW`cwkB{!3e=?< zy-uUqOdqv{V!(ZE4GGB#u6_(l9`Ozl^egx7Gj}QMzkK+qQ+t1m0vnGCS=7%Z`!9R# z2MVy}i$1VPLj=7T{p0neg<=yHY1|(*##S$uu&N-vRPQEe$ov+$uo}10nZoeGBn3j^Q5DDh0H1l%p){7q;(omO`%i1bXFDbPcpg7o>C7o^b7E9r>%@71w#WE~VC>f z1(`x89=~O6RPR*1JG;lxW>_@2*lX}?BdvB-VRF|YYq_)dvv`-_TjmA;lM)1|)4Qzs z16$sv$a)UgRgi8;dkz+>P17pP6r+>rcilX(kr6OgGIV~jB!#T+2D>A4E!wM`6eD1G zkTFWNw0R2#U?{nMC*ghvYVy{XwmR1xqm$jQa}(Ly({))+YlBu@0p_>gqk{^OVT1zi zM|GL+Ctpza7Znn??&I7=vKQw90H5mu7OGS%R4CdIHHqu zJy~jrD`VK17CBEJzk0FbGVE1gNw+0>{#m{PZ_A9c`}yktzlVs?{Wtfou=ip&3x;H+ zOb6eVcP3B~u;qrYnQtm$>GcDxoUmqzbvz_E}TKz0i^rdptC3Ggh z&leF=MC>l#_~Q6#DdAvF%LD-%zmmD z%}$r^Lv~rCQZixw&m(N~!6a9}O1?iBM~?qf1kurT^d zfh!8(js!1?l8as&Q-bko_ zpF*5e0Pls2X|nubirN%#{rO5+!8TH6X4=w+@&30+S>HV@)i3qc2v(~qg14uhX-hkq zbg|BH4)D81EF~@de)B}-!_e9ijY(@T3gh|i*;D1x_AfpXQymEcl(U7EmCx~*CFAH+ z7jrnRIWtymF+nHFch2F@LuBaCGxgBP`r1-zJY}K@MVccqpoAJ+@7}qskmtJ6bq-#2#6!_Vs;H2t>vQgHi26+;mid;Z<7dCeKEi^TmrV_; zPA8yJLN65qjWdrw99O94*5_@|F(%+sp>2y=S`^gXhKI?b6n+&0UQ8PZN>QAHx+k;V zdoLe6`S`Rl)Iy}W7Q^7$;Gv;!*>=1}?onIZ38s0^uwn%!=u_0KbAfk}CHg~Sg5jYPa9 z06v0NRs0UDh(z8wUo?r0A2-u>dXv04s1TX_FDDZk67mqC}3>U9fobtULS zyc8coP;#EXC?_*;0DX^7?Cz$L`35^P4V@|6C7YuLAO?x2Ta?Y!`Cs(M!8T2mEn}kd zGCE=4-4Qy@JFvLnexz@IY_|H2cgEuYx9qjV=yZ=JIwBXSlYgIioQ@-d;!5jN5_=vD zW_CWAfj%OXvT^l-$CFyXLXWkdOXGoT0OiCxBGi3Ov?J&pg|9@py3KgzwfRXCnTVZa zB&Axiix1^~B0k)_Tzy#GgHVx~7~kAzTe&0}g;(NJHTH}?j5~qW3Z;$%N(=Gz)tAW2`(C-WT<9;N#&CJ6XY3KNCV*dOxexj*Sb+kfg6V=w*D(Ad$h zo}#I<+>a#%0O#ez`Z4Cb^{CXy?6oiER0_ z3GCMq6fcOc7dHAk)rAP>udxseKWMx7ay`nDkS?XXp-OV*G!Jx+avtS-dVks%6iT^w z@G#Ha;U<-dW?mi;x2iM+-O$y^e?8IT(|XnF6`9`TBbE4>+|4$|KWemJulDI})koUd zK1Z*y_wtA>(2OgSfJ82wsvq0ogPHh`R2;cft8EfU%fvs4S;_cCzK$L1Y}#E3M8Vf69Ux2 zZ@EidosNNyNRKFc4qdJ*<}Q}#rOy>&AG$iQ(ZE)~Z5hfF2h?o?mYJV09#A*qH+sBz zgwz%aPAdNlV;i_wE)O-2{)bzqn0L-&p!{|Pg_7DI2!^;n>4e*Fsn?R;Itu-Jjn`z>ZkOd|EIE!5%v%_8BS}s79tUbKlzgvCJj%IFCw3nMSSm27Q3+?2xIIXD|bm%#(cg*5}t zj=@E+i`+IWOFZ&<_86VnV;BMV-- zFzV3|zb)&F({JWaQ3ywm2G&R}qi0gz+kJVW~&s0IkDPH%^!*74q=oMKUM%eFdx0WWzpHchs7TGa2{o{Nj|I zhleM__8ThQVpo!ap-Qong!Ak>dqU@aG2Dd0?*wIsUAuS?SA60vUs7goOQ{AAZ(m)x z?jP8z6zbwyzRUB|9!k!LOoi`@rpMv&oUZpaK3E-7?d>eqsusPo%HO6t&8)$vFg^5U zxscPmfLXuc=|n^Uy}LvY-YxTZRqCzic22knS8$Q&C9ZP?e#v#MMWnBXt>pP~5uqMd z;b!Ee)FCDPkT#GkfnY?0(b1uQnrRV<_?=aFW8>&m#5&Q*1x-=6xM3ah7)3$0xnt~u zh$n1$zvaxZ9kZ{H7lJQoMNrINjgRbZxef3=J@y7 z)@Dj9sD)w(QZwlYmCVbh@%lgA!}2$m)TwbIacTn)#dEEQw_n%FY4FFb5!tePLuUd# z45o8AKLrChzQK>0@+Q3NMUEn>-KgA`X6G(ap?ZY0 zDZ1nZpR=h4aUs?bEm3Z;)fxAr4;5C+sxp7Z`c|vHoFZM z&7DB+V|o4LR)Qkrs74}dls3AWEX#%>bjTXZ?4=|kY=9q`K^J1EU4ZZ|B|3Q$tjf1*BBPj!?@ zzaw!MyY1-kzRN8UySh1*ws`m%R$3U+_+E1XEvk-~1X#_D#W^~<#>v+hKA)~pUi3B3lN)0K<6 zXe2XIsT%epf@tJK*DmlIqxm6r#2$u|#f&^|CJ)QMh@aHuv`jHrzuoTiZ&;nly+eoF z6%;(uGe7GixVzgT*iUVvSBL2eTRMK;(!j6LCYKh!;aP>lfDtQ=O z9FP98SQ$A`3RM_TKNgpLT1c8TKq`9lwY)_n$I?a!T*&dMCWmu(1+8WU2P!Fl{=U7X zNDQn52CLEiR9;uD_AS9u1p2WeMOn-bS6^gS=VIV$+|Q{8^LjJptGA~D7?{4OT-j*8 zGafm^fynFx?0)@_Ws!*)ip$Ao_9^LE1Hy5}n4cY;e&r+1;VCNYca zfKB0-QcS96FF&kiE(m-(akXDvg}LDNsYwuC8E$gWop3&mr7{cGNMtwl*UJ4|VJp`A zzQJG8IfV>>9-YY#_@Ci1`Jc)z8*F`4owF&H4gPd;bxHWnr2FlnNnaukw8&wWFng?& zmSr?hXgcx91+ilFGstykwq@Lx=Cwu3JYCZ<95GmzXsH_fMFw!E^+uUY6L0(H4{m?< zT9HpvJoTpRU=7WW1Zf0|Nq6o7} z`b^0r(cMHgl8YRUqgfaYJ}_=E;LeSBhYHvNJ)A#&3cJ=H6AP)4>Ajn;^#KPM`f$l# ztvc;c$^?>6;|l_cVqXu|hBWHk%4(MdTeD?el!DV|%WsX2Of*}cD^}eN5VXO;b~BT{ zV@zSNvk(o~ut%8nx3|PKZkv2=xs-A=%A_lRhgF@3!>Wd?e4vyg5!CIiSzobKE=hW{ zaAa2NwjJDjAq2mJ%Gi_o&R+2d`Oa2bx$}*a(m7Lii7j#8!-Bf~*r@K(xbrUMH1effBz zJuK=Ep)%#@bSAW_@~%8C77srnZ$xE3P_}C7u3hL3`i9SK6wRdBr}nftPa(b3dW3|y*ekD zJuE88lU)pSE*m$KPpK%VGq}+^Q}6sW2TCRQvFY^g21+W3ZUy(Mq!IyzI=;$p%e{fm zpCc{zr$;q-9(&xmJUc8ezD=Mx*=&cisC@_+7r0Zpp6pg9&wU1T&{dQlPs4S`huH$7 zoF3!7U|o&!4aC|5g2H%uWS5-)y#DK%^~LUX$JLQc_-?#U{`)}=bYEs0eV>>&p_b?g zsCSjkx2dj(%fvod(U|URp~JxEwj;u%ma~YfMNnNs2X+H=Ol4o1_XU|j)Izk3A4iko zK=UmS;6BLQ=<)hnOB<(mwSx^kkXAs0t*a^jUP^2>IOl`rX-p5l(8qyFF?mnkEGqw;Uwi z!K1-8Mu>!?k)UMbiZow*mjoe@fEldBN34En3~~^?CLAX0!E7A_m zX}tEx$2&7E$KY9XIwuRAI)^*#)ZvmRh!`R(CtX<~h{V(kuY}yQU3?9MRCoI`754_$ ztwtBicswAa)ZzHy&sXGI&)E>;WKh-oZzXWAhG>)-k^5Yn+vGLL4 zr*{Z(<<1Y*vh)~*t90B4-i+Ovs&|3kCf0si`9b#_buU)R4p18*wPCcLct$axqO-d; zM6+QfO;))?IQT<+(i74ySmYTGZ|_Zve#~5R%zbTsU3^z?JL+6IMPt5qN`ho{xSW_d zg5+R>wHCsTq|<2jCW({f@gd;((A5zT5CA`kLU^2}!kq8UFzYA34cDTJPaD8oet7Q6 zP(>sVSR0{DWEQs9^r zkm(~*446qYkA5V#qv6WlfdsA9w>$2h?1?$sc8ftqjJvr`s67D?(MYA~T+h>>W-Fv5 z1VK!s;U4?676R1ijhDLPLJ8P&-LYe};DMBQ8~rnBpquUE1;swmBzqu9G%ZKb9wmP- zE4o_{6`sfm=DU40iC3GX*SERNUx9ZSa@bPbz@I1)q?VRUuG{xAe<=I*_S84`opT`M zyq2q+r`0Y3-Hup|N`L4(KNVEo`GkQV$geI&!tL^S}p3icJ!1x0{tUzh+H zk10)kFQHgZ{PbSVXV=g=x>i0Qe&r0h?Y6LdCDqZ zdZ-ua`x90d8(+Tb#a|S{cG4iewlRGq)gU|*pdkcCnW)~(CQ{*>k3@~kSkNw1w0}IC zldoTgLEVpE(snkAO3i1*C*-+@EajRe`g9kA#bK(cGntovlwl@xVr1Ru^RCaQ+ygW$ zK4jca8Uf>FMp>pExj_1TJHA&pg53LBqT`kAo??njp1_sgTnKBtTVY#Y!(^rqABQu! z+2iEr=;WgF2k&1z%FQ8aTdTyD4JF}verv4X9SumT!~;NgY^r((SpYm&$^fd55~;6_ zx2KgFO-=sh^a#e|=z?JQtrj7`X!YX`P8uHbQJwXPYPg}(FVn!X`6VfW-bV)|g(;jN z6&~%$p+pbZPEM!KD(@NfME26@=W*$lnYGd#tj@ys5>|?aud=GvAB(N4SVlh(Gs8*a za^~Bd_Ci!jeNARFUYhaE>-wu;T(0kUN$;ZsIHcRf@u9jdeH*BQ$rL|&|Lm|E&es!`&>4CNm!_=E0 zP<(xPxRGF9yFGuHG8T%>`g(hGDAT97x3kgr8nuh{&`>eOc7|ENIx9F26w`a%3Rg;c$svLFkiEX2=WryITbg?uhTu^%YU0jHGX zwa)ZBJw{oN6meT1RSTa5|rir4Es?w2URa059 zn6qPdWfBNrve9jj?eaNYrvflz z@+TRWwx6jS7#T~lq#`0Mv_7xB=1-tk_3vuD-87M@zq(adZ828F8H|P>R6qk>IC;f> z*7a&^AgjGOrMg6L$9-@8ke=n$*o@ef0DLpSVg!BF!fsjMt_Dw!CvN%7^C4@slN<+6 zVW2!fvpQNt7|*O930wx&aSaz~!G%9#%#xE<4>4qQ$!79RmQA(j`TD z?`L-bhW5z0AFux;fIuJjsS5w!Y5`9$8i_8fLVRKn(00h>5?Q2EX~UiYE{+^fli;0+ zaQnp1nIlQE*qCh!h*zr`P3}eA9hH4RBv7{YSA9C;&-^VSolUYG!o}l0h&{pJa^D&J z0Kwh^&hkzd6Cwx~rttwWlMWAa`$^~n{l?jivikd-5oBaQO^jN&d7>k8Q`2F7F6I)7 z)J18$Yr}b*mqG8$x{J3D(Sp9=i7bpBmRg*`URpdXIkjkf6ttfIrIhYoMPUJNb59JV zH50_;biY#8La{DJYiBY?7tLvF8bjypoVwtl73VO?PLvWq8H6SjwTRESWzc;t7S>zh zt(CK6I66iy(%Urcp>4Vj6u6&2iEc@Z?>#KC8LZq=;dYpxc~v5MTP*a^9e@Wgx_4D4n`{{E10l zV1e!Fp=`Cmc7+73{#yz+LH7eEv-VG6ju3}g-rXR9zJqsHaY&Mr#WU{|Yqn$=C;X2}OVkKwCRzd!3M%WT-nX>2!jX8iD6X+$O0|}sGo#>8 zF2MtpWntjJ%%6!C{!@ToxR{y0E*-qwGZBLha^O%w7W-3FT8MliHU|a?3&;Xtg3{nU zRRXI2<@qtR0Rey;?fN}LPVNC7@=2T+cDl8s!}H4W=!k5vz!4Lk35+s}21c|!t;W~* z?tnY~KZrEzNrWL#&ilBzL>O>K^vy>aQ0T)gVb7D7O!keG(F~ewi=UFPfJ4t+!6)cX z=5s=W#${fA{WSAjV$RC|K+a+?ChGmFAzfM`MzXdJ?giIV#_+gET z$f+iIq$q;$_?1Ct8?VJzA)SCQ*6#<>SiTb?qH`FD++dPdf}X)ZKa+=Me@|tDf5Nik z9X_%zvN^3ic;HgsUVEFwO4$Gd0~OS0_3u41r=P^bDpdfLjAb3by<)=WL)DBJ>hXUX zHR@Rn4y1ZQZ7_84<&PsqS;Tv=#vPGC;1+P0zwA=hvM6VHmPb@=~`NA;Oop9}$Q&Pa*D>y#}{T)yiB# zHJlXjgzGlz%sd7DS++bksz2i!-Z_8^!vhF)Tm3f46ZWh1IsQ{eKA&ibl1`7pmv|ZGI z_xA@%)h$iv8cDJG(DMNQCX8Ldk4>zhYmEu<*donU=s>BrYV$!+nJ`Gm`wZk0(F@I= zp5sac-bP_D{)p^#b@2tZ5+@FNl7I`SUMFr5o$Xx)=nlB7^gYb$$)x^aSTsfhuO$!( zxeCwNwDT1+SzNbuD#1XChlK*2M}iK_BPnFCJwSu8_qY_39)yR1!x*mylTK8Fqb!GV z-Cq@`ya^EWJi@2de1-%YAbbm>^xOsj)q?P#TPVOBPV~ieGJ{7TA|j&Q%X3#|)$GS9 zxJ^En`Exl0X?nq8*m2_DmS?R;=cIM9@a3p*#fyQuas! z_S@mrgVbRYP)V2~Lh57eJG;ZF*Y6Pn`$V9=LBs~tQ+&=>qU7P_HSCBap34(XV9*Qz zj!^^l^c=g>$P9!)*=CcHehU%dVpX=rmTA{Oabn}4WULrE3!*}Le zK%)h6EPc>-X(H0XvZ2CbKj<>=K?F6gRW2>dP69nQB69$gf;e6Hx-M*^Y)w=mz3R@v z{hqFJ04h5u9PR58(R_VNXI z{+i679cokUAF)Ovg8jNuXC}FeUq9)om3*QCx&u4Uq3q;9CJx{=LV*0sv5B<($Rrr(E^wVr3%7Y*j?7uDS5>|W{sispp` zgaf+hEO%i^ANqifmwMCqxdGV#rJT2B-lx=+N_6Pt-{o==2)MwjTlc$M{QZXmE%b_4!h|bZHJh&?l3ZFmFtA*Sq0Yco~L<1rgHG`U+;IqDlV1sXG zI4xgr-vD+a4Vs#sj-gQ?$Oxdff%+0f)$W9D$e0aBpPL!?UQRIx!$kE2itlhwFg zDGq-FvO5arq+3qdPR$^4gyBf_v-6_N#zMgX$WNk}bZaxj!!Xx@LbLP8g+jWJ;mBK6 zi;=f+wcMj$zqK;DHJ02aDU=SaL!y*MM-K0X*?$Ij+3(ER7s$!&T9ApG+eApSm&}OdUmiD5!}|3RkU|z!p$Sm z`}Dp$9+~Re0wxG9%i(*&2m5}n#UhRfwL~*p2RT8yNjT~~1iV7`LX3F=Mc) z&_dpb^}}3eKIw1KsDi|^wgsY&4JqiePzDfA>wr~`1&CTFhLN|&=-;;n?PYVRfEq1A)}U0%}*EX9CH#T5)u?0^m;yr-9#iB z96@5DsjP^;i}HhGKu1nrbv-7S7pf4vRp^hArJ6t6car5DceV&7@>oSrB-`{I?5!ss zBU2(Aw~a;=Als3X7Nyr*gLxm>#%4z^h6YMUxNm1oSrKz`OUApDMRe=Grm z90>pEEg;pvzu(q(mz3WH8A!N3yff`l=>a+vina+lGAK}Zyxcv|F$vR2!9nWW*AjY@eem`Km}jKbRO z5QbxcbL?c0#7w1S{Vf1wc?1LmO1(}Ua?T@(`G*A;@Kv&5LP+)XI`8Sh?Y?*|BIRej z(b<$C8y(vc1h}AX|$Y3su>vA3VA?46&Yk+RlIG9Lm> zR)NjqWyW9iOYu!5FrW?*-5D;}&(|1z2##9D0q~+xP*0ZW&A)I7K-b~=tcjLPk@BO^ z1Txk$RATc>L>VNIKN&@H!uSP}2sUH;_cLrn^zWe@Et;%rkscO2>n(hFbQvw+3eS`C zDT-o5%cwVA@YQKnHr=g|8cjj5gNh)fOc9#Y_iiKXQQH){Emm~;*VnmguLb(AMi!Ek zn7kyKY2|lX&s|we4v?MJIq}KGo{C3ZQL>&iy-8%VODw@jVG_BI1RL+~S5QEIffS$l z#)2Gxd{xiBzlm+-R?9#!pop)XrK{NWaDbOJd)&qdTHbV8I>Zvqz$Ozz!mQ``Y7p3LNV2 zd!c}jq5vwsaw#lC<_g5^OGxJ4C7Q}0AlYH@z48K%>|g-peC2-}&aI3ig1In14~xf` zEt?>}h9l_V1dZ)VS&|Z~pEH%pb2n?z?;GSOHls)DP>YcP4dXmsC#7ABgilyH$Ziva zgomwPn=of3=U&AHKKHCc1+fp_;f4Rv0c; z!Bj|ZYRj>%7Wh^;UW&zbb8C{3nIH4W@J> zZJi>5tX39alg^jD9gnD#(OI})XR2Y4Fesju6(Cj!_}mHe_dEc)_;CSD)VA35qRiva zioLxQ+G3s>pF^2}w{s)S#9z9Lo3u!cdS z<^;S>D?20Yp&M@1H>AsD!uGY^L|%S1TmIa}kE0Dc5V^$n$Xj)X9iIn=4mV=0MVgr~ zc$`+Flh;PT7w5-2WzHLFm$X2?BM63GbZ%`ZFJG-FdBlQYA=*;zaBkoU6^~1}#ro!? z;cUzkt0Jbcqgw1YIlcRhAKq$GV(==1R2PR&>1%w>UpaUHr8C8M26hbEO}aUI=sy)? z^FYY=2$ZJ%KGULDrSs(!0cX$q`SGvbw0gOiMs3d)JbPm)5XbMp0$Z!s@o-p=A{S{@ zinc$)f#z^+k;Ieo=%vGE&-9?}>CkG6*8JzPtp0L$kp!FMST@w8+hgObS@bPv=>qEJ zv#10^7lFDxc}6|2?;w&gF|g&z_+Pwd^E4alU5~f7E=gU`d_ESi;n}ZSTf7gdAc@mN zZKN=W9y&kjwN_zFBD7X#XsfFRN3%Wl0M3yx9CM?@mN4H#&X+m$>El5n&3fo_q!Pt> zbxv!T(BOl|tVaekRzO@`tML%C(*pb+Q|xX}^UI|R5d+`9s5&|7;xW00W%}s&oDhC! z`TB~F(fago1Mks@<@1T6sTS*x_L9}Y+YGev4{5YG^Mfn3kByF7u;i2tPtSL~De*Su_>^|f?#mMcNO>|(gRgq@#3+lxy#uf!m9%M2v@-=djXaTD zDi6j5OZ(J&M`-tWI1w-A9W3gKzT2A7qgS?HDS>0J=rS9XxZ|DKEC5tse>gbNJixA4C`s=`$RVG`y(JJ@2uPu_B0|f_S``h0@~KpZPgb)ofqZJiQ+n?0 z3>#MU&#Y#&FS!YI98EsPRyHNHz~_*RQnk>-0x=Ka*iLHc-XU=0ORUfujEr-abA$8A z9>URuui&#WtD=?d7t8f^V@x5F3H@0y=ZYMwtmq}L1lvpFo%cjircrHVYa`Q9s*5jS zwdMA;Spl!xC;_A_N`)G%HSgd1Tf=xBtomco$ZL2MFlarzDvK@(V3-hh$x00jgk!jD z=K|n_SO<_M=TjjuZm73!c}B4~bbApA_}~goa*2oJR(ch3B$s1Na1^2zFC1#rl1x?l z6Cf>hb-ysD&7aPGj_h-p}D}BHa*s# zPNDy$yYtCg*t=?l{$`hw8Eg%S*8bG7z&S@QAOiQ@&tjXtM^-LJ+QaJ;AJGSqU# z*XylEjBvmu2zC#bwRs?OerJ`1Q7FU|AJ5f-_s&iqy9*k%Ax_mgBlV_mt82%VF>P~U zr1F)@Ya_u-f1vS6)d1LI({nZz(9_Z)^03i+gHu7o6Ve_Ml8eeQuyUmWdZAL+)hQ@kAPjz?iGGFN^wUfhLo_nLT2-D8V zzOf$hUnNRhP*h(Xym|2r_i7}It*b==gzVtaWPv)7g>Y=YIQOdQ!=Cf0q~ zu+p!T1`8KSyZEEpyu~uW(NN|iP7Ky(&K?fv=>2h7GnUZ)2b3w70My+{~r z*?eG49>T-gL#e`(J0lI-5TkxBxz#VYU4!p4G_Nsu8m*Dk9+QToKlCxMNr(?6;b*FS zoET_PHgB*uZkX|{h~64(&KI7>?;5ONc)Gs?bePoPNjG|*2b=c9pC+cW(e}&2V9gK# zd64(nfym+dNap0DBA2a+8n4rLcdRSD2Ruy+$R@epF*6D{E{8KeXu-M!zJ%38H=pPT zWYv(ZP5m%jbn=C0czh8R7JjG&4EtOahUII&+12;oldC>LS8}vD@7KJuvdYRC;w0dq z!t7tG`NHsPVrGV;hf@c2<)=)w^H)~XHIStV9!CT2nR+i&+k3DVC2mExJGE;uUt7vE z>^K|lOf0RxI|Oi^dM=yJK6YCs_mnt@Rbco9 zyiRsuQf2lY2>4zRdA-mg#hv*U#G1a(9=U(;4dIZFY-zszc?bc!S^g68R7wJK#Q?cN zG?|F-Gg%USbT=|>_|t%J@f0K&S)a1UAITldMAs|tLuEVZyoGQbK*hX3YRsu z)!tpuKj7O(cV9LIL-ynK^T3obVea#Z5+AC{mxdSVnllMer0!~X%rbZTtuLRe_rJ=Y zd~0c3@Qz=d>BDm=w79HE!(k4A246fu>v+)|-`B>9L-$TD_5!<^5jI3K05Nrc-TIsv z=+K9Na_{LvM^qM2d>9=;M;hYF&_3x@6Tvm(Ayf}xu8JTLoS4_LoVV|spP;{c2Udt2 z2n>^Nv-mHNgjB5_+v{lazqS>dYVwirV5`S!aj9 zXr0j%f{Y=$bnF9(#E|V8W*)PhCyHY<9~j)_@q)iDnh6>_Or9T)?B%I)EPwPO(3w1% zN){J@3Zjhmb9xPMw~?FQsuo0Z7K|m}78a;3kKK^DTn&6L4V~3Ud>5fOr&9AHxommp zsxc~*r@fgF$XFK@JOWqI$GPRoGK)=?PefB##&uNzfmxJniw+V;qEzTYM)!62&(EJc z%{$s^+a8fcXA%|nRqa*J#RQI;A_-Ue^Xs*7-Saoh(RQ+Au8L++|s?~y> zDBZY1K!=4b3#IYLa5LM`7uC~*`N$oqI`16}hx3(yR4tMI1GLVzJlI?95taTvl;gDE z?czrDj^Xvd(E8cM5}|Z*d1@`!h~d?UNU@ON0#(-g<%kMoWs3Q^RqglJEFc8HnH;_AWyb zpR9biP9+wS{ETFEm}AUp`Y>IQTf~_`5{IVaTTc=*;+oLL@`P{7L9vyDhJ#hciO##2 zTB!F>wy|5D2H;p5G*69acwEJ3$xdbWvy6X}1E=vh&uMqQph2sF$G0DXpJAV=W(yx% z;v9Jo`TAj(Iq|KXXUxXz4~DQo`_|^T443cDxPl=Nh@wK=Nx<72@@;5=Qg$eb)VZd{ zJW1KQ_2_c0v3i|45sm2Kd`Fn&ffh}_X5=|qI!Lql&B5J#(X%sG8#TfwS6c+1Uz_!t zmk!lAZg>dPxN37Q4zzkEvboS|S(m?%U4E=?@34yoBU7ykIuW^h9Z*!IJ3)mwjs@$~ zxV9@9hS@%!yanuiFsD*05^|MT=~nc1e`%u{5}JZDc}%Q&e4!yWis_u0vbKc0leqSr z#d_c#GVw$0r8`)B9JeEq!ZZk;k!#Cv+srsCwr`O3>C`__;fRQNpWtJCtm{KH0-!tVIiwjnSgjtYc)-zuxL+QR2=zTlpD9B7VB*Wq`zEiIoMf;e@ObO>IjR)bBj^pUlx5G(pxj{2X^_F13 zk@t1$M`8q(#+Vl?wy>{^4l?Pk>n>YGY>h0`0HPkxdo#1(mt2pnv)4}3F0}ehl(@wch24qka~7d<5fkHcT$)2^1&X{(Sk2eG%1o-ad zZKc=PSRfm87zUMt|HJyf*wgOY)wo_ANODK42VIk9f`hBUF% z&k&+=Ht@Fyh=_^okLEdFGRKfkCde!6yAOS3^=+5bX>=j98Eea&W-;Bq%frX#7C)j} z%9MU!;!zaepqMERGbQj&vX$XMEi%!!gsV?^NF&M4Qi1nOM?!db&SlMyUU6k@CA`v+ z|B!Tv^6jHhdpO2=NORpWsWU}{4_D`!E}JjkyA=nCGG(0I{dRVui6*4p$;0LrN3^+E zDuj5ZL`$b2i3~CW+uwH(+(sp`uzif-4XtmZ!N~gBKUPpZY@NjXNY3|OO8&~lgZIT` zvHfEr9v8_s9w&>u*qw0_y5}y4ICR-(ZQwS9fh+^*TO@5aU4?pO1+KC zpJGXphVqo%`8kS;cr*CpzSVCu+p93?YB-vRkv&?Jrqv!+T^e}EBM}+;WTn{PaCzG5 z|EcRMqpIw>wFS0`rBFs-?Qh^R4GfBNUGBQ+MDsDBmUYBj9o& zSDL&kok&p>Rx%wy?1^P?GuqG;5~#sv8WOm4G!<^Z1Rpjs*Lr{_lYl>*as zx125!1$lCNophMm?V!*je_vwCc48x*CQA#JvP)A;S^Qv3+K}*bQlS;V?R!^hGw`zE zaJPr?=H^m0{ZKF%+=;KJA=rwvaSCL%JOLNCeed<}urqQ2n8oAj27B6et{_Rpj#mQY zq&jB0`xFh9H+Hx~h}iti^jmkg-Hx|D6^lOfNvjjAy}FZL7agmgk)_pdob~ z>%Z?{#&`a4apluw)EDx3XmevXJxejGWsJy$mc{M1@sM7)!t?T~lwmVH>z1Ee zDv@1c%&XtErrG#UB??1Z5o%NrLKGf7d)EDBduJ?M(+UwMpGuz07jbXbdlC$ayN^x` zDyX2VC_v3344h(a7H)a3Vq;hggjnnyC=o6qee<+2VcCp()r3u$rD@xoD)Q_Ll@_gj zR_v#2_VfB%I!)%wd?vB%3&keR=s5jCEGnIAtPQ5!jl1i-tf7~3E{}!6g_L4NV@+!o zhR^db-2TU-PFb_H=(DtKLc?do;Ly%%xIB8nE`}xI_*R*DG zHB3IY;rN0Ki_ISU_&$f*@}y=?f*g>8&Kdg3WEa8d49$;WSD($8Mx?%BFoe-U3e%w)51yhb)-Q7w42%F8cjW(Q4*SP#sW_Q&uArtP&j?ImeXECVP`uwvGm z$x*nAqto<3TrN4hgM6+yNJ(MmrmV>E4GOY;hfNFR~?wNp+}Ef)8|K9>B&(=UzY+Y+m z8ne5xbiT1p;+8!V_%&;Wcd@jXnU2A3 zLRY=d->!;=uud_6|`b$3PB|iFh67|a|`Z|>&>CKbTHhyGhXJ?*_Q5$KQH6G&!S_@VBwck z)bXXsglDsifi-i-RUjxaNhqGg*>jH;uefP55GYY?z{+ZM4OP2j$dy|-o6SAzoOQh& zk`=UhEweU0xmCC@Nc@U#tPLRCkC^m=+!&GJ^u0n&R~#Wp)EJeb|E`W`$>l$ zG2ba+q<3c8o9$-opB49XgIFWsN2mO6uKZuq=~1x^;y6lFW^7S3s{7z{tdrKSrB zzM2}MaL7aGSE_&VuFgejs#3x;VLdr3oEA;FiSvVWg6v*BTeXxWwU~hq_Tko7qh7mV z&H7o#TVvesaKiod1y!@fS2U$*T>=1NGK}$9}#M zqQh&l_PLUMVHmFkkIHj+U9pC7v+7So>Kx(rov%+Oc)h&ez<-W*(ij~Z>9W)kf6OO! zbt!0ul}nCJ!)xsscCA%x?kl?<{5HN5r`<`Hcw_ku493L)AfE+gk$SH`Lf)I33HK&Z z?K7P5PkqNVBzr%TD|Vl4*FkbGaBK(RKxlgM$O#V3rOA z@>o+SM1YlYc@D4p!cbkJ80iUwCpUvuM-r=iMw^+NPL+zZUR9D+&3gPb-ug+It^h8m zad!Bw#0wDOQ-~v!%(}?6boV-a`#`2I{uK9#t&k_Gkp;b}92Ae!mBPL!It>ayIi&So zTHt>B)vyCLAf1##?uF<5jX=8tRW6w!(*v7W^GH0(0t1R9XI_h0ac#=Gg>QU|7bMG3 z_5oxKeLZ$aW?12MxyA~NpRK7Il25?$qvEfh2)VnWrB-d6&F3lvivp}fY`M}?0k<0w zT{6-Joi7`wMH|T8nXuAgduDhT?-jjKOBq+Y3rnURK>xvRg5%$||Iju+Ox} z#SpCoJ(yV~sLDehYKG53>$EC#ISNRF&`6`~*Lqh#BJfEauTkX_S9{CY|Npzd4|m%I zufqN3RnN5?jYf_xHCxSVw`47KC!0(boeq$!-H>p&>zy0oyTy_&Dp;Mb?E~J1`esv# z+*kkr)Gw8i?7XLH>uJ2VU%hYL1w77>u^I2Y9e>0yboDYvjjwcvrN~VMm zV+#dB75c}`SRwoZVCW?Keyh(hVUZt1lzPdf#J2ye3Srr=Qj32>m5*Uppu2g-Po>g_ z6tJkLO=fYVWFJZZ#AxNmuzz=Kj6^I>Ev3= zY+dp0zd75TF<+eL(Ww$25rS#!?CBWs;ym2E)7DzEOn<@d@#VjOrqNxQP*@nr_G`Ru zkG9^NM@V<^v7Tfs{ZP$=%XlhU(ROo$fAC1L@BJ`rVkU<+P}0vb1Fo~j3$?$HBVy49 zzH=rpUJe=1tlOdLlnTMa)4Lg#4gR9qVO4*)jDN+n;O8E%F2l<9nq~yD*FD=>3*Rj;f=OCV`k-q^Js9LDm6UNF~ zk65ayUizPgoZrtWm%)a&2f(9!FR^KG*!Z5x5>st4`6QY|5V3?#`1n}a@x(J6qx?vZ zs^nDE|G&VjO@#VgAK82=BHJ)b%_;gTdhbxJ;hoSSl z|3k#*vKx}btn*344|aQZb2Es;Y*bU3QT#R7mcE*T0(7P6PUxTBm+hd&f04L#z=9{M z%os;mj0mfbZs+3iBmtzEde5^>t%y} z*KBkun*CJbyfbY+TTKrj2ME3C2K?h-1H^`<_z{P`>`J*7L#-w5#0C%GQ)zI{MU!Rd z%LI#`B*H=d6qm>2jOn>SYlKlxSW!)j%i|9()B$2^I0#g)O#}29YRMqOt0&q@*Am#G z?du58lq>+s^a44r*P1}l24Gw;_?*n+e=uV|(2hmGkE1}u+=Zf=_A+56yyMBCCYOtg!`x)oh6;X2UWdQ$02JW0jxPIH{R3c@shcnays(IK z_lqR#+<9?-c}%NPpb)8Apz!AA;;_-27YfKBGsFZhV}Dvg@x2fM@{!fvJpj$JXkZkb z$6*}-5%+lnVBmPv&X12{EHu5B0xQb?;^56USD>>51&3ilI}}k(;&TlJ0K(O1E1cB2 zLM}Af6f$^(wgwg zaak|lpv}8TYq2}@nTKMW#{-BeA`JAOWZ-_a2$0!7FHYQk_Dr)*2}be{CH zlV9U-fcmF;eZ~jsD#;8g(O`Q21PodLfh%&$pYanH$ygMDrI~r`*G>^5;O3A(rwj{H z*N+YK9bH^{_vY%z@1Zb2GIU+gn^?N9WCfEVzz8N;5s!}`Afy;{pYHrnt}v3Q>3%bu z!Uos_k=k2LMm>nfjI;#MhU!7_CVDAa1i#ThJFnnk0CXUTg4OfF&SWSF1$2{Cz1>Oz zk9W2aXy&#yxHv&EcLaVbpy*0q6l?n!q-!l7OJZV|68eF_swj04X!p2t<$-1AdU24m z)uQ{*9RR@xCJJ>U#hf+UA=T;lY0N|W0I{Mn|D=vPX@pX zKQZ6vL0^bikPJ{uEcHpV&zrWnk?;xd0LGX4fdI$TTvS-Ux(J-k*{fOfMlY zg8z!cw3swh+d(%pJGfAHn3qu8(rTu@khbvUFOoFeVL~zyxmanFmn(`Os_>UuhN@xE z0$kaLQk}Zh{#Z)QR?mjnrsV7MCkhiw4VQ(NdmG`eRLgFbLNO^3QV{;CzsRWthH$cj z8I6yxgkk0a@n=WXYMyM`p3uUQ|2Vt590scduuqWicJi?rOAS!ImmB%x?&{hu#sYyp zm2`hL6rxpipGgL=u7?;06tMt-)GR(2*#kxN^`uE+aXk75(wyb)M2i2coAVJS{X3?I zuuSu4S7H<}1Y_w{Y3iGrjCX%l==V>wg}_EWfQuTlu~?BnD^nl4 z6n*c0!2Co_T6RjdT;51eAEco#wTEmH7|c?#zNKPmXCY5yF(3gD1JzL$VnaPvtdF+) zOG5!iHRI)PTBEakR?rLKE&)hnn@g z`vI9ujd>v9`JWt_icfTDjJTuvEn(VvO)FOmO`nUolpG9Nyu#1A?s_(K>fm0F)(s2x zMTvcU`V6C!4MOm`ID}N#Cws1N+fBbjaGspvEY*jU)!(mYRPHV zzxurqpEb`g=r!+ZQmPIY8lEU3Tw0ou_}O=Mp{dJj9-?nFIFVl*DO+bH5tUXWLKtCC zuaxgr9vqv|Gph4Lex^Wjt4s#1cPIEbt!n5@gIS*_F&sy)K%q{y}2n|=2i)=b;j+&yBFXdlJn ztW(diKbK!>+>~dI&dp1wR^%3OqvHJHsoMVc$84YYj2%edC-zF+*O3Z{10|gAx5J5F zlyXg;VhzTME$pDeD}Gc)b;_)KD{#zi{mDbIB%OyO7QnA!Jo!=+!>}Yku-E^(5uW#=OHblhxBXlK5NysgUtazA_ zxh_W!XGWAN5*)Q9-Ljxu>dJ;SnhIy>^2Y>=h{#vme$~h@efrnrC*D=_%IEi59y36H@l`(xs*1N z&bAU4<1l`nT0qFp*Cb!8cIGr+LwCBrGFh-q#Hg+2eD)h1{qpTG zu2NN(757^^!QE(5VUWTDX%Tz49I?Reh`U=miv@!2@7t?rPc|uqQdRPgl)v>RGSVZ{ zRY{y~4@W)2Wht(@^Gi8ywa(NYq|Xl+zLw>-Eh439o}M~BJf>Y-?G8kGmAg{!)%(IK z<%M$3Z_-~s%RchJROCu^{c1S=POyIw7)Doc+xP9GW}8{G_U4#I6&vyUI)}F%HC_84 zc{e0<<*+e(g^uvYzW)jxGjb0G8fXkbQzYomDT=i>NuKEwYGhSW37LAQ-y%(ij1uP%&_o&yWy+uyV=U-jNRw5M* zB}v_MN=yxxC(HTRIG7Imb|K*|-&Rl#vbl_B9Cu89HqJjYUL6QeX3$7gFU5xEVf+>X zR@os{M-cjfHL_7N3a4rs1;RxrI2^q@DMKG*XK_ReYWfBQu5tXIFPz6YrLFdag}&9P zeYtgEDt8&}Zc?s8(>?)o4>h9Kek1R6QtTZHPTnBiC9Z{=G#j zr5@SO$8L1Gbu2L-KiIo|&w1SMS{A*bwRDNP^6NMzk?k#mX-~8BPIjA4S9a0lcWs?A zWt0$4>h55;qFGX7_Y z<9C}QzRP-LjFgAVo3(Z6{+!zse10Flf)?kv2Al?)^Q?N$ouo=mv*YU;tDHF71s`9{ zSu0N4UZ?$HZ>-<0F?2RWlCoc6AUtOoTwOdStWFf*&Ikj0Pns%EHa{&=O&_k5P=M+waBSh0xEcbQB~uXk!)y}VU|la~3?fmw-+?u%{X zrq$ZGP7+-usR!$edf?9WRRMAlyb0pX<(WFeuq0j{oE6#U0dt+~Y30tsN zs(mS&cA|;G)p$KbjpBr^sMlKZ#|*^73pHT}8|Wzxjrj7T?F#ypSU>*EEAJ>aZ1U*- zWGxN?C%)z3d*yOnUS~xqa5dV)W3%LQ)P%Gqe_Vg`6jnJE`$>NmOc# zY0-a_-ABHR_r4X$L^AyrG*MP2^KlK=o2Blj_XR7?J7E{pQy+STXZP0@ls@bEje;mv&5l%!Uy*uyz1nw9N~it>b+zCE zUaHQzr$1@r@N@{58J_jCmyT5yKm1z)u9r;}Ys+~fCi0H^fmPS%Uz~2cg6nL@+iaMd zy)H4~a9AWtAWu)@H?HmxG)JbuH8NH*edhbpr{U|GB7&~e0hD+eGPh2MW}_QYlEo;7 znb@V#k6qCKwHnWgFqT*2Go|~BgicNbudnW-SrZ3>#S8~Ii`AYw@#rAq6;4+u{P2df zdLBQS4P_KwgGL@e2tzAzzp%AN769>0@MEK4YE3e#T&D7gS_`XEG(2>`xC8AEN zYv0anV$D~y6%WVk36B^qQU&H;@0-2uMR{9fUp%K$OhMP(N@v#N>y9EJw(O0D`(dfE zKQD28XbvWr5lz2E=~bDoR-7T+ z+-xVN2%7u4OEbo8%56Kn)2=}rEz|W&Pv^qfY&FBedapcd6*wQ z#fb*P+^6++(7n+%+8w6Ev%~+w^@dJ80zY5hROf2xmGk~$34M?M@A-E?d8lREzR8AH ze6Txo>L{cPg|N`Gm3Vf#EYMgMXJsk}jzRdnP6!dXzuB4ZAT6pVOmy_ER*H_7J$}bf zz;#DW#l7?I5+eQ2%I~+Ev>eC0CgM@In{tEgJ$6i$+Q|2285a1a9pNg*SHLds*Us0V zpX_kC=6~*46n%bmI`;Grhy%_>PL(bemMT!@VFE6jgWWfVRz_=?ixsjoB8hYL*}rcL zZNy<*Z;iZ`Wxe-oFQOWK4wjfqOh6zRIx8m_s>5JIwHZF;$fo(dz){jt8Xejs z|Atxxtb$}@WRrTQ8@yKUy9ocpFBIbaN?G|F>jUA93mz2jN;Daszug?>dH;*7*Ll}> zjlY)Cs{VjZzu@^QjePNh&n;}L*KHWa?CwFnao?C%uN;-^N74KN(Z6blAnm6Pxo`>? zO1bz>-&j>;#_wL@uiP z3pSb6T=^HP4Xi^FU1;V|k~v3%O=0G96v;ZvfI1d^R;|D<1q!PdXV%+S^L}vKR=-3C zbx>jZs!2HjFUH@t!{G(CJT&E{j=|c;AWgXWs^y#P##@E^#zK z_2gHvE@HBrCYLwUD9MFK3(K)I*zu!Cg;CMa&pxuc@W7{Pr zhe7P^j(AP$$D#Q8U|zOE3(MYmu!wT}=%J&bAxc;;7U%R^&Cfa{EQa`sWt+RC(;U zfGJ9)GF{#S2}~gP#H~aCqC5=-pS@$X9KPnUHH)rti_+Q}JK zgz1%AEgUn1ozVBfvHkk8{g$UqMvm7zqxY9EI7B^9D`y+T;8}}wG=}j75RUJO!-P1C zSJ$rJZ$IX7y=F1JftU-O7zp@Wpc~KD940Z;O^1mHSl0@xewI66*0gLAw}obdNg%^) zsNg&_box;iQ~GS-?M}D3@*8>`T0U*Xx*8^V!jnG%L0(tnCcl(^RvMB#C*k#Po-NTo z|IMMCqlNV0DK2Uzt=yAjo{}{7r-U_9^S%MIEjhaJra-~5`R!1SxE^=Ld<)V%+)p5% zZ6=5S0&z0Q2)yasujW^{us1Rb2C-aN&|U11nrnv7p2K8EdP&FO!ub6WwY-OcfGd!b$af)*sr8hINaZ%@;ocDzX>n31mXNI`J!9dE z#4B2LbG=@H%W&N27~-~dWBg~Gwj)A`$%qkD42U2-`3(W&3J~Te9o7=0f9|m%6|fj| zfUxbgsnlL5qVU(R&a-L<#sqsos9o)~rBlIp`Ym4Y3$*-~(u)Wyoo_aNj%#w;H($*k zM0%XJN5A@jLQHT&C(#1#hE7B3Hy3M4wzGAlmsM{i)C&5GpYuMXDuJZD5H#J}8YwOY zrU$4FPw~50M2`$o%H+M;MzUZA!Pcx^lb|E+aF)DYQ=Uxd2m4hraMfWTZR0yrvu(5D z`#PNR!rODbKLrMyifg52{Rs@%p8%=&I;V@l=J@WmP_zDPV^RR{M5F;dxYV22v%`*@^DAf6lM;an`jB1~+QF3stJ=ruP>EO5xEcJQ|(exo8RN zl|3+;(IHHP;Ksu|C-f;3{E7=dkqruouXbIM2R-bjYp2D;{BHP#U(MGf6Yn=~uX!9c znJV*T!^d)7u3blyX6?ADQptp@KYi8-(i3(_6t6}8rj^lQo~rvn%MWj8lqYl%4yb7t zY#wb+h;E+}`*rd{U~WI=#VJ+GV=Y^12}i8VO16X0J;)`~Rhb-NK-I3j;_m_6Y!p9{!8ANSn%4|#?KcgYoG!@IiW$D<%AX1e_O0gp#qHOMEq|0~ zeeE+&YTSGycV1?$QNYFWnl6^MoWbMG2t#$zYuTQ|uSUak^K-4(o)}T*hsJ#`Q*X)! z$v0ad_Ycy}N5Tc`fQ1Tei)^whv@L}HX-luP${vy5t5^zPCW7V?<^SR%f`IoO5v7?& zwsO8qb0WG3J-rmS(Gx{X2&jHFn%H6z5h3M1txTpBGTQYj&9SOxT<|#V=lJZ!o*0Vw zLW#9G4BP>)RjRRlg0-!k9&1fSNq$ zT9x4c=(htg#t^`?(%Eg)-$H&KlEHxTyiCtHY%^R=v|Z}2gTOIc*T+|7xyGQ!uAWgB zkdcYp03~_km-g|v&?X#pt|v-A_HQTs``_2kHr7I+iu@?}M;WuFlBr{0vTro|eL4i6%bPS#docbr&s@ z_k(}npPR6`Kn3iLhd67S0eI_6@(d6sF|`^8&5;r)nsXK(4ONY`9fkeZjX+BzWazFl zS?Z?`Y@ERnl*dk$)jhSJ0U860mX0KP$?=0DNB$DhVLP3mMh(D?;Jq9sEqhk8>yYJB zf03^gP=%QeZ3}e9At1bfN-9LuJ)t6pjz9xpDkOk@14(6I`!zXu?lvI>3OdC9>0iSK z=P3GTd~3hIoP&g6`g4U*XvLRo2|9z<6Y+Ck0lQ&$sg0CWcra^R(pOddClBl{?JmnM zH`n5KLu-;l;B3LI$(S?<=%Hf1+m#Qd7YA9Qdz0zUEu7}-&oj<3A%2FUzMho({`u^b zYM6k`d+=kmOs^?|OkNxua*$(@NI#L3mjE5+TY582D?N{hfcaJx1;}+!^ zi&jNOtFy#I`sIbHnxnOTQ0omXRZE!!IzK?Y^0)j@B5gbVniQsiixYO26xzVgWkVzz z&mN3CDsCrCMZsD0kCDfghJtNl-uM9k(a-xFNeat@gDalwuhWP$18c03nVDGvsQ>Kz zDCbs2p-@Nv>J5Q9l>9U(Y2!@#0lO;W8CtbrK}(>80?SoK_t$E~TQzZVDCA}8+S*Ct zb#nqJgkn8-p;%DUV2&C@LQ6ZqN_C%a@!)o4 zM9rE7il|Vpk_V?3h_o*jA{HzZ5^^qrod6_UwB2GeVwHk-W|H45pQOMVJKc`f zVcHJZff5YTn4}s2GKByAZlD`yMfq$fw>|CM*_4AtDcJ!xEYy5w7uToYF6wagguPfY z*5_b*&m*JZ=m9I4OC}-|tFfM}3`tWCDceTGW*`ILfD~TG&mf}p_gx?t0k%J1g@5X; zF=uylX98Owob0GVFf5N@jy~S{-gdA&zH@L-ohcQ6N7QDArty}Lqm#Tt1Ol9rLYvu@ z5_+)9wW?_9;g>Tm@(6U57p*);|h?dKnE{<%jN?OM@i&ho{fh zH@FyUateugAQzPg4|Ya=jLZ%4eoz&KvFFv%^xNpE{=U@YBESi1UGS0=eE~dOi&aa2 zwz?z*=2~GsCLa|d{<0n8B0gT0pq~`H;_8`oJgtxly>>MnD3CMHrOxC9<}33->WR@G zoR%70dDBT@W&>Puyv%8jEvZ1k|Fh`7Mo&CfiLp^K*W0Of$9Ei{4yotje1OCxeJt+V z^dg339pzGt#EoY5;4$vE^X(}lfIsWpovGRwvgWd~^spjmvlG{kUs~tAw29_{d>+~C zmu(sos(!X7(UdBNR-}r~`WSYd!%f~herw&saQkrbcRra=+#hX5B#J=i_D_+tZ%GaO zDLMd(35~_=Wor!gdUScJHLKrT9@I?@$lP>&eFOb_qP1fg;8I&aV?RRlAImKpMLbO% z9ShRg(6dhq=EAe%v$Nz^h_HN+ey9f-P5FTBk!~`{SXG-sxdQr9W4SgfuYy5q@Tmt& zKW(^kJLI>ON8le}w_I0I?-<(k@Vy%LW#-jn!ysgQe;_)U>vMmnS!a_ueDj1zXutvn z!A>%;nc8Q-_$alx_R*Ah;3Eo=e~rjsfGj%%rV1!{086&NreLD4nc5Xt7?*Ga)cp$?4w689Z*lf|JcAm-$|&)jbZKpG3TC*{?|)R zzRXt*P;f9|j^b{p6bCepl5VJ@2ew4Qj^hNCfaHcr7P9?#lebI4Xd=2TkUW@o?n{94 zT>frdGuJh#v7bmeT$9@##2MEaicUPp>-4yTTPeIvH<=yFj20SuK~3&}{*L`mwf||b=%pW5(EG>i r9`4bj`};in7rc)iJ$eEiOt?pe%p=jQ{@zyLlSk4L3gTri4gLQgQurNg literal 0 HcmV?d00001 diff --git a/docs/input-data/specific-input-data/ics.md b/docs/input-data/specific-input-data/ics.md index 171e13f279..5394d29d2a 100644 --- a/docs/input-data/specific-input-data/ics.md +++ b/docs/input-data/specific-input-data/ics.md @@ -42,7 +42,6 @@ RDP+, positive values), and the Pmin of redispatching (Pmin_RD). These values ar | Node | UCT code of the node described with 8 characters. | | Weight | Weight for GSK at respective node. Sum of weights for all nodes in one GSK should be 1. | -![ICS Importer](../../_static/img/ics-importer.png) ## Read ICS data @@ -52,3 +51,25 @@ InputStream seriesIcs = new FileInputStream("path/to/ics/series.csv"); InputStream gskIcs = new FileInputStream("path/to/ics/gsk.csv"); IcsData icsData = new IcsDataImporter.read(staticIcs, seriesIcs, gskIcs); ``` + +## ICS data to to create TimeCoupledRaoInput + +![ICS Importer](../../_static/img/icsdata.png)_ + +```java + Network network1 = LazyNetwork.of("networkPath"); + TemporalData raoInputs = new TemporalDataImpl<>( + Map.of( + timestamp1, RaoInput.build(network1, crac1).build(), + // other timestamps + )); + + TimeCoupledRaoInput timeCoupledRaoInput = new TimeCoupledRaoInput(raoInputs, new TimeCoupledConstraints()); + IcsData icsData = IcsDataImporter.read( + getClass().getResourceAsStream("/ics/static.csv"), + getClass().getResourceAsStream("/ics/series.csv"), + getClass().getResourceAsStream("/glsk/gsk.csv"), + timestampsToRun); + + TimeCoupledRaoInput postIcsRaoInputs = icsData.processAllRedispatchingActions(timeCoupledRaoInput, costUp, costDown, exportDirectory); +``` \ No newline at end of file From 6f76e2cf88809198e652a2416936b2b2829e2702 Mon Sep 17 00:00:00 2001 From: Thomas Bouquet Date: Tue, 7 Apr 2026 10:57:10 +0200 Subject: [PATCH 47/64] close LazyNetworks Signed-off-by: Thomas Bouquet --- .../fillers/GeneratorConstraintsFiller.java | 51 ++++++--- .../openrao/searchtreerao/marmot/Marmot.java | 106 +++++++++--------- .../searchtreerao/marmot/MarmotUtils.java | 33 +++++- .../features/4_time_coupled/US93_5.feature | 42 +++---- 4 files changed, 140 insertions(+), 92 deletions(-) diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/GeneratorConstraintsFiller.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/GeneratorConstraintsFiller.java index 14be985fe5..d114004a67 100644 --- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/GeneratorConstraintsFiller.java +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/GeneratorConstraintsFiller.java @@ -18,9 +18,11 @@ import com.powsybl.openrao.data.crac.api.State; import com.powsybl.openrao.data.crac.api.rangeaction.InjectionRangeAction; import com.powsybl.openrao.data.timecoupledconstraints.GeneratorConstraints; +import com.powsybl.openrao.raoapi.LazyNetwork; 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; +import com.powsybl.openrao.searchtreerao.marmot.MarmotUtils; import com.powsybl.openrao.searchtreerao.result.api.FlowResult; import com.powsybl.openrao.searchtreerao.result.api.RangeActionActivationResult; import com.powsybl.openrao.searchtreerao.result.api.SensitivityResult; @@ -40,7 +42,8 @@ * @author Godelaine de Montmorillon {@literal } */ public class GeneratorConstraintsFiller implements ProblemFiller { - private final TemporalData networks; + private final TemporalData networks; + private final Map> generatorsData; private final TemporalData preventiveStates; private final TemporalData> injectionRangeActionsPerTimestamp; private final Set generatorConstraints; @@ -59,7 +62,10 @@ public GeneratorConstraintsFiller(TemporalData networks, TemporalData preventiveStates, TemporalData> injectionRangeActionsPerTimestamp, Set generatorConstraints) { - this.networks = networks; + // TODO: do not rely on MarmotUtils + this.networks = MarmotUtils.cloneNetworks(networks); + MarmotUtils.releaseAll(networks); + this.generatorsData = processGenerators(this.networks); this.preventiveStates = preventiveStates; this.injectionRangeActionsPerTimestamp = injectionRangeActionsPerTimestamp; this.generatorConstraints = generatorConstraints; @@ -153,10 +159,10 @@ public void fill(LinearProblem linearProblem, FlowResult flowResult, Sensitivity // Specific first timestamp constraints OffsetDateTime firstTimestamp = timestamps.getFirst(); if (!individualGeneratorConstraints.isShutDownAllowed()) { - addShutDownProhibitedOnFirstTimestampConstraint(linearProblem, generatorId, firstTimestamp, networks.getData(firstTimestamp).orElseThrow()); + addShutDownProhibitedOnFirstTimestampConstraint(linearProblem, generatorId, firstTimestamp); } if (!individualGeneratorConstraints.isStartUpAllowed()) { - addStartUpProhibitedOnFirstTimestampConstraint(linearProblem, generatorId, firstTimestamp, networks.getData(firstTimestamp).orElseThrow()); + addStartUpProhibitedOnFirstTimestampConstraint(linearProblem, generatorId, firstTimestamp); } } } @@ -164,7 +170,7 @@ public void fill(LinearProblem linearProblem, FlowResult flowResult, Sensitivity // ---- Variables private void addPowerVariable(LinearProblem linearProblem, String generatorId, OffsetDateTime timestamp) { - double pMax = getMaxP(generatorId, networks.getData(timestamp).orElseThrow()); + double pMax = generatorsData.getOrDefault(generatorId, new TemporalDataImpl<>()).getData(timestamp).orElseThrow().pMax(); linearProblem.addGeneratorPowerVariable(generatorId, pMax, timestamp); } @@ -242,8 +248,8 @@ private static void addStateToTransitionConstraints(LinearProblem linearProblem, * P <= P_max ON + OFF_POWER_THRESHOLD OFF */ private void addOnOffPowerConstraints(LinearProblem linearProblem, String generatorId, OffsetDateTime timestamp) { - double pMin = getMinP(generatorId, networks.getData(timestamp).orElseThrow()); - double pMax = getMaxP(generatorId, networks.getData(timestamp).orElseThrow()); + double pMin = generatorsData.getOrDefault(generatorId, new TemporalDataImpl<>()).getData(timestamp).orElseThrow().pMin(); + double pMax = generatorsData.getOrDefault(generatorId, new TemporalDataImpl<>()).getData(timestamp).orElseThrow().pMax(); OpenRaoMPVariable generatorPowerVariable = linearProblem.getGeneratorPowerVariable(generatorId, timestamp); OpenRaoMPVariable generatorOnVariable = linearProblem.getGeneratorStateVariable(generatorId, timestamp, LinearProblem.GeneratorState.ON); OpenRaoMPVariable generatorOffVariable = linearProblem.getGeneratorStateVariable(generatorId, timestamp, LinearProblem.GeneratorState.OFF); @@ -291,7 +297,7 @@ private void addPowerVariationConstraints(LinearProblem linearProblem, OffsetDateTime nextTimestamp) { double upwardPowerGradient = generatorConstraints.getUpwardPowerGradient().orElse(DEFAULT_POWER_GRADIENT); double downwardPowerGradient = generatorConstraints.getDownwardPowerGradient().orElse(-DEFAULT_POWER_GRADIENT); - double pMin = getMinP(generatorConstraints.getGeneratorId(), networks.getData(timestamp).orElseThrow()); + double pMin = generatorsData.getOrDefault(generatorConstraints.getGeneratorId(), new TemporalDataImpl<>()).getData(timestamp).orElseThrow().pMin(); OpenRaoMPConstraint powerTransitionConstraintInf = linearProblem.addGeneratorPowerTransitionConstraint( generatorConstraints.getGeneratorId(), 0, linearProblem.infinity(), timestamp, LinearProblem.AbsExtension.POSITIVE @@ -320,7 +326,7 @@ private void addPowerVariationConstraints(LinearProblem linearProblem, powerTransitionConstraintSup.setCoefficient(offOffTransitionVariable, -OFF_POWER_THRESHOLD); // OFF -> ON - double nextPMin = getMinP(generatorConstraints.getGeneratorId(), networks.getData(nextTimestamp).orElseThrow()); + double nextPMin = generatorsData.getOrDefault(generatorConstraints.getGeneratorId(), new TemporalDataImpl<>()).getData(nextTimestamp).orElseThrow().pMin(); OpenRaoMPVariable offOnTransitionVariable = linearProblem.getGeneratorStateTransitionVariable( generatorConstraints.getGeneratorId(), timestamp, LinearProblem.GeneratorState.OFF, LinearProblem.GeneratorState.ON ); @@ -365,8 +371,8 @@ private void addShutDownProhibitedConstraint(LinearProblem linearProblem, String * ON(t) = 1 on first timestamp when P(t) >= Pmin *
*/ - private void addShutDownProhibitedOnFirstTimestampConstraint(LinearProblem linearProblem, String generatorId, OffsetDateTime timestamp, Network network) { - if (getP(generatorId, network) >= getMinP(generatorId, network)) { + private void addShutDownProhibitedOnFirstTimestampConstraint(LinearProblem linearProblem, String generatorId, OffsetDateTime timestamp) { + if (generatorsData.getOrDefault(generatorId, new TemporalDataImpl<>()).getData(timestamp).orElseThrow().p() >= generatorsData.getOrDefault(generatorId, new TemporalDataImpl<>()).getData(timestamp).orElseThrow().pMin()) { OpenRaoMPConstraint shutDownOnFirstTimestampProhibitedConstraint = linearProblem.addGeneratorShutDownOnFirstTimestampProhibitedConstraint(generatorId, timestamp); shutDownOnFirstTimestampProhibitedConstraint.setCoefficient(linearProblem.getGeneratorStateVariable(generatorId, timestamp, LinearProblem.GeneratorState.ON), 1.0); } @@ -389,8 +395,8 @@ private void addStartUpProhibitedConstraint(LinearProblem linearProblem, String * OFF(t) = 1 on first timestamp when P(t) < Pmin *
*/ - private void addStartUpProhibitedOnFirstTimestampConstraint(LinearProblem linearProblem, String generatorId, OffsetDateTime timestamp, Network network) { - if (getP(generatorId, network) < getMinP(generatorId, network)) { + private void addStartUpProhibitedOnFirstTimestampConstraint(LinearProblem linearProblem, String generatorId, OffsetDateTime timestamp) { + if (generatorsData.getOrDefault(generatorId, new TemporalDataImpl<>()).getData(timestamp).orElseThrow().p() < generatorsData.getOrDefault(generatorId, new TemporalDataImpl<>()).getData(timestamp).orElseThrow().pMin()) { OpenRaoMPConstraint startUpOnFirstTimestampProhibitedConstraint = linearProblem.addGeneratorStartUpOnFirstTimestampProhibitedConstraint(generatorId, timestamp); startUpOnFirstTimestampProhibitedConstraint.setCoefficient(linearProblem.getGeneratorStateVariable(generatorId, timestamp, LinearProblem.GeneratorState.OFF), 1.0); } @@ -477,4 +483,23 @@ private Optional addLeadAndLag(Optional lead, Optional l public void updateBetweenMipIteration(LinearProblem linearProblem, RangeActionActivationResult rangeActionActivationResult) { // nothing to do } + + private static Map> processGenerators(TemporalData lazyNetworks) { + Map> dataPerGeneratorPerTimestamp = new HashMap<>(); + lazyNetworks.getDataPerTimestamp().forEach((timestamp, lazyNetwork) -> { + for (Generator generator : lazyNetwork.getGenerators()) { + dataPerGeneratorPerTimestamp.computeIfAbsent(generator.getId(), k -> new HashMap<>()).put( + timestamp, new GeneratorData(generator.getTargetP(), generator.getMinP(), generator.getMaxP()) + ); + } + lazyNetwork.release(); + }); + Map> globalGeneratorData = new HashMap<>(); + dataPerGeneratorPerTimestamp.forEach((generatorId, dataPerTimestamp) -> globalGeneratorData.put(generatorId, new TemporalDataImpl<>(dataPerTimestamp))); + return globalGeneratorData; + } + + private record GeneratorData(double p, double pMin, double pMax) { + public static GeneratorData DEFAULT = new GeneratorData(0.0, 0.0, Double.MAX_VALUE); + } } 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 235da9bb8a..8936cb3c72 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 @@ -8,7 +8,6 @@ package com.powsybl.openrao.searchtreerao.marmot; import com.google.auto.service.AutoService; -import com.powsybl.iidm.network.Network; import com.powsybl.openrao.commons.TemporalData; import com.powsybl.openrao.commons.TemporalDataImpl; import com.powsybl.openrao.commons.Unit; @@ -64,7 +63,6 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; import static com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider.TECHNICAL_LOGS; import static com.powsybl.openrao.raoapi.parameters.extensions.SearchTreeRaoRangeActionsOptimizationParameters.RaRangeShrinking.ENABLED; @@ -91,10 +89,18 @@ public class Marmot implements TimeCoupledRaoProvider { @Override public CompletableFuture run(TimeCoupledRaoInput timeCoupledRaoInput, RaoParameters raoParameters) { + // Initiate lazy networks + TemporalData cracs = timeCoupledRaoInput.getRaoInputs().map(RaoInput::getCrac); + TemporalData initialNetworks = MarmotUtils.cloneNetworks(timeCoupledRaoInput.getRaoInputs().map(RaoInput::getNetwork)); + MarmotUtils.releaseAll(timeCoupledRaoInput.getRaoInputs().map(RaoInput::getNetwork)); + initialNetworks.getDataPerTimestamp().values().forEach(LazyNetwork::release); + + TemporalData initialInputs = MarmotUtils.merge(initialNetworks, cracs); + // 1. Run independent RAOs to compute optimal preventive topological remedial actions TECHNICAL_LOGS.info("[MARMOT] ----- Topological optimization [start]"); TemporalData> consideredCnecs = new TemporalDataImpl<>(); - TemporalData topologicalOptimizationResults = runTopologicalOptimization(timeCoupledRaoInput.getRaoInputs(), consideredCnecs, raoParameters); + TemporalData topologicalOptimizationResults = runTopologicalOptimization(initialInputs, consideredCnecs, raoParameters); TECHNICAL_LOGS.info("[MARMOT] ----- Topological optimization [end]"); // 2. Get the initial results from the various independent results to avoid recomputing them @@ -105,7 +111,7 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl // 3. Apply independent topological remedial actions (and preventive range actions if there are no time-coupled constraints) TECHNICAL_LOGS.info("[MARMOT] Applying optimal topological actions on networks"); - ObjectiveFunction fullObjectiveFunction = buildGlobalObjectiveFunction(timeCoupledRaoInput.getRaoInputs().map(RaoInput::getCrac), new GlobalFlowResult(initialResults), raoParameters); + ObjectiveFunction fullObjectiveFunction = buildGlobalObjectiveFunction(cracs, new GlobalFlowResult(initialResults), raoParameters); LinearOptimizationResult initialObjectiveFunctionResult = getInitialObjectiveFunctionResult(initialResults, fullObjectiveFunction); // 4. Evaluate objective function after independent optimizations @@ -113,13 +119,13 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl TemporalData postTopologicalActionsResults = topologicalOptimizationResults.map( raoResult -> ((FastRaoResultImpl) raoResult).getFinalResult() ); - TemporalData initialSetpointResults = getInitialSetpointResults(topologicalOptimizationResults, timeCoupledRaoInput.getRaoInputs()); + TemporalData initialSetpointResults = getInitialSetpointResults(topologicalOptimizationResults, initialInputs); LinearOptimizationResult postTopologicalOptimizationResult = getPostTopologicalOptimizationResult( initialSetpointResults, postTopologicalActionsResults, fullObjectiveFunction, topologicalOptimizationResults, - timeCoupledRaoInput.getRaoInputs().map(individualRaoInput -> individualRaoInput.getCrac().getPreventiveState())); + cracs.map(Crac::getPreventiveState)); // if no time-coupled constraints are defined, the results can be returned // TODO @@ -129,24 +135,15 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl // } // 5. Get and apply topological actions applied in independent optimizations - TemporalData preventiveTopologicalActions = getPreventiveTopologicalActions( - timeCoupledRaoInput.getRaoInputs().map(RaoInput::getCrac), - topologicalOptimizationResults - ); + TemporalData preventiveTopologicalActions = getPreventiveTopologicalActions(cracs, topologicalOptimizationResults); - TemporalData postTopologicalActionsInputs = applyPreventiveTopologicalActionsOnNetworks(timeCoupledRaoInput.getRaoInputs(), preventiveTopologicalActions); - TemporalData inputsForMip = new TemporalDataImpl<>( - postTopologicalActionsInputs.getDataPerTimestamp() - .entrySet() - .stream() - .collect( - Collectors.toMap(Map.Entry::getKey, entry -> RaoInput.build(new LazyNetwork(entry.getValue().getNetwork()), entry.getValue().getCrac()).build()) - ) - ); + TemporalData postTopologicalActionsInputs = applyPreventiveTopologicalActionsOnNetworks(initialInputs, preventiveTopologicalActions); + TemporalData postTopoNetworks = MarmotUtils.cloneNetworks(postTopologicalActionsInputs.map(RaoInput::getNetwork)); + MarmotUtils.releaseAll(postTopologicalActionsInputs.map(RaoInput::getNetwork)); // 6. Create and iteratively solve MIP to find optimal range actions' set-points // Get the curative actions applied in the individual results to be able to apply them during sensitivity computations - TemporalData curativeRemedialActions = MarmotUtils.getAppliedRemedialActionsInCurative(inputsForMip, topologicalOptimizationResults); + TemporalData curativeRemedialActions = MarmotUtils.getAppliedRemedialActionsInCurative(cracs, topologicalOptimizationResults); TECHNICAL_LOGS.info("[MARMOT] ----- Global range actions optimization [start]"); // make fast rao result lighter by keeping only initial flow result and filtered rao result for actions @@ -158,11 +155,8 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl GlobalLinearOptimizationResult fullResults; int counter = 1; do { - // Clone the PostTopoScenario variant to make sure we work on a clean variant every time - inputsForMip.getDataPerTimestamp().values().forEach(raoInput -> { - raoInput.getNetwork().getVariantManager().cloneVariant(raoInput.getNetwork().getVariantManager().getWorkingVariantId(), MIP_SCENARIO, true); - raoInput.getNetwork().getVariantManager().setWorkingVariant(MIP_SCENARIO); - }); + // Clone the post-topological actions networks to make sure we work on a clean variant every time + TemporalData inputsForMip = MarmotUtils.merge(postTopoNetworks, cracs); // Run post topo sensitivity analysis on all timestamps ON CONSIDERED CNECS ONLY (which is why we do it every loop) TECHNICAL_LOGS.info("[MARMOT] Systematic time-coupled sensitivity analysis [start]"); @@ -177,7 +171,7 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl // Build objective function with ONLY THE CONSIDERED CNECS ObjectiveFunction filteredObjectiveFunction = buildFilteredObjectiveFunction( - timeCoupledRaoInput.getRaoInputs().map(RaoInput::getCrac), + cracs, new GlobalFlowResult(initialResults), raoParameters, consideredCnecs @@ -196,6 +190,7 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl consideredCnecs, filteredObjectiveFunction ); + MarmotUtils.releaseAll(inputsForMip.map(RaoInput::getNetwork)); TECHNICAL_LOGS.info("[MARMOT] ----- Global range actions optimization [end] for iteration {}", counter); // Compute the flows on ALL the cnecs to check if the worst cnecs have changed and were considered in the MIP or not @@ -248,6 +243,7 @@ private TemporalData getInitialSetpointResults(Tempor setPointMap ); initialSetpointResults.put(timestamp, rangeActionSetpointResult); + MarmotUtils.releaseNetwork(raoInput.getNetwork()); }); return initialSetpointResults; } @@ -404,9 +400,7 @@ private static TemporalData applyActionsAndRunFullLoadflow(T curativeRemedialActions.getData(timestamp).orElseThrow(), initialResults.getData(timestamp).orElseThrow(), raoParameters)); - if (raoInput.getNetwork() instanceof LazyNetwork lazyNetwork) { - lazyNetwork.release(); - } + MarmotUtils.releaseNetwork(raoInput.getNetwork()); }); return prePerimeterResults; } @@ -428,11 +422,7 @@ private static TemporalData runTopologicalOptimization(TemporalData applyPreventiveTopologicalActionsOnNetwork RaoInput raoInput = raoInputs.getData(timestamp).orElseThrow(); NetworkActionsResult networkActionsResult = preventiveTopologicalActionsResults.getData(timestamp).orElseThrow(); MarmotUtils.applyPreventiveRemedialActions(raoInput, networkActionsResult, INITIAL_SCENARIO, POST_TOPO_SCENARIO); - postTopologicalActionsInputs.put(timestamp, RaoInput.build(raoInput.getNetwork(), raoInput.getCrac()).build()); - if (raoInput.getNetwork() instanceof LazyNetwork lazyNetwork) { - lazyNetwork.release(); - } + postTopologicalActionsInputs.put(timestamp, RaoInput.build(new LazyNetwork(raoInput.getNetwork()), raoInput.getCrac()).build()); + MarmotUtils.releaseNetwork(raoInput.getNetwork()); }); return new TemporalDataImpl<>(postTopologicalActionsInputs); } @@ -465,13 +453,17 @@ private static TemporalData runAllSensitivityAnalysesBasedOn TemporalData> consideredCnecs) { TemporalData prePerimeterResults = new TemporalDataImpl<>(); raoInputs.getTimestamps().forEach(timestamp -> + { + RaoInput raoInput = raoInputs.getData(timestamp).orElseThrow(); prePerimeterResults.put(timestamp, runSensitivityAnalysisBasedOnInitialResult( - raoInputs.getData(timestamp).orElseThrow(), + raoInput, curativeRemedialActions.getData(timestamp).orElseThrow(), initialFlowResults.getData(timestamp).orElseThrow(), raoParameters, consideredCnecs.getData(timestamp).orElseThrow() - ))); + )); + MarmotUtils.releaseNetwork(raoInput.getNetwork()); + }); return prePerimeterResults; } @@ -505,21 +497,25 @@ private static GlobalLinearOptimizationResult optimizeLinearRemedialActions(Time // no objective function defined in individual IteratingLinearOptimizerInputs as it is global Map linearOptimizerInputPerTimestamp = new HashMap<>(); - raoInput.getRaoInputs().getTimestamps().forEach(timestamp -> linearOptimizerInputPerTimestamp.put(timestamp, - IteratingLinearOptimizerInput.create() - .withNetwork(raoInput.getRaoInputs().getData(timestamp).orElseThrow().getNetwork()) - .withOptimizationPerimeter(optimizationPerimeterPerTimestamp.getData(timestamp).orElseThrow() - .copyWithFilteredAvailableHvdcRangeAction(raoInput.getRaoInputs().getData(timestamp).orElseThrow().getNetwork())) - .withInitialFlowResult(initialResults.getData(timestamp).orElseThrow()) - .withPrePerimeterFlowResult(initialResults.getData(timestamp).orElseThrow()) - .withPreOptimizationFlowResult(postTopologicalActionsResults.getData(timestamp).orElseThrow()) - .withPrePerimeterSetpoints(initialSetpoints.getData(timestamp).orElseThrow()) - .withPreOptimizationSensitivityResult(postTopologicalActionsResults.getData(timestamp).orElseThrow()) - .withPreOptimizationAppliedRemedialActions(curativeRemedialActions.getData(timestamp).orElseThrow()) - .withToolProvider(ToolProvider.buildFromRaoInputAndParameters(raoInput.getRaoInputs().getData(timestamp).orElseThrow(), parameters)) - .withOutageInstant(raoInput.getRaoInputs().getData(timestamp).orElseThrow().getCrac().getOutageInstant()) - .withAppliedNetworkActionsInPrimaryState(preventiveTopologicalActions.getData(timestamp).orElseThrow()) - .build())); + raoInput.getRaoInputs().getDataPerTimestamp().forEach((timestamp, individualRaoInput) -> { + IteratingLinearOptimizerInput iteratingLinearOptimizerInput = IteratingLinearOptimizerInput.create() + .withNetwork(new LazyNetwork(individualRaoInput.getNetwork())) + .withOptimizationPerimeter(optimizationPerimeterPerTimestamp.getData(timestamp).orElseThrow() + .copyWithFilteredAvailableHvdcRangeAction(individualRaoInput.getNetwork())) + .withInitialFlowResult(initialResults.getData(timestamp).orElseThrow()) + .withPrePerimeterFlowResult(initialResults.getData(timestamp).orElseThrow()) + .withPreOptimizationFlowResult(postTopologicalActionsResults.getData(timestamp).orElseThrow()) + .withPrePerimeterSetpoints(initialSetpoints.getData(timestamp).orElseThrow()) + .withPreOptimizationSensitivityResult(postTopologicalActionsResults.getData(timestamp).orElseThrow()) + .withPreOptimizationAppliedRemedialActions(curativeRemedialActions.getData(timestamp).orElseThrow()) + .withToolProvider(ToolProvider.buildFromRaoInputAndParameters(raoInput.getRaoInputs().getData(timestamp).orElseThrow(), parameters)) + .withOutageInstant(individualRaoInput.getCrac().getOutageInstant()) + .withAppliedNetworkActionsInPrimaryState(preventiveTopologicalActions.getData(timestamp).orElseThrow()) + .build(); + MarmotUtils.releaseNetwork(individualRaoInput.getNetwork()); + MarmotUtils.releaseNetwork(iteratingLinearOptimizerInput.network()); + linearOptimizerInputPerTimestamp.put(timestamp, iteratingLinearOptimizerInput); + }); TimeCoupledIteratingLinearOptimizerInput timeCoupledLinearOptimizerInput = new TimeCoupledIteratingLinearOptimizerInput( new TemporalDataImpl<>(linearOptimizerInputPerTimestamp), objectiveFunction, raoInput.getTimeCoupledConstraints()); diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java index d31799e65f..ee7f301e1c 100644 --- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java @@ -19,6 +19,7 @@ import com.powsybl.openrao.data.crac.api.rangeaction.RangeAction; import com.powsybl.openrao.data.raoresult.api.ComputationStatus; import com.powsybl.openrao.data.raoresult.api.RaoResult; +import com.powsybl.openrao.raoapi.LazyNetwork; import com.powsybl.openrao.raoapi.RaoInput; import com.powsybl.openrao.raoapi.parameters.RaoParameters; import com.powsybl.openrao.searchtreerao.castor.algorithm.PrePerimeterSensitivityAnalysis; @@ -36,6 +37,7 @@ import java.util.Map; import java.util.Set; import java.util.function.Function; +import java.util.stream.Collectors; /** * @author Thomas Bouquet {@literal } @@ -73,10 +75,10 @@ public static PrePerimeterResult runInitialPrePerimeterSensitivityAnalysisWithou ).runBasedOnInitialResults(network, initialResult, null, curativeRemedialActions); } - public static TemporalData getAppliedRemedialActionsInCurative(TemporalData inputs, TemporalData raoResults) { + public static TemporalData getAppliedRemedialActionsInCurative(TemporalData cracs, TemporalData raoResults) { TemporalData curativeRemedialActions = new TemporalDataImpl<>(); - inputs.getTimestamps().forEach(timestamp -> { - Crac crac = inputs.getData(timestamp).orElseThrow().getCrac(); + cracs.getTimestamps().forEach(timestamp -> { + Crac crac = cracs.getData(timestamp).orElseThrow(); RaoResult raoResult = raoResults.getData(timestamp).orElseThrow(); AppliedRemedialActions appliedRemedialActions = new AppliedRemedialActions(); // TODO: maybe check it is indeed curative @@ -164,4 +166,29 @@ public static void applyPreventiveRemedialActions(RaoInput raoInput, NetworkActi networkActionsToBeApplied.forEach(networkAction -> networkAction.apply(network)); } } + + public static TemporalData cloneNetworks(TemporalData networks) { + return new TemporalDataImpl<>( + networks.getDataPerTimestamp().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> new LazyNetwork(entry.getValue()))) + ); + } + + public static TemporalData merge(TemporalData networks, TemporalData cracs) { + Map raoInputs = new HashMap<>(); + networks.getDataPerTimestamp().forEach((timestamp, network) -> { + raoInputs.put(timestamp, RaoInput.build(network, cracs.getData(timestamp).orElseThrow()).build()); + network.release(); + }); + return new TemporalDataImpl<>(raoInputs); + } + + public static void releaseAll(TemporalData networks) { + networks.getDataPerTimestamp().values().forEach(MarmotUtils::releaseNetwork); + } + + public static void releaseNetwork(Network network) { + if (network instanceof LazyNetwork lazyNetwork) { + lazyNetwork.release(); + } + } } diff --git a/tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature b/tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature index 557a299df3..4fae98c392 100644 --- a/tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature +++ b/tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature @@ -139,30 +139,30 @@ Feature: US 93.3: CORE IDCC data Given time-coupled RefProg file is "idcc/20240224-FID2-632-v3-10V1001C--00264T-to-10V1001C--00085T.xml" Given time-coupled rao inputs for CORE are: | Timestamp | Network | - | 2024-02-24 00:30 | 20240224_0030_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 01:30 | 20240224_0130_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 02:30 | 20240224_0230_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 03:30 | 20240224_0330_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 04:30 | 20240224_0430_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 05:30 | 20240224_0530_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 06:30 | 20240224_0630_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 07:30 | 20240224_0730_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + # | 2024-02-24 00:30 | 20240224_0030_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + # | 2024-02-24 01:30 | 20240224_0130_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + # | 2024-02-24 02:30 | 20240224_0230_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + # | 2024-02-24 03:30 | 20240224_0330_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + # | 2024-02-24 04:30 | 20240224_0430_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + # | 2024-02-24 05:30 | 20240224_0530_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + # | 2024-02-24 06:30 | 20240224_0630_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + # | 2024-02-24 07:30 | 20240224_0730_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | | 2024-02-24 08:30 | 20240224_0830_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | | 2024-02-24 09:30 | 20240224_0930_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | | 2024-02-24 10:30 | 20240224_1030_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 11:30 | 20240224_1130_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 12:30 | 20240224_1230_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 13:30 | 20240224_1330_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 14:30 | 20240224_1430_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 15:30 | 20240224_1530_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 16:30 | 20240224_1630_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 17:30 | 20240224_1730_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 18:30 | 20240224_1830_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 19:30 | 20240224_1930_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 20:30 | 20240224_2030_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 21:30 | 20240224_2130_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 22:30 | 20240224_2230_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 23:30 | 20240224_2330_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + # | 2024-02-24 11:30 | 20240224_1130_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + # | 2024-02-24 12:30 | 20240224_1230_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + # | 2024-02-24 13:30 | 20240224_1330_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + # | 2024-02-24 14:30 | 20240224_1430_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + # | 2024-02-24 15:30 | 20240224_1530_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + # | 2024-02-24 16:30 | 20240224_1630_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + # | 2024-02-24 17:30 | 20240224_1730_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + # | 2024-02-24 18:30 | 20240224_1830_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + # | 2024-02-24 19:30 | 20240224_1930_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + # | 2024-02-24 20:30 | 20240224_2030_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + # | 2024-02-24 21:30 | 20240224_2130_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + # | 2024-02-24 22:30 | 20240224_2230_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + # | 2024-02-24 23:30 | 20240224_2330_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | When I launch marmot When I export marmot results to "raoresults/results_20240224.zip" From 15434eddccf68dbc42addd1303663212794aa956 Mon Sep 17 00:00:00 2001 From: Godelaine de Montmorillon Date: Tue, 7 Apr 2026 16:04:41 +0200 Subject: [PATCH 48/64] p0RespectsConstraints method --- data/crac/crac-util/pom.xml | 5 + .../openrao/data/crac/util/IcsImporter.java | 301 +++++++++--------- .../data/crac/util/IcsImporterTest.java | 111 ++++--- .../src/test/resources/ics/series.csv | 2 +- .../src/test/resources/ics/static.csv | 2 +- .../features/4_time_coupled/US93_5.feature | 8 +- tests/src/test/resources/logback.xml | 7 + 7 files changed, 232 insertions(+), 204 deletions(-) diff --git a/data/crac/crac-util/pom.xml b/data/crac/crac-util/pom.xml index 9bfcdee722..36bbe19e0b 100644 --- a/data/crac/crac-util/pom.xml +++ b/data/crac/crac-util/pom.xml @@ -41,6 +41,11 @@
+ + ch.qos.logback + logback-classic + test + org.junit.jupiter junit-jupiter diff --git a/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java b/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java index 5bd7da16cc..f9cb0181ec 100644 --- a/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java +++ b/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java @@ -107,10 +107,10 @@ public static TimeCoupledRaoInput populateInputWithICS(TimeCoupledRaoInput timeC }); CSVFormat csvFormat = CSVFormat.DEFAULT.builder() - .setDelimiter(";") - .setHeader() - .setSkipHeaderRecord(true) - .get(); + .setDelimiter(";") + .setHeader() + .setSkipHeaderRecord(true) + .get(); Iterable staticCsvRecords = csvFormat.parse(new InputStreamReader(staticInputStream)); Iterable seriesCsvRecords = csvFormat.parse(new InputStreamReader(seriesInputStream)); @@ -134,20 +134,14 @@ public static TimeCoupledRaoInput populateInputWithICS(TimeCoupledRaoInput timeC if (shouldBeImported(staticRecord, weightPerNodePerGsk)) { String raId = staticRecord.get(RA_RD_ID); Map seriesPerType = seriesPerIdAndType.get(raId); -// if (seriesPerType != null && -// seriesPerType.containsKey(P0) && -// seriesPerType.containsKey(RDP_DOWN) && -// seriesPerType.containsKey(RDP_UP) && -// rangeIsOkay(seriesPerType, timeCoupledRaoInput.getTimestampsToRun().stream().sorted().toList()) && -// p0RespectsGradients(staticRecord, seriesPerType.get(P0), timeCoupledRaoInput.getTimestampsToRun().stream().sorted().toList())) { -// p0RespectsConstraints(staticRecord, seriesPerType, timeCoupledRaoInput.getTimestampsToRun().stream().sorted().toList()); -// } if (seriesPerType != null && - seriesPerType.containsKey(P0) && - seriesPerType.containsKey(RDP_DOWN) && - seriesPerType.containsKey(RDP_UP) && - rangeIsOkay(seriesPerType, sortedTimestamps) && - p0RespectsGradients(staticRecord, seriesPerType.get(P0), sortedTimestamps)) { + seriesPerType.containsKey(P0) && + seriesPerType.containsKey(RDP_DOWN) && + seriesPerType.containsKey(RDP_UP) && + rangeIsOkay(seriesPerType, sortedTimestamps) && + p0RespectsGradients(staticRecord, seriesPerType.get(P0), sortedTimestamps) && + p0RespectsConstraints(staticRecord, seriesPerType, timeCoupledRaoInput.getTimestampsToRun().stream().sorted().toList()) + ) { if (staticRecord.get(RD_DESCRIPTION_MODE).equalsIgnoreCase(NODE)) { importNodeRedispatchingAction(timeCoupledRaoInput, staticRecord, initialNetworks, seriesPerType, raId); } else { @@ -228,13 +222,13 @@ private static void importGskRedispatchingAction(TimeCoupledRaoInput timeCoupled builder.withLagTime(parseDoubleWithPossibleCommas(staticRecord.get(LAG_TIME))); } if (staticRecord.get(SHUTDOWN_ALLOWED).isEmpty() || - !staticRecord.get(SHUTDOWN_ALLOWED).equalsIgnoreCase(TRUE) && !staticRecord.get(SHUTDOWN_ALLOWED).equalsIgnoreCase(FALSE)) { + !staticRecord.get(SHUTDOWN_ALLOWED).equalsIgnoreCase(TRUE) && !staticRecord.get(SHUTDOWN_ALLOWED).equalsIgnoreCase(FALSE)) { throw new OpenRaoException("Could not parse shutDownAllowed value " + staticRecord.get(SHUTDOWN_ALLOWED) + " for nodeId " + nodeId); } else { builder.withShutDownAllowed(Boolean.parseBoolean(staticRecord.get(SHUTDOWN_ALLOWED))); } if (staticRecord.get(STARTUP_ALLOWED).isEmpty() || - !staticRecord.get(STARTUP_ALLOWED).equalsIgnoreCase(TRUE) && !staticRecord.get(STARTUP_ALLOWED).equalsIgnoreCase(FALSE)) { + !staticRecord.get(STARTUP_ALLOWED).equalsIgnoreCase(TRUE) && !staticRecord.get(STARTUP_ALLOWED).equalsIgnoreCase(FALSE)) { throw new OpenRaoException("Could not parse startUpAllowed value " + staticRecord.get(STARTUP_ALLOWED) + " for nodeId " + nodeId); } else { builder.withStartUpAllowed(Boolean.parseBoolean(staticRecord.get(STARTUP_ALLOWED))); @@ -253,16 +247,16 @@ private static void importGskRedispatchActionForOneTimestamp(CSVRecord staticRec Crac crac = raoInput.getCrac(); double p0 = parseDoubleWithPossibleCommas(seriesPerType.get(P0).get(dateTime.getHour() + OFFSET)); InjectionRangeActionAdder injectionRangeActionAdder = crac.newInjectionRangeAction() - .withId(raId + RD_SUFFIX) - .withName(staticRecord.get(GENERATOR_NAME)) - .withInitialSetpoint(p0) - .withVariationCost(costUp, VariationDirection.UP) - .withVariationCost(costDown, VariationDirection.DOWN) - //.withActivationCost(ACTIVATION_COST) - .newRange() - .withMin(p0 - parseDoubleWithPossibleCommas(seriesPerType.get(RDP_DOWN).get(dateTime.getHour() + OFFSET))) - .withMax(p0 + parseDoubleWithPossibleCommas(seriesPerType.get(RDP_UP).get(dateTime.getHour() + OFFSET))) - .add(); + .withId(raId + RD_SUFFIX) + .withName(staticRecord.get(GENERATOR_NAME)) + .withInitialSetpoint(p0) + .withVariationCost(costUp, VariationDirection.UP) + .withVariationCost(costDown, VariationDirection.DOWN) + //.withActivationCost(ACTIVATION_COST) + .newRange() + .withMin(p0 - parseDoubleWithPossibleCommas(seriesPerType.get(RDP_DOWN).get(dateTime.getHour() + OFFSET))) + .withMax(p0 + parseDoubleWithPossibleCommas(seriesPerType.get(RDP_UP).get(dateTime.getHour() + OFFSET))) + .add(); weightPerNode.forEach((nodeId, shiftKey) -> { injectionRangeActionAdder.withNetworkElementAndKey(shiftKey, networkElementPerGskElement.get(nodeId)); @@ -270,13 +264,13 @@ private static void importGskRedispatchActionForOneTimestamp(CSVRecord staticRec if (staticRecord.get(PREVENTIVE).equalsIgnoreCase(TRUE)) { injectionRangeActionAdder.newOnInstantUsageRule() - .withInstant(crac.getPreventiveInstant().getId()) - .add(); + .withInstant(crac.getPreventiveInstant().getId()) + .add(); } if (importCurative && staticRecord.get(CURATIVE).equalsIgnoreCase(TRUE)) { injectionRangeActionAdder.newOnInstantUsageRule() - .withInstant(crac.getLastInstant().getId()) - .add(); + .withInstant(crac.getLastInstant().getId()) + .add(); } injectionRangeActionAdder.add(); @@ -313,13 +307,13 @@ private static void importNodeRedispatchingAction(TimeCoupledRaoInput timeCouple builder.withLagTime(parseDoubleWithPossibleCommas(staticRecord.get(LAG_TIME))); } if (staticRecord.get(SHUTDOWN_ALLOWED).isEmpty() || - !staticRecord.get(SHUTDOWN_ALLOWED).equalsIgnoreCase(TRUE) && !staticRecord.get(SHUTDOWN_ALLOWED).equalsIgnoreCase(FALSE)) { + !staticRecord.get(SHUTDOWN_ALLOWED).equalsIgnoreCase(TRUE) && !staticRecord.get(SHUTDOWN_ALLOWED).equalsIgnoreCase(FALSE)) { throw new OpenRaoException("Could not parse shutDownAllowed value " + staticRecord.get(SHUTDOWN_ALLOWED) + " for raId " + raId); } else { builder.withShutDownAllowed(Boolean.parseBoolean(staticRecord.get(SHUTDOWN_ALLOWED))); } if (staticRecord.get(STARTUP_ALLOWED).isEmpty() || - !staticRecord.get(STARTUP_ALLOWED).equalsIgnoreCase(TRUE) && !staticRecord.get(STARTUP_ALLOWED).equalsIgnoreCase(FALSE)) { + !staticRecord.get(STARTUP_ALLOWED).equalsIgnoreCase(TRUE) && !staticRecord.get(STARTUP_ALLOWED).equalsIgnoreCase(FALSE)) { throw new OpenRaoException("Could not parse startUpAllowed value " + staticRecord.get(STARTUP_ALLOWED) + " for raId " + raId); } else { builder.withStartUpAllowed(Boolean.parseBoolean(staticRecord.get(STARTUP_ALLOWED))); @@ -336,33 +330,33 @@ private static void importNodeRedispatchingActionForOneTimestamp(CSVRecord stati Crac crac = raoInput.getCrac(); double p0 = parseDoubleWithPossibleCommas(seriesPerType.get(P0).get(dateTime.getHour() + OFFSET)); InjectionRangeActionAdder injectionRangeActionAdder = crac.newInjectionRangeAction() - .withId(raId + RD_SUFFIX) - .withName(staticRecord.get(GENERATOR_NAME)) - .withNetworkElement(networkElementId) - .withInitialSetpoint(p0) - .withVariationCost(costUp, VariationDirection.UP) - .withVariationCost(costDown, VariationDirection.DOWN) - //.withActivationCost(ACTIVATION_COST) - .newRange() - .withMin(p0 - parseDoubleWithPossibleCommas(seriesPerType.get(RDP_DOWN).get(dateTime.getHour() + OFFSET))) - .withMax(p0 + parseDoubleWithPossibleCommas(seriesPerType.get(RDP_UP).get(dateTime.getHour() + OFFSET))) - .add(); + .withId(raId + RD_SUFFIX) + .withName(staticRecord.get(GENERATOR_NAME)) + .withNetworkElement(networkElementId) + .withInitialSetpoint(p0) + .withVariationCost(costUp, VariationDirection.UP) + .withVariationCost(costDown, VariationDirection.DOWN) + //.withActivationCost(ACTIVATION_COST) + .newRange() + .withMin(p0 - parseDoubleWithPossibleCommas(seriesPerType.get(RDP_DOWN).get(dateTime.getHour() + OFFSET))) + .withMax(p0 + parseDoubleWithPossibleCommas(seriesPerType.get(RDP_UP).get(dateTime.getHour() + OFFSET))) + .add(); if (staticRecord.get(PREVENTIVE).equalsIgnoreCase(TRUE)) { injectionRangeActionAdder.newOnInstantUsageRule() - .withInstant(crac.getPreventiveInstant().getId()) - .add(); + .withInstant(crac.getPreventiveInstant().getId()) + .add(); } if (importCurative && staticRecord.get(CURATIVE).equalsIgnoreCase(TRUE)) { injectionRangeActionAdder.newOnInstantUsageRule() - .withInstant(crac.getLastInstant().getId()) - .add(); + .withInstant(crac.getLastInstant().getId()) + .add(); } injectionRangeActionAdder.add(); } private static String processNetworks(String - nodeId, TemporalData initialNetworks, Map seriesPerType, double shiftKey) { + nodeId, TemporalData initialNetworks, Map seriesPerType, double shiftKey) { String generatorId = seriesPerType.get(P0).get(RA_RD_ID) + "_" + nodeId + GENERATOR_SUFFIX; for (Map.Entry entry : initialNetworks.getDataPerTimestamp().entrySet()) { Bus bus = findBus(nodeId, entry.getValue()); @@ -378,7 +372,7 @@ private static String processNetworks(String } private static Optional parseValue(Map seriesPerType, String key, OffsetDateTime - timestamp, double shiftKey) { + timestamp, double shiftKey) { if (seriesPerType.containsKey(key)) { CSVRecord series = seriesPerType.get(key); String value = series.get(timestamp.getHour() + OFFSET); @@ -408,39 +402,36 @@ private static Bus findBus(String nodeId, Network network) { private static void processBus(Bus bus, String generatorId, Double p0, double pMinRd) { bus.getVoltageLevel().newGenerator() - .setBus(bus.getId()) - .setEnsureIdUnicity(true) - .setId(generatorId) - .setMaxP(999999) - .setMinP(pMinRd) - .setTargetP(p0) - .setTargetQ(0) - .setTargetV(bus.getVoltageLevel().getNominalV()) - .setVoltageRegulatorOn(false) - .add() - .setFictitious(true); + .setBus(bus.getId()) + .setEnsureIdUnicity(true) + .setId(generatorId) + .setMaxP(999999) + .setMinP(pMinRd) + .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(); + .setBus(bus.getId()) + .setEnsureIdUnicity(true) + .setId(bus.getId() + "_LOAD") + .setP0(p0) + .setQ0(0) + .setLoadType(LoadType.FICTITIOUS) + .add(); } private static boolean shouldBeImported(CSVRecord - staticRecord, Map> weightPerNodePerGsk) { + staticRecord, Map> weightPerNodePerGsk) { return (staticRecord.get(RD_DESCRIPTION_MODE).equalsIgnoreCase(NODE) || weightPerNodePerGsk.containsKey(staticRecord.get(UCT_NODE_OR_GSK_ID))) && - (staticRecord.get(PREVENTIVE).equalsIgnoreCase(TRUE) /*|| staticRecord.get(CURATIVE).equalsIgnoreCase(TRUE)*/); + (staticRecord.get(PREVENTIVE).equalsIgnoreCase(TRUE) /*|| staticRecord.get(CURATIVE).equalsIgnoreCase(TRUE)*/); } - private static void p0RespectsConstraints(CSVRecord staticRecord, Map seriesRecord, List dateTimes) { - // 1) check that P0 > Pmin or P0 < 1d - // 2) check that if shutDown not allowe, no switch to 0 - // 3) check that if startUp not allowed, no switch from P0 < 1 to P0 > Pmin - CSVRecord p0 = seriesRecord.get(P0); + private static boolean p0RespectsConstraints(CSVRecord staticRecord, Map seriesRecord, List dateTimes) { + CSVRecord p0 = seriesRecord.get(P0); Boolean shutDownAllowed = Boolean.parseBoolean(staticRecord.get(SHUTDOWN_ALLOWED)); Boolean startUpAllowed = Boolean.parseBoolean(staticRecord.get(STARTUP_ALLOWED)); Optional lead = Optional.empty(); @@ -455,10 +446,8 @@ private static void p0RespectsConstraints(CSVRecord staticRecord, Map dateTimeIterator = dateTimes.iterator(); OffsetDateTime currentDateTime = dateTimeIterator.next(); - boolean count_lead = false; - int lead_count = 0; boolean count_lag = false; - int lag_count = 0; + int count_consecutive_null_values = 0; while (dateTimeIterator.hasNext()) { OffsetDateTime nextDateTime = dateTimeIterator.next(); double next_p0 = parseDoubleWithPossibleCommas(p0.get(nextDateTime.getHour() + OFFSET)); @@ -466,103 +455,105 @@ private static void p0RespectsConstraints(CSVRecord staticRecord, Map pMinRD = parseValue(seriesRecord, P_MIN_RD, currentDateTime, 1); double pMin = pMinRD.orElse(ON_POWER_THRESHOLD); - if (count_lead) { - if (current_p0 < pMin) { - if (lead_count < lead.get()) { - // DO NOT IMPORT - BUSINESS_WARNS.warn("RA {} was ON after start up for only {} altough lead is {}", staticRecord.get(0), count_lead, lead.get()); - count_lead = false; - lead_count = 0; - } - } else { - lead_count += 1; - } - } - - if (count_lag) { - if (current_p0 >= pMin) { - if (lag_count < lagAndLead.get()) { - // DO NOT IMPORT - BUSINESS_WARNS.warn("RA {} was OFF after shutDown for only {} altough lagAndLead is {}", staticRecord.get(0), count_lag, lagAndLead.get()); - count_lag = false; - lag_count = 0; - } - } else { - lag_count += 1; - } + if (current_p0 < pMin) { + count_consecutive_null_values += 1; } - - + // 1) Pmin not respected if (current_p0 < pMin && current_p0 > ON_POWER_THRESHOLD) { - BUSINESS_WARNS.warn("RA {} has P0 at {} and Pmin at {}", staticRecord.get(0), current_p0, pMin); + BUSINESS_WARNS.warn("DO NOT IMPORT - Timestamp {} - RA {} has P0 at {} and Pmin at {}", currentDateTime, staticRecord.get(0), current_p0, pMin); + return false; } - if (current_p0 < pMin && next_p0 > pMin) { + + // 2) Starting up next timestamp + if (current_p0 < pMin && next_p0 >= pMin) { if (!startUpAllowed) { - BUSINESS_WARNS.warn("RA {} starting up even though it's prohibited", staticRecord.get(0)); + BUSINESS_WARNS.warn("DO NOT IMPORT - Timestamp {} - RA {} starting up even though it's prohibited", currentDateTime, staticRecord.get(0)); + return false; } - // TODO : integerer la notion d'arrondi comme dans le filler : lead de 1 => - // TODO : forcer le passage par Pmin pour le lead et le lag => une modification de P0 - if (lead.isPresent()) { - BUSINESS_WARNS.warn("RA {} starting up at {}. TODO : check lead ({}) is respected", staticRecord.get(0), currentDateTime.getHour(), lead); - count_lead = true; + if (lead.isPresent() && count_consecutive_null_values < lead.get()) { + BUSINESS_WARNS.warn("DO NOT IMPORT - Timestamp {} - RA {} was OFF before start up for only {} timestamps altough lead is {}", currentDateTime, staticRecord.get(0), count_consecutive_null_values, lead.get()); + return false; + } + if (count_lag) { + if (count_consecutive_null_values < lagAndLead.get()) { + BUSINESS_WARNS.warn("DO NOT IMPORT - Timestamp {} - RA {} was OFF after shutDown for only {} timestamps although lagAndLead is {}", currentDateTime, staticRecord.get(0), count_consecutive_null_values, lagAndLead.get()); + return false; + } + // Re-initialize + count_lag = false; } } - if (current_p0 > pMin && next_p0 < pMin) { + // 3) Shutting down next timestamp + if (current_p0 >= pMin && next_p0 < pMin) { if (!shutDownAllowed) { - BUSINESS_WARNS.warn("RA {} shutting down even though it's prohibited", staticRecord.get(0)); + BUSINESS_WARNS.warn("DO NOT IMPORT - Timestamp {} - RA {} shutting down even though it's prohibited", currentDateTime, staticRecord.get(0)); + return false; } if (lagAndLead.isPresent()) { - BUSINESS_WARNS.warn("RA {} shutting down at {}. TODO : check lagAndLead ({}) is respected", staticRecord.get(0), currentDateTime.getHour(), lagAndLead); count_lag = true; } } - } - } - - private static boolean p0RespectsGradients(CSVRecord staticRecord, CSVRecord - p0record, List dateTimes) { - double maxGradient = staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT).isEmpty() ? - MAX_GRADIENT : parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT)); - double minGradient = staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT).isEmpty() ? - -MAX_GRADIENT : -parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT)); - - Iterator dateTimeIterator = dateTimes.iterator(); - OffsetDateTime currentDateTime = dateTimeIterator.next(); - while (dateTimeIterator.hasNext()) { - OffsetDateTime nextDateTime = dateTimeIterator.next(); - double diff = parseDoubleWithPossibleCommas(p0record.get(nextDateTime.getHour() + OFFSET)) - parseDoubleWithPossibleCommas(p0record.get(currentDateTime.getHour() + OFFSET)); - if (diff > maxGradient || diff < minGradient) { - BUSINESS_WARNS.warn( - "Redispatching action {} will not be imported because it does not respect power gradients : min/max/diff {} {} {}", - staticRecord.get(0), minGradient, maxGradient, diff - ); - return false; + // Re-init count_consecutive_null_values + if (current_p0 >= pMin) { + count_consecutive_null_values = 0; } currentDateTime = nextDateTime; } + // Last timestamp + double current_p0 = parseDoubleWithPossibleCommas(p0.get(currentDateTime.getHour() + OFFSET)); + Optional pMinRD = parseValue(seriesRecord, P_MIN_RD, currentDateTime, 1); + double pMin = pMinRD.orElse(ON_POWER_THRESHOLD); + if (current_p0 < pMin && current_p0 > ON_POWER_THRESHOLD) { + BUSINESS_WARNS.warn("DO NOT IMPORT - Timestamp {} - RA {} has P0 at {} and Pmin at {}", currentDateTime, staticRecord.get(0), current_p0, pMin); + return false; + } return true; } - private static boolean rangeIsOkay(Map seriesPerType, List dateTimes) { - double maxRange = 0.; - for (OffsetDateTime dateTime : dateTimes) { - double rdpPlus = parseDoubleWithPossibleCommas(seriesPerType.get(RDP_UP).get(dateTime.getHour() + OFFSET)); - double rdpMinus = parseDoubleWithPossibleCommas(seriesPerType.get(RDP_DOWN).get(dateTime.getHour() + OFFSET)); - maxRange = Math.max(maxRange, rdpPlus + rdpMinus); - if (rdpPlus < -1e-6 || rdpMinus < -1e-6) { - BUSINESS_WARNS.warn("Redispatching action {} will not be imported because of RDP+ {} or RDP- {} is negative", seriesPerType.get(P0).get(RA_RD_ID), rdpPlus, rdpMinus); - return false; + private static boolean p0RespectsGradients (CSVRecord staticRecord, CSVRecord + p0record, List < OffsetDateTime > dateTimes){ + double maxGradient = staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT).isEmpty() ? + MAX_GRADIENT : parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT)); + double minGradient = staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT).isEmpty() ? + -MAX_GRADIENT : -parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT)); + + Iterator dateTimeIterator = dateTimes.iterator(); + OffsetDateTime currentDateTime = dateTimeIterator.next(); + while (dateTimeIterator.hasNext()) { + OffsetDateTime nextDateTime = dateTimeIterator.next(); + double diff = parseDoubleWithPossibleCommas(p0record.get(nextDateTime.getHour() + OFFSET)) - parseDoubleWithPossibleCommas(p0record.get(currentDateTime.getHour() + OFFSET)); + if (diff > maxGradient || diff < minGradient) { + BUSINESS_WARNS.warn( + "Redispatching action {} will not be imported because it does not respect power gradients : min/max/diff {} {} {}", + staticRecord.get(0), minGradient, maxGradient, diff + ); + return false; + } + currentDateTime = nextDateTime; } + return true; } - if (maxRange < 1) { - BUSINESS_WARNS.warn("Redispatching action {} will not be imported because max range in the day {} MW is too small", seriesPerType.get(P0).get(RA_RD_ID), maxRange); - return false; + + private static boolean rangeIsOkay (Map < String, CSVRecord > seriesPerType, List < OffsetDateTime > dateTimes){ + double maxRange = 0.; + for (OffsetDateTime dateTime : dateTimes) { + double rdpPlus = parseDoubleWithPossibleCommas(seriesPerType.get(RDP_UP).get(dateTime.getHour() + OFFSET)); + double rdpMinus = parseDoubleWithPossibleCommas(seriesPerType.get(RDP_DOWN).get(dateTime.getHour() + OFFSET)); + maxRange = Math.max(maxRange, rdpPlus + rdpMinus); + if (rdpPlus < -1e-6 || rdpMinus < -1e-6) { + BUSINESS_WARNS.warn("Redispatching action {} will not be imported because of RDP+ {} or RDP- {} is negative", seriesPerType.get(P0).get(RA_RD_ID), rdpPlus, rdpMinus); + return false; + } + } + if (maxRange < 1) { + BUSINESS_WARNS.warn("Redispatching action {} will not be imported because max range in the day {} MW is too small", seriesPerType.get(P0).get(RA_RD_ID), maxRange); + return false; + } + return true; } - return true; - } - private static double parseDoubleWithPossibleCommas(String string) { - return Double.parseDouble(string.replaceAll(",", ".")); + private static double parseDoubleWithPossibleCommas (String string){ + return Double.parseDouble(string.replaceAll(",", ".")); + } } -} diff --git a/data/crac/crac-util/src/test/java/com/powsybl/openrao/data/crac/util/IcsImporterTest.java b/data/crac/crac-util/src/test/java/com/powsybl/openrao/data/crac/util/IcsImporterTest.java index b5ff2e810a..0d092a8c3b 100644 --- a/data/crac/crac-util/src/test/java/com/powsybl/openrao/data/crac/util/IcsImporterTest.java +++ b/data/crac/crac-util/src/test/java/com/powsybl/openrao/data/crac/util/IcsImporterTest.java @@ -44,8 +44,20 @@ class IcsImporterTest { private TimeCoupledRaoInput timeCoupledRaoInput; private Crac crac1; private Crac crac2; + private Crac crac3; + private Crac crac4; + private Crac crac5; + private Crac crac6; + private Crac crac7; + private Crac crac8; private final OffsetDateTime timestamp1 = OffsetDateTime.of(2025, 2, 13, 0, 30, 0, 0, ZoneOffset.UTC); private final OffsetDateTime timestamp2 = OffsetDateTime.of(2025, 2, 13, 1, 30, 0, 0, ZoneOffset.UTC); + private final OffsetDateTime timestamp3 = OffsetDateTime.of(2025, 2, 13, 2, 30, 0, 0, ZoneOffset.UTC); + private final OffsetDateTime timestamp4 = OffsetDateTime.of(2025, 2, 13, 3, 30, 0, 0, ZoneOffset.UTC); + private final OffsetDateTime timestamp5 = OffsetDateTime.of(2025, 2, 13, 4, 30, 0, 0, ZoneOffset.UTC); + private final OffsetDateTime timestamp6 = OffsetDateTime.of(2025, 2, 13, 5, 30, 0, 0, ZoneOffset.UTC); + private final OffsetDateTime timestamp7 = OffsetDateTime.of(2025, 2, 13, 6, 30, 0, 0, ZoneOffset.UTC); + private final OffsetDateTime timestamp8 = OffsetDateTime.of(2025, 2, 13, 7, 30, 0, 0, ZoneOffset.UTC); @BeforeEach void setUp() throws IOException { @@ -57,12 +69,25 @@ void setUp() throws IOException { crac1 = Crac.read("/crac/crac-0030.json", IcsImporterTest.class.getResourceAsStream("/crac/crac-0030.json"), network1); crac2 = Crac.read("/crac/crac-0130.json", IcsImporterTest.class.getResourceAsStream("/crac/crac-0130.json"), network2); + crac3 = Crac.read("/crac/crac-0230.json", IcsImporterTest.class.getResourceAsStream("/crac/crac-0230.json"), network2); + crac4 = Crac.read("/crac/crac-0330.json", IcsImporterTest.class.getResourceAsStream("/crac/crac-0330.json"), network2); + crac5 = Crac.read("/crac/crac-0430.json", IcsImporterTest.class.getResourceAsStream("/crac/crac-0430.json"), network2); + crac6 = Crac.read("/crac/crac-0530.json", IcsImporterTest.class.getResourceAsStream("/crac/crac-0530.json"), network2); + crac7 = Crac.read("/crac/crac-0630.json", IcsImporterTest.class.getResourceAsStream("/crac/crac-0630.json"), network2); + crac8 = Crac.read("/crac/crac-0730.json", IcsImporterTest.class.getResourceAsStream("/crac/crac-0730.json"), network2); + TemporalData raoInputs = new TemporalDataImpl<>( - Map.of( - timestamp1, RaoInput.build(network1, crac1).build(), - timestamp2, RaoInput.build(network2, crac2).build() - )); + Map.of( + timestamp1, RaoInput.build(network1, crac1).build(), + timestamp2, RaoInput.build(network2, crac2).build(), + timestamp3, RaoInput.build(network2, crac3).build(), + timestamp4, RaoInput.build(network2, crac4).build(), + timestamp5, RaoInput.build(network2, crac5).build(), + timestamp6, RaoInput.build(network2, crac6).build(), + timestamp7, RaoInput.build(network2, crac7).build(), + timestamp8, RaoInput.build(network2, crac8).build() + )); timeCoupledRaoInput = new TimeCoupledRaoInput(raoInputs, new TimeCoupledConstraints()); } @@ -79,45 +104,45 @@ void testIcsImporterOneAction() throws IOException { InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); timeCoupledRaoInput = IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR); - assertEquals(1, timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().size()); - GeneratorConstraints generatorConstraints = timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().iterator().next(); - assertEquals("Redispatching_RA_BBE1AA1_GENERATOR", generatorConstraints.getGeneratorId()); - assertTrue(generatorConstraints.getDownwardPowerGradient().isPresent()); - assertEquals(-10., generatorConstraints.getDownwardPowerGradient().get(), DOUBLE_EPSILON); - assertTrue(generatorConstraints.getUpwardPowerGradient().isPresent()); - assertEquals(10., generatorConstraints.getUpwardPowerGradient().get(), DOUBLE_EPSILON); - assertTrue(generatorConstraints.getLeadTime().isPresent()); - assertEquals(1.0, generatorConstraints.getLeadTime().get(), DOUBLE_EPSILON); - assertTrue(generatorConstraints.getLagTime().isPresent()); - assertEquals(1.0, generatorConstraints.getLagTime().get(), DOUBLE_EPSILON); - assertFalse(generatorConstraints.isShutDownAllowed()); - assertFalse(generatorConstraints.isStartUpAllowed()); - - assertEquals(1, crac1.getInjectionRangeActions().size()); - InjectionRangeAction ra1 = crac1.getInjectionRangeActions().iterator().next(); - assertEquals("Redispatching_RA_RD", ra1.getId()); - assertEquals(116., ra1.getInitialSetpoint(), DOUBLE_EPSILON); - assertTrue(ra1.getVariationCost(VariationDirection.UP).isPresent()); - assertEquals(5., ra1.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); - assertTrue(ra1.getVariationCost(VariationDirection.DOWN).isPresent()); - assertEquals(5., ra1.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); - Network network1 = timeCoupledRaoInput.getRaoInputs().getData(timestamp1).orElseThrow().getNetwork(); - Generator generator1 = network1.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); - assertEquals(116., generator1.getTargetP(), DOUBLE_EPSILON); - assertEquals(10.0, generator1.getMinP(), DOUBLE_EPSILON); - - assertEquals(1, crac2.getInjectionRangeActions().size()); - InjectionRangeAction ra2 = crac2.getInjectionRangeActions().iterator().next(); - assertEquals("Redispatching_RA_RD", ra2.getId()); - assertEquals(120., ra2.getInitialSetpoint(), DOUBLE_EPSILON); - assertTrue(ra2.getVariationCost(VariationDirection.UP).isPresent()); - assertEquals(5., ra2.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); - assertTrue(ra2.getVariationCost(VariationDirection.DOWN).isPresent()); - assertEquals(5., ra2.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); - Network network2 = timeCoupledRaoInput.getRaoInputs().getData(timestamp2).orElseThrow().getNetwork(); - Generator generator2 = network2.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); - assertEquals(120., generator2.getTargetP(), DOUBLE_EPSILON); - assertEquals(15.0, generator2.getMinP(), DOUBLE_EPSILON); + assertEquals(0, timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().size()); +// GeneratorConstraints generatorConstraints = timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().iterator().next(); +// assertEquals("Redispatching_RA_BBE1AA1_GENERATOR", generatorConstraints.getGeneratorId()); +// assertTrue(generatorConstraints.getDownwardPowerGradient().isPresent()); +// assertEquals(-10., generatorConstraints.getDownwardPowerGradient().get(), DOUBLE_EPSILON); +// assertTrue(generatorConstraints.getUpwardPowerGradient().isPresent()); +// assertEquals(10., generatorConstraints.getUpwardPowerGradient().get(), DOUBLE_EPSILON); +// assertTrue(generatorConstraints.getLeadTime().isPresent()); +// assertEquals(1.0, generatorConstraints.getLeadTime().get(), DOUBLE_EPSILON); +// assertTrue(generatorConstraints.getLagTime().isPresent()); +// assertEquals(1.0, generatorConstraints.getLagTime().get(), DOUBLE_EPSILON); +// assertFalse(generatorConstraints.isShutDownAllowed()); +// assertFalse(generatorConstraints.isStartUpAllowed()); +// +// assertEquals(1, crac1.getInjectionRangeActions().size()); +// InjectionRangeAction ra1 = crac1.getInjectionRangeActions().iterator().next(); +// assertEquals("Redispatching_RA_RD", ra1.getId()); +// assertEquals(116., ra1.getInitialSetpoint(), DOUBLE_EPSILON); +// assertTrue(ra1.getVariationCost(VariationDirection.UP).isPresent()); +// assertEquals(5., ra1.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); +// assertTrue(ra1.getVariationCost(VariationDirection.DOWN).isPresent()); +// assertEquals(5., ra1.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); +// Network network1 = timeCoupledRaoInput.getRaoInputs().getData(timestamp1).orElseThrow().getNetwork(); +// Generator generator1 = network1.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); +// assertEquals(116., generator1.getTargetP(), DOUBLE_EPSILON); +// assertEquals(10.0, generator1.getMinP(), DOUBLE_EPSILON); +// +// assertEquals(1, crac2.getInjectionRangeActions().size()); +// InjectionRangeAction ra2 = crac2.getInjectionRangeActions().iterator().next(); +// assertEquals("Redispatching_RA_RD", ra2.getId()); +// assertEquals(120., ra2.getInitialSetpoint(), DOUBLE_EPSILON); +// assertTrue(ra2.getVariationCost(VariationDirection.UP).isPresent()); +// assertEquals(5., ra2.getVariationCost(VariationDirection.UP).get(), DOUBLE_EPSILON); +// assertTrue(ra2.getVariationCost(VariationDirection.DOWN).isPresent()); +// assertEquals(5., ra2.getVariationCost(VariationDirection.DOWN).get(), DOUBLE_EPSILON); +// Network network2 = timeCoupledRaoInput.getRaoInputs().getData(timestamp2).orElseThrow().getNetwork(); +// Generator generator2 = network2.getGenerator("Redispatching_RA_BBE1AA1_GENERATOR"); +// assertEquals(120., generator2.getTargetP(), DOUBLE_EPSILON); +// assertEquals(15.0, generator2.getMinP(), DOUBLE_EPSILON); } @Test diff --git a/data/crac/crac-util/src/test/resources/ics/series.csv b/data/crac/crac-util/src/test/resources/ics/series.csv index e46a12e2c4..a8003c9767 100644 --- a/data/crac/crac-util/src/test/resources/ics/series.csv +++ b/data/crac/crac-util/src/test/resources/ics/series.csv @@ -1,5 +1,5 @@ RA RD ID;Type of timeseries;00:30;01:30;02:30;03:30;04:30;05:30;06:30;07:30;08:30;09:30;10:30;11:30;12:30;13:30;14:30;15:30;16:30;17:30;18:30;19:30;20:30;21:30;22:30;23:30 Redispatching_RA;RDP-;35;35;36;35;34;32;28;27;25;9;8;9;8;8;8;8;23;25;30;31;34;31;29;39 Redispatching_RA;RDP+;43;43;41;42;43;45;50;51;53;68;70;69;70;70;70;69;54;52;48;47;44;46;49;39 -Redispatching_RA;P0;116;120;117;116;115;113;109;108;106;90;89;90;89;89;89;89;104;106;111;112;115;112;110;120 +Redispatching_RA;P0;116;110;110;110;110;116;0;25;109;108;106;90;89;89;89;89;89;104;106;111;112;112;110;120 Redispatching_RA;Pmin_RD;10;15;20;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30 \ No newline at end of file diff --git a/data/crac/crac-util/src/test/resources/ics/static.csv b/data/crac/crac-util/src/test/resources/ics/static.csv index 39f5ab7ee0..d1f48f1a04 100644 --- a/data/crac/crac-util/src/test/resources/ics/static.csv +++ b/data/crac/crac-util/src/test/resources/ics/static.csv @@ -1,2 +1,2 @@ RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed -Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;10;10;1;1;FALSE;FALSE \ No newline at end of file +Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;10000;1000000;2;2;TRUE;TRUE \ No newline at end of file diff --git a/tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature b/tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature index 557a299df3..0743fb1138 100644 --- a/tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature +++ b/tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature @@ -132,10 +132,10 @@ Feature: US 93.3: CORE IDCC data Scenario: US 93.3.4: 20240224 Given network files are in folder "idcc/20240224-FID2-734-v1-10V1001C--00264T-to-10V1001C--00085T" Given crac file is "idcc/20240224-FSC-ID2-CB-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given ics static file is "idcc/_110V1001C--00200I_CSA-COMRA-RDSTATIC-D_CORE-20240224-V001_.csv" - Given ics series file is "idcc/_110V1001C--00200I_CSA-COMRA-RDSERIES-D_CORE-20240224-V001_.csv" - Given ics gsk file is "idcc/_10V1001C--00200I_CSA-INDRA-GSK-D_D7-20240224-V001_.csv" - Given configuration file is "idcc/RAOParameters_minCost_megawatt_dc.json" + Given ics static file is "_110V1001C--00200I_CSA-COMRA-RDSTATIC-D_CORE-20240224-V001_.csv" + Given ics series file is "_110V1001C--00200I_CSA-COMRA-RDSERIES-D_CORE-20240224-V001_.csv" + Given ics gsk file is "_10V1001C--00200I_CSA-INDRA-GSK-D_D7-20240224-V001_.csv" + Given configuration file is "idcc/RaoParameters_minCost_megawatt_dc.json" Given time-coupled RefProg file is "idcc/20240224-FID2-632-v3-10V1001C--00264T-to-10V1001C--00085T.xml" Given time-coupled rao inputs for CORE are: | Timestamp | Network | diff --git a/tests/src/test/resources/logback.xml b/tests/src/test/resources/logback.xml index d1073c8347..44af834f8c 100644 --- a/tests/src/test/resources/logback.xml +++ b/tests/src/test/resources/logback.xml @@ -4,9 +4,16 @@ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + /tmp/RAO_LOGS/openrao.log + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + From 3ccf480af36aa09c42bd458a687727fe4d480a80 Mon Sep 17 00:00:00 2001 From: Thomas Bouquet Date: Wed, 8 Apr 2026 11:29:47 +0200 Subject: [PATCH 49/64] add multi-threading (need to fix concurrent access to LF parameters) Signed-off-by: Thomas Bouquet --- .../powsybl/openrao/commons/TemporalData.java | 2 + .../openrao/commons/TemporalDataImpl.java | 35 ++++ .../openrao/data/crac/util/IcsImporter.java | 29 +-- .../powsybl/openrao/raoapi/LazyNetwork.java | 5 +- ...ractMultiPerimeterSensitivityAnalysis.java | 3 +- .../fillers/GeneratorConstraintsFiller.java | 17 +- .../openrao/searchtreerao/marmot/Marmot.java | 186 ++++++++++-------- .../searchtreerao/marmot/MarmotUtils.java | 25 ++- .../tests/steps/TimeCoupledRaoSteps.java | 45 +++-- .../powsybl/openrao/tests/utils/Helpers.java | 5 +- .../features/4_time_coupled/US93_5.feature | 42 ++-- 11 files changed, 237 insertions(+), 157 deletions(-) diff --git a/commons/src/main/java/com/powsybl/openrao/commons/TemporalData.java b/commons/src/main/java/com/powsybl/openrao/commons/TemporalData.java index 239c235caa..a057a0d4fc 100644 --- a/commons/src/main/java/com/powsybl/openrao/commons/TemporalData.java +++ b/commons/src/main/java/com/powsybl/openrao/commons/TemporalData.java @@ -30,4 +30,6 @@ default List getTimestamps() { void put(OffsetDateTime timestamp, T data); TemporalData map(Function function); + + TemporalData mapMultiThreading(Function function, int parallelism); } diff --git a/commons/src/main/java/com/powsybl/openrao/commons/TemporalDataImpl.java b/commons/src/main/java/com/powsybl/openrao/commons/TemporalDataImpl.java index b9895b8d38..ab70eb2694 100644 --- a/commons/src/main/java/com/powsybl/openrao/commons/TemporalDataImpl.java +++ b/commons/src/main/java/com/powsybl/openrao/commons/TemporalDataImpl.java @@ -8,8 +8,15 @@ package com.powsybl.openrao.commons; import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.TreeMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.function.Function; import java.util.stream.Collectors; @@ -38,4 +45,32 @@ public void put(OffsetDateTime timestamp, T data) { public TemporalData map(Function function) { return new TemporalDataImpl<>(dataPerTimestamp.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> function.apply(entry.getValue())))); } + + public TemporalData mapMultiThreading(Function function, int parallelism) { + try (ExecutorService executor = Executors.newFixedThreadPool(parallelism)) { + try { + List>> futures = new ArrayList<>(); + + for (Map.Entry entry : dataPerTimestamp.entrySet()) { + futures.add(executor.submit(() -> + Map.entry(entry.getKey(), function.apply(entry.getValue())) + )); + } + + Map result = new HashMap<>(); + + for (Future> future : futures) { + Map.Entry e = future.get(); + result.put(e.getKey(), e.getValue()); + } + + return new TemporalDataImpl<>(result); + + } catch (InterruptedException | ExecutionException e) { + throw new OpenRaoException(e); + } finally { + executor.shutdown(); + } + } + } } diff --git a/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java b/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java index 5bd7da16cc..868c029b3e 100644 --- a/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java +++ b/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java @@ -101,8 +101,12 @@ public static TimeCoupledRaoInput populateInputWithICS(TimeCoupledRaoInput timeC Network network = raoInput.getNetwork(); preProcessNetwork(network); initialNetworks.put(dateTime, new LazyNetwork(network)); // use a copy not to modify initial network - if (network instanceof LazyNetwork) { - ((LazyNetwork) network).release(); + if (network instanceof LazyNetwork lazyNetwork) { + try { + lazyNetwork.close(); + } catch (Exception e) { + throw new OpenRaoException(e); + } } }); @@ -159,16 +163,19 @@ public static TimeCoupledRaoInput populateInputWithICS(TimeCoupledRaoInput timeC TemporalData postIcsRaoInputs = new TemporalDataImpl<>(); - initialNetworks.getDataPerTimestamp().forEach((dateTime, initialNetwork) -> { - String exportedNetworkPath = exportDirectory + dateTime.format(DateTimeFormatter.ofPattern("%y%m%d_%H%M%S")) + ".jiidm"; - initialNetwork.write("JIIDM", new Properties(), Path.of(exportedNetworkPath)); - postIcsRaoInputs.put(dateTime, RaoInput.build(new LazyNetwork(exportedNetworkPath), timeCoupledRaoInput.getRaoInputs().getData(dateTime).orElseThrow().getCrac()).build()); - initialNetwork.release(); - }); - - TimeCoupledRaoInput output = new TimeCoupledRaoInput(postIcsRaoInputs, timeCoupledRaoInput.getTimestampsToRun(), timeCoupledRaoInput.getTimeCoupledConstraints()); + for (OffsetDateTime timestamp : initialNetworks.getTimestamps()) { + try (LazyNetwork initialNetwork = initialNetworks.getData(timestamp).orElseThrow()) { + try (LazyNetwork lazyNetwork = new LazyNetwork(initialNetwork)) { + postIcsRaoInputs.put(timestamp, RaoInput.build(lazyNetwork, timeCoupledRaoInput.getRaoInputs().getData(timestamp).orElseThrow().getCrac()).build()); + } catch (Exception e) { + throw new OpenRaoException(e); + } + } catch (Exception e) { + throw new OpenRaoException(e); + } + } - return output; + return new TimeCoupledRaoInput(postIcsRaoInputs, timeCoupledRaoInput.getTimestampsToRun(), timeCoupledRaoInput.getTimeCoupledConstraints()); } private static void preProcessNetwork(Network network) { diff --git a/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/LazyNetwork.java b/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/LazyNetwork.java index ddce5706d0..33713d4cf5 100644 --- a/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/LazyNetwork.java +++ b/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/LazyNetwork.java @@ -88,7 +88,7 @@ * @author Thomas Bouquet {@literal } */ -public class LazyNetwork implements Network { +public class LazyNetwork implements Network, AutoCloseable { private static final String TEMP_DIR = System.getProperty("java.io.tmpdir") + File.separator; private final String networkPath; private boolean isLoaded; @@ -113,7 +113,8 @@ private void load() { } } - public void release() { + @Override + public void close() throws Exception { // TODO: currently modifications on the network will not be saved -> perhaps override networkPath with a UUID and write content to it network = null; isLoaded = false; diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/castor/algorithm/AbstractMultiPerimeterSensitivityAnalysis.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/castor/algorithm/AbstractMultiPerimeterSensitivityAnalysis.java index 90bc13f9f3..f3ec6405b3 100644 --- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/castor/algorithm/AbstractMultiPerimeterSensitivityAnalysis.java +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/castor/algorithm/AbstractMultiPerimeterSensitivityAnalysis.java @@ -88,7 +88,8 @@ protected int setNewThreadCountAndGetOldValue() { } OpenSensitivityAnalysisParameters openSensitivityAnalysisParameters = sensitivityAnalysisParameters.getExtension(OpenSensitivityAnalysisParameters.class); - int oldThreadCount = openSensitivityAnalysisParameters.getThreadCount(); + int oldThreadCount = + openSensitivityAnalysisParameters.getThreadCount(); if (multiThreadedSensitivities) { openSensitivityAnalysisParameters.setThreadCount(MultithreadingParameters.getAvailableCPUs(raoParameters)); } diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/GeneratorConstraintsFiller.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/GeneratorConstraintsFiller.java index d114004a67..6aa981d447 100644 --- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/GeneratorConstraintsFiller.java +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/GeneratorConstraintsFiller.java @@ -486,14 +486,17 @@ public void updateBetweenMipIteration(LinearProblem linearProblem, RangeActionAc private static Map> processGenerators(TemporalData lazyNetworks) { Map> dataPerGeneratorPerTimestamp = new HashMap<>(); - lazyNetworks.getDataPerTimestamp().forEach((timestamp, lazyNetwork) -> { - for (Generator generator : lazyNetwork.getGenerators()) { - dataPerGeneratorPerTimestamp.computeIfAbsent(generator.getId(), k -> new HashMap<>()).put( - timestamp, new GeneratorData(generator.getTargetP(), generator.getMinP(), generator.getMaxP()) - ); + for (OffsetDateTime timestamp : lazyNetworks.getTimestamps()) { + try (LazyNetwork lazyNetwork = lazyNetworks.getData(timestamp).orElseThrow()) { + for (Generator generator : lazyNetwork.getGenerators()) { + dataPerGeneratorPerTimestamp.computeIfAbsent(generator.getId(), k -> new HashMap<>()).put( + timestamp, new GeneratorData(generator.getTargetP(), generator.getMinP(), generator.getMaxP()) + ); + } + } catch (Exception e) { + throw new OpenRaoException(e); } - lazyNetwork.release(); - }); + } Map> globalGeneratorData = new HashMap<>(); dataPerGeneratorPerTimestamp.forEach((generatorId, dataPerTimestamp) -> globalGeneratorData.put(generatorId, new TemporalDataImpl<>(dataPerTimestamp))); return globalGeneratorData; 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 8936cb3c72..fa99144d31 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 @@ -8,6 +8,7 @@ package com.powsybl.openrao.searchtreerao.marmot; import com.google.auto.service.AutoService; +import com.powsybl.openrao.commons.OpenRaoException; import com.powsybl.openrao.commons.TemporalData; import com.powsybl.openrao.commons.TemporalDataImpl; import com.powsybl.openrao.commons.Unit; @@ -87,13 +88,15 @@ public class Marmot implements TimeCoupledRaoProvider { private static final String MIP_SCENARIO = "MipScenario"; private static final String MIN_MARGIN_VIOLATION_EVALUATOR = "min-margin-violation-evaluator"; + private static final int PARALLELISM = 8; + @Override public CompletableFuture run(TimeCoupledRaoInput timeCoupledRaoInput, RaoParameters raoParameters) { // Initiate lazy networks TemporalData cracs = timeCoupledRaoInput.getRaoInputs().map(RaoInput::getCrac); TemporalData initialNetworks = MarmotUtils.cloneNetworks(timeCoupledRaoInput.getRaoInputs().map(RaoInput::getNetwork)); MarmotUtils.releaseAll(timeCoupledRaoInput.getRaoInputs().map(RaoInput::getNetwork)); - initialNetworks.getDataPerTimestamp().values().forEach(LazyNetwork::release); + MarmotUtils.releaseAll(initialNetworks); TemporalData initialInputs = MarmotUtils.merge(initialNetworks, cracs); @@ -119,7 +122,7 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl TemporalData postTopologicalActionsResults = topologicalOptimizationResults.map( raoResult -> ((FastRaoResultImpl) raoResult).getFinalResult() ); - TemporalData initialSetpointResults = getInitialSetpointResults(topologicalOptimizationResults, initialInputs); + TemporalData initialSetpointResults = getInitialSetpointResults(topologicalOptimizationResults, cracs); LinearOptimizationResult postTopologicalOptimizationResult = getPostTopologicalOptimizationResult( initialSetpointResults, postTopologicalActionsResults, @@ -231,21 +234,19 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl return CompletableFuture.completedFuture(timeCoupledRaoResult); } - private TemporalData getInitialSetpointResults(TemporalData postTopologicalActionsResults, TemporalData raoInputs) { - TemporalData initialSetpointResults = new TemporalDataImpl<>(); - raoInputs.getDataPerTimestamp().forEach((timestamp, raoInput) -> { - Map, Double> setPointMap = new HashMap<>(); - raoInput.getCrac().getRangeActions().forEach(rangeAction -> - setPointMap.put(rangeAction, postTopologicalActionsResults.getData(timestamp).orElseThrow() - .getPreOptimizationSetPointOnState(raoInput.getCrac().getPreventiveState(), rangeAction)) - ); - RangeActionSetpointResult rangeActionSetpointResult = new RangeActionSetpointResultImpl( - setPointMap - ); - initialSetpointResults.put(timestamp, rangeActionSetpointResult); - MarmotUtils.releaseNetwork(raoInput.getNetwork()); - }); - return initialSetpointResults; + private TemporalData getInitialSetpointResults(TemporalData postTopologicalActionsResults, TemporalData cracs) { + return cracs.mapMultiThreading( + crac -> { + OffsetDateTime timestamp = crac.getTimestamp().orElseThrow(); + Map, Double> setPointMap = new HashMap<>(); + crac.getRangeActions().forEach(rangeAction -> + setPointMap.put(rangeAction, postTopologicalActionsResults.getData(timestamp).orElseThrow() + .getPreOptimizationSetPointOnState(crac.getPreventiveState(), rangeAction)) + ); + return new RangeActionSetpointResultImpl(setPointMap); + }, + PARALLELISM + ); } private boolean shouldContinueAndAddCnecs(TemporalData loadFlowResults, TemporalData> consideredCnecs, Unit flowUnit) { @@ -389,20 +390,23 @@ private static TemporalData applyActionsAndRunFullLoadflow(T LinearOptimizationResult filteredResult, TemporalData initialResults, RaoParameters raoParameters) { - TemporalData prePerimeterResults = new TemporalDataImpl<>(); - postTopoInputs.getDataPerTimestamp().forEach((timestamp, raoInput) -> { - State preventiveState = raoInput.getCrac().getPreventiveState(); - raoInput.getCrac().getRangeActions(preventiveState).forEach(rangeAction -> - rangeAction.apply(raoInput.getNetwork(), filteredResult.getOptimizedSetpoint(rangeAction, preventiveState)) - ); - prePerimeterResults.put(timestamp, runInitialPrePerimeterSensitivityAnalysisWithoutRangeActions( - postTopoInputs.getData(timestamp).orElseThrow(), - curativeRemedialActions.getData(timestamp).orElseThrow(), - initialResults.getData(timestamp).orElseThrow(), - raoParameters)); - MarmotUtils.releaseNetwork(raoInput.getNetwork()); - }); - return prePerimeterResults; + return postTopoInputs.mapMultiThreading( + raoInput -> { + OffsetDateTime timestamp = MarmotUtils.getTimestamp(raoInput); + State preventiveState = raoInput.getCrac().getPreventiveState(); + raoInput.getCrac().getRangeActions(preventiveState).forEach(rangeAction -> + rangeAction.apply(raoInput.getNetwork(), filteredResult.getOptimizedSetpoint(rangeAction, preventiveState)) + ); + PrePerimeterResult sensitivityAnalysisResults = runInitialPrePerimeterSensitivityAnalysisWithoutRangeActions( + postTopoInputs.getData(timestamp).orElseThrow(), + curativeRemedialActions.getData(timestamp).orElseThrow(), + initialResults.getData(timestamp).orElseThrow(), + raoParameters); + MarmotUtils.releaseNetwork(raoInput.getNetwork()); + return sensitivityAnalysisResults; + }, + PARALLELISM + ); } private void replaceFastRaoResultsWithLightVersions(TemporalData topologicalOptimizationResults) { @@ -413,30 +417,37 @@ private void replaceFastRaoResultsWithLightVersions(TemporalData topo private static TemporalData runTopologicalOptimization(TemporalData raoInputs, TemporalData> consideredCnecs, RaoParameters raoParameters) { raoParameters.getExtension(OpenRaoSearchTreeParameters.class).getRangeActionsOptimizationParameters().getLinearOptimizationSolver().setSolverSpecificParameters("MAXTIME 15"); - TemporalData individualResults = new TemporalDataImpl<>(); - raoInputs.getDataPerTimestamp().forEach((datetime, raoInput) -> { - Set cnecs = new HashSet<>(); - String logMessage = "[MARMOT] Running RAO for timestamp %s [{}]".formatted(raoInput.getCrac().getTimestamp().orElseThrow()); - TECHNICAL_LOGS.info(logMessage, "start"); - RaoResult raoResult = FastRao.launchFastRaoOptimization(raoInput, raoParameters, null, cnecs); - TECHNICAL_LOGS.info(logMessage, "end"); - consideredCnecs.put(datetime, cnecs); - individualResults.put(datetime, raoResult); - MarmotUtils.releaseNetwork(raoInput.getNetwork()); - }); - return individualResults; + return raoInputs.mapMultiThreading( + raoInput -> { + Set cnecs = new HashSet<>(); + OffsetDateTime timestamp = MarmotUtils.getTimestamp(raoInput); + String logMessage = "[MARMOT] Running RAO for timestamp %s [{}]".formatted(timestamp); + TECHNICAL_LOGS.info(logMessage, "start"); + RaoResult raoResult = FastRao.launchFastRaoOptimization(raoInput, raoParameters, null, cnecs); + TECHNICAL_LOGS.info(logMessage, "end"); + consideredCnecs.put(timestamp, cnecs); + MarmotUtils.releaseNetwork(raoInput.getNetwork()); + return raoResult; + }, + PARALLELISM + ); } private static TemporalData applyPreventiveTopologicalActionsOnNetworks(TemporalData raoInputs, TemporalData preventiveTopologicalActionsResults) { - Map postTopologicalActionsInputs = new HashMap<>(); - raoInputs.getTimestamps().forEach(timestamp -> { - RaoInput raoInput = raoInputs.getData(timestamp).orElseThrow(); - NetworkActionsResult networkActionsResult = preventiveTopologicalActionsResults.getData(timestamp).orElseThrow(); - MarmotUtils.applyPreventiveRemedialActions(raoInput, networkActionsResult, INITIAL_SCENARIO, POST_TOPO_SCENARIO); - postTopologicalActionsInputs.put(timestamp, RaoInput.build(new LazyNetwork(raoInput.getNetwork()), raoInput.getCrac()).build()); - MarmotUtils.releaseNetwork(raoInput.getNetwork()); - }); - return new TemporalDataImpl<>(postTopologicalActionsInputs); + return raoInputs.mapMultiThreading( + raoInput -> { + OffsetDateTime timestamp = MarmotUtils.getTimestamp(raoInput); + NetworkActionsResult networkActionsResult = preventiveTopologicalActionsResults.getData(timestamp).orElseThrow(); + MarmotUtils.applyPreventiveRemedialActions(raoInput, networkActionsResult, INITIAL_SCENARIO, POST_TOPO_SCENARIO); + try (LazyNetwork lazyNetwork = new LazyNetwork(raoInput.getNetwork())) { + MarmotUtils.releaseNetwork(raoInput.getNetwork()); + return RaoInput.build(lazyNetwork, raoInput.getCrac()).build(); + } catch (Exception e) { + throw new OpenRaoException(e); + } + }, + PARALLELISM + ); } private TemporalData buildInitialResults(TemporalData topologicalOptimizationResults) { @@ -451,35 +462,35 @@ private static TemporalData runAllSensitivityAnalysesBasedOn TemporalData initialFlowResults, RaoParameters raoParameters, TemporalData> consideredCnecs) { - TemporalData prePerimeterResults = new TemporalDataImpl<>(); - raoInputs.getTimestamps().forEach(timestamp -> - { - RaoInput raoInput = raoInputs.getData(timestamp).orElseThrow(); - prePerimeterResults.put(timestamp, runSensitivityAnalysisBasedOnInitialResult( - raoInput, - curativeRemedialActions.getData(timestamp).orElseThrow(), - initialFlowResults.getData(timestamp).orElseThrow(), - raoParameters, - consideredCnecs.getData(timestamp).orElseThrow() - )); - MarmotUtils.releaseNetwork(raoInput.getNetwork()); - }); - return prePerimeterResults; + return raoInputs.mapMultiThreading( + raoInput -> { + OffsetDateTime timestamp = MarmotUtils.getTimestamp(raoInput); + PrePerimeterResult sensitivityAnalysisResult = runSensitivityAnalysisBasedOnInitialResult( + raoInput, + curativeRemedialActions.getData(timestamp).orElseThrow(), + initialFlowResults.getData(timestamp).orElseThrow(), + raoParameters, + consideredCnecs.getData(timestamp).orElseThrow() + ); + MarmotUtils.releaseNetwork(raoInput.getNetwork()); + return sensitivityAnalysisResult; + }, + PARALLELISM + ); } private static TemporalData getPreventiveTopologicalActions(TemporalData cracs, TemporalData raoResults) { - Map preventiveTopologicalActions = new HashMap<>(); - cracs.getTimestamps().forEach(timestamp -> { - State preventiveState = cracs.getData(timestamp).orElseThrow().getPreventiveState(); - preventiveTopologicalActions.put( - timestamp, - new NetworkActionsResultImpl(Map.of( - preventiveState, - raoResults.getData(timestamp).orElseThrow().getActivatedNetworkActionsDuringState(preventiveState) - )) - ); - }); - return new TemporalDataImpl<>(preventiveTopologicalActions); + return cracs.mapMultiThreading( + crac -> { + OffsetDateTime timestamp = crac.getTimestamp().orElseThrow(); + return new NetworkActionsResultImpl( + Map.of( + crac.getPreventiveState(), + raoResults.getData(timestamp).orElseThrow().getActivatedNetworkActionsDuringState(crac.getPreventiveState()) + )); + }, + PARALLELISM + ); } private static GlobalLinearOptimizationResult optimizeLinearRemedialActions(TimeCoupledRaoInput raoInput, @@ -497,9 +508,11 @@ private static GlobalLinearOptimizationResult optimizeLinearRemedialActions(Time // no objective function defined in individual IteratingLinearOptimizerInputs as it is global Map linearOptimizerInputPerTimestamp = new HashMap<>(); - raoInput.getRaoInputs().getDataPerTimestamp().forEach((timestamp, individualRaoInput) -> { + for (OffsetDateTime timestamp : raoInput.getTimestampsToRun()) { + RaoInput individualRaoInput = raoInput.getRaoInputs().getData(timestamp).orElseThrow(); + try (LazyNetwork lazyNetwork = new LazyNetwork(individualRaoInput.getNetwork())) { IteratingLinearOptimizerInput iteratingLinearOptimizerInput = IteratingLinearOptimizerInput.create() - .withNetwork(new LazyNetwork(individualRaoInput.getNetwork())) + .withNetwork(lazyNetwork) .withOptimizationPerimeter(optimizationPerimeterPerTimestamp.getData(timestamp).orElseThrow() .copyWithFilteredAvailableHvdcRangeAction(individualRaoInput.getNetwork())) .withInitialFlowResult(initialResults.getData(timestamp).orElseThrow()) @@ -512,13 +525,18 @@ private static GlobalLinearOptimizationResult optimizeLinearRemedialActions(Time .withOutageInstant(individualRaoInput.getCrac().getOutageInstant()) .withAppliedNetworkActionsInPrimaryState(preventiveTopologicalActions.getData(timestamp).orElseThrow()) .build(); - MarmotUtils.releaseNetwork(individualRaoInput.getNetwork()); - MarmotUtils.releaseNetwork(iteratingLinearOptimizerInput.network()); - linearOptimizerInputPerTimestamp.put(timestamp, iteratingLinearOptimizerInput); - }); + MarmotUtils.releaseNetwork(individualRaoInput.getNetwork()); + linearOptimizerInputPerTimestamp.put(timestamp, iteratingLinearOptimizerInput); + } catch (Exception e) { + throw new OpenRaoException(e); + } + } + TimeCoupledIteratingLinearOptimizerInput timeCoupledLinearOptimizerInput = new TimeCoupledIteratingLinearOptimizerInput( new TemporalDataImpl<>(linearOptimizerInputPerTimestamp), objectiveFunction, raoInput.getTimeCoupledConstraints()); + MarmotUtils.releaseAll(timeCoupledLinearOptimizerInput.iteratingLinearOptimizerInputs().map(IteratingLinearOptimizerInput::network)); + // Build parameters // Unoptimized cnec parameters ignored because only PRAs // TODO: define static method to define Ra Limitation Parameters from crac and topos (mutualize with search tree) : SearchTreeParameters::decreaseRemedialActionsUsageLimits diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java index ee7f301e1c..44c5557681 100644 --- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java @@ -175,20 +175,31 @@ public static TemporalData cloneNetworks(TemporalData netw public static TemporalData merge(TemporalData networks, TemporalData cracs) { Map raoInputs = new HashMap<>(); - networks.getDataPerTimestamp().forEach((timestamp, network) -> { - raoInputs.put(timestamp, RaoInput.build(network, cracs.getData(timestamp).orElseThrow()).build()); - network.release(); - }); + for (OffsetDateTime timestamp : networks.getTimestamps()) { + try (LazyNetwork lazyNetwork = networks.getData(timestamp).orElseThrow()) { + raoInputs.put(timestamp, RaoInput.build(lazyNetwork, cracs.getData(timestamp).orElseThrow()).build()); + } catch (Exception e) { + throw new OpenRaoException(e); + } + } return new TemporalDataImpl<>(raoInputs); } - public static void releaseAll(TemporalData networks) { + public static void releaseAll(TemporalData networks) { networks.getDataPerTimestamp().values().forEach(MarmotUtils::releaseNetwork); } - public static void releaseNetwork(Network network) { + public static void releaseNetwork(N network) { if (network instanceof LazyNetwork lazyNetwork) { - lazyNetwork.release(); + try { + lazyNetwork.close(); + } catch (Exception e) { + throw new OpenRaoException(e); + } } } + + public static OffsetDateTime getTimestamp(RaoInput raoInput) { + return raoInput.getCrac().getTimestamp().orElseThrow(); + } } diff --git a/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java b/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java index 5b209233f1..20c5427f30 100644 --- a/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java +++ b/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java @@ -10,6 +10,7 @@ import com.powsybl.iidm.network.Bus; import com.powsybl.iidm.network.Generator; import com.powsybl.iidm.network.Network; +import com.powsybl.openrao.commons.OpenRaoException; import com.powsybl.openrao.commons.PhysicalParameter; import com.powsybl.openrao.commons.TemporalData; import com.powsybl.openrao.commons.TemporalDataImpl; @@ -196,9 +197,12 @@ public static void loadDataForTimeCoupledRao(String timeCoupledConstraintsPath, List> inputs = arg1.asMaps(String.class, String.class); for (Map tsInput : inputs) { OffsetDateTime offsetDateTime = getOffsetDateTimeFromBrusselsTimestamp(tsInput.get("Timestamp")); - Network network = importNetwork(getFile(networkFolderPath.concat(tsInput.get("Network"))), false); - Crac crac = importCrac(getFile(cracFolderPath.concat(tsInput.get("CRAC"))), network, null).getLeft(); - raoInputs.put(offsetDateTime, RaoInput.build(LazyNetwork.of(networkFolderPath.concat(tsInput.get("Network"))), crac).build()); + try (LazyNetwork lazyNetwork = importNetwork(getFile(networkFolderPath.concat(tsInput.get("Network"))), false)) { + Crac crac = importCrac(getFile(cracFolderPath.concat(tsInput.get("CRAC"))), lazyNetwork, null).getLeft(); + raoInputs.put(offsetDateTime, RaoInput.build(lazyNetwork, crac).build()); + } catch (Exception e) { + throw new OpenRaoException(e); + } } TimeCoupledConstraints timeCoupledConstraints = JsonTimeCoupledConstraints.read(new FileInputStream(timeCoupledConstraintsFolderPath.concat(timeCoupledConstraintsPath))); @@ -232,26 +236,25 @@ public static void loadDataForCoreTimeCoupledRao(DataTable arg1) throws IOExcept for (Map tsInput : inputs) { OffsetDateTime offsetDateTime = getOffsetDateTimeFromBrusselsTimestamp(tsInput.get("Timestamp")); TECHNICAL_LOGS.info("**** Loading data for TS {} ****", offsetDateTime); - // Network - String postIcsNetworkPath = networkFolderPathPostIcsImport.concat(tsInput.get("Network")).split(".uct")[0].concat(".jiidm"); - Network network = importNetwork(getFile(networkFolderPath.concat(tsInput.get("Network"))), false); - CoreCcPreprocessor.applyCoreCcNetworkPreprocessing(network); - // Crac - Pair cracImportResult; - if (useIndividualCracs) { // only works with json - cracImportResult = importCrac(getFile(cracFolderPath.concat(tsInput.get("Crac"))), network, null); - } else { - cracCreationParameters.getExtension(FbConstraintCracCreationParameters.class).setTimestamp(offsetDateTime); - cracImportResult = importCrac(cracFile, network, cracCreationParameters); - if (network instanceof LazyNetwork) { - ((LazyNetwork) network).release(); + try (LazyNetwork lazyNetwork = importNetwork(getFile(networkFolderPath.concat(tsInput.get("Network"))), false)) { + CoreCcPreprocessor.applyCoreCcNetworkPreprocessing(lazyNetwork); + // Crac + Pair cracImportResult; + if (useIndividualCracs) { // only works with json + cracImportResult = importCrac(getFile(cracFolderPath.concat(tsInput.get("Crac"))), lazyNetwork, null); + } else { + cracCreationParameters.getExtension(FbConstraintCracCreationParameters.class).setTimestamp(offsetDateTime); + cracImportResult = importCrac(cracFile, lazyNetwork, cracCreationParameters); } + RaoInput raoInput = RaoInput + .build(lazyNetwork, cracImportResult.getLeft()) + .build(); + raoInputs.put(offsetDateTime, raoInput); + cracCreationContexts.put(offsetDateTime, cracImportResult.getRight()); + } catch (Exception e) { + throw new OpenRaoException(e); } - RaoInput raoInput = RaoInput - .build(network, cracImportResult.getLeft()) - .build(); - raoInputs.put(offsetDateTime, raoInput); - cracCreationContexts.put(offsetDateTime, cracImportResult.getRight()); + } InputStream gskInputStream = icsGskPath == null ? null : new FileInputStream(getFile(icsGskPath)); diff --git a/tests/src/test/java/com/powsybl/openrao/tests/utils/Helpers.java b/tests/src/test/java/com/powsybl/openrao/tests/utils/Helpers.java index c82fcc75ac..5e2fefce07 100644 --- a/tests/src/test/java/com/powsybl/openrao/tests/utils/Helpers.java +++ b/tests/src/test/java/com/powsybl/openrao/tests/utils/Helpers.java @@ -64,14 +64,13 @@ private Helpers() { // must nor be used } - public static Network importNetwork(File networkFile, boolean useRdfId) { + public static LazyNetwork importNetwork(File networkFile, boolean useRdfId) { Properties importParams = new Properties(); if (useRdfId) { importParams.put("iidm.import.cgmes.source-for-iidm-id", "rdfID"); } Network network = Network.read(Paths.get(networkFile.toString()), LocalComputationManager.getDefault(), Suppliers.memoize(ImportConfig::load).get(), importParams); - LazyNetwork lazyNetwork = new LazyNetwork(network); - return lazyNetwork; + return new LazyNetwork(network); } public static Pair importCrac(File cracFile, Network network, CracCreationParameters cracCreationParameters) throws IOException { diff --git a/tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature b/tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature index 4fae98c392..557a299df3 100644 --- a/tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature +++ b/tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature @@ -139,30 +139,30 @@ Feature: US 93.3: CORE IDCC data Given time-coupled RefProg file is "idcc/20240224-FID2-632-v3-10V1001C--00264T-to-10V1001C--00085T.xml" Given time-coupled rao inputs for CORE are: | Timestamp | Network | - # | 2024-02-24 00:30 | 20240224_0030_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - # | 2024-02-24 01:30 | 20240224_0130_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - # | 2024-02-24 02:30 | 20240224_0230_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - # | 2024-02-24 03:30 | 20240224_0330_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - # | 2024-02-24 04:30 | 20240224_0430_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - # | 2024-02-24 05:30 | 20240224_0530_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - # | 2024-02-24 06:30 | 20240224_0630_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - # | 2024-02-24 07:30 | 20240224_0730_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + | 2024-02-24 00:30 | 20240224_0030_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + | 2024-02-24 01:30 | 20240224_0130_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + | 2024-02-24 02:30 | 20240224_0230_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + | 2024-02-24 03:30 | 20240224_0330_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + | 2024-02-24 04:30 | 20240224_0430_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + | 2024-02-24 05:30 | 20240224_0530_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + | 2024-02-24 06:30 | 20240224_0630_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + | 2024-02-24 07:30 | 20240224_0730_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | | 2024-02-24 08:30 | 20240224_0830_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | | 2024-02-24 09:30 | 20240224_0930_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | | 2024-02-24 10:30 | 20240224_1030_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - # | 2024-02-24 11:30 | 20240224_1130_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - # | 2024-02-24 12:30 | 20240224_1230_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - # | 2024-02-24 13:30 | 20240224_1330_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - # | 2024-02-24 14:30 | 20240224_1430_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - # | 2024-02-24 15:30 | 20240224_1530_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - # | 2024-02-24 16:30 | 20240224_1630_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - # | 2024-02-24 17:30 | 20240224_1730_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - # | 2024-02-24 18:30 | 20240224_1830_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - # | 2024-02-24 19:30 | 20240224_1930_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - # | 2024-02-24 20:30 | 20240224_2030_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - # | 2024-02-24 21:30 | 20240224_2130_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - # | 2024-02-24 22:30 | 20240224_2230_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - # | 2024-02-24 23:30 | 20240224_2330_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + | 2024-02-24 11:30 | 20240224_1130_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + | 2024-02-24 12:30 | 20240224_1230_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + | 2024-02-24 13:30 | 20240224_1330_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + | 2024-02-24 14:30 | 20240224_1430_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + | 2024-02-24 15:30 | 20240224_1530_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + | 2024-02-24 16:30 | 20240224_1630_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + | 2024-02-24 17:30 | 20240224_1730_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + | 2024-02-24 18:30 | 20240224_1830_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + | 2024-02-24 19:30 | 20240224_1930_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + | 2024-02-24 20:30 | 20240224_2030_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + | 2024-02-24 21:30 | 20240224_2130_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + | 2024-02-24 22:30 | 20240224_2230_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | + | 2024-02-24 23:30 | 20240224_2330_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | When I launch marmot When I export marmot results to "raoresults/results_20240224.zip" From d77d1fb8b0700ca228673a4ed764bafc319be617 Mon Sep 17 00:00:00 2001 From: Godelaine de Montmorillon Date: Thu, 9 Apr 2026 13:20:04 +0200 Subject: [PATCH 50/64] missing logback and crac test files --- .../src/test/resources/crac/crac-0230.json | 81 +++++++++++++++++++ .../src/test/resources/crac/crac-0330.json | 81 +++++++++++++++++++ .../src/test/resources/crac/crac-0430.json | 81 +++++++++++++++++++ .../src/test/resources/crac/crac-0530.json | 81 +++++++++++++++++++ .../src/test/resources/crac/crac-0630.json | 81 +++++++++++++++++++ .../src/test/resources/crac/crac-0730.json | 81 +++++++++++++++++++ .../src/test/resources/logback-test.xml | 15 ++++ 7 files changed, 501 insertions(+) create mode 100644 data/crac/crac-util/src/test/resources/crac/crac-0230.json create mode 100644 data/crac/crac-util/src/test/resources/crac/crac-0330.json create mode 100644 data/crac/crac-util/src/test/resources/crac/crac-0430.json create mode 100644 data/crac/crac-util/src/test/resources/crac/crac-0530.json create mode 100644 data/crac/crac-util/src/test/resources/crac/crac-0630.json create mode 100644 data/crac/crac-util/src/test/resources/crac/crac-0730.json create mode 100644 data/crac/crac-util/src/test/resources/logback-test.xml diff --git a/data/crac/crac-util/src/test/resources/crac/crac-0230.json b/data/crac/crac-util/src/test/resources/crac/crac-0230.json new file mode 100644 index 0000000000..28653c5195 --- /dev/null +++ b/data/crac/crac-util/src/test/resources/crac/crac-0230.json @@ -0,0 +1,81 @@ +{ + "type": "CRAC", + "version": "2.7", + "info": "Generated by PowSyBl OpenRAO https://powsybl.readthedocs.io/projects/openrao/", + "id": "crac-20250215", + "name": "crac-20250215", + "timestamp": "2025-02-13T02:30:00Z", + "instants": [ + { + "id": "preventive", + "kind": "PREVENTIVE" + }, + { + "id": "outage", + "kind": "OUTAGE" + }, + { + "id": "curative", + "kind": "CURATIVE" + } + ], + "networkElementsNamePerId": {}, + "flowCnecs": [ + { + "id": "cnecBeFrPreventive", + "name": "cnecBeFrPreventive", + "networkElementId": "BBE1AA1 FFR1AA1 1", + "operator": "FR", + "instant": "preventive", + "contingencyId": null, + "optimized": true, + "monitored": false, + "iMax": [ + NaN + ], + "nominalV": [ + 400.0 + ], + "thresholds": [ + { + "unit": "megawatt", + "min": -300.0, + "max": 300.0, + "side": 1 + }, + { + "unit": "megawatt", + "min": -300.0, + "max": 300.0, + "side": 2 + } + ] + } + ], + "pstRangeActions": [ + { + "id": "pstBeFr2", + "name": "pstBeFr2", + "operator": "BE", + "activationCost": 5.0, + "variationCosts": { + "up": 10.0, + "down": 10.0 + }, + "onInstantUsageRules": [ + { + "instant": "preventive", + "usageMethod": "available" + } + ], + "networkElementId": "BBE1AA1 FFR1AA1 2", + "ranges": [ + { + "min": -16, + "max": 16, + "rangeType": "absolute" + } + ] + } + ] +} \ No newline at end of file diff --git a/data/crac/crac-util/src/test/resources/crac/crac-0330.json b/data/crac/crac-util/src/test/resources/crac/crac-0330.json new file mode 100644 index 0000000000..995949c68a --- /dev/null +++ b/data/crac/crac-util/src/test/resources/crac/crac-0330.json @@ -0,0 +1,81 @@ +{ + "type": "CRAC", + "version": "2.7", + "info": "Generated by PowSyBl OpenRAO https://powsybl.readthedocs.io/projects/openrao/", + "id": "crac-20250216", + "name": "crac-20250216", + "timestamp": "2025-02-13T03:30:00Z", + "instants": [ + { + "id": "preventive", + "kind": "PREVENTIVE" + }, + { + "id": "outage", + "kind": "OUTAGE" + }, + { + "id": "curative", + "kind": "CURATIVE" + } + ], + "networkElementsNamePerId": {}, + "flowCnecs": [ + { + "id": "cnecBeFrPreventive", + "name": "cnecBeFrPreventive", + "networkElementId": "BBE1AA1 FFR1AA1 1", + "operator": "FR", + "instant": "preventive", + "contingencyId": null, + "optimized": true, + "monitored": false, + "iMax": [ + NaN + ], + "nominalV": [ + 400.0 + ], + "thresholds": [ + { + "unit": "megawatt", + "min": -300.0, + "max": 300.0, + "side": 1 + }, + { + "unit": "megawatt", + "min": -300.0, + "max": 300.0, + "side": 2 + } + ] + } + ], + "pstRangeActions": [ + { + "id": "pstBeFr2", + "name": "pstBeFr2", + "operator": "BE", + "activationCost": 5.0, + "variationCosts": { + "up": 10.0, + "down": 10.0 + }, + "onInstantUsageRules": [ + { + "instant": "preventive", + "usageMethod": "available" + } + ], + "networkElementId": "BBE1AA1 FFR1AA1 2", + "ranges": [ + { + "min": -16, + "max": 16, + "rangeType": "absolute" + } + ] + } + ] +} \ No newline at end of file diff --git a/data/crac/crac-util/src/test/resources/crac/crac-0430.json b/data/crac/crac-util/src/test/resources/crac/crac-0430.json new file mode 100644 index 0000000000..8c9791907f --- /dev/null +++ b/data/crac/crac-util/src/test/resources/crac/crac-0430.json @@ -0,0 +1,81 @@ +{ + "type": "CRAC", + "version": "2.7", + "info": "Generated by PowSyBl OpenRAO https://powsybl.readthedocs.io/projects/openrao/", + "id": "crac-20250217", + "name": "crac-20250217", + "timestamp": "2025-02-13T04:30:00Z", + "instants": [ + { + "id": "preventive", + "kind": "PREVENTIVE" + }, + { + "id": "outage", + "kind": "OUTAGE" + }, + { + "id": "curative", + "kind": "CURATIVE" + } + ], + "networkElementsNamePerId": {}, + "flowCnecs": [ + { + "id": "cnecBeFrPreventive", + "name": "cnecBeFrPreventive", + "networkElementId": "BBE1AA1 FFR1AA1 1", + "operator": "FR", + "instant": "preventive", + "contingencyId": null, + "optimized": true, + "monitored": false, + "iMax": [ + NaN + ], + "nominalV": [ + 400.0 + ], + "thresholds": [ + { + "unit": "megawatt", + "min": -300.0, + "max": 300.0, + "side": 1 + }, + { + "unit": "megawatt", + "min": -300.0, + "max": 300.0, + "side": 2 + } + ] + } + ], + "pstRangeActions": [ + { + "id": "pstBeFr2", + "name": "pstBeFr2", + "operator": "BE", + "activationCost": 5.0, + "variationCosts": { + "up": 10.0, + "down": 10.0 + }, + "onInstantUsageRules": [ + { + "instant": "preventive", + "usageMethod": "available" + } + ], + "networkElementId": "BBE1AA1 FFR1AA1 2", + "ranges": [ + { + "min": -16, + "max": 16, + "rangeType": "absolute" + } + ] + } + ] +} \ No newline at end of file diff --git a/data/crac/crac-util/src/test/resources/crac/crac-0530.json b/data/crac/crac-util/src/test/resources/crac/crac-0530.json new file mode 100644 index 0000000000..eee4d889a2 --- /dev/null +++ b/data/crac/crac-util/src/test/resources/crac/crac-0530.json @@ -0,0 +1,81 @@ +{ + "type": "CRAC", + "version": "2.7", + "info": "Generated by PowSyBl OpenRAO https://powsybl.readthedocs.io/projects/openrao/", + "id": "crac-20250218", + "name": "crac-20250218", + "timestamp": "2025-02-13T05:30:00Z", + "instants": [ + { + "id": "preventive", + "kind": "PREVENTIVE" + }, + { + "id": "outage", + "kind": "OUTAGE" + }, + { + "id": "curative", + "kind": "CURATIVE" + } + ], + "networkElementsNamePerId": {}, + "flowCnecs": [ + { + "id": "cnecBeFrPreventive", + "name": "cnecBeFrPreventive", + "networkElementId": "BBE1AA1 FFR1AA1 1", + "operator": "FR", + "instant": "preventive", + "contingencyId": null, + "optimized": true, + "monitored": false, + "iMax": [ + NaN + ], + "nominalV": [ + 400.0 + ], + "thresholds": [ + { + "unit": "megawatt", + "min": -300.0, + "max": 300.0, + "side": 1 + }, + { + "unit": "megawatt", + "min": -300.0, + "max": 300.0, + "side": 2 + } + ] + } + ], + "pstRangeActions": [ + { + "id": "pstBeFr2", + "name": "pstBeFr2", + "operator": "BE", + "activationCost": 5.0, + "variationCosts": { + "up": 10.0, + "down": 10.0 + }, + "onInstantUsageRules": [ + { + "instant": "preventive", + "usageMethod": "available" + } + ], + "networkElementId": "BBE1AA1 FFR1AA1 2", + "ranges": [ + { + "min": -16, + "max": 16, + "rangeType": "absolute" + } + ] + } + ] +} \ No newline at end of file diff --git a/data/crac/crac-util/src/test/resources/crac/crac-0630.json b/data/crac/crac-util/src/test/resources/crac/crac-0630.json new file mode 100644 index 0000000000..94a0355e27 --- /dev/null +++ b/data/crac/crac-util/src/test/resources/crac/crac-0630.json @@ -0,0 +1,81 @@ +{ + "type": "CRAC", + "version": "2.7", + "info": "Generated by PowSyBl OpenRAO https://powsybl.readthedocs.io/projects/openrao/", + "id": "crac-20250219", + "name": "crac-20250219", + "timestamp": "2025-02-13T06:30:00Z", + "instants": [ + { + "id": "preventive", + "kind": "PREVENTIVE" + }, + { + "id": "outage", + "kind": "OUTAGE" + }, + { + "id": "curative", + "kind": "CURATIVE" + } + ], + "networkElementsNamePerId": {}, + "flowCnecs": [ + { + "id": "cnecBeFrPreventive", + "name": "cnecBeFrPreventive", + "networkElementId": "BBE1AA1 FFR1AA1 1", + "operator": "FR", + "instant": "preventive", + "contingencyId": null, + "optimized": true, + "monitored": false, + "iMax": [ + NaN + ], + "nominalV": [ + 400.0 + ], + "thresholds": [ + { + "unit": "megawatt", + "min": -300.0, + "max": 300.0, + "side": 1 + }, + { + "unit": "megawatt", + "min": -300.0, + "max": 300.0, + "side": 2 + } + ] + } + ], + "pstRangeActions": [ + { + "id": "pstBeFr2", + "name": "pstBeFr2", + "operator": "BE", + "activationCost": 5.0, + "variationCosts": { + "up": 10.0, + "down": 10.0 + }, + "onInstantUsageRules": [ + { + "instant": "preventive", + "usageMethod": "available" + } + ], + "networkElementId": "BBE1AA1 FFR1AA1 2", + "ranges": [ + { + "min": -16, + "max": 16, + "rangeType": "absolute" + } + ] + } + ] +} \ No newline at end of file diff --git a/data/crac/crac-util/src/test/resources/crac/crac-0730.json b/data/crac/crac-util/src/test/resources/crac/crac-0730.json new file mode 100644 index 0000000000..c619037447 --- /dev/null +++ b/data/crac/crac-util/src/test/resources/crac/crac-0730.json @@ -0,0 +1,81 @@ +{ + "type": "CRAC", + "version": "2.7", + "info": "Generated by PowSyBl OpenRAO https://powsybl.readthedocs.io/projects/openrao/", + "id": "crac-20250220", + "name": "crac-20250220", + "timestamp": "2025-02-13T07:30:00Z", + "instants": [ + { + "id": "preventive", + "kind": "PREVENTIVE" + }, + { + "id": "outage", + "kind": "OUTAGE" + }, + { + "id": "curative", + "kind": "CURATIVE" + } + ], + "networkElementsNamePerId": {}, + "flowCnecs": [ + { + "id": "cnecBeFrPreventive", + "name": "cnecBeFrPreventive", + "networkElementId": "BBE1AA1 FFR1AA1 1", + "operator": "FR", + "instant": "preventive", + "contingencyId": null, + "optimized": true, + "monitored": false, + "iMax": [ + NaN + ], + "nominalV": [ + 400.0 + ], + "thresholds": [ + { + "unit": "megawatt", + "min": -300.0, + "max": 300.0, + "side": 1 + }, + { + "unit": "megawatt", + "min": -300.0, + "max": 300.0, + "side": 2 + } + ] + } + ], + "pstRangeActions": [ + { + "id": "pstBeFr2", + "name": "pstBeFr2", + "operator": "BE", + "activationCost": 5.0, + "variationCosts": { + "up": 10.0, + "down": 10.0 + }, + "onInstantUsageRules": [ + { + "instant": "preventive", + "usageMethod": "available" + } + ], + "networkElementId": "BBE1AA1 FFR1AA1 2", + "ranges": [ + { + "min": -16, + "max": 16, + "rangeType": "absolute" + } + ] + } + ] +} \ No newline at end of file diff --git a/data/crac/crac-util/src/test/resources/logback-test.xml b/data/crac/crac-util/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..dc4a8872fc --- /dev/null +++ b/data/crac/crac-util/src/test/resources/logback-test.xml @@ -0,0 +1,15 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + From 359207e2404a8d447e01927922ff07b0ea8e4c4a Mon Sep 17 00:00:00 2001 From: Thomas Bouquet Date: Thu, 9 Apr 2026 15:05:38 +0200 Subject: [PATCH 51/64] mark LazyNetwork as beta feature Signed-off-by: Thomas Bouquet --- .../openrao/data/crac/util/IcsImporter.java | 8 +- .../data/crac/util/IcsImporterTest.java | 28 ++-- .../powsybl/openrao/raoapi/LazyNetwork.java | 8 +- .../openrao/searchtreerao/marmot/Marmot.java | 137 +++++++++--------- .../searchtreerao/marmot/MarmotUtils.java | 10 +- .../tests/steps/TimeCoupledRaoSteps.java | 15 +- .../powsybl/openrao/tests/utils/Helpers.java | 5 +- 7 files changed, 108 insertions(+), 103 deletions(-) diff --git a/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java b/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java index 868c029b3e..61dc1db0bf 100644 --- a/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java +++ b/data/crac/crac-util/src/main/java/com/powsybl/openrao/data/crac/util/IcsImporter.java @@ -26,15 +26,12 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.nio.file.Path; import java.time.OffsetDateTime; -import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Properties; import static com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider.BUSINESS_WARNS; @@ -91,8 +88,7 @@ public static TimeCoupledRaoInput populateInputWithICS(TimeCoupledRaoInput timeC InputStream staticInputStream, InputStream seriesInputStream, InputStream gskInputStream, double icsCostUp, - double icsCostDown, - String exportDirectory) throws IOException { + double icsCostDown) throws IOException { costUp = icsCostUp; costDown = icsCostDown; @@ -447,7 +443,7 @@ private static void p0RespectsConstraints(CSVRecord staticRecord, Map Pmin or P0 < 1d // 2) check that if shutDown not allowe, no switch to 0 // 3) check that if startUp not allowed, no switch from P0 < 1 to P0 > Pmin - CSVRecord p0 = seriesRecord.get(P0); + CSVRecord p0 = seriesRecord.get(P0); Boolean shutDownAllowed = Boolean.parseBoolean(staticRecord.get(SHUTDOWN_ALLOWED)); Boolean startUpAllowed = Boolean.parseBoolean(staticRecord.get(STARTUP_ALLOWED)); Optional lead = Optional.empty(); diff --git a/data/crac/crac-util/src/test/java/com/powsybl/openrao/data/crac/util/IcsImporterTest.java b/data/crac/crac-util/src/test/java/com/powsybl/openrao/data/crac/util/IcsImporterTest.java index b5ff2e810a..fbe5100412 100644 --- a/data/crac/crac-util/src/test/java/com/powsybl/openrao/data/crac/util/IcsImporterTest.java +++ b/data/crac/crac-util/src/test/java/com/powsybl/openrao/data/crac/util/IcsImporterTest.java @@ -77,7 +77,7 @@ void testIcsImporterOneAction() throws IOException { InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static.csv"); InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - timeCoupledRaoInput = IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR); + timeCoupledRaoInput = IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost); assertEquals(1, timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().size()); GeneratorConstraints generatorConstraints = timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().iterator().next(); @@ -126,7 +126,7 @@ void testIcsImporterShutDownAndStartUpTrue() throws IOException { InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_shutdown_startup_true.csv"); InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR); + IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost); assertEquals(1, timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().size()); GeneratorConstraints generatorConstraints = timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().iterator().next(); @@ -142,7 +142,7 @@ void testIcsImporterNoShutDown() { InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR)) + .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost)) .withMessage("Could not parse shutDownAllowed value for raId Redispatching_RA"); } @@ -154,7 +154,7 @@ void testIcsImporterWrongShutDown() { InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR)) + .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost)) .withMessage("Could not parse shutDownAllowed value wrongValue for raId Redispatching_RA"); } @@ -166,7 +166,7 @@ void testIcsImporterNoStartUp() { InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR)) + .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost)) .withMessage("Could not parse startUpAllowed value for raId Redispatching_RA"); } @@ -178,7 +178,7 @@ void testIcsImporterWrongStartUp() { InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR)) + .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost)) .withMessage("Could not parse startUpAllowed value wrongValue for raId Redispatching_RA"); } @@ -190,7 +190,7 @@ void testIcsImporterWithGskNoShutDown() { InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR)) + .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost)) .withMessage("Could not parse shutDownAllowed value for nodeId FFR1AA1"); } @@ -202,7 +202,7 @@ void testIcsImporterWithGskWrongShutDown() { InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR)) + .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost)) .withMessage("Could not parse shutDownAllowed value wrongValue for nodeId FFR1AA1"); } @@ -214,7 +214,7 @@ void testIcsImporterWithGskNoStartUp() { InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR)) + .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost)) .withMessage("Could not parse startUpAllowed value for nodeId FFR1AA1"); } @@ -226,7 +226,7 @@ void testIcsImporterWithGskWrongStartUp() { InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); Assertions.assertThatExceptionOfType(OpenRaoException.class) - .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR)) + .isThrownBy(() -> IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost)) .withMessage("Could not parse startUpAllowed value wrongValue for nodeId FFR1AA1"); } @@ -236,7 +236,7 @@ void testIcsImporterOneActionNoPminRd() throws IOException { InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static.csv"); InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series_no_pmin_rd.csv"); InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - timeCoupledRaoInput = IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR); + timeCoupledRaoInput = IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost); assertEquals(1, timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().size()); GeneratorConstraints generatorConstraints = timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().iterator().next(); @@ -285,7 +285,7 @@ void testIcsImporterOneActionNoGradientNoLeadNoLag() throws IOException { InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_no_gradient_no_lead_no_lag.csv"); InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - timeCoupledRaoInput = IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR); + timeCoupledRaoInput = IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost); assertEquals(1, timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().size()); GeneratorConstraints generatorConstraints = timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().iterator().next(); @@ -332,7 +332,7 @@ void testIcsImporterGradientNotOk() throws IOException { InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static.csv"); InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series_gradient_not_ok.csv"); InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - timeCoupledRaoInput = IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR); + timeCoupledRaoInput = IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost); assertEquals(0, timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().size()); assertEquals(0, crac1.getInjectionRangeActions().size()); @@ -345,7 +345,7 @@ void testIcsImporterWithGSK() throws IOException { InputStream staticInputStream = IcsImporterTest.class.getResourceAsStream("/ics/static_with_gsk.csv"); InputStream seriesInputStream = IcsImporterTest.class.getResourceAsStream("/ics/series.csv"); InputStream gskInputStream = IcsImporterTest.class.getResourceAsStream("/glsk/gsk.csv"); - timeCoupledRaoInput = IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost, TMP_DIR); + timeCoupledRaoInput = IcsImporter.populateInputWithICS(timeCoupledRaoInput, staticInputStream, seriesInputStream, gskInputStream, cost, cost); assertEquals(2, timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().size()); GeneratorConstraints generatorConstraintsBE = timeCoupledRaoInput.getTimeCoupledConstraints().getGeneratorConstraints().stream() diff --git a/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/LazyNetwork.java b/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/LazyNetwork.java index 33713d4cf5..275d8fb423 100644 --- a/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/LazyNetwork.java +++ b/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/LazyNetwork.java @@ -7,6 +7,7 @@ package com.powsybl.openrao.raoapi; +import com.google.common.annotations.Beta; import com.powsybl.commons.datasource.DataSource; import com.powsybl.commons.datasource.ReadOnlyDataSource; import com.powsybl.commons.extensions.Extension; @@ -87,7 +88,7 @@ * * @author Thomas Bouquet {@literal } */ - +@Beta public class LazyNetwork implements Network, AutoCloseable { private static final String TEMP_DIR = System.getProperty("java.io.tmpdir") + File.separator; private final String networkPath; @@ -100,8 +101,8 @@ public LazyNetwork(String networkPath) { } public LazyNetwork(Network network) { - String networkName = TEMP_DIR + UUID.randomUUID().toString() + ".xiidm"; - network.write("XIIDM", new Properties(), Path.of(networkName)); + String networkName = TEMP_DIR + UUID.randomUUID() + ".jiidm"; + network.write("JIIDM", new Properties(), Path.of(networkName)); this.networkPath = networkName; this.isLoaded = false; } @@ -118,6 +119,7 @@ public void close() throws Exception { // TODO: currently modifications on the network will not be saved -> perhaps override networkPath with a UUID and write content to it network = null; isLoaded = false; + System.gc(); } @Override 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 fa99144d31..4d0f358d74 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 @@ -88,7 +88,7 @@ public class Marmot implements TimeCoupledRaoProvider { private static final String MIP_SCENARIO = "MipScenario"; private static final String MIN_MARGIN_VIOLATION_EVALUATOR = "min-margin-violation-evaluator"; - private static final int PARALLELISM = 8; + private static final int PARALLELISM = 12; @Override public CompletableFuture run(TimeCoupledRaoInput timeCoupledRaoInput, RaoParameters raoParameters) { @@ -96,7 +96,6 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl TemporalData cracs = timeCoupledRaoInput.getRaoInputs().map(RaoInput::getCrac); TemporalData initialNetworks = MarmotUtils.cloneNetworks(timeCoupledRaoInput.getRaoInputs().map(RaoInput::getNetwork)); MarmotUtils.releaseAll(timeCoupledRaoInput.getRaoInputs().map(RaoInput::getNetwork)); - MarmotUtils.releaseAll(initialNetworks); TemporalData initialInputs = MarmotUtils.merge(initialNetworks, cracs); @@ -415,22 +414,23 @@ private void replaceFastRaoResultsWithLightVersions(TemporalData topo } private static TemporalData runTopologicalOptimization(TemporalData raoInputs, TemporalData> consideredCnecs, RaoParameters raoParameters) { - raoParameters.getExtension(OpenRaoSearchTreeParameters.class).getRangeActionsOptimizationParameters().getLinearOptimizationSolver().setSolverSpecificParameters("MAXTIME 15"); + raoParameters.getExtension(OpenRaoSearchTreeParameters.class).getRangeActionsOptimizationParameters().getLinearOptimizationSolver().setSolverSpecificParameters("MAXTIME 15"); // for XPRESS only + return raoInputs.mapMultiThreading(raoInput -> runSingleTopologicalOptimization(raoInput, consideredCnecs, raoParameters), PARALLELISM); + } - return raoInputs.mapMultiThreading( - raoInput -> { - Set cnecs = new HashSet<>(); - OffsetDateTime timestamp = MarmotUtils.getTimestamp(raoInput); - String logMessage = "[MARMOT] Running RAO for timestamp %s [{}]".formatted(timestamp); - TECHNICAL_LOGS.info(logMessage, "start"); - RaoResult raoResult = FastRao.launchFastRaoOptimization(raoInput, raoParameters, null, cnecs); - TECHNICAL_LOGS.info(logMessage, "end"); - consideredCnecs.put(timestamp, cnecs); - MarmotUtils.releaseNetwork(raoInput.getNetwork()); - return raoResult; - }, - PARALLELISM - ); + private static RaoResult runSingleTopologicalOptimization(RaoInput raoInput, TemporalData> consideredCnecs, RaoParameters raoParameters) { + try (LazyNetwork ignored = (LazyNetwork) raoInput.getNetwork()) { + Set cnecs = new HashSet<>(); + OffsetDateTime timestamp = MarmotUtils.getTimestamp(raoInput); + String logMessage = "[MARMOT] Running RAO for timestamp %s [{}]".formatted(timestamp); + TECHNICAL_LOGS.info(logMessage, "start"); + RaoResult raoResult = FastRao.launchFastRaoOptimization(raoInput, raoParameters, null, cnecs); + TECHNICAL_LOGS.info(logMessage, "end"); + consideredCnecs.put(timestamp, cnecs); + return raoResult; + } catch (Exception e) { + throw new OpenRaoException(e); + } } private static TemporalData applyPreventiveTopologicalActionsOnNetworks(TemporalData raoInputs, TemporalData preventiveTopologicalActionsResults) { @@ -507,33 +507,35 @@ private static GlobalLinearOptimizationResult optimizeLinearRemedialActions(Time TemporalData optimizationPerimeterPerTimestamp = computeOptimizationPerimetersPerTimestamp(raoInput.getRaoInputs().map(RaoInput::getCrac), consideredCnecs); // no objective function defined in individual IteratingLinearOptimizerInputs as it is global - Map linearOptimizerInputPerTimestamp = new HashMap<>(); - for (OffsetDateTime timestamp : raoInput.getTimestampsToRun()) { - RaoInput individualRaoInput = raoInput.getRaoInputs().getData(timestamp).orElseThrow(); - try (LazyNetwork lazyNetwork = new LazyNetwork(individualRaoInput.getNetwork())) { - IteratingLinearOptimizerInput iteratingLinearOptimizerInput = IteratingLinearOptimizerInput.create() - .withNetwork(lazyNetwork) - .withOptimizationPerimeter(optimizationPerimeterPerTimestamp.getData(timestamp).orElseThrow() - .copyWithFilteredAvailableHvdcRangeAction(individualRaoInput.getNetwork())) - .withInitialFlowResult(initialResults.getData(timestamp).orElseThrow()) - .withPrePerimeterFlowResult(initialResults.getData(timestamp).orElseThrow()) - .withPreOptimizationFlowResult(postTopologicalActionsResults.getData(timestamp).orElseThrow()) - .withPrePerimeterSetpoints(initialSetpoints.getData(timestamp).orElseThrow()) - .withPreOptimizationSensitivityResult(postTopologicalActionsResults.getData(timestamp).orElseThrow()) - .withPreOptimizationAppliedRemedialActions(curativeRemedialActions.getData(timestamp).orElseThrow()) - .withToolProvider(ToolProvider.buildFromRaoInputAndParameters(raoInput.getRaoInputs().getData(timestamp).orElseThrow(), parameters)) - .withOutageInstant(individualRaoInput.getCrac().getOutageInstant()) - .withAppliedNetworkActionsInPrimaryState(preventiveTopologicalActions.getData(timestamp).orElseThrow()) - .build(); - MarmotUtils.releaseNetwork(individualRaoInput.getNetwork()); - linearOptimizerInputPerTimestamp.put(timestamp, iteratingLinearOptimizerInput); - } catch (Exception e) { - throw new OpenRaoException(e); - } - } + TemporalData linearOptimizerInputs = raoInput.getRaoInputs().mapMultiThreading( + individualRaoInput -> { + OffsetDateTime timestamp = MarmotUtils.getTimestamp(individualRaoInput); + try (LazyNetwork lazyNetwork = new LazyNetwork(individualRaoInput.getNetwork())) { + IteratingLinearOptimizerInput iteratingLinearOptimizerInput = IteratingLinearOptimizerInput.create() + .withNetwork(lazyNetwork) + .withOptimizationPerimeter(optimizationPerimeterPerTimestamp.getData(timestamp).orElseThrow() + .copyWithFilteredAvailableHvdcRangeAction(individualRaoInput.getNetwork())) + .withInitialFlowResult(initialResults.getData(timestamp).orElseThrow()) + .withPrePerimeterFlowResult(initialResults.getData(timestamp).orElseThrow()) + .withPreOptimizationFlowResult(postTopologicalActionsResults.getData(timestamp).orElseThrow()) + .withPrePerimeterSetpoints(initialSetpoints.getData(timestamp).orElseThrow()) + .withPreOptimizationSensitivityResult(postTopologicalActionsResults.getData(timestamp).orElseThrow()) + .withPreOptimizationAppliedRemedialActions(curativeRemedialActions.getData(timestamp).orElseThrow()) + .withToolProvider(ToolProvider.buildFromRaoInputAndParameters(raoInput.getRaoInputs().getData(timestamp).orElseThrow(), parameters)) + .withOutageInstant(individualRaoInput.getCrac().getOutageInstant()) + .withAppliedNetworkActionsInPrimaryState(preventiveTopologicalActions.getData(timestamp).orElseThrow()) + .build(); + MarmotUtils.releaseNetwork(individualRaoInput.getNetwork()); + return iteratingLinearOptimizerInput; + } catch (Exception e) { + throw new OpenRaoException(e); + } + }, + PARALLELISM + ); TimeCoupledIteratingLinearOptimizerInput timeCoupledLinearOptimizerInput = new TimeCoupledIteratingLinearOptimizerInput( - new TemporalDataImpl<>(linearOptimizerInputPerTimestamp), objectiveFunction, raoInput.getTimeCoupledConstraints()); + linearOptimizerInputs, objectiveFunction, raoInput.getTimeCoupledConstraints()); MarmotUtils.releaseAll(timeCoupledLinearOptimizerInput.iteratingLinearOptimizerInputs().map(IteratingLinearOptimizerInput::network)); @@ -562,18 +564,19 @@ private static GlobalLinearOptimizationResult optimizeLinearRemedialActions(Time } private static TemporalData computeOptimizationPerimetersPerTimestamp(TemporalData cracs, TemporalData> consideredCnecs) { - TemporalData optimizationPerimeters = new TemporalDataImpl<>(); - cracs.getTimestamps().forEach(timestamp -> { - Crac crac = cracs.getData(timestamp).orElseThrow(); - optimizationPerimeters.put(timestamp, new PreventiveOptimizationPerimeter( - crac.getPreventiveState(), - consideredCnecs.getData(timestamp).orElseThrow(), - new HashSet<>(), // no loopflows for now - new HashSet<>(), // don't re-optimize topological actions in Marmot - crac.getRangeActions(crac.getPreventiveState()) - )); - }); - return optimizationPerimeters; + return cracs.mapMultiThreading( + crac -> { + OffsetDateTime timestamp = crac.getTimestamp().orElseThrow(); + return new PreventiveOptimizationPerimeter( + crac.getPreventiveState(), + consideredCnecs.getData(timestamp).orElseThrow(), + new HashSet<>(), // no loopflows for now + new HashSet<>(), // don't re-optimize topological actions in Marmot + crac.getRangeActions(crac.getPreventiveState()) + ); + }, + PARALLELISM + ); } private static TimeCoupledRaoResultImpl mergeTopologicalAndLinearOptimizationResults(TemporalData raoInputs, @@ -657,31 +660,29 @@ private LinearOptimizationResult getPostTopologicalOptimizationResult(TemporalDa private static TemporalData getRangeActionActivationResults(TemporalData allInitialSetPoints, TemporalData topologicalOptimizationResults, TemporalData preventiveStates) { - Map rangeActionsResults = new HashMap<>(); - topologicalOptimizationResults.getTimestamps().forEach( - timestamp -> { - State preventiveState = preventiveStates.getData(timestamp).orElseThrow(); + return preventiveStates.mapMultiThreading( + preventiveState -> { + OffsetDateTime timestamp = preventiveState.getTimestamp().orElseThrow(); RangeActionSetpointResult initialSetPoints = allInitialSetPoints.getData(timestamp).orElseThrow(); RangeActionSetpointResult optimizedSetPoints = new RangeActionSetpointResultImpl( topologicalOptimizationResults.getData(timestamp).orElseThrow().getOptimizedSetPointsOnState(preventiveState)); RangeActionActivationResultImpl rangeActionActivationResult = new RangeActionActivationResultImpl(initialSetPoints); optimizedSetPoints.getRangeActions().forEach(rangeAction -> rangeActionActivationResult.putResult(rangeAction, preventiveState, optimizedSetPoints.getSetpoint(rangeAction))); - rangeActionsResults.put(timestamp, rangeActionActivationResult); - } + return rangeActionActivationResult; + }, + PARALLELISM ); - return new TemporalDataImpl<>(rangeActionsResults); } private static TemporalData getNetworkActionActivationResults(TemporalData topologicalOptimizationResults, TemporalData preventiveStates) { - Map networkActionsResults = new HashMap<>(); - topologicalOptimizationResults.getTimestamps().forEach( - timestamp -> { - State preventiveState = preventiveStates.getData(timestamp).orElseThrow(); + return preventiveStates.mapMultiThreading( + preventiveState -> { + OffsetDateTime timestamp = preventiveState.getTimestamp().orElseThrow(); Set activatedNetworkActions = topologicalOptimizationResults.getData(timestamp).orElseThrow().getActivatedNetworkActionsDuringState(preventiveState); - networkActionsResults.put(timestamp, new NetworkActionsResultImpl(Map.of(preventiveState, activatedNetworkActions))); - } + return new NetworkActionsResultImpl(Map.of(preventiveState, activatedNetworkActions)); + }, + PARALLELISM ); - return new TemporalDataImpl<>(networkActionsResults); } @Override diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java index 44c5557681..29d17e31fd 100644 --- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java @@ -163,14 +163,18 @@ public static void applyPreventiveRemedialActions(RaoInput raoInput, NetworkActi if (networkActionsToBeApplied.isEmpty()) { OpenRaoLoggerProvider.TECHNICAL_LOGS.info("[MARMOT] No preventive topological actions applied for timestamp {}", crac.getTimestamp().orElseThrow()); } else { + // TODO: close systematically networkActionsToBeApplied.forEach(networkAction -> networkAction.apply(network)); } } public static TemporalData cloneNetworks(TemporalData networks) { - return new TemporalDataImpl<>( - networks.getDataPerTimestamp().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> new LazyNetwork(entry.getValue()))) - ); + TemporalData lazyNetworks = new TemporalDataImpl<>(); + networks.getDataPerTimestamp().forEach((timestamp, network) -> { + lazyNetworks.put(timestamp, new LazyNetwork(network)); + MarmotUtils.releaseNetwork(network); + }); + return lazyNetworks; } public static TemporalData merge(TemporalData networks, TemporalData cracs) { diff --git a/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java b/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java index 20c5427f30..34c805d7f6 100644 --- a/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java +++ b/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java @@ -40,6 +40,7 @@ import com.powsybl.openrao.raoapi.RaoInput; import com.powsybl.openrao.raoapi.TimeCoupledRao; import com.powsybl.openrao.raoapi.TimeCoupledRaoInput; +import com.powsybl.openrao.searchtreerao.marmot.MarmotUtils; import com.powsybl.openrao.tests.utils.CoreCcPreprocessor; import io.cucumber.datatable.DataTable; import io.cucumber.java.After; @@ -197,7 +198,7 @@ public static void loadDataForTimeCoupledRao(String timeCoupledConstraintsPath, List> inputs = arg1.asMaps(String.class, String.class); for (Map tsInput : inputs) { OffsetDateTime offsetDateTime = getOffsetDateTimeFromBrusselsTimestamp(tsInput.get("Timestamp")); - try (LazyNetwork lazyNetwork = importNetwork(getFile(networkFolderPath.concat(tsInput.get("Network"))), false)) { + try (LazyNetwork lazyNetwork = new LazyNetwork(importNetwork(getFile(networkFolderPath.concat(tsInput.get("Network"))), false))) { Crac crac = importCrac(getFile(cracFolderPath.concat(tsInput.get("CRAC"))), lazyNetwork, null).getLeft(); raoInputs.put(offsetDateTime, RaoInput.build(lazyNetwork, crac).build()); } catch (Exception e) { @@ -212,6 +213,7 @@ public static void loadDataForTimeCoupledRao(String timeCoupledConstraintsPath, @Given("time-coupled rao inputs for CORE are:") public static void coreTimeCoupledRaoInputsAre(DataTable arg1) throws IOException { loadDataForCoreTimeCoupledRao(arg1); + System.gc(); } public static void loadDataForCoreTimeCoupledRao(DataTable arg1) throws IOException { @@ -236,8 +238,9 @@ public static void loadDataForCoreTimeCoupledRao(DataTable arg1) throws IOExcept for (Map tsInput : inputs) { OffsetDateTime offsetDateTime = getOffsetDateTimeFromBrusselsTimestamp(tsInput.get("Timestamp")); TECHNICAL_LOGS.info("**** Loading data for TS {} ****", offsetDateTime); - try (LazyNetwork lazyNetwork = importNetwork(getFile(networkFolderPath.concat(tsInput.get("Network"))), false)) { - CoreCcPreprocessor.applyCoreCcNetworkPreprocessing(lazyNetwork); + Network network = importNetwork(getFile(networkFolderPath.concat(tsInput.get("Network"))), false); + CoreCcPreprocessor.applyCoreCcNetworkPreprocessing(network); + try (LazyNetwork lazyNetwork = new LazyNetwork(network)) { // Crac Pair cracImportResult; if (useIndividualCracs) { // only works with json @@ -254,7 +257,6 @@ public static void loadDataForCoreTimeCoupledRao(DataTable arg1) throws IOExcept } catch (Exception e) { throw new OpenRaoException(e); } - } InputStream gskInputStream = icsGskPath == null ? null : new FileInputStream(getFile(icsGskPath)); @@ -269,9 +271,10 @@ public static void loadDataForCoreTimeCoupledRao(DataTable arg1) throws IOExcept new FileInputStream(getFile(icsSeriesPath)), gskInputStream, fbConstraintParameters.getIcsCostUp(), - fbConstraintParameters.getIcsCostDown(), - networkFolderPathPostIcsImport.concat(inputs.getFirst().get("Network")).split(".uct")[0] + fbConstraintParameters.getIcsCostDown() ); + MarmotUtils.releaseAll(raoInputs.map(RaoInput::getNetwork)); + MarmotUtils.releaseAll(timeCoupledRaoInput.getRaoInputs().map(RaoInput::getNetwork)); } @When("I launch marmot") diff --git a/tests/src/test/java/com/powsybl/openrao/tests/utils/Helpers.java b/tests/src/test/java/com/powsybl/openrao/tests/utils/Helpers.java index 5e2fefce07..4fb76b8b1f 100644 --- a/tests/src/test/java/com/powsybl/openrao/tests/utils/Helpers.java +++ b/tests/src/test/java/com/powsybl/openrao/tests/utils/Helpers.java @@ -64,13 +64,12 @@ private Helpers() { // must nor be used } - public static LazyNetwork importNetwork(File networkFile, boolean useRdfId) { + public static Network importNetwork(File networkFile, boolean useRdfId) { Properties importParams = new Properties(); if (useRdfId) { importParams.put("iidm.import.cgmes.source-for-iidm-id", "rdfID"); } - Network network = Network.read(Paths.get(networkFile.toString()), LocalComputationManager.getDefault(), Suppliers.memoize(ImportConfig::load).get(), importParams); - return new LazyNetwork(network); + return Network.read(Paths.get(networkFile.toString()), LocalComputationManager.getDefault(), Suppliers.memoize(ImportConfig::load).get(), importParams); } public static Pair importCrac(File cracFile, Network network, CracCreationParameters cracCreationParameters) throws IOException { From 41477444cdde1e7445b114b6db5efe96133731ef Mon Sep 17 00:00:00 2001 From: Thomas Bouquet Date: Thu, 9 Apr 2026 15:26:08 +0200 Subject: [PATCH 52/64] duplicate RaoParameters to concurrent accesses Signed-off-by: Thomas Bouquet --- .../openrao/searchtreerao/marmot/Marmot.java | 12 ++++++++---- .../openrao/searchtreerao/marmot/MarmotUtils.java | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) 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 4d0f358d74..7922f710b7 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 @@ -99,10 +99,15 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl TemporalData initialInputs = MarmotUtils.merge(initialNetworks, cracs); + // custom settings for XPRESS optimization + raoParameters.getExtension(OpenRaoSearchTreeParameters.class).getRangeActionsOptimizationParameters().getLinearOptimizationSolver().setSolverSpecificParameters("MAXTIME 15"); + TemporalData raoParametersDuplicates = new TemporalDataImpl<>(); + timeCoupledRaoInput.getTimestampsToRun().forEach(timestamp -> raoParametersDuplicates.put(timestamp, MarmotUtils.cloneParameters(raoParameters))); + // 1. Run independent RAOs to compute optimal preventive topological remedial actions TECHNICAL_LOGS.info("[MARMOT] ----- Topological optimization [start]"); TemporalData> consideredCnecs = new TemporalDataImpl<>(); - TemporalData topologicalOptimizationResults = runTopologicalOptimization(initialInputs, consideredCnecs, raoParameters); + TemporalData topologicalOptimizationResults = runTopologicalOptimization(initialInputs, consideredCnecs, raoParametersDuplicates); TECHNICAL_LOGS.info("[MARMOT] ----- Topological optimization [end]"); // 2. Get the initial results from the various independent results to avoid recomputing them @@ -413,9 +418,8 @@ private void replaceFastRaoResultsWithLightVersions(TemporalData topo timestamp, new LightFastRaoResultImpl((FastRaoResultImpl) raoResult))); } - private static TemporalData runTopologicalOptimization(TemporalData raoInputs, TemporalData> consideredCnecs, RaoParameters raoParameters) { - raoParameters.getExtension(OpenRaoSearchTreeParameters.class).getRangeActionsOptimizationParameters().getLinearOptimizationSolver().setSolverSpecificParameters("MAXTIME 15"); // for XPRESS only - return raoInputs.mapMultiThreading(raoInput -> runSingleTopologicalOptimization(raoInput, consideredCnecs, raoParameters), PARALLELISM); + private static TemporalData runTopologicalOptimization(TemporalData raoInputs, TemporalData> consideredCnecs, TemporalData raoParameters) { + return raoInputs.mapMultiThreading(raoInput -> runSingleTopologicalOptimization(raoInput, consideredCnecs, raoParameters.getData(MarmotUtils.getTimestamp(raoInput)).orElseThrow()), PARALLELISM); } private static RaoResult runSingleTopologicalOptimization(RaoInput raoInput, TemporalData> consideredCnecs, RaoParameters raoParameters) { diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java index 29d17e31fd..2b15b631be 100644 --- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java @@ -21,6 +21,7 @@ import com.powsybl.openrao.data.raoresult.api.RaoResult; import com.powsybl.openrao.raoapi.LazyNetwork; import com.powsybl.openrao.raoapi.RaoInput; +import com.powsybl.openrao.raoapi.json.JsonRaoParameters; import com.powsybl.openrao.raoapi.parameters.RaoParameters; import com.powsybl.openrao.searchtreerao.castor.algorithm.PrePerimeterSensitivityAnalysis; import com.powsybl.openrao.searchtreerao.commons.ToolProvider; @@ -30,6 +31,9 @@ import com.powsybl.openrao.searchtreerao.result.api.PrePerimeterResult; import com.powsybl.openrao.sensitivityanalysis.AppliedRemedialActions; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; import java.time.OffsetDateTime; import java.util.HashMap; import java.util.HashSet; @@ -206,4 +210,15 @@ public static void releaseNetwork(N network) { public static OffsetDateTime getTimestamp(RaoInput raoInput) { return raoInput.getCrac().getTimestamp().orElseThrow(); } + + public static RaoParameters cloneParameters(RaoParameters raoParameters) { + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + JsonRaoParameters.write(raoParameters, outputStream); + try (InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray())) { + return JsonRaoParameters.read(inputStream); + } + } catch (Exception e) { + throw new OpenRaoException(e); + } + } } From cf930fb72ef85fcdf14026a9e7820c533239e4a3 Mon Sep 17 00:00:00 2001 From: Thomas Bouquet Date: Thu, 9 Apr 2026 15:26:20 +0200 Subject: [PATCH 53/64] use ConcurrentHashMaps for TemporalDataImpl Signed-off-by: Thomas Bouquet --- .../com/powsybl/openrao/commons/TemporalDataImpl.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/commons/src/main/java/com/powsybl/openrao/commons/TemporalDataImpl.java b/commons/src/main/java/com/powsybl/openrao/commons/TemporalDataImpl.java index ab70eb2694..168814f3dc 100644 --- a/commons/src/main/java/com/powsybl/openrao/commons/TemporalDataImpl.java +++ b/commons/src/main/java/com/powsybl/openrao/commons/TemporalDataImpl.java @@ -12,7 +12,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -27,15 +27,15 @@ public class TemporalDataImpl implements TemporalData { private final Map dataPerTimestamp; public TemporalDataImpl() { - this(new TreeMap<>()); + this(new ConcurrentHashMap<>()); } public TemporalDataImpl(Map dataPerTimestamp) { - this.dataPerTimestamp = new TreeMap<>(dataPerTimestamp); + this.dataPerTimestamp = new ConcurrentHashMap<>(dataPerTimestamp); } public Map getDataPerTimestamp() { - return new TreeMap<>(dataPerTimestamp); + return new ConcurrentHashMap<>(dataPerTimestamp); } public void put(OffsetDateTime timestamp, T data) { From fd050a8cf815971a415edcf5325f73e2a7236249 Mon Sep 17 00:00:00 2001 From: Thomas Bouquet Date: Thu, 9 Apr 2026 15:51:03 +0200 Subject: [PATCH 54/64] smart use of threads Signed-off-by: Thomas Bouquet --- .../openrao/searchtreerao/marmot/Marmot.java | 100 ++++++++++-------- .../searchtreerao/marmot/MarmotUtils.java | 12 ++- 2 files changed, 69 insertions(+), 43 deletions(-) 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 7922f710b7..6acb3fbb7f 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 @@ -88,7 +88,7 @@ public class Marmot implements TimeCoupledRaoProvider { private static final String MIP_SCENARIO = "MipScenario"; private static final String MIN_MARGIN_VIOLATION_EVALUATOR = "min-margin-violation-evaluator"; - private static final int PARALLELISM = 12; + private static final int PARALLELISM = 12; // TODO: configure as a parameter in MARMOT extension @Override public CompletableFuture run(TimeCoupledRaoInput timeCoupledRaoInput, RaoParameters raoParameters) { @@ -104,10 +104,12 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl TemporalData raoParametersDuplicates = new TemporalDataImpl<>(); timeCoupledRaoInput.getTimestampsToRun().forEach(timestamp -> raoParametersDuplicates.put(timestamp, MarmotUtils.cloneParameters(raoParameters))); + int parallelism = Math.min(PARALLELISM, timeCoupledRaoInput.getTimestampsToRun().size()); + // 1. Run independent RAOs to compute optimal preventive topological remedial actions TECHNICAL_LOGS.info("[MARMOT] ----- Topological optimization [start]"); TemporalData> consideredCnecs = new TemporalDataImpl<>(); - TemporalData topologicalOptimizationResults = runTopologicalOptimization(initialInputs, consideredCnecs, raoParametersDuplicates); + TemporalData topologicalOptimizationResults = runTopologicalOptimization(initialInputs, consideredCnecs, raoParametersDuplicates, parallelism); TECHNICAL_LOGS.info("[MARMOT] ----- Topological optimization [end]"); // 2. Get the initial results from the various independent results to avoid recomputing them @@ -126,13 +128,14 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl TemporalData postTopologicalActionsResults = topologicalOptimizationResults.map( raoResult -> ((FastRaoResultImpl) raoResult).getFinalResult() ); - TemporalData initialSetpointResults = getInitialSetpointResults(topologicalOptimizationResults, cracs); + TemporalData initialSetpointResults = getInitialSetpointResults(topologicalOptimizationResults, cracs, parallelism); LinearOptimizationResult postTopologicalOptimizationResult = getPostTopologicalOptimizationResult( initialSetpointResults, postTopologicalActionsResults, fullObjectiveFunction, topologicalOptimizationResults, - cracs.map(Crac::getPreventiveState)); + cracs.map(Crac::getPreventiveState), + parallelism); // if no time-coupled constraints are defined, the results can be returned // TODO @@ -142,9 +145,9 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl // } // 5. Get and apply topological actions applied in independent optimizations - TemporalData preventiveTopologicalActions = getPreventiveTopologicalActions(cracs, topologicalOptimizationResults); + TemporalData preventiveTopologicalActions = getPreventiveTopologicalActions(cracs, topologicalOptimizationResults, parallelism); - TemporalData postTopologicalActionsInputs = applyPreventiveTopologicalActionsOnNetworks(initialInputs, preventiveTopologicalActions); + TemporalData postTopologicalActionsInputs = applyPreventiveTopologicalActionsOnNetworks(initialInputs, preventiveTopologicalActions, parallelism); TemporalData postTopoNetworks = MarmotUtils.cloneNetworks(postTopologicalActionsInputs.map(RaoInput::getNetwork)); MarmotUtils.releaseAll(postTopologicalActionsInputs.map(RaoInput::getNetwork)); @@ -172,7 +175,8 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl curativeRemedialActions, initialResults, raoParameters, - consideredCnecs + consideredCnecs, + parallelism ); TECHNICAL_LOGS.info("[MARMOT] Systematic time-coupled sensitivity analysis [end]"); @@ -195,13 +199,14 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl preventiveTopologicalActions, curativeRemedialActions, consideredCnecs, - filteredObjectiveFunction + filteredObjectiveFunction, + parallelism ); MarmotUtils.releaseAll(inputsForMip.map(RaoInput::getNetwork)); TECHNICAL_LOGS.info("[MARMOT] ----- Global range actions optimization [end] for iteration {}", counter); // Compute the flows on ALL the cnecs to check if the worst cnecs have changed and were considered in the MIP or not - loadFlowResults = applyActionsAndRunFullLoadflow(postTopologicalActionsInputs, curativeRemedialActions, linearOptimizationResults, initialResults, raoParameters); + loadFlowResults = applyActionsAndRunFullLoadflow(postTopologicalActionsInputs, curativeRemedialActions, linearOptimizationResults, initialResults, raoParameters, parallelism); // Create a global result with the flows on ALL cnecs and the actions applied during MIP TemporalData rangeActionActivationResultTemporalData = linearOptimizationResults.getRangeActionActivationResultTemporalData(); @@ -238,8 +243,9 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl return CompletableFuture.completedFuture(timeCoupledRaoResult); } - private TemporalData getInitialSetpointResults(TemporalData postTopologicalActionsResults, TemporalData cracs) { - return cracs.mapMultiThreading( + private TemporalData getInitialSetpointResults(TemporalData postTopologicalActionsResults, TemporalData cracs, int parallelism) { + return MarmotUtils.smartMap( + cracs, crac -> { OffsetDateTime timestamp = crac.getTimestamp().orElseThrow(); Map, Double> setPointMap = new HashMap<>(); @@ -249,7 +255,7 @@ private TemporalData getInitialSetpointResults(Tempor ); return new RangeActionSetpointResultImpl(setPointMap); }, - PARALLELISM + parallelism ); } @@ -393,8 +399,9 @@ private static TemporalData applyActionsAndRunFullLoadflow(T TemporalData curativeRemedialActions, LinearOptimizationResult filteredResult, TemporalData initialResults, - RaoParameters raoParameters) { - return postTopoInputs.mapMultiThreading( + RaoParameters raoParameters, int parallelism) { + return MarmotUtils.smartMap( + postTopoInputs, raoInput -> { OffsetDateTime timestamp = MarmotUtils.getTimestamp(raoInput); State preventiveState = raoInput.getCrac().getPreventiveState(); @@ -409,7 +416,7 @@ private static TemporalData applyActionsAndRunFullLoadflow(T MarmotUtils.releaseNetwork(raoInput.getNetwork()); return sensitivityAnalysisResults; }, - PARALLELISM + parallelism ); } @@ -418,8 +425,8 @@ private void replaceFastRaoResultsWithLightVersions(TemporalData topo timestamp, new LightFastRaoResultImpl((FastRaoResultImpl) raoResult))); } - private static TemporalData runTopologicalOptimization(TemporalData raoInputs, TemporalData> consideredCnecs, TemporalData raoParameters) { - return raoInputs.mapMultiThreading(raoInput -> runSingleTopologicalOptimization(raoInput, consideredCnecs, raoParameters.getData(MarmotUtils.getTimestamp(raoInput)).orElseThrow()), PARALLELISM); + private static TemporalData runTopologicalOptimization(TemporalData raoInputs, TemporalData> consideredCnecs, TemporalData raoParameters, int parallelism) { + return MarmotUtils.smartMap(raoInputs, raoInput -> runSingleTopologicalOptimization(raoInput, consideredCnecs, raoParameters.getData(MarmotUtils.getTimestamp(raoInput)).orElseThrow()), parallelism); } private static RaoResult runSingleTopologicalOptimization(RaoInput raoInput, TemporalData> consideredCnecs, RaoParameters raoParameters) { @@ -437,8 +444,9 @@ private static RaoResult runSingleTopologicalOptimization(RaoInput raoInput, Tem } } - private static TemporalData applyPreventiveTopologicalActionsOnNetworks(TemporalData raoInputs, TemporalData preventiveTopologicalActionsResults) { - return raoInputs.mapMultiThreading( + private static TemporalData applyPreventiveTopologicalActionsOnNetworks(TemporalData raoInputs, TemporalData preventiveTopologicalActionsResults, int parallelism) { + return MarmotUtils.smartMap( + raoInputs, raoInput -> { OffsetDateTime timestamp = MarmotUtils.getTimestamp(raoInput); NetworkActionsResult networkActionsResult = preventiveTopologicalActionsResults.getData(timestamp).orElseThrow(); @@ -450,7 +458,7 @@ private static TemporalData applyPreventiveTopologicalActionsOnNetwork throw new OpenRaoException(e); } }, - PARALLELISM + parallelism ); } @@ -465,8 +473,9 @@ private static TemporalData runAllSensitivityAnalysesBasedOn TemporalData curativeRemedialActions, TemporalData initialFlowResults, RaoParameters raoParameters, - TemporalData> consideredCnecs) { - return raoInputs.mapMultiThreading( + TemporalData> consideredCnecs, int parallelism) { + return MarmotUtils.smartMap( + raoInputs, raoInput -> { OffsetDateTime timestamp = MarmotUtils.getTimestamp(raoInput); PrePerimeterResult sensitivityAnalysisResult = runSensitivityAnalysisBasedOnInitialResult( @@ -479,12 +488,13 @@ private static TemporalData runAllSensitivityAnalysesBasedOn MarmotUtils.releaseNetwork(raoInput.getNetwork()); return sensitivityAnalysisResult; }, - PARALLELISM + parallelism ); } - private static TemporalData getPreventiveTopologicalActions(TemporalData cracs, TemporalData raoResults) { - return cracs.mapMultiThreading( + private static TemporalData getPreventiveTopologicalActions(TemporalData cracs, TemporalData raoResults, int parallelism) { + return MarmotUtils.smartMap( + cracs, crac -> { OffsetDateTime timestamp = crac.getTimestamp().orElseThrow(); return new NetworkActionsResultImpl( @@ -493,7 +503,7 @@ private static TemporalData getPreventiveTopologicalAction raoResults.getData(timestamp).orElseThrow().getActivatedNetworkActionsDuringState(crac.getPreventiveState()) )); }, - PARALLELISM + parallelism ); } @@ -505,13 +515,15 @@ private static GlobalLinearOptimizationResult optimizeLinearRemedialActions(Time TemporalData preventiveTopologicalActions, TemporalData curativeRemedialActions, TemporalData> consideredCnecs, - ObjectiveFunction objectiveFunction) { + ObjectiveFunction objectiveFunction, + int parallelism) { // -- Build IteratingLinearOptimizertimeCoupledInput - TemporalData optimizationPerimeterPerTimestamp = computeOptimizationPerimetersPerTimestamp(raoInput.getRaoInputs().map(RaoInput::getCrac), consideredCnecs); + TemporalData optimizationPerimeterPerTimestamp = computeOptimizationPerimetersPerTimestamp(raoInput.getRaoInputs().map(RaoInput::getCrac), consideredCnecs, parallelism); // no objective function defined in individual IteratingLinearOptimizerInputs as it is global - TemporalData linearOptimizerInputs = raoInput.getRaoInputs().mapMultiThreading( + TemporalData linearOptimizerInputs = MarmotUtils.smartMap( + raoInput.getRaoInputs(), individualRaoInput -> { OffsetDateTime timestamp = MarmotUtils.getTimestamp(individualRaoInput); try (LazyNetwork lazyNetwork = new LazyNetwork(individualRaoInput.getNetwork())) { @@ -535,7 +547,7 @@ private static GlobalLinearOptimizationResult optimizeLinearRemedialActions(Time throw new OpenRaoException(e); } }, - PARALLELISM + parallelism ); TimeCoupledIteratingLinearOptimizerInput timeCoupledLinearOptimizerInput = new TimeCoupledIteratingLinearOptimizerInput( @@ -567,8 +579,9 @@ private static GlobalLinearOptimizationResult optimizeLinearRemedialActions(Time return TimeCoupledIteratingLinearOptimizer.optimize(timeCoupledLinearOptimizerInput, linearOptimizerParameters); } - private static TemporalData computeOptimizationPerimetersPerTimestamp(TemporalData cracs, TemporalData> consideredCnecs) { - return cracs.mapMultiThreading( + private static TemporalData computeOptimizationPerimetersPerTimestamp(TemporalData cracs, TemporalData> consideredCnecs, int parallelism) { + return MarmotUtils.smartMap( + cracs, crac -> { OffsetDateTime timestamp = crac.getTimestamp().orElseThrow(); return new PreventiveOptimizationPerimeter( @@ -579,7 +592,7 @@ private static TemporalData computeOptimizationPerimeters crac.getRangeActions(crac.getPreventiveState()) ); }, - PARALLELISM + parallelism ); } @@ -648,9 +661,10 @@ private LinearOptimizationResult getPostTopologicalOptimizationResult(TemporalDa TemporalData prePerimeterResults, ObjectiveFunction objectiveFunction, TemporalData topologicalOptimizationResults, - TemporalData preventiveStates) { - TemporalData rangeActionActivationResults = getRangeActionActivationResults(allInitialSetPoints, topologicalOptimizationResults, preventiveStates); - TemporalData networkActionsResults = getNetworkActionActivationResults(topologicalOptimizationResults, preventiveStates); + TemporalData preventiveStates, + int parallelism) { + TemporalData rangeActionActivationResults = getRangeActionActivationResults(allInitialSetPoints, topologicalOptimizationResults, preventiveStates, parallelism); + TemporalData networkActionsResults = getNetworkActionActivationResults(topologicalOptimizationResults, preventiveStates, parallelism); return new GlobalLinearOptimizationResult( prePerimeterResults.map(PrePerimeterResult::getFlowResult), prePerimeterResults.map(PrePerimeterResult::getSensitivityResult), @@ -663,8 +677,9 @@ private LinearOptimizationResult getPostTopologicalOptimizationResult(TemporalDa private static TemporalData getRangeActionActivationResults(TemporalData allInitialSetPoints, TemporalData topologicalOptimizationResults, - TemporalData preventiveStates) { - return preventiveStates.mapMultiThreading( + TemporalData preventiveStates, int parallelism) { + return MarmotUtils.smartMap( + preventiveStates, preventiveState -> { OffsetDateTime timestamp = preventiveState.getTimestamp().orElseThrow(); RangeActionSetpointResult initialSetPoints = allInitialSetPoints.getData(timestamp).orElseThrow(); @@ -674,18 +689,19 @@ private static TemporalData getRangeActionActivatio optimizedSetPoints.getRangeActions().forEach(rangeAction -> rangeActionActivationResult.putResult(rangeAction, preventiveState, optimizedSetPoints.getSetpoint(rangeAction))); return rangeActionActivationResult; }, - PARALLELISM + parallelism ); } - private static TemporalData getNetworkActionActivationResults(TemporalData topologicalOptimizationResults, TemporalData preventiveStates) { - return preventiveStates.mapMultiThreading( + private static TemporalData getNetworkActionActivationResults(TemporalData topologicalOptimizationResults, TemporalData preventiveStates, int parallelism) { + return MarmotUtils.smartMap( + preventiveStates, preventiveState -> { OffsetDateTime timestamp = preventiveState.getTimestamp().orElseThrow(); Set activatedNetworkActions = topologicalOptimizationResults.getData(timestamp).orElseThrow().getActivatedNetworkActionsDuringState(preventiveState); return new NetworkActionsResultImpl(Map.of(preventiveState, activatedNetworkActions)); }, - PARALLELISM + parallelism ); } diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java index 2b15b631be..c1e6bb00bc 100644 --- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java @@ -41,7 +41,6 @@ import java.util.Map; import java.util.Set; import java.util.function.Function; -import java.util.stream.Collectors; /** * @author Thomas Bouquet {@literal } @@ -221,4 +220,15 @@ public static RaoParameters cloneParameters(RaoParameters raoParameters) { throw new OpenRaoException(e); } } + + /** + * Select the best TemporalData mapping strategy based on the number of threads. + * Necessary not to create a pool for only one thread. + */ + public static TemporalData smartMap(TemporalData temporalData, Function function, int threads) { + if (threads == 1) { + return temporalData.map(function); + } + return temporalData.mapMultiThreading(function, threads); + } } From 4ec7f9140c4e1fec499e2a28086d05981a0eea67 Mon Sep 17 00:00:00 2001 From: Godelaine de Montmorillon Date: Fri, 10 Apr 2026 13:45:44 +0200 Subject: [PATCH 55/64] fix existing UT --- .../openrao/data/icsimporter/IcsData.java | 12 +- .../data/icsimporter/IcsDataImporter.java | 160 ++++++++++++++++-- .../openrao/data/icsimporter/IcsUtil.java | 26 +++ .../data/icsimporter/IcsDataImporterTest.java | 10 +- .../openrao/data/icsimporter/IcsDataTest.java | 16 +- .../src/test/resources/ics/series.csv | 2 +- .../src/test/resources/ics/static.csv | 2 +- .../features/4_time_coupled/US93_5.feature | 6 +- 8 files changed, 196 insertions(+), 38 deletions(-) diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsData.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsData.java index ab350ef385..0b5effe302 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsData.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsData.java @@ -158,7 +158,7 @@ public static Set createGeneratorConstraints(String raId, * @return A map associating each node identifier to its corresponding generator identifier. * Returns an empty map if the process is aborted due to missing network components. */ - public static Map createGeneratorAndLoadAndUpdateNetworks(TemporalData initialNetworksToModify, + public static Map createGeneratorAndLoadAndUpdateNetworks(TemporalData initialNetworksToModify, String raId) { Map networkElementPerGskElement = new HashMap<>(); @@ -171,7 +171,7 @@ public static Map createGeneratorAndLoadAndUpdateNetworks(Tempor Double shiftKey = entry.getValue(); String generatorId = getGeneratorIdFromRaIdAndNodeId(raId, nodeId); - for (Map.Entry networkEntry : initialNetworksToModify.getDataPerTimestamp().entrySet()) { + for (Map.Entry networkEntry : initialNetworksToModify.getDataPerTimestamp().entrySet()) { OffsetDateTime dateTime = networkEntry.getKey(); Network network = networkEntry.getValue(); @@ -263,11 +263,14 @@ public TimeCoupledRaoInput processAllRedispatchingActions(TimeCoupledRaoInput ti String exportDirectory) { // Update voltage monitoring - TemporalData modifiedInitialNetworks = new TemporalDataImpl<>(); + TemporalData modifiedInitialNetworks = new TemporalDataImpl<>(); timeCoupledRaoInput.getRaoInputs().getDataPerTimestamp().forEach((dateTime, raoInput) -> { Network network = raoInput.getNetwork(); updateNominalVoltage(network); - modifiedInitialNetworks.put(dateTime, network); + modifiedInitialNetworks.put(dateTime, new LazyNetwork(network)); + if (network instanceof LazyNetwork) { + ((LazyNetwork) network).release(); + } }); TemporalData cracToModify = new TemporalDataImpl<>(); @@ -299,6 +302,7 @@ public TimeCoupledRaoInput processAllRedispatchingActions(TimeCoupledRaoInput ti String exportedNetworkPath = exportDirectory + dateTime.format(DateTimeFormatter.ofPattern("%y%m%d_%H%M%S")) + ".jiidm"; initialNetwork.write("JIIDM", new Properties(), Path.of(exportedNetworkPath)); postIcsRaoInputs.put(dateTime, RaoInput.build(new LazyNetwork(exportedNetworkPath), timeCoupledRaoInput.getRaoInputs().getData(dateTime).orElseThrow().getCrac()).build()); + initialNetwork.release(); }); return new TimeCoupledRaoInput(postIcsRaoInputs, timeCoupledRaoInput.getTimestampsToRun(), timeCoupledRaoInput.getTimeCoupledConstraints()); diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java index 6c5c91078e..0f8f498f1c 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java @@ -176,49 +176,175 @@ private static boolean shouldBeImported(CSVRecord staticRecord, List dateTimes) { + private static boolean p0RespectsConstraints(CSVRecord staticRecord, Map seriesRecord, List dateTimes) { + // Generatcr constraints varaibles + double timestampDuration = IcsUtil.computeTimestampDuration(dateTimes); + Boolean shutDownAllowed = Boolean.parseBoolean(staticRecord.get(SHUTDOWN_ALLOWED)); + Boolean startUpAllowed = Boolean.parseBoolean(staticRecord.get(STARTUP_ALLOWED)); + Optional lead = Optional.empty(); + Optional lagAndLead = Optional.empty(); + Optional parsedLead = Optional.empty(); + if (!staticRecord.get(LEAD_TIME).isEmpty()) { + parsedLead = Optional.of(parseDoubleWithPossibleCommas(staticRecord.get(LEAD_TIME))); + lead = Optional.of((int) Math.ceil( parsedLead.get()/ timestampDuration)); + } + if (!staticRecord.get(LAG_TIME).isEmpty()) { + double parsedLag = parseDoubleWithPossibleCommas(staticRecord.get(LAG_TIME)); + double parsedLagAndLead = parsedLead.map(aDouble -> aDouble + parsedLag).orElse(parsedLag); + lagAndLead = Optional.of((int) Math.ceil( parsedLagAndLead/ timestampDuration)); + } double maxGradient = staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT).isEmpty() ? - MAX_GRADIENT : parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT)); + MAX_GRADIENT : parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT)); double minGradient = staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT).isEmpty() ? - -MAX_GRADIENT : -parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT)); + -MAX_GRADIENT : -parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT)); + + + // Intermediate variables + boolean count_lag = false; + int count_consecutive_null_values = 0; Iterator dateTimeIterator = dateTimes.iterator(); OffsetDateTime currentDateTime = dateTimeIterator.next(); while (dateTimeIterator.hasNext()) { OffsetDateTime nextDateTime = dateTimeIterator.next(); - double diff = parseDoubleWithPossibleCommas(p0record.get(nextDateTime.getHour() + OFFSET)) - parseDoubleWithPossibleCommas(p0record.get(currentDateTime.getHour() + OFFSET)); - if (diff > maxGradient || diff < minGradient) { - BUSINESS_WARNS.warn( - "Redispatching action {} will not be imported because it does not respect power gradients : min/max/diff = {} / {} / {}", - staticRecord.get(0), minGradient, maxGradient, diff - ); - return false; + double next_p0 = parseDoubleWithPossibleCommas(seriesRecord.get(P0).get(nextDateTime.getHour() + OFFSET)); + double current_p0 = parseDoubleWithPossibleCommas(seriesRecord.get(P0).get(currentDateTime.getHour() + OFFSET)); + Optional pMinRD = parseValue(seriesRecord, P_MIN_RD, currentDateTime, 1); + double pMin = pMinRD.orElse(ON_POWER_THRESHOLD); + + // 1- Check gradients + if (!areGradientsRespected(staticRecord, next_p0, current_p0, maxGradient, minGradient, currentDateTime)) return false; + + // 2 - Check Pmin is respected + if (!isPminRespected(staticRecord, current_p0, pMin, currentDateTime)) return false; + + // 3 - Starting up next timestamp + // a) Check start up is allowed + // b) Check lead time is respected + // c) Check lag time + lead time is respected + if (current_p0 < pMin) { + count_consecutive_null_values += 1; + } + if (current_p0 < pMin && next_p0 >= pMin) { + if (!isStartUpRespected(staticRecord, startUpAllowed, currentDateTime)) return false; + if (!isLeadTimeRespected(staticRecord, lead, count_consecutive_null_values, currentDateTime)) return false; + if (count_lag) { + if (!isLeadTimeAndLagTimeRespected(staticRecord, count_consecutive_null_values, lagAndLead, currentDateTime)) + return false; + // Re-initialize + count_lag = false; + } + } + // 4 - Shutting down next timestamp + // a) Check shut down is allowed + // activate lag time + lead time checking + if (current_p0 >= pMin && next_p0 < pMin) { + if (!isShutDownRespected(staticRecord, shutDownAllowed, currentDateTime)) return false; + if (lagAndLead.isPresent()) { + count_lag = true; + } + } + + // Re-init count_consecutive_null_values + if (current_p0 >= pMin) { + count_consecutive_null_values = 0; } currentDateTime = nextDateTime; } + + // Last timestamp + double current_p0 = parseDoubleWithPossibleCommas(seriesRecord.get(P0).get(currentDateTime.getHour() + OFFSET)); + Optional pMinRD = parseValue(seriesRecord, P_MIN_RD, currentDateTime, 1); + double pMin = pMinRD.orElse(ON_POWER_THRESHOLD); + return isPminRespected(staticRecord, current_p0, pMin, currentDateTime); + } + + private static boolean isShutDownRespected(CSVRecord staticRecord, Boolean shutDownAllowed, OffsetDateTime currentDateTime) { + if (!shutDownAllowed) { + BUSINESS_WARNS.warn("Redispatching action {} will not be imported (hour {}) because it does not respect prohibited shut down", + staticRecord.get(0), currentDateTime.getHour()); + return false; + } + return true; + } + + private static boolean isLeadTimeAndLagTimeRespected(CSVRecord staticRecord, int count_consecutive_null_values, Optional lagAndLead, OffsetDateTime currentDateTime) { + if (count_consecutive_null_values < lagAndLead.get()) { + BUSINESS_WARNS.warn("Redispatching action {} will not be imported (hour {}) because it does not respect lagTime + leadTime ({}). RA was OFF after shut down for only {} timestamps", + staticRecord.get(0), currentDateTime.getHour(), lagAndLead.get(), count_consecutive_null_values); + return false; + } return true; } + private static boolean isLeadTimeRespected(CSVRecord staticRecord, Optional lead, int count_consecutive_null_values, OffsetDateTime currentDateTime) { + if (lead.isPresent() && count_consecutive_null_values < lead.get()) { + BUSINESS_WARNS.warn("Redispatching action {} will not be imported (hour {}) because it does not respect leadTime ({}). RA was OFF before start up for only {} timestamps", + staticRecord.get(0), currentDateTime.getHour(), lead.get(), count_consecutive_null_values); + return false; + } + return true; + } + + private static boolean isStartUpRespected(CSVRecord staticRecord, Boolean startUpAllowed, OffsetDateTime currentDateTime) { + if (!startUpAllowed) { + BUSINESS_WARNS.warn("Redispatching action {} will not be imported (hour {}) because it does not respect prohibited start up", + staticRecord.get(0), currentDateTime.getHour()); + return false; + } + return true; + } + + private static boolean areGradientsRespected(CSVRecord staticRecord, double next_p0, double current_p0, double maxGradient, double minGradient, OffsetDateTime currentDateTime) { + double diff = next_p0 - current_p0; + if (diff > maxGradient || diff < minGradient) { + BUSINESS_WARNS.warn( + "Redispatching action {} will not be imported (hour {}) because it does not respect power gradients : min/max/diff = {} / {} / {}", + staticRecord.get(0), currentDateTime.getHour(), minGradient, maxGradient, diff + ); + return false; + } + return true; + } + + private static boolean isPminRespected(CSVRecord staticRecord, double current_p0, double pMin, OffsetDateTime currentDateTime) { + if (current_p0 < pMin && current_p0 > ON_POWER_THRESHOLD) { + BUSINESS_WARNS.warn("Redispatching action {} will not be imported (hour {}) because it does not respect Pmin : P0 is {} and Pmin at {}", + staticRecord.get(0), currentDateTime.getHour(), current_p0, pMin); + return false; + } + return true; + } + + /** * Verifies whether the range of redispatching parameters is valid for the input time series, * ensuring that redispatching values are non-negative and exceed a minimum threshold. diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsUtil.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsUtil.java index bf8847e01a..1272518b5b 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsUtil.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsUtil.java @@ -10,9 +10,12 @@ import com.powsybl.iidm.network.Bus; import com.powsybl.iidm.network.LoadType; import com.powsybl.iidm.network.Network; +import com.powsybl.openrao.commons.OpenRaoException; import org.apache.commons.csv.CSVRecord; import java.time.OffsetDateTime; +import java.time.temporal.ChronoUnit; +import java.util.List; import java.util.Map; import java.util.Optional; @@ -131,4 +134,27 @@ public static void updateNominalVoltage(Network network) { private static boolean safeDoubleEquals(double a, double b) { return Math.abs(a - b) < 1e-3; } + + private static double computeTimeGap(OffsetDateTime timestamp1, OffsetDateTime timestamp2) { + if (timestamp1 == null || timestamp2 == null) { + throw new OpenRaoException("timestamp1 and timestamp2 cannot both be null"); + } else if (timestamp1.isAfter(timestamp2)) { + throw new OpenRaoException("timestamp1 is expected to come before timestamp2"); + } + return timestamp1.until(timestamp2, ChronoUnit.SECONDS) / 3600.0; + } + + public static double computeTimestampDuration(List timestamps) { + if (timestamps.size() < 2) { + throw new OpenRaoException("There must be at least two timestamps."); + } + double referenceTimestampDuration = computeTimeGap(timestamps.getFirst(), timestamps.get(1)); + for (int timestampIndex = 1; timestampIndex < timestamps.size() - 1; timestampIndex++) { + double timestampDuration = computeTimeGap(timestamps.get(timestampIndex), timestamps.get(timestampIndex + 1)); + if (timestampDuration != referenceTimestampDuration) { + throw new OpenRaoException("All timestamps are not evenly spread."); + } + } + return referenceTimestampDuration; + } } diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataImporterTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataImporterTest.java index 363c6b5e13..8f32e7843b 100644 --- a/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataImporterTest.java +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataImporterTest.java @@ -101,8 +101,8 @@ void testParseStaticCsv() throws IOException { assertEquals("Node", record.get("RD description mode")); assertEquals("BBE1AA1", record.get("UCT Node or GSK ID")); assertEquals("50", record.get("Minimum Redispatch [MW]")); - assertEquals("FALSE", record.get("Startup allowed")); - assertEquals("FALSE", record.get("Shutdown allowed")); + assertEquals("TRUE", record.get("Startup allowed")); + assertEquals("TRUE", record.get("Shutdown allowed")); } @Test @@ -282,11 +282,11 @@ private static Stream gradientNotRespectCsvCases() { return Stream.of( Arguments.of( tooHighGradientCsv, - "Redispatching action Redispatching_RA will not be imported because it does not respect power gradients : min/max/diff = -20.0 / 20.0 / 34.0" + "Redispatching action Redispatching_RA will not be imported (hour 0) because it does not respect power gradients : min/max/diff = -20.0 / 20.0 / 34.0" ), Arguments.of( tooLowGradientCsv, - "Redispatching action Redispatching_RA will not be imported because it does not respect power gradients : min/max/diff = -20.0 / 20.0 / -36.0" + "Redispatching action Redispatching_RA will not be imported (hour 0) because it does not respect power gradients : min/max/diff = -20.0 / 20.0 / -36.0" ) ); } @@ -327,6 +327,6 @@ void testMissingGradientInStaticCsv() throws IOException { generateOffsetDateTimeList(3)); assertEquals(0, icsData.getRedispatchingActions().size()); - assertEquals("Redispatching action Redispatching_RA will not be imported because it does not respect power gradients : min/max/diff = -1000.0 / 1000.0 / 1964.0", logsList.get(0).getFormattedMessage()); + assertEquals("Redispatching action Redispatching_RA will not be imported (hour 0) because it does not respect power gradients : min/max/diff = -1000.0 / 1000.0 / 1964.0", logsList.get(0).getFormattedMessage()); } } diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataTest.java index b19aa6d185..a4c7b90b29 100644 --- a/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataTest.java +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataTest.java @@ -27,6 +27,7 @@ import com.powsybl.openrao.raoapi.LazyNetwork; import com.powsybl.openrao.raoapi.RaoInput; import com.powsybl.openrao.raoapi.TimeCoupledRaoInput; +import org.apache.logging.log4j.util.Lazy; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -55,9 +56,9 @@ public class IcsDataTest { private static final double DOUBLE_EPSILON = 1e-6; private Crac crac1; private Crac crac2; - private Network network1; - private Network network2; - private TemporalData networkTemporalData; + private LazyNetwork network1; + private LazyNetwork network2; + private TemporalData networkTemporalData; private TemporalData cracTemporalData; List logsList; private final OffsetDateTime timestamp1 = OffsetDateTime.of(2025, 2, 13, 0, 30, 0, 0, ZoneOffset.UTC); @@ -68,8 +69,9 @@ void setUp() throws IOException { // we need to import twice the network to avoid variant names conflicts on the same network object String networkFilePath1 = "2Nodes2ParallelLinesPST_0030.uct"; String networkFilePath2 = "2Nodes2ParallelLinesPST_0130.uct"; - network1 = Network.read(networkFilePath1, IcsDataTest.class.getResourceAsStream("/network/" + networkFilePath1)); - network2 = Network.read(networkFilePath2, IcsDataTest.class.getResourceAsStream("/network/" + networkFilePath2)); + network1 = new LazyNetwork(getResourcePath("/network/" + networkFilePath1)); + network2 = new LazyNetwork(getResourcePath("/network/" + networkFilePath2)); + crac1 = Crac.read("/crac/crac-0030.json", getClass().getResourceAsStream("/crac/crac-0030.json"), network1); crac2 = Crac.read("/crac/crac-0130.json", getClass().getResourceAsStream("/crac/crac-0130.json"), network2); @@ -124,8 +126,8 @@ void testCreateGeneratorConstraintRaDefinedOnANode() throws IOException { assertEquals(1.0, generatorConstraints.getLeadTime().get(), DOUBLE_EPSILON); assertTrue(generatorConstraints.getLagTime().isPresent()); assertEquals(1.0, generatorConstraints.getLagTime().get(), DOUBLE_EPSILON); - assertFalse(generatorConstraints.isShutDownAllowed()); - assertFalse(generatorConstraints.isStartUpAllowed()); + assertTrue(generatorConstraints.isShutDownAllowed()); + assertTrue(generatorConstraints.isStartUpAllowed()); } @Test diff --git a/data/ics-importer/src/test/resources/ics/series.csv b/data/ics-importer/src/test/resources/ics/series.csv index a8003c9767..e46a12e2c4 100644 --- a/data/ics-importer/src/test/resources/ics/series.csv +++ b/data/ics-importer/src/test/resources/ics/series.csv @@ -1,5 +1,5 @@ RA RD ID;Type of timeseries;00:30;01:30;02:30;03:30;04:30;05:30;06:30;07:30;08:30;09:30;10:30;11:30;12:30;13:30;14:30;15:30;16:30;17:30;18:30;19:30;20:30;21:30;22:30;23:30 Redispatching_RA;RDP-;35;35;36;35;34;32;28;27;25;9;8;9;8;8;8;8;23;25;30;31;34;31;29;39 Redispatching_RA;RDP+;43;43;41;42;43;45;50;51;53;68;70;69;70;70;70;69;54;52;48;47;44;46;49;39 -Redispatching_RA;P0;116;110;110;110;110;116;0;25;109;108;106;90;89;89;89;89;89;104;106;111;112;112;110;120 +Redispatching_RA;P0;116;120;117;116;115;113;109;108;106;90;89;90;89;89;89;89;104;106;111;112;115;112;110;120 Redispatching_RA;Pmin_RD;10;15;20;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30;30 \ No newline at end of file diff --git a/data/ics-importer/src/test/resources/ics/static.csv b/data/ics-importer/src/test/resources/ics/static.csv index 4ec750bbc7..129c323405 100644 --- a/data/ics-importer/src/test/resources/ics/static.csv +++ b/data/ics-importer/src/test/resources/ics/static.csv @@ -1,2 +1,2 @@ RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed -Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;20;20;1;1;FALSE;FALSE \ No newline at end of file +Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;20;20;1;1;TRUE;TRUE \ No newline at end of file diff --git a/tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature b/tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature index 0743fb1138..eab587c716 100644 --- a/tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature +++ b/tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature @@ -132,9 +132,9 @@ Feature: US 93.3: CORE IDCC data Scenario: US 93.3.4: 20240224 Given network files are in folder "idcc/20240224-FID2-734-v1-10V1001C--00264T-to-10V1001C--00085T" Given crac file is "idcc/20240224-FSC-ID2-CB-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given ics static file is "_110V1001C--00200I_CSA-COMRA-RDSTATIC-D_CORE-20240224-V001_.csv" - Given ics series file is "_110V1001C--00200I_CSA-COMRA-RDSERIES-D_CORE-20240224-V001_.csv" - Given ics gsk file is "_10V1001C--00200I_CSA-INDRA-GSK-D_D7-20240224-V001_.csv" + Given ics static file is "idcc/_110V1001C--00200I_CSA-COMRA-RDSTATIC-D_CORE-20240224-V001_.csv" + Given ics series file is "idcc/_110V1001C--00200I_CSA-COMRA-RDSERIES-D_CORE-20240224-V001_.csv" + Given ics gsk file is "idcc/_10V1001C--00200I_CSA-INDRA-GSK-D_D7-20240224-V001_.csv" Given configuration file is "idcc/RaoParameters_minCost_megawatt_dc.json" Given time-coupled RefProg file is "idcc/20240224-FID2-632-v3-10V1001C--00264T-to-10V1001C--00085T.xml" Given time-coupled rao inputs for CORE are: From 0d64c4301a6f76bba550075ce3256df0e2a971a5 Mon Sep 17 00:00:00 2001 From: Godelaine de Montmorillon Date: Fri, 10 Apr 2026 15:26:42 +0200 Subject: [PATCH 56/64] remove print lp --- .../algorithms/linearproblem/LinearProblem.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/linearproblem/LinearProblem.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/linearproblem/LinearProblem.java index f8b45bf00a..472af26d77 100644 --- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/linearproblem/LinearProblem.java +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/linearproblem/LinearProblem.java @@ -133,15 +133,7 @@ public void updateBetweenMipIteration(RangeActionActivationResult rangeActionAct public LinearProblemStatus solve() { solver.setRelativeMipGap(relativeMipGap); solver.setSolverSpecificParametersAsString(solverSpecificParameters); - LinearProblemStatus status = solver.solve(); - try { - OutputStream os = new FileOutputStream("lp.txt"); - String exportedModel = solver.getMpSolver().exportModelAsLpFormat(); - os.write(exportedModel.getBytes()); - } catch (IOException e) { - // do nothing - } - return status; + return solver.solve(); } public OpenRaoMPObjective getObjective() { From 0c1ce9e8e006445a8f2c7d3eaeefa74a0b260a4a Mon Sep 17 00:00:00 2001 From: Godelaine de Montmorillon Date: Fri, 10 Apr 2026 17:06:48 +0200 Subject: [PATCH 57/64] WIP --- .../data/icsimporter/IcsDataImporter.java | 83 ++-- .../openrao/data/icsimporter/IcsDataTest.java | 8 - .../powsybl/openrao/raoapi/LazyNetwork.java | 1 - .../fillers/GeneratorConstraintsFiller.java | 81 ++-- .../linearproblem/LinearProblem.java | 4 - .../openrao/searchtreerao/marmot/Marmot.java | 389 +++++++++--------- .../searchtreerao/marmot/MarmotUtils.java | 65 ++- .../tests/steps/TimeCoupledRaoSteps.java | 1 + 8 files changed, 338 insertions(+), 294 deletions(-) diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java index 23d91abf29..1a077602a8 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java @@ -270,77 +270,87 @@ private static boolean p0RespectsConstraints(CSVRecord staticRecord, Map parsedLead = Optional.empty(); if (!staticRecord.get(LEAD_TIME).isEmpty()) { parsedLead = Optional.of(parseDoubleWithPossibleCommas(staticRecord.get(LEAD_TIME))); - lead = Optional.of((int) Math.ceil( parsedLead.get()/ timestampDuration)); + lead = Optional.of((int) Math.ceil(parsedLead.get() / timestampDuration)); } if (!staticRecord.get(LAG_TIME).isEmpty()) { double parsedLag = parseDoubleWithPossibleCommas(staticRecord.get(LAG_TIME)); double parsedLagAndLead = parsedLead.map(aDouble -> aDouble + parsedLag).orElse(parsedLag); - lagAndLead = Optional.of((int) Math.ceil( parsedLagAndLead/ timestampDuration)); + lagAndLead = Optional.of((int) Math.ceil(parsedLagAndLead / timestampDuration)); } double maxGradient = staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT).isEmpty() ? MAX_GRADIENT : parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_POSITIVE_POWER_GRADIENT)); double minGradient = staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT).isEmpty() ? -MAX_GRADIENT : -parseDoubleWithPossibleCommas(staticRecord.get(MAXIMUM_NEGATIVE_POWER_GRADIENT)); - // Intermediate variables - boolean count_lag = false; - int count_consecutive_null_values = 0; + boolean countLag = false; + int countConsecutiveNullValues = 0; Iterator dateTimeIterator = dateTimes.iterator(); OffsetDateTime currentDateTime = dateTimeIterator.next(); while (dateTimeIterator.hasNext()) { OffsetDateTime nextDateTime = dateTimeIterator.next(); - double next_p0 = parseDoubleWithPossibleCommas(seriesRecord.get(P0).get(nextDateTime.getHour() + OFFSET)); - double current_p0 = parseDoubleWithPossibleCommas(seriesRecord.get(P0).get(currentDateTime.getHour() + OFFSET)); + double nextP0 = parseDoubleWithPossibleCommas(seriesRecord.get(P0).get(nextDateTime.getHour() + OFFSET)); + double currentP0 = parseDoubleWithPossibleCommas(seriesRecord.get(P0).get(currentDateTime.getHour() + OFFSET)); Optional pMinRD = parseValue(seriesRecord, P_MIN_RD, currentDateTime, 1); double pMin = pMinRD.orElse(ON_POWER_THRESHOLD); // 1- Check gradients - if (!areGradientsRespected(staticRecord, next_p0, current_p0, maxGradient, minGradient, currentDateTime)) return false; + if (!areGradientsRespected(staticRecord, nextP0, currentP0, maxGradient, minGradient, currentDateTime)) { + return false; + } // 2 - Check Pmin is respected - if (!isPminRespected(staticRecord, current_p0, pMin, currentDateTime)) return false; + if (!isPminRespected(staticRecord, currentP0, pMin, currentDateTime)) { + return false; + } // 3 - Starting up next timestamp // a) Check start up is allowed // b) Check lead time is respected // c) Check lag time + lead time is respected - if (current_p0 < pMin) { - count_consecutive_null_values += 1; + if (currentP0 < pMin) { + countConsecutiveNullValues += 1; } - if (current_p0 < pMin && next_p0 >= pMin) { - if (!isStartUpRespected(staticRecord, startUpAllowed, currentDateTime)) return false; - if (!isLeadTimeRespected(staticRecord, lead, count_consecutive_null_values, currentDateTime)) return false; - if (count_lag) { - if (!isLeadTimeAndLagTimeRespected(staticRecord, count_consecutive_null_values, lagAndLead, currentDateTime)) + if (currentP0 < pMin && nextP0 >= pMin) { + if (!isStartUpRespected(staticRecord, startUpAllowed, currentDateTime)) { + return false; + } + if (!isLeadTimeRespected(staticRecord, lead, countConsecutiveNullValues, currentDateTime)) { + return false; + } + if (countLag) { + if (!isLeadTimeAndLagTimeRespected(staticRecord, countConsecutiveNullValues, lagAndLead, currentDateTime)) { return false; + } // Re-initialize - count_lag = false; + countLag = false; } } // 4 - Shutting down next timestamp // a) Check shut down is allowed // activate lag time + lead time checking - if (current_p0 >= pMin && next_p0 < pMin) { - if (!isShutDownRespected(staticRecord, shutDownAllowed, currentDateTime)) return false; + if (currentP0 >= pMin && nextP0 < pMin) { + if (!isShutDownRespected(staticRecord, shutDownAllowed, currentDateTime)) { + return false; + } if (lagAndLead.isPresent()) { - count_lag = true; + countLag = true; } } - // Re-init count_consecutive_null_values - if (current_p0 >= pMin) { - count_consecutive_null_values = 0; + // Re-init countConsecutiveNullValues + if (currentP0 >= pMin) { + countConsecutiveNullValues = 0; } currentDateTime = nextDateTime; } // Last timestamp - double current_p0 = parseDoubleWithPossibleCommas(seriesRecord.get(P0).get(currentDateTime.getHour() + OFFSET)); + double currentP0 = parseDoubleWithPossibleCommas(seriesRecord.get(P0).get(currentDateTime.getHour() + OFFSET)); Optional pMinRD = parseValue(seriesRecord, P_MIN_RD, currentDateTime, 1); double pMin = pMinRD.orElse(ON_POWER_THRESHOLD); - return isPminRespected(staticRecord, current_p0, pMin, currentDateTime); + return isPminRespected(staticRecord, currentP0, pMin, currentDateTime); } private static boolean isShutDownRespected(CSVRecord staticRecord, Boolean shutDownAllowed, OffsetDateTime currentDateTime) { @@ -352,19 +362,19 @@ private static boolean isShutDownRespected(CSVRecord staticRecord, Boolean shutD return true; } - private static boolean isLeadTimeAndLagTimeRespected(CSVRecord staticRecord, int count_consecutive_null_values, Optional lagAndLead, OffsetDateTime currentDateTime) { - if (count_consecutive_null_values < lagAndLead.get()) { + private static boolean isLeadTimeAndLagTimeRespected(CSVRecord staticRecord, int countConsecutiveNullValues, Optional lagAndLead, OffsetDateTime currentDateTime) { + if (countConsecutiveNullValues < lagAndLead.get()) { BUSINESS_WARNS.warn("Redispatching action {} is not imported (hour {}): lagTime + leadTime ({}) not respected. RA was OFF after shut down for only {} timestamps", - staticRecord.get(0), currentDateTime.getHour(), lagAndLead.get(), count_consecutive_null_values); + staticRecord.get(0), currentDateTime.getHour(), lagAndLead.get(), countConsecutiveNullValues); return false; } return true; } - private static boolean isLeadTimeRespected(CSVRecord staticRecord, Optional lead, int count_consecutive_null_values, OffsetDateTime currentDateTime) { - if (lead.isPresent() && count_consecutive_null_values < lead.get()) { + private static boolean isLeadTimeRespected(CSVRecord staticRecord, Optional lead, int countConsecutiveNullValues, OffsetDateTime currentDateTime) { + if (lead.isPresent() && countConsecutiveNullValues < lead.get()) { BUSINESS_WARNS.warn("Redispatching action {} is not imported (hour {}): leadTime ({}) not respected. RA was OFF before start up for only {} timestamps", - staticRecord.get(0), currentDateTime.getHour(), lead.get(), count_consecutive_null_values); + staticRecord.get(0), currentDateTime.getHour(), lead.get(), countConsecutiveNullValues); return false; } return true; @@ -379,8 +389,8 @@ private static boolean isStartUpRespected(CSVRecord staticRecord, Boolean startU return true; } - private static boolean areGradientsRespected(CSVRecord staticRecord, double next_p0, double current_p0, double maxGradient, double minGradient, OffsetDateTime currentDateTime) { - double diff = next_p0 - current_p0; + private static boolean areGradientsRespected(CSVRecord staticRecord, double nextP0, double currentP0, double maxGradient, double minGradient, OffsetDateTime currentDateTime) { + double diff = nextP0 - currentP0; if (diff > maxGradient || diff < minGradient) { BUSINESS_WARNS.warn( "Redispatching action {} is not imported (hour {}): does not respect power gradients : min/max/diff = {} / {} / {}", @@ -391,16 +401,15 @@ private static boolean areGradientsRespected(CSVRecord staticRecord, double next return true; } - private static boolean isPminRespected(CSVRecord staticRecord, double current_p0, double pMin, OffsetDateTime currentDateTime) { - if (current_p0 < pMin && current_p0 > ON_POWER_THRESHOLD) { + private static boolean isPminRespected(CSVRecord staticRecord, double currentP0, double pMin, OffsetDateTime currentDateTime) { + if (currentP0 < pMin && currentP0 > ON_POWER_THRESHOLD) { BUSINESS_WARNS.warn("Redispatching action {} is not imported (hour {}): does not respect Pmin : P0 is {} and Pmin at {}", - staticRecord.get(0), currentDateTime.getHour(), current_p0, pMin); + staticRecord.get(0), currentDateTime.getHour(), currentP0, pMin); return false; } return true; } - /** * Verifies whether the range of redispatching parameters is valid for the input time series, * ensuring that redispatching values are non-negative and exceed a minimum threshold. diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataTest.java index 1950572b6f..785c2fe9f8 100644 --- a/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataTest.java +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataTest.java @@ -14,7 +14,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.powsybl.iidm.network.Generator; import com.powsybl.iidm.network.Network; -import com.powsybl.openrao.commons.OpenRaoException; import com.powsybl.openrao.commons.TemporalData; import com.powsybl.openrao.commons.TemporalDataImpl; import com.powsybl.openrao.commons.logs.RaoBusinessWarns; @@ -27,13 +26,8 @@ import com.powsybl.openrao.raoapi.LazyNetwork; import com.powsybl.openrao.raoapi.RaoInput; import com.powsybl.openrao.raoapi.TimeCoupledRaoInput; -import org.apache.logging.log4j.util.Lazy; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; import org.slf4j.LoggerFactory; import java.io.*; @@ -42,7 +36,6 @@ import java.time.ZoneOffset; import java.util.*; import java.util.stream.Collectors; -import java.util.stream.Stream; import static com.powsybl.openrao.data.icsimporter.IcsData.getDefaultGeneratorIdPerNode; import static com.powsybl.openrao.data.icsimporter.IcsDataImporterTest.generateOffsetDateTimeList; @@ -73,7 +66,6 @@ void setUp() throws IOException { network1 = new LazyNetwork(getResourcePath("/network/" + networkFilePath1)); network2 = new LazyNetwork(getResourcePath("/network/" + networkFilePath2)); - crac1 = Crac.read("/crac/crac-0030.json", getClass().getResourceAsStream("/crac/crac-0030.json"), network1); crac2 = Crac.read("/crac/crac-0130.json", getClass().getResourceAsStream("/crac/crac-0130.json"), network2); diff --git a/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/LazyNetwork.java b/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/LazyNetwork.java index ddce5706d0..020f291f34 100644 --- a/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/LazyNetwork.java +++ b/ra-optimisation/rao-api/src/main/java/com/powsybl/openrao/raoapi/LazyNetwork.java @@ -87,7 +87,6 @@ * * @author Thomas Bouquet {@literal } */ - public class LazyNetwork implements Network { private static final String TEMP_DIR = System.getProperty("java.io.tmpdir") + File.separator; private final String networkPath; diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/GeneratorConstraintsFiller.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/GeneratorConstraintsFiller.java index 14be985fe5..f0c3c09651 100644 --- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/GeneratorConstraintsFiller.java +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/GeneratorConstraintsFiller.java @@ -18,9 +18,11 @@ import com.powsybl.openrao.data.crac.api.State; import com.powsybl.openrao.data.crac.api.rangeaction.InjectionRangeAction; import com.powsybl.openrao.data.timecoupledconstraints.GeneratorConstraints; +import com.powsybl.openrao.raoapi.LazyNetwork; 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; +import com.powsybl.openrao.searchtreerao.marmot.MarmotUtils; import com.powsybl.openrao.searchtreerao.result.api.FlowResult; import com.powsybl.openrao.searchtreerao.result.api.RangeActionActivationResult; import com.powsybl.openrao.searchtreerao.result.api.SensitivityResult; @@ -40,7 +42,8 @@ * @author Godelaine de Montmorillon {@literal } */ public class GeneratorConstraintsFiller implements ProblemFiller { - private final TemporalData networks; + private final TemporalData networks; + private final Map> generatorsData; private final TemporalData preventiveStates; private final TemporalData> injectionRangeActionsPerTimestamp; private final Set generatorConstraints; @@ -59,7 +62,10 @@ public GeneratorConstraintsFiller(TemporalData networks, TemporalData preventiveStates, TemporalData> injectionRangeActionsPerTimestamp, Set generatorConstraints) { - this.networks = networks; + // TODO: do not rely on MarmotUtils + this.networks = MarmotUtils.cloneNetworks(networks); + MarmotUtils.releaseAll(networks); + this.generatorsData = processGenerators(this.networks); this.preventiveStates = preventiveStates; this.injectionRangeActionsPerTimestamp = injectionRangeActionsPerTimestamp; this.generatorConstraints = generatorConstraints; @@ -143,20 +149,20 @@ public void fill(LinearProblem linearProblem, FlowResult flowResult, Sensitivity } addPowerToInjectionConstraint( - linearProblem, - generatorId, - timestamp, - associatedInjections.get().getData(timestamps.get(timestampIndex)).orElseThrow(), - preventiveStates.getData(timestamp).orElseThrow() + linearProblem, + generatorId, + timestamp, + associatedInjections.get().getData(timestamps.get(timestampIndex)).orElseThrow(), + preventiveStates.getData(timestamp).orElseThrow() ); } // Specific first timestamp constraints OffsetDateTime firstTimestamp = timestamps.getFirst(); if (!individualGeneratorConstraints.isShutDownAllowed()) { - addShutDownProhibitedOnFirstTimestampConstraint(linearProblem, generatorId, firstTimestamp, networks.getData(firstTimestamp).orElseThrow()); + addShutDownProhibitedOnFirstTimestampConstraint(linearProblem, generatorId, firstTimestamp); } if (!individualGeneratorConstraints.isStartUpAllowed()) { - addStartUpProhibitedOnFirstTimestampConstraint(linearProblem, generatorId, firstTimestamp, networks.getData(firstTimestamp).orElseThrow()); + addStartUpProhibitedOnFirstTimestampConstraint(linearProblem, generatorId, firstTimestamp); } } } @@ -164,7 +170,7 @@ public void fill(LinearProblem linearProblem, FlowResult flowResult, Sensitivity // ---- Variables private void addPowerVariable(LinearProblem linearProblem, String generatorId, OffsetDateTime timestamp) { - double pMax = getMaxP(generatorId, networks.getData(timestamp).orElseThrow()); + double pMax = generatorsData.getOrDefault(generatorId, new TemporalDataImpl<>()).getData(timestamp).orElseThrow().pMax(); linearProblem.addGeneratorPowerVariable(generatorId, pMax, timestamp); } @@ -242,8 +248,8 @@ private static void addStateToTransitionConstraints(LinearProblem linearProblem, * P <= P_max ON + OFF_POWER_THRESHOLD OFF */ private void addOnOffPowerConstraints(LinearProblem linearProblem, String generatorId, OffsetDateTime timestamp) { - double pMin = getMinP(generatorId, networks.getData(timestamp).orElseThrow()); - double pMax = getMaxP(generatorId, networks.getData(timestamp).orElseThrow()); + double pMin = generatorsData.getOrDefault(generatorId, new TemporalDataImpl<>()).getData(timestamp).orElseThrow().pMin(); + double pMax = generatorsData.getOrDefault(generatorId, new TemporalDataImpl<>()).getData(timestamp).orElseThrow().pMax(); OpenRaoMPVariable generatorPowerVariable = linearProblem.getGeneratorPowerVariable(generatorId, timestamp); OpenRaoMPVariable generatorOnVariable = linearProblem.getGeneratorStateVariable(generatorId, timestamp, LinearProblem.GeneratorState.ON); OpenRaoMPVariable generatorOffVariable = linearProblem.getGeneratorStateVariable(generatorId, timestamp, LinearProblem.GeneratorState.OFF); @@ -291,38 +297,38 @@ private void addPowerVariationConstraints(LinearProblem linearProblem, OffsetDateTime nextTimestamp) { double upwardPowerGradient = generatorConstraints.getUpwardPowerGradient().orElse(DEFAULT_POWER_GRADIENT); double downwardPowerGradient = generatorConstraints.getDownwardPowerGradient().orElse(-DEFAULT_POWER_GRADIENT); - double pMin = getMinP(generatorConstraints.getGeneratorId(), networks.getData(timestamp).orElseThrow()); + double pMin = generatorsData.getOrDefault(generatorConstraints.getGeneratorId(), new TemporalDataImpl<>()).getData(timestamp).orElseThrow().pMin(); OpenRaoMPConstraint powerTransitionConstraintInf = linearProblem.addGeneratorPowerTransitionConstraint( - generatorConstraints.getGeneratorId(), 0, linearProblem.infinity(), timestamp, LinearProblem.AbsExtension.POSITIVE + generatorConstraints.getGeneratorId(), 0, linearProblem.infinity(), timestamp, LinearProblem.AbsExtension.POSITIVE ); powerTransitionConstraintInf.setCoefficient(linearProblem.getGeneratorPowerVariable(generatorConstraints.getGeneratorId(), nextTimestamp), 1.0); powerTransitionConstraintInf.setCoefficient(linearProblem.getGeneratorPowerVariable(generatorConstraints.getGeneratorId(), timestamp), -1.0); OpenRaoMPConstraint powerTransitionConstraintSup = linearProblem.addGeneratorPowerTransitionConstraint( - generatorConstraints.getGeneratorId(), -linearProblem.infinity(), 0, timestamp, LinearProblem.AbsExtension.NEGATIVE + generatorConstraints.getGeneratorId(), -linearProblem.infinity(), 0, timestamp, LinearProblem.AbsExtension.NEGATIVE ); powerTransitionConstraintSup.setCoefficient(linearProblem.getGeneratorPowerVariable(generatorConstraints.getGeneratorId(), nextTimestamp), 1.0); powerTransitionConstraintSup.setCoefficient(linearProblem.getGeneratorPowerVariable(generatorConstraints.getGeneratorId(), timestamp), -1.0); // ON -> ON OpenRaoMPVariable onOnTransitionVariable = linearProblem.getGeneratorStateTransitionVariable( - generatorConstraints.getGeneratorId(), timestamp, LinearProblem.GeneratorState.ON, LinearProblem.GeneratorState.ON + generatorConstraints.getGeneratorId(), timestamp, LinearProblem.GeneratorState.ON, LinearProblem.GeneratorState.ON ); powerTransitionConstraintInf.setCoefficient(onOnTransitionVariable, -downwardPowerGradient * timestampDuration); powerTransitionConstraintSup.setCoefficient(onOnTransitionVariable, -upwardPowerGradient * timestampDuration); // OFF -> OFF OpenRaoMPVariable offOffTransitionVariable = linearProblem.getGeneratorStateTransitionVariable( - generatorConstraints.getGeneratorId(), timestamp, LinearProblem.GeneratorState.OFF, LinearProblem.GeneratorState.OFF + generatorConstraints.getGeneratorId(), timestamp, LinearProblem.GeneratorState.OFF, LinearProblem.GeneratorState.OFF ); powerTransitionConstraintInf.setCoefficient(offOffTransitionVariable, OFF_POWER_THRESHOLD); powerTransitionConstraintSup.setCoefficient(offOffTransitionVariable, -OFF_POWER_THRESHOLD); // OFF -> ON - double nextPMin = getMinP(generatorConstraints.getGeneratorId(), networks.getData(nextTimestamp).orElseThrow()); + double nextPMin = generatorsData.getOrDefault(generatorConstraints.getGeneratorId(), new TemporalDataImpl<>()).getData(nextTimestamp).orElseThrow().pMin(); OpenRaoMPVariable offOnTransitionVariable = linearProblem.getGeneratorStateTransitionVariable( - generatorConstraints.getGeneratorId(), timestamp, LinearProblem.GeneratorState.OFF, LinearProblem.GeneratorState.ON + generatorConstraints.getGeneratorId(), timestamp, LinearProblem.GeneratorState.OFF, LinearProblem.GeneratorState.ON ); powerTransitionConstraintInf.setCoefficient(offOnTransitionVariable, -(nextPMin - OFF_POWER_THRESHOLD)); if (generatorConstraints.getLeadTime().isPresent()) { @@ -335,7 +341,7 @@ private void addPowerVariationConstraints(LinearProblem linearProblem, // ON -> OFF OpenRaoMPVariable onOffTransitionVariable = linearProblem.getGeneratorStateTransitionVariable( - generatorConstraints.getGeneratorId(), timestamp, LinearProblem.GeneratorState.ON, LinearProblem.GeneratorState.OFF + generatorConstraints.getGeneratorId(), timestamp, LinearProblem.GeneratorState.ON, LinearProblem.GeneratorState.OFF ); if (lagTimeWithLeadTime.isPresent()) { // if the generator has a lag time, ON state finishes at Pmin on a timestamp before power decreases @@ -365,8 +371,8 @@ private void addShutDownProhibitedConstraint(LinearProblem linearProblem, String * ON(t) = 1 on first timestamp when P(t) >= Pmin *
*/ - private void addShutDownProhibitedOnFirstTimestampConstraint(LinearProblem linearProblem, String generatorId, OffsetDateTime timestamp, Network network) { - if (getP(generatorId, network) >= getMinP(generatorId, network)) { + private void addShutDownProhibitedOnFirstTimestampConstraint(LinearProblem linearProblem, String generatorId, OffsetDateTime timestamp) { + if (generatorsData.getOrDefault(generatorId, new TemporalDataImpl<>()).getData(timestamp).orElseThrow().p() >= generatorsData.getOrDefault(generatorId, new TemporalDataImpl<>()).getData(timestamp).orElseThrow().pMin()) { OpenRaoMPConstraint shutDownOnFirstTimestampProhibitedConstraint = linearProblem.addGeneratorShutDownOnFirstTimestampProhibitedConstraint(generatorId, timestamp); shutDownOnFirstTimestampProhibitedConstraint.setCoefficient(linearProblem.getGeneratorStateVariable(generatorId, timestamp, LinearProblem.GeneratorState.ON), 1.0); } @@ -389,8 +395,8 @@ private void addStartUpProhibitedConstraint(LinearProblem linearProblem, String * OFF(t) = 1 on first timestamp when P(t) < Pmin *
*/ - private void addStartUpProhibitedOnFirstTimestampConstraint(LinearProblem linearProblem, String generatorId, OffsetDateTime timestamp, Network network) { - if (getP(generatorId, network) < getMinP(generatorId, network)) { + private void addStartUpProhibitedOnFirstTimestampConstraint(LinearProblem linearProblem, String generatorId, OffsetDateTime timestamp) { + if (generatorsData.getOrDefault(generatorId, new TemporalDataImpl<>()).getData(timestamp).orElseThrow().p() < generatorsData.getOrDefault(generatorId, new TemporalDataImpl<>()).getData(timestamp).orElseThrow().pMin()) { OpenRaoMPConstraint startUpOnFirstTimestampProhibitedConstraint = linearProblem.addGeneratorStartUpOnFirstTimestampProhibitedConstraint(generatorId, timestamp); startUpOnFirstTimestampProhibitedConstraint.setCoefficient(linearProblem.getGeneratorStateVariable(generatorId, timestamp, LinearProblem.GeneratorState.OFF), 1.0); } @@ -433,14 +439,14 @@ private Optional> getInjectionRangeActionOfGe private static Optional getInjectionRangeActionOfGenerator(String generatorId, Set allInjectionRangeActions) { return allInjectionRangeActions.stream() - .filter(injectionRangeAction -> injectionRangeAction.getNetworkElements().stream().map(NetworkElement::getId).anyMatch(generatorId::equals)) - .min(Comparator.comparing(Identifiable::getId)); + .filter(injectionRangeAction -> injectionRangeAction.getNetworkElements().stream().map(NetworkElement::getId).anyMatch(generatorId::equals)) + .min(Comparator.comparing(Identifiable::getId)); } private static double getDistributionKey(String generatorId, InjectionRangeAction injectionRangeAction) { NetworkElement networkElement = injectionRangeAction.getNetworkElements().stream() - .filter(element -> element.getId().equals(generatorId)) - .findFirst().orElseThrow(); + .filter(element -> element.getId().equals(generatorId)) + .findFirst().orElseThrow(); return injectionRangeAction.getInjectionDistributionKeys().get(networkElement); } @@ -477,4 +483,23 @@ private Optional addLeadAndLag(Optional lead, Optional l public void updateBetweenMipIteration(LinearProblem linearProblem, RangeActionActivationResult rangeActionActivationResult) { // nothing to do } + + private static Map> processGenerators(TemporalData lazyNetworks) { + Map> dataPerGeneratorPerTimestamp = new HashMap<>(); + lazyNetworks.getDataPerTimestamp().forEach((timestamp, lazyNetwork) -> { + for (Generator generator : lazyNetwork.getGenerators()) { + dataPerGeneratorPerTimestamp.computeIfAbsent(generator.getId(), k -> new HashMap<>()).put( + timestamp, new GeneratorData(generator.getTargetP(), generator.getMinP(), generator.getMaxP()) + ); + } + lazyNetwork.release(); + }); + Map> globalGeneratorData = new HashMap<>(); + dataPerGeneratorPerTimestamp.forEach((generatorId, dataPerTimestamp) -> globalGeneratorData.put(generatorId, new TemporalDataImpl<>(dataPerTimestamp))); + return globalGeneratorData; + } + + private record GeneratorData(double p, double pMin, double pMax) { + public static GeneratorData DEFAULT = new GeneratorData(0.0, 0.0, Double.MAX_VALUE); + } } diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/linearproblem/LinearProblem.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/linearproblem/LinearProblem.java index 472af26d77..ab8aded312 100644 --- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/linearproblem/LinearProblem.java +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/linearproblem/LinearProblem.java @@ -20,10 +20,6 @@ import com.powsybl.openrao.searchtreerao.result.api.RangeActionActivationResult; import com.powsybl.openrao.searchtreerao.result.api.SensitivityResult; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; import java.time.OffsetDateTime; import java.util.List; import java.util.Optional; 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 235da9bb8a..b4cc288bc1 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 @@ -8,7 +8,6 @@ package com.powsybl.openrao.searchtreerao.marmot; import com.google.auto.service.AutoService; -import com.powsybl.iidm.network.Network; import com.powsybl.openrao.commons.TemporalData; import com.powsybl.openrao.commons.TemporalDataImpl; import com.powsybl.openrao.commons.Unit; @@ -64,7 +63,6 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; import static com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider.TECHNICAL_LOGS; import static com.powsybl.openrao.raoapi.parameters.extensions.SearchTreeRaoRangeActionsOptimizationParameters.RaRangeShrinking.ENABLED; @@ -91,10 +89,18 @@ public class Marmot implements TimeCoupledRaoProvider { @Override public CompletableFuture run(TimeCoupledRaoInput timeCoupledRaoInput, RaoParameters raoParameters) { + // Initiate lazy networks + TemporalData cracs = timeCoupledRaoInput.getRaoInputs().map(RaoInput::getCrac); + TemporalData initialNetworks = MarmotUtils.cloneNetworks(timeCoupledRaoInput.getRaoInputs().map(RaoInput::getNetwork)); + MarmotUtils.releaseAll(timeCoupledRaoInput.getRaoInputs().map(RaoInput::getNetwork)); + initialNetworks.getDataPerTimestamp().values().forEach(LazyNetwork::release); + + TemporalData initialInputs = MarmotUtils.merge(initialNetworks, cracs); + // 1. Run independent RAOs to compute optimal preventive topological remedial actions TECHNICAL_LOGS.info("[MARMOT] ----- Topological optimization [start]"); TemporalData> consideredCnecs = new TemporalDataImpl<>(); - TemporalData topologicalOptimizationResults = runTopologicalOptimization(timeCoupledRaoInput.getRaoInputs(), consideredCnecs, raoParameters); + TemporalData topologicalOptimizationResults = runTopologicalOptimization(initialInputs, consideredCnecs, raoParameters); TECHNICAL_LOGS.info("[MARMOT] ----- Topological optimization [end]"); // 2. Get the initial results from the various independent results to avoid recomputing them @@ -105,21 +111,21 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl // 3. Apply independent topological remedial actions (and preventive range actions if there are no time-coupled constraints) TECHNICAL_LOGS.info("[MARMOT] Applying optimal topological actions on networks"); - ObjectiveFunction fullObjectiveFunction = buildGlobalObjectiveFunction(timeCoupledRaoInput.getRaoInputs().map(RaoInput::getCrac), new GlobalFlowResult(initialResults), raoParameters); + ObjectiveFunction fullObjectiveFunction = buildGlobalObjectiveFunction(cracs, new GlobalFlowResult(initialResults), raoParameters); LinearOptimizationResult initialObjectiveFunctionResult = getInitialObjectiveFunctionResult(initialResults, fullObjectiveFunction); // 4. Evaluate objective function after independent optimizations TECHNICAL_LOGS.info("[MARMOT] Evaluating global result after independent optimizations"); TemporalData postTopologicalActionsResults = topologicalOptimizationResults.map( - raoResult -> ((FastRaoResultImpl) raoResult).getFinalResult() + raoResult -> ((FastRaoResultImpl) raoResult).getFinalResult() ); - TemporalData initialSetpointResults = getInitialSetpointResults(topologicalOptimizationResults, timeCoupledRaoInput.getRaoInputs()); + TemporalData initialSetpointResults = getInitialSetpointResults(topologicalOptimizationResults, initialInputs); LinearOptimizationResult postTopologicalOptimizationResult = getPostTopologicalOptimizationResult( - initialSetpointResults, - postTopologicalActionsResults, - fullObjectiveFunction, - topologicalOptimizationResults, - timeCoupledRaoInput.getRaoInputs().map(individualRaoInput -> individualRaoInput.getCrac().getPreventiveState())); + initialSetpointResults, + postTopologicalActionsResults, + fullObjectiveFunction, + topologicalOptimizationResults, + cracs.map(Crac::getPreventiveState)); // if no time-coupled constraints are defined, the results can be returned // TODO @@ -129,24 +135,15 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl // } // 5. Get and apply topological actions applied in independent optimizations - TemporalData preventiveTopologicalActions = getPreventiveTopologicalActions( - timeCoupledRaoInput.getRaoInputs().map(RaoInput::getCrac), - topologicalOptimizationResults - ); + TemporalData preventiveTopologicalActions = getPreventiveTopologicalActions(cracs, topologicalOptimizationResults); - TemporalData postTopologicalActionsInputs = applyPreventiveTopologicalActionsOnNetworks(timeCoupledRaoInput.getRaoInputs(), preventiveTopologicalActions); - TemporalData inputsForMip = new TemporalDataImpl<>( - postTopologicalActionsInputs.getDataPerTimestamp() - .entrySet() - .stream() - .collect( - Collectors.toMap(Map.Entry::getKey, entry -> RaoInput.build(new LazyNetwork(entry.getValue().getNetwork()), entry.getValue().getCrac()).build()) - ) - ); + TemporalData postTopologicalActionsInputs = applyPreventiveTopologicalActionsOnNetworks(initialInputs, preventiveTopologicalActions); + TemporalData postTopoNetworks = MarmotUtils.cloneNetworks(postTopologicalActionsInputs.map(RaoInput::getNetwork)); + MarmotUtils.releaseAll(postTopologicalActionsInputs.map(RaoInput::getNetwork)); // 6. Create and iteratively solve MIP to find optimal range actions' set-points // Get the curative actions applied in the individual results to be able to apply them during sensitivity computations - TemporalData curativeRemedialActions = MarmotUtils.getAppliedRemedialActionsInCurative(inputsForMip, topologicalOptimizationResults); + TemporalData curativeRemedialActions = MarmotUtils.getAppliedRemedialActionsInCurative(cracs, topologicalOptimizationResults); TECHNICAL_LOGS.info("[MARMOT] ----- Global range actions optimization [start]"); // make fast rao result lighter by keeping only initial flow result and filtered rao result for actions @@ -158,44 +155,42 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl GlobalLinearOptimizationResult fullResults; int counter = 1; do { - // Clone the PostTopoScenario variant to make sure we work on a clean variant every time - inputsForMip.getDataPerTimestamp().values().forEach(raoInput -> { - raoInput.getNetwork().getVariantManager().cloneVariant(raoInput.getNetwork().getVariantManager().getWorkingVariantId(), MIP_SCENARIO, true); - raoInput.getNetwork().getVariantManager().setWorkingVariant(MIP_SCENARIO); - }); + // Clone the post-topological actions networks to make sure we work on a clean variant every time + TemporalData inputsForMip = MarmotUtils.merge(postTopoNetworks, cracs); // Run post topo sensitivity analysis on all timestamps ON CONSIDERED CNECS ONLY (which is why we do it every loop) TECHNICAL_LOGS.info("[MARMOT] Systematic time-coupled sensitivity analysis [start]"); TemporalData postTopoResults = runAllSensitivityAnalysesBasedOnInitialResult( - inputsForMip, - curativeRemedialActions, - initialResults, - raoParameters, - consideredCnecs + inputsForMip, + curativeRemedialActions, + initialResults, + raoParameters, + consideredCnecs ); TECHNICAL_LOGS.info("[MARMOT] Systematic time-coupled sensitivity analysis [end]"); // Build objective function with ONLY THE CONSIDERED CNECS ObjectiveFunction filteredObjectiveFunction = buildFilteredObjectiveFunction( - timeCoupledRaoInput.getRaoInputs().map(RaoInput::getCrac), - new GlobalFlowResult(initialResults), - raoParameters, - consideredCnecs + cracs, + new GlobalFlowResult(initialResults), + raoParameters, + consideredCnecs ); // Create and iteratively solve MIP to find optimal range actions' set-points FOR THE CONSIDERED CNECS TECHNICAL_LOGS.info("[MARMOT] ----- Global range actions optimization [start] for iteration {}", counter); linearOptimizationResults = optimizeLinearRemedialActions( - new TimeCoupledRaoInput(inputsForMip, timeCoupledRaoInput.getTimestampsToRun(), timeCoupledRaoInput.getTimeCoupledConstraints()), - initialResults, - initialSetpointResults, - postTopoResults, - raoParameters, - preventiveTopologicalActions, - curativeRemedialActions, - consideredCnecs, - filteredObjectiveFunction + new TimeCoupledRaoInput(inputsForMip, timeCoupledRaoInput.getTimestampsToRun(), timeCoupledRaoInput.getTimeCoupledConstraints()), + initialResults, + initialSetpointResults, + postTopoResults, + raoParameters, + preventiveTopologicalActions, + curativeRemedialActions, + consideredCnecs, + filteredObjectiveFunction ); + MarmotUtils.releaseAll(inputsForMip.map(RaoInput::getNetwork)); TECHNICAL_LOGS.info("[MARMOT] ----- Global range actions optimization [end] for iteration {}", counter); // Compute the flows on ALL the cnecs to check if the worst cnecs have changed and were considered in the MIP or not @@ -204,12 +199,12 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl // Create a global result with the flows on ALL cnecs and the actions applied during MIP TemporalData rangeActionActivationResultTemporalData = linearOptimizationResults.getRangeActionActivationResultTemporalData(); fullResults = new GlobalLinearOptimizationResult( - loadFlowResults, - loadFlowResults.map(PrePerimeterResult::getSensitivityResult), - rangeActionActivationResultTemporalData, - preventiveTopologicalActions, - fullObjectiveFunction, - LinearProblemStatus.OPTIMAL + loadFlowResults, + loadFlowResults.map(PrePerimeterResult::getSensitivityResult), + rangeActionActivationResultTemporalData, + preventiveTopologicalActions, + fullObjectiveFunction, + LinearProblemStatus.OPTIMAL ); logCost("[MARMOT] next iteration of MIP: ", fullResults, raoParameters, 10); @@ -220,12 +215,12 @@ public CompletableFuture run(TimeCoupledRaoInput timeCoupl // 7. Merge topological and linear result TECHNICAL_LOGS.info("[MARMOT] Merging topological and linear remedial action results"); TimeCoupledRaoResultImpl timeCoupledRaoResult = mergeTopologicalAndLinearOptimizationResults( - postTopologicalActionsInputs, - initialResults, - initialObjectiveFunctionResult, - fullResults, - topologicalOptimizationResults, - raoParameters + postTopologicalActionsInputs, + initialResults, + initialObjectiveFunctionResult, + fullResults, + topologicalOptimizationResults, + raoParameters ); // 8. Log initial and final results @@ -241,13 +236,14 @@ private TemporalData getInitialSetpointResults(Tempor raoInputs.getDataPerTimestamp().forEach((timestamp, raoInput) -> { Map, Double> setPointMap = new HashMap<>(); raoInput.getCrac().getRangeActions().forEach(rangeAction -> - setPointMap.put(rangeAction, postTopologicalActionsResults.getData(timestamp).orElseThrow() - .getPreOptimizationSetPointOnState(raoInput.getCrac().getPreventiveState(), rangeAction)) + setPointMap.put(rangeAction, postTopologicalActionsResults.getData(timestamp).orElseThrow() + .getPreOptimizationSetPointOnState(raoInput.getCrac().getPreventiveState(), rangeAction)) ); RangeActionSetpointResult rangeActionSetpointResult = new RangeActionSetpointResultImpl( - setPointMap + setPointMap ); initialSetpointResults.put(timestamp, rangeActionSetpointResult); + MarmotUtils.releaseNetwork(raoInput.getNetwork()); }); return initialSetpointResults; } @@ -278,18 +274,18 @@ private static void updateShouldContinue(TemporalData loadFl // for margin violation - need to compare to min improvement on margin // ordered list of cnecs with an overload List worstCnecsForMarginViolation = loadFlowResult.getCostlyElements( - MIN_MARGIN_VIOLATION_EVALUATOR, - Integer.MAX_VALUE + MIN_MARGIN_VIOLATION_EVALUATOR, + Integer.MAX_VALUE ); double worstConsideredMargin = worstCnecsForMarginViolation.stream() - .filter(previousCnecs::contains) - .findFirst() - .map(cnec -> loadFlowResult.getMargin(cnec, flowUnit)) - .orElse(0.); + .filter(previousCnecs::contains) + .findFirst() + .map(cnec -> loadFlowResult.getMargin(cnec, flowUnit)) + .orElse(0.); double worstMarginOfAll = worstCnecsForMarginViolation.stream() - .findFirst() - .map(cnec -> loadFlowResult.getMargin(cnec, flowUnit)) - .orElse(0.); + .findFirst() + .map(cnec -> loadFlowResult.getMargin(cnec, flowUnit)) + .orElse(0.); // if worst overload > worst considered overload *( 1 + minImprovementOnLoad) if (worstMarginOfAll < worstConsideredMargin * (1 + minRelativeImprovementOnMargin) - 1e-6) { shouldContinue.set(true); @@ -297,13 +293,13 @@ private static void updateShouldContinue(TemporalData loadFl // for other violations - just check if cnec was considered loadFlowResult.getVirtualCostNames().stream() - .filter(vcName -> !vcName.equals(MIN_MARGIN_VIOLATION_EVALUATOR)) - .forEach(vcName -> { - Optional worstCnec = loadFlowResult.getCostlyElements(vcName, 1).stream().findFirst(); - if (worstCnec.isPresent() && !previousCnecs.contains(worstCnec.get())) { - shouldContinue.set(true); - } - }); + .filter(vcName -> !vcName.equals(MIN_MARGIN_VIOLATION_EVALUATOR)) + .forEach(vcName -> { + Optional worstCnec = loadFlowResult.getCostlyElements(vcName, 1).stream().findFirst(); + if (worstCnec.isPresent() && !previousCnecs.contains(worstCnec.get())) { + shouldContinue.set(true); + } + }); } } @@ -319,11 +315,11 @@ private static void updateConsideredCnecs(TemporalData loadF Set nextIterationCnecs = new HashSet<>(previousIterationCnecs); double worstConsideredMargin = loadFlowResult.getCostlyElements(MIN_MARGIN_VIOLATION_EVALUATOR, Integer.MAX_VALUE) - .stream() - .filter(previousIterationCnecs::contains) - .findFirst() - .map(cnec -> loadFlowResult.getMargin(cnec, flowUnit)) - .orElse(0.); + .stream() + .filter(previousIterationCnecs::contains) + .findFirst() + .map(cnec -> loadFlowResult.getMargin(cnec, flowUnit)) + .orElse(0.); for (String vcName : loadFlowResult.getVirtualCostNames()) { LoggingAddedCnecs currentLoggingAddedCnecs = new LoggingAddedCnecs(timestamp, vcName, new ArrayList<>(), new HashMap<>()); @@ -333,8 +329,8 @@ private static void updateConsideredCnecs(TemporalData loadF if (vcName.equals(MIN_MARGIN_VIOLATION_EVALUATOR)) { for (FlowCnec cnec : loadFlowResult.getCostlyElements(vcName, Integer.MAX_VALUE)) { if (loadFlowResult.getMargin( - cnec, - flowUnit + cnec, + flowUnit ) > worstConsideredMargin + marginWindowToConsider && addedCnecsForVcName > cnecsToAddPerVirtualCostName) { // stop if out of window and already added enough break; @@ -367,8 +363,8 @@ private static void logCnecs(List addedCnecsForLogging) { logMessage.append(" for timestamp ").append(loggingAddedCnecs.offsetDateTime().toString()).append(" and virtual cost ").append(loggingAddedCnecs.vcName()).append(" "); for (String cnec : loggingAddedCnecs.addedCnecs()) { String cnecString = loggingAddedCnecs.vcName().equals(MIN_MARGIN_VIOLATION_EVALUATOR) ? - cnec + "(" + loggingAddedCnecs.margins().get(cnec) + ")" + "," : - cnec + ","; + cnec + "(" + loggingAddedCnecs.margins().get(cnec) + ")" + "," : + cnec + ","; logMessage.append(cnecString); } } @@ -397,23 +393,21 @@ private static TemporalData applyActionsAndRunFullLoadflow(T postTopoInputs.getDataPerTimestamp().forEach((timestamp, raoInput) -> { State preventiveState = raoInput.getCrac().getPreventiveState(); raoInput.getCrac().getRangeActions(preventiveState).forEach(rangeAction -> - rangeAction.apply(raoInput.getNetwork(), filteredResult.getOptimizedSetpoint(rangeAction, preventiveState)) + rangeAction.apply(raoInput.getNetwork(), filteredResult.getOptimizedSetpoint(rangeAction, preventiveState)) ); prePerimeterResults.put(timestamp, runInitialPrePerimeterSensitivityAnalysisWithoutRangeActions( - postTopoInputs.getData(timestamp).orElseThrow(), - curativeRemedialActions.getData(timestamp).orElseThrow(), - initialResults.getData(timestamp).orElseThrow(), - raoParameters)); - if (raoInput.getNetwork() instanceof LazyNetwork lazyNetwork) { - lazyNetwork.release(); - } + postTopoInputs.getData(timestamp).orElseThrow(), + curativeRemedialActions.getData(timestamp).orElseThrow(), + initialResults.getData(timestamp).orElseThrow(), + raoParameters)); + MarmotUtils.releaseNetwork(raoInput.getNetwork()); }); return prePerimeterResults; } private void replaceFastRaoResultsWithLightVersions(TemporalData topologicalOptimizationResults) { topologicalOptimizationResults.getDataPerTimestamp().forEach((timestamp, raoResult) -> topologicalOptimizationResults.put( - timestamp, new LightFastRaoResultImpl((FastRaoResultImpl) raoResult))); + timestamp, new LightFastRaoResultImpl((FastRaoResultImpl) raoResult))); } private static TemporalData runTopologicalOptimization(TemporalData raoInputs, TemporalData> consideredCnecs, RaoParameters raoParameters) { @@ -428,11 +422,7 @@ private static TemporalData runTopologicalOptimization(TemporalData applyPreventiveTopologicalActionsOnNetwork RaoInput raoInput = raoInputs.getData(timestamp).orElseThrow(); NetworkActionsResult networkActionsResult = preventiveTopologicalActionsResults.getData(timestamp).orElseThrow(); MarmotUtils.applyPreventiveRemedialActions(raoInput, networkActionsResult, INITIAL_SCENARIO, POST_TOPO_SCENARIO); - postTopologicalActionsInputs.put(timestamp, RaoInput.build(raoInput.getNetwork(), raoInput.getCrac()).build()); - if (raoInput.getNetwork() instanceof LazyNetwork lazyNetwork) { - lazyNetwork.release(); - } + postTopologicalActionsInputs.put(timestamp, RaoInput.build(new LazyNetwork(raoInput.getNetwork()), raoInput.getCrac()).build()); + MarmotUtils.releaseNetwork(raoInput.getNetwork()); }); return new TemporalDataImpl<>(postTopologicalActionsInputs); } @@ -454,7 +442,7 @@ private static TemporalData applyPreventiveTopologicalActionsOnNetwork private TemporalData buildInitialResults(TemporalData topologicalOptimizationResults) { TemporalData initialResults = new TemporalDataImpl<>(); topologicalOptimizationResults.getDataPerTimestamp().forEach((timestamp, raoResult) -> - initialResults.put(timestamp, ((FastRaoResultImpl) raoResult).getInitialResult())); + initialResults.put(timestamp, ((FastRaoResultImpl) raoResult).getInitialResult())); return initialResults; } @@ -464,14 +452,17 @@ private static TemporalData runAllSensitivityAnalysesBasedOn RaoParameters raoParameters, TemporalData> consideredCnecs) { TemporalData prePerimeterResults = new TemporalDataImpl<>(); - raoInputs.getTimestamps().forEach(timestamp -> + raoInputs.getTimestamps().forEach(timestamp -> { + RaoInput raoInput = raoInputs.getData(timestamp).orElseThrow(); prePerimeterResults.put(timestamp, runSensitivityAnalysisBasedOnInitialResult( - raoInputs.getData(timestamp).orElseThrow(), - curativeRemedialActions.getData(timestamp).orElseThrow(), - initialFlowResults.getData(timestamp).orElseThrow(), - raoParameters, - consideredCnecs.getData(timestamp).orElseThrow() - ))); + raoInput, + curativeRemedialActions.getData(timestamp).orElseThrow(), + initialFlowResults.getData(timestamp).orElseThrow(), + raoParameters, + consideredCnecs.getData(timestamp).orElseThrow() + )); + MarmotUtils.releaseNetwork(raoInput.getNetwork()); + }); return prePerimeterResults; } @@ -480,11 +471,11 @@ private static TemporalData getPreventiveTopologicalAction cracs.getTimestamps().forEach(timestamp -> { State preventiveState = cracs.getData(timestamp).orElseThrow().getPreventiveState(); preventiveTopologicalActions.put( - timestamp, - new NetworkActionsResultImpl(Map.of( - preventiveState, - raoResults.getData(timestamp).orElseThrow().getActivatedNetworkActionsDuringState(preventiveState) - )) + timestamp, + new NetworkActionsResultImpl(Map.of( + preventiveState, + raoResults.getData(timestamp).orElseThrow().getActivatedNetworkActionsDuringState(preventiveState) + )) ); }); return new TemporalDataImpl<>(preventiveTopologicalActions); @@ -505,39 +496,43 @@ private static GlobalLinearOptimizationResult optimizeLinearRemedialActions(Time // no objective function defined in individual IteratingLinearOptimizerInputs as it is global Map linearOptimizerInputPerTimestamp = new HashMap<>(); - raoInput.getRaoInputs().getTimestamps().forEach(timestamp -> linearOptimizerInputPerTimestamp.put(timestamp, - IteratingLinearOptimizerInput.create() - .withNetwork(raoInput.getRaoInputs().getData(timestamp).orElseThrow().getNetwork()) - .withOptimizationPerimeter(optimizationPerimeterPerTimestamp.getData(timestamp).orElseThrow() - .copyWithFilteredAvailableHvdcRangeAction(raoInput.getRaoInputs().getData(timestamp).orElseThrow().getNetwork())) - .withInitialFlowResult(initialResults.getData(timestamp).orElseThrow()) - .withPrePerimeterFlowResult(initialResults.getData(timestamp).orElseThrow()) - .withPreOptimizationFlowResult(postTopologicalActionsResults.getData(timestamp).orElseThrow()) - .withPrePerimeterSetpoints(initialSetpoints.getData(timestamp).orElseThrow()) - .withPreOptimizationSensitivityResult(postTopologicalActionsResults.getData(timestamp).orElseThrow()) - .withPreOptimizationAppliedRemedialActions(curativeRemedialActions.getData(timestamp).orElseThrow()) - .withToolProvider(ToolProvider.buildFromRaoInputAndParameters(raoInput.getRaoInputs().getData(timestamp).orElseThrow(), parameters)) - .withOutageInstant(raoInput.getRaoInputs().getData(timestamp).orElseThrow().getCrac().getOutageInstant()) - .withAppliedNetworkActionsInPrimaryState(preventiveTopologicalActions.getData(timestamp).orElseThrow()) - .build())); + raoInput.getRaoInputs().getDataPerTimestamp().forEach((timestamp, individualRaoInput) -> { + IteratingLinearOptimizerInput iteratingLinearOptimizerInput = IteratingLinearOptimizerInput.create() + .withNetwork(new LazyNetwork(individualRaoInput.getNetwork())) + .withOptimizationPerimeter(optimizationPerimeterPerTimestamp.getData(timestamp).orElseThrow() + .copyWithFilteredAvailableHvdcRangeAction(individualRaoInput.getNetwork())) + .withInitialFlowResult(initialResults.getData(timestamp).orElseThrow()) + .withPrePerimeterFlowResult(initialResults.getData(timestamp).orElseThrow()) + .withPreOptimizationFlowResult(postTopologicalActionsResults.getData(timestamp).orElseThrow()) + .withPrePerimeterSetpoints(initialSetpoints.getData(timestamp).orElseThrow()) + .withPreOptimizationSensitivityResult(postTopologicalActionsResults.getData(timestamp).orElseThrow()) + .withPreOptimizationAppliedRemedialActions(curativeRemedialActions.getData(timestamp).orElseThrow()) + .withToolProvider(ToolProvider.buildFromRaoInputAndParameters(raoInput.getRaoInputs().getData(timestamp).orElseThrow(), parameters)) + .withOutageInstant(individualRaoInput.getCrac().getOutageInstant()) + .withAppliedNetworkActionsInPrimaryState(preventiveTopologicalActions.getData(timestamp).orElseThrow()) + .build(); + MarmotUtils.releaseNetwork(individualRaoInput.getNetwork()); + MarmotUtils.releaseNetwork(iteratingLinearOptimizerInput.network()); + linearOptimizerInputPerTimestamp.put(timestamp, iteratingLinearOptimizerInput); + }); TimeCoupledIteratingLinearOptimizerInput timeCoupledLinearOptimizerInput = new TimeCoupledIteratingLinearOptimizerInput( - new TemporalDataImpl<>(linearOptimizerInputPerTimestamp), objectiveFunction, raoInput.getTimeCoupledConstraints()); + new TemporalDataImpl<>(linearOptimizerInputPerTimestamp), objectiveFunction, raoInput.getTimeCoupledConstraints()); // Build parameters // Unoptimized cnec parameters ignored because only PRAs // TODO: define static method to define Ra Limitation Parameters from crac and topos (mutualize with search tree) : SearchTreeParameters::decreaseRemedialActionsUsageLimits IteratingLinearOptimizerParameters.LinearOptimizerParametersBuilder linearOptimizerParametersBuilder = IteratingLinearOptimizerParameters.create() - .withObjectiveFunction(parameters.getObjectiveFunctionParameters().getType()) - .withFlowUnit(getFlowUnit(parameters)) - .withRangeActionParameters(parameters.getRangeActionsOptimizationParameters()) - .withRangeActionParametersExtension(parameters.getExtension(OpenRaoSearchTreeParameters.class).getRangeActionsOptimizationParameters()) - .withMaxNumberOfIterations(parameters.getExtension(OpenRaoSearchTreeParameters.class).getRangeActionsOptimizationParameters().getMaxMipIterations()) - .withRaRangeShrinking(ENABLED.equals(parameters.getExtension(OpenRaoSearchTreeParameters.class).getRangeActionsOptimizationParameters().getRaRangeShrinking()) - || ENABLED_IN_FIRST_PRAO_AND_CRAO.equals(parameters.getExtension(OpenRaoSearchTreeParameters.class).getRangeActionsOptimizationParameters().getRaRangeShrinking())) - .withSolverParameters(parameters.getExtension(OpenRaoSearchTreeParameters.class).getRangeActionsOptimizationParameters().getLinearOptimizationSolver()) - .withMaxMinRelativeMarginParameters(parameters.getExtension(SearchTreeRaoRelativeMarginsParameters.class)) - .withRaLimitationParameters(new RangeActionLimitationParameters()) - .withMinMarginParameters(parameters.getExtension(OpenRaoSearchTreeParameters.class).getMinMarginsParameters().orElse(new SearchTreeRaoCostlyMinMarginParameters())); + .withObjectiveFunction(parameters.getObjectiveFunctionParameters().getType()) + .withFlowUnit(getFlowUnit(parameters)) + .withRangeActionParameters(parameters.getRangeActionsOptimizationParameters()) + .withRangeActionParametersExtension(parameters.getExtension(OpenRaoSearchTreeParameters.class).getRangeActionsOptimizationParameters()) + .withMaxNumberOfIterations(parameters.getExtension(OpenRaoSearchTreeParameters.class).getRangeActionsOptimizationParameters().getMaxMipIterations()) + .withRaRangeShrinking(ENABLED.equals(parameters.getExtension(OpenRaoSearchTreeParameters.class).getRangeActionsOptimizationParameters().getRaRangeShrinking()) + || ENABLED_IN_FIRST_PRAO_AND_CRAO.equals(parameters.getExtension(OpenRaoSearchTreeParameters.class).getRangeActionsOptimizationParameters().getRaRangeShrinking())) + .withSolverParameters(parameters.getExtension(OpenRaoSearchTreeParameters.class).getRangeActionsOptimizationParameters().getLinearOptimizationSolver()) + .withMaxMinRelativeMarginParameters(parameters.getExtension(SearchTreeRaoRelativeMarginsParameters.class)) + .withRaLimitationParameters(new RangeActionLimitationParameters()) + .withMinMarginParameters(parameters.getExtension(OpenRaoSearchTreeParameters.class).getMinMarginsParameters().orElse(new SearchTreeRaoCostlyMinMarginParameters())); parameters.getMnecParameters().ifPresent(linearOptimizerParametersBuilder::withMnecParameters); parameters.getExtension(OpenRaoSearchTreeParameters.class).getMnecParameters().ifPresent(linearOptimizerParametersBuilder::withMnecParametersExtension); parameters.getLoopFlowParameters().ifPresent(linearOptimizerParametersBuilder::withLoopFlowParameters); @@ -552,11 +547,11 @@ private static TemporalData computeOptimizationPerimeters cracs.getTimestamps().forEach(timestamp -> { Crac crac = cracs.getData(timestamp).orElseThrow(); optimizationPerimeters.put(timestamp, new PreventiveOptimizationPerimeter( - crac.getPreventiveState(), - consideredCnecs.getData(timestamp).orElseThrow(), - new HashSet<>(), // no loopflows for now - new HashSet<>(), // don't re-optimize topological actions in Marmot - crac.getRangeActions(crac.getPreventiveState()) + crac.getPreventiveState(), + consideredCnecs.getData(timestamp).orElseThrow(), + new HashSet<>(), // no loopflows for now + new HashSet<>(), // don't re-optimize topological actions in Marmot + crac.getRangeActions(crac.getPreventiveState()) )); }); return optimizationPerimeters; @@ -569,14 +564,14 @@ private static TimeCoupledRaoResultImpl mergeTopologicalAndLinearOptimizationRes TemporalData topologicalOptimizationResults, RaoParameters raoParameters) { return new TimeCoupledRaoResultImpl( - initialLinearOptimizationResult, - globalLinearOptimizationResult, - getPostOptimizationResults( - raoInputs, - initialResults, + initialLinearOptimizationResult, globalLinearOptimizationResult, - topologicalOptimizationResults, - raoParameters)); + getPostOptimizationResults( + raoInputs, + initialResults, + globalLinearOptimizationResult, + topologicalOptimizationResults, + raoParameters)); } private static ObjectiveFunction buildGlobalObjectiveFunction(TemporalData cracs, FlowResult globalInitialFlowResult, RaoParameters raoParameters) { @@ -584,12 +579,12 @@ private static ObjectiveFunction buildGlobalObjectiveFunction(TemporalData cracs.map(Crac::getFlowCnecs).getDataPerTimestamp().values().forEach(allFlowCnecs::addAll); Set allOptimizedStates = new HashSet<>(cracs.map(Crac::getPreventiveState).getDataPerTimestamp().values()); return ObjectiveFunction.build(allFlowCnecs, - new HashSet<>(), // no loop flows for now - globalInitialFlowResult, - globalInitialFlowResult, // always building from preventive so prePerimeter = initial - Collections.emptySet(), - raoParameters, - allOptimizedStates); + new HashSet<>(), // no loop flows for now + globalInitialFlowResult, + globalInitialFlowResult, // always building from preventive so prePerimeter = initial + Collections.emptySet(), + raoParameters, + allOptimizedStates); } private static ObjectiveFunction buildFilteredObjectiveFunction(TemporalData cracs, @@ -601,25 +596,25 @@ private static ObjectiveFunction buildFilteredObjectiveFunction(TemporalData allOptimizedStates = new HashSet<>(cracs.map(Crac::getPreventiveState).getDataPerTimestamp().values()); return ObjectiveFunction.build( - flatConsideredCnecs, - new HashSet<>(), // no loop flows for now - globalInitialFlowResult, - globalInitialFlowResult, // always building from preventive so prePerimeter = initial - Collections.emptySet(), - raoParameters, - allOptimizedStates); + flatConsideredCnecs, + new HashSet<>(), // no loop flows for now + globalInitialFlowResult, + globalInitialFlowResult, // always building from preventive so prePerimeter = initial + Collections.emptySet(), + raoParameters, + allOptimizedStates); } private LinearOptimizationResult getInitialObjectiveFunctionResult(TemporalData prePerimeterResults, ObjectiveFunction objectiveFunction) { TemporalData rangeActionActivationResults = prePerimeterResults.map(RangeActionActivationResultImpl::new); TemporalData networkActionsResults = new TemporalDataImpl<>(); return new GlobalLinearOptimizationResult( - prePerimeterResults.map(PrePerimeterResult::getFlowResult), - prePerimeterResults.map(PrePerimeterResult::getSensitivityResult), - rangeActionActivationResults, - networkActionsResults, - objectiveFunction, - LinearProblemStatus.OPTIMAL + prePerimeterResults.map(PrePerimeterResult::getFlowResult), + prePerimeterResults.map(PrePerimeterResult::getSensitivityResult), + rangeActionActivationResults, + networkActionsResults, + objectiveFunction, + LinearProblemStatus.OPTIMAL ); } @@ -631,12 +626,12 @@ private LinearOptimizationResult getPostTopologicalOptimizationResult(TemporalDa TemporalData rangeActionActivationResults = getRangeActionActivationResults(allInitialSetPoints, topologicalOptimizationResults, preventiveStates); TemporalData networkActionsResults = getNetworkActionActivationResults(topologicalOptimizationResults, preventiveStates); return new GlobalLinearOptimizationResult( - prePerimeterResults.map(PrePerimeterResult::getFlowResult), - prePerimeterResults.map(PrePerimeterResult::getSensitivityResult), - rangeActionActivationResults, - networkActionsResults, - objectiveFunction, - LinearProblemStatus.OPTIMAL + prePerimeterResults.map(PrePerimeterResult::getFlowResult), + prePerimeterResults.map(PrePerimeterResult::getSensitivityResult), + rangeActionActivationResults, + networkActionsResults, + objectiveFunction, + LinearProblemStatus.OPTIMAL ); } @@ -645,15 +640,15 @@ private static TemporalData getRangeActionActivatio TemporalData preventiveStates) { Map rangeActionsResults = new HashMap<>(); topologicalOptimizationResults.getTimestamps().forEach( - timestamp -> { - State preventiveState = preventiveStates.getData(timestamp).orElseThrow(); - RangeActionSetpointResult initialSetPoints = allInitialSetPoints.getData(timestamp).orElseThrow(); - RangeActionSetpointResult optimizedSetPoints = new RangeActionSetpointResultImpl( - topologicalOptimizationResults.getData(timestamp).orElseThrow().getOptimizedSetPointsOnState(preventiveState)); - RangeActionActivationResultImpl rangeActionActivationResult = new RangeActionActivationResultImpl(initialSetPoints); - optimizedSetPoints.getRangeActions().forEach(rangeAction -> rangeActionActivationResult.putResult(rangeAction, preventiveState, optimizedSetPoints.getSetpoint(rangeAction))); - rangeActionsResults.put(timestamp, rangeActionActivationResult); - } + timestamp -> { + State preventiveState = preventiveStates.getData(timestamp).orElseThrow(); + RangeActionSetpointResult initialSetPoints = allInitialSetPoints.getData(timestamp).orElseThrow(); + RangeActionSetpointResult optimizedSetPoints = new RangeActionSetpointResultImpl( + topologicalOptimizationResults.getData(timestamp).orElseThrow().getOptimizedSetPointsOnState(preventiveState)); + RangeActionActivationResultImpl rangeActionActivationResult = new RangeActionActivationResultImpl(initialSetPoints); + optimizedSetPoints.getRangeActions().forEach(rangeAction -> rangeActionActivationResult.putResult(rangeAction, preventiveState, optimizedSetPoints.getSetpoint(rangeAction))); + rangeActionsResults.put(timestamp, rangeActionActivationResult); + } ); return new TemporalDataImpl<>(rangeActionsResults); } @@ -661,11 +656,11 @@ private static TemporalData getRangeActionActivatio private static TemporalData getNetworkActionActivationResults(TemporalData topologicalOptimizationResults, TemporalData preventiveStates) { Map networkActionsResults = new HashMap<>(); topologicalOptimizationResults.getTimestamps().forEach( - timestamp -> { - State preventiveState = preventiveStates.getData(timestamp).orElseThrow(); - Set activatedNetworkActions = topologicalOptimizationResults.getData(timestamp).orElseThrow().getActivatedNetworkActionsDuringState(preventiveState); - networkActionsResults.put(timestamp, new NetworkActionsResultImpl(Map.of(preventiveState, activatedNetworkActions))); - } + timestamp -> { + State preventiveState = preventiveStates.getData(timestamp).orElseThrow(); + Set activatedNetworkActions = topologicalOptimizationResults.getData(timestamp).orElseThrow().getActivatedNetworkActionsDuringState(preventiveState); + networkActionsResults.put(timestamp, new NetworkActionsResultImpl(Map.of(preventiveState, activatedNetworkActions))); + } ); return new TemporalDataImpl<>(networkActionsResults); } diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java index d31799e65f..08e6228772 100644 --- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/marmot/MarmotUtils.java @@ -19,6 +19,7 @@ import com.powsybl.openrao.data.crac.api.rangeaction.RangeAction; import com.powsybl.openrao.data.raoresult.api.ComputationStatus; import com.powsybl.openrao.data.raoresult.api.RaoResult; +import com.powsybl.openrao.raoapi.LazyNetwork; import com.powsybl.openrao.raoapi.RaoInput; import com.powsybl.openrao.raoapi.parameters.RaoParameters; import com.powsybl.openrao.searchtreerao.castor.algorithm.PrePerimeterSensitivityAnalysis; @@ -36,6 +37,7 @@ import java.util.Map; import java.util.Set; import java.util.function.Function; +import java.util.stream.Collectors; /** * @author Thomas Bouquet {@literal } @@ -54,7 +56,7 @@ public static PrePerimeterResult runSensitivityAnalysis(RaoInput raoInput, RaoPa Set> rangeActions = crac.getRangeActions(preventiveState); ToolProvider toolProvider = ToolProvider.buildFromRaoInputAndParameters(raoInput, raoParameters); return new PrePerimeterSensitivityAnalysis(crac, crac.getFlowCnecs(), rangeActions, raoParameters, toolProvider, false) - .runInitialSensitivityAnalysis(network); + .runInitialSensitivityAnalysis(network); } public static PrePerimeterResult runInitialPrePerimeterSensitivityAnalysisWithoutRangeActions(RaoInput raoInput, @@ -64,19 +66,19 @@ public static PrePerimeterResult runInitialPrePerimeterSensitivityAnalysisWithou Crac crac = raoInput.getCrac(); Network network = raoInput.getNetwork(); return new PrePerimeterSensitivityAnalysis( - crac, - crac.getFlowCnecs(), // want results on all cnecs - new HashSet<>(), // with no range actions for faster computations, only flow values are required - raoParameters, - ToolProvider.buildFromRaoInputAndParameters(raoInput, raoParameters), - false + crac, + crac.getFlowCnecs(), // want results on all cnecs + new HashSet<>(), // with no range actions for faster computations, only flow values are required + raoParameters, + ToolProvider.buildFromRaoInputAndParameters(raoInput, raoParameters), + false ).runBasedOnInitialResults(network, initialResult, null, curativeRemedialActions); } - public static TemporalData getAppliedRemedialActionsInCurative(TemporalData inputs, TemporalData raoResults) { + public static TemporalData getAppliedRemedialActionsInCurative(TemporalData cracs, TemporalData raoResults) { TemporalData curativeRemedialActions = new TemporalDataImpl<>(); - inputs.getTimestamps().forEach(timestamp -> { - Crac crac = inputs.getData(timestamp).orElseThrow().getCrac(); + cracs.getTimestamps().forEach(timestamp -> { + Crac crac = cracs.getData(timestamp).orElseThrow(); RaoResult raoResult = raoResults.getData(timestamp).orElseThrow(); AppliedRemedialActions appliedRemedialActions = new AppliedRemedialActions(); // TODO: maybe check it is indeed curative @@ -106,7 +108,7 @@ public static PrePerimeterResult runSensitivityAnalysisBasedOnInitialResult(RaoI Set> rangeActions = crac.getRangeActions(preventiveState); ToolProvider toolProvider = ToolProvider.buildFromRaoInputAndParameters(raoInput, raoParameters); return new PrePerimeterSensitivityAnalysis(crac, consideredCnecs, rangeActions, raoParameters, toolProvider, false) - .runBasedOnInitialResults(network, initialFlowResult, Set.of(), curativeRemedialActions); + .runBasedOnInitialResults(network, initialFlowResult, Set.of(), curativeRemedialActions); } public static TemporalData getPostOptimizationResults(TemporalData raoInputs, @@ -117,14 +119,14 @@ public static TemporalData getPostOptimizationResults(Te List timestamps = raoInputs.getTimestamps(); Map postOptimizationResults = new HashMap<>(); timestamps.forEach(timestamp -> postOptimizationResults.put( - timestamp, - new PostOptimizationResult( - raoInputs.getData(timestamp).orElseThrow(), - initialResults.getData(timestamp).orElseThrow(), - globalLinearOptimizationResult, - topologicalOptimizationResults.getData(timestamp).orElseThrow(), - raoParameters - ) + timestamp, + new PostOptimizationResult( + raoInputs.getData(timestamp).orElseThrow(), + initialResults.getData(timestamp).orElseThrow(), + globalLinearOptimizationResult, + topologicalOptimizationResults.getData(timestamp).orElseThrow(), + raoParameters + ) )); return new TemporalDataImpl<>(postOptimizationResults); } @@ -164,4 +166,29 @@ public static void applyPreventiveRemedialActions(RaoInput raoInput, NetworkActi networkActionsToBeApplied.forEach(networkAction -> networkAction.apply(network)); } } + + public static TemporalData cloneNetworks(TemporalData networks) { + return new TemporalDataImpl<>( + networks.getDataPerTimestamp().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> new LazyNetwork(entry.getValue()))) + ); + } + + public static TemporalData merge(TemporalData networks, TemporalData cracs) { + Map raoInputs = new HashMap<>(); + networks.getDataPerTimestamp().forEach((timestamp, network) -> { + raoInputs.put(timestamp, RaoInput.build(network, cracs.getData(timestamp).orElseThrow()).build()); + network.release(); + }); + return new TemporalDataImpl<>(raoInputs); + } + + public static void releaseAll(TemporalData networks) { + networks.getDataPerTimestamp().values().forEach(MarmotUtils::releaseNetwork); + } + + public static void releaseNetwork(Network network) { + if (network instanceof LazyNetwork lazyNetwork) { + lazyNetwork.release(); + } + } } diff --git a/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java b/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java index ed01b101e9..c26870e817 100644 --- a/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java +++ b/tests/src/test/java/com/powsybl/openrao/tests/steps/TimeCoupledRaoSteps.java @@ -273,6 +273,7 @@ public static void loadDataForCoreTimeCoupledRao(DataTable arg1) throws IOExcept fbConstraintParameters.getIcsCostUp(), fbConstraintParameters.getIcsCostDown(), networkFolderPathPostIcsImport.concat(inputs.getFirst().get("Network")).split(".uct")[0]); + int debug = 0; } @When("I launch marmot") From 1c4e5aee784d39469c951fb0193e32ec4bb18c5b Mon Sep 17 00:00:00 2001 From: Godelaine de Montmorillon Date: Fri, 10 Apr 2026 17:26:30 +0200 Subject: [PATCH 58/64] WIP to make GeneratorConstraintsFillerTest pass --- .../algorithms/fillers/GeneratorConstraintsFillerTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/GeneratorConstraintsFillerTest.java b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/GeneratorConstraintsFillerTest.java index f75deed656..0d5ea64e74 100644 --- a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/GeneratorConstraintsFillerTest.java +++ b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/GeneratorConstraintsFillerTest.java @@ -840,7 +840,7 @@ void testShortLeadAndLongLagTimesAndPowerGradientsAndShutDownProhibited() { // - shut down prohibited constraint assertEquals(51, linearProblem.numVariables()); - assertEquals(70, linearProblem.numConstraints()); + assertEquals(71, linearProblem.numConstraints()); checkInjectionKey(); checkUpwardGradient(); @@ -889,7 +889,7 @@ void testShortLeadAndLongLagTimesAndPowerGradientsAndStartUpProhibited() { // - start up prohibited first timestamp constraint assertEquals(51, linearProblem.numVariables()); - assertEquals(71, linearProblem.numConstraints()); + assertEquals(70, linearProblem.numConstraints()); checkInjectionKey(); checkUpwardGradient(); From cb7bef29914c5c5bc6c0df80723036df2307bdff Mon Sep 17 00:00:00 2001 From: Thomas Bouquet Date: Mon, 13 Apr 2026 09:46:43 +0200 Subject: [PATCH 59/64] rollback GeneratorConstraintsFiller Signed-off-by: Thomas Bouquet --- .../fillers/GeneratorConstraintsFiller.java | 54 +++++-------------- 1 file changed, 13 insertions(+), 41 deletions(-) diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/GeneratorConstraintsFiller.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/GeneratorConstraintsFiller.java index 6aa981d447..14be985fe5 100644 --- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/GeneratorConstraintsFiller.java +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/GeneratorConstraintsFiller.java @@ -18,11 +18,9 @@ import com.powsybl.openrao.data.crac.api.State; import com.powsybl.openrao.data.crac.api.rangeaction.InjectionRangeAction; import com.powsybl.openrao.data.timecoupledconstraints.GeneratorConstraints; -import com.powsybl.openrao.raoapi.LazyNetwork; 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; -import com.powsybl.openrao.searchtreerao.marmot.MarmotUtils; import com.powsybl.openrao.searchtreerao.result.api.FlowResult; import com.powsybl.openrao.searchtreerao.result.api.RangeActionActivationResult; import com.powsybl.openrao.searchtreerao.result.api.SensitivityResult; @@ -42,8 +40,7 @@ * @author Godelaine de Montmorillon {@literal } */ public class GeneratorConstraintsFiller implements ProblemFiller { - private final TemporalData networks; - private final Map> generatorsData; + private final TemporalData networks; private final TemporalData preventiveStates; private final TemporalData> injectionRangeActionsPerTimestamp; private final Set generatorConstraints; @@ -62,10 +59,7 @@ public GeneratorConstraintsFiller(TemporalData networks, TemporalData preventiveStates, TemporalData> injectionRangeActionsPerTimestamp, Set generatorConstraints) { - // TODO: do not rely on MarmotUtils - this.networks = MarmotUtils.cloneNetworks(networks); - MarmotUtils.releaseAll(networks); - this.generatorsData = processGenerators(this.networks); + this.networks = networks; this.preventiveStates = preventiveStates; this.injectionRangeActionsPerTimestamp = injectionRangeActionsPerTimestamp; this.generatorConstraints = generatorConstraints; @@ -159,10 +153,10 @@ public void fill(LinearProblem linearProblem, FlowResult flowResult, Sensitivity // Specific first timestamp constraints OffsetDateTime firstTimestamp = timestamps.getFirst(); if (!individualGeneratorConstraints.isShutDownAllowed()) { - addShutDownProhibitedOnFirstTimestampConstraint(linearProblem, generatorId, firstTimestamp); + addShutDownProhibitedOnFirstTimestampConstraint(linearProblem, generatorId, firstTimestamp, networks.getData(firstTimestamp).orElseThrow()); } if (!individualGeneratorConstraints.isStartUpAllowed()) { - addStartUpProhibitedOnFirstTimestampConstraint(linearProblem, generatorId, firstTimestamp); + addStartUpProhibitedOnFirstTimestampConstraint(linearProblem, generatorId, firstTimestamp, networks.getData(firstTimestamp).orElseThrow()); } } } @@ -170,7 +164,7 @@ public void fill(LinearProblem linearProblem, FlowResult flowResult, Sensitivity // ---- Variables private void addPowerVariable(LinearProblem linearProblem, String generatorId, OffsetDateTime timestamp) { - double pMax = generatorsData.getOrDefault(generatorId, new TemporalDataImpl<>()).getData(timestamp).orElseThrow().pMax(); + double pMax = getMaxP(generatorId, networks.getData(timestamp).orElseThrow()); linearProblem.addGeneratorPowerVariable(generatorId, pMax, timestamp); } @@ -248,8 +242,8 @@ private static void addStateToTransitionConstraints(LinearProblem linearProblem, * P <= P_max ON + OFF_POWER_THRESHOLD OFF */ private void addOnOffPowerConstraints(LinearProblem linearProblem, String generatorId, OffsetDateTime timestamp) { - double pMin = generatorsData.getOrDefault(generatorId, new TemporalDataImpl<>()).getData(timestamp).orElseThrow().pMin(); - double pMax = generatorsData.getOrDefault(generatorId, new TemporalDataImpl<>()).getData(timestamp).orElseThrow().pMax(); + double pMin = getMinP(generatorId, networks.getData(timestamp).orElseThrow()); + double pMax = getMaxP(generatorId, networks.getData(timestamp).orElseThrow()); OpenRaoMPVariable generatorPowerVariable = linearProblem.getGeneratorPowerVariable(generatorId, timestamp); OpenRaoMPVariable generatorOnVariable = linearProblem.getGeneratorStateVariable(generatorId, timestamp, LinearProblem.GeneratorState.ON); OpenRaoMPVariable generatorOffVariable = linearProblem.getGeneratorStateVariable(generatorId, timestamp, LinearProblem.GeneratorState.OFF); @@ -297,7 +291,7 @@ private void addPowerVariationConstraints(LinearProblem linearProblem, OffsetDateTime nextTimestamp) { double upwardPowerGradient = generatorConstraints.getUpwardPowerGradient().orElse(DEFAULT_POWER_GRADIENT); double downwardPowerGradient = generatorConstraints.getDownwardPowerGradient().orElse(-DEFAULT_POWER_GRADIENT); - double pMin = generatorsData.getOrDefault(generatorConstraints.getGeneratorId(), new TemporalDataImpl<>()).getData(timestamp).orElseThrow().pMin(); + double pMin = getMinP(generatorConstraints.getGeneratorId(), networks.getData(timestamp).orElseThrow()); OpenRaoMPConstraint powerTransitionConstraintInf = linearProblem.addGeneratorPowerTransitionConstraint( generatorConstraints.getGeneratorId(), 0, linearProblem.infinity(), timestamp, LinearProblem.AbsExtension.POSITIVE @@ -326,7 +320,7 @@ private void addPowerVariationConstraints(LinearProblem linearProblem, powerTransitionConstraintSup.setCoefficient(offOffTransitionVariable, -OFF_POWER_THRESHOLD); // OFF -> ON - double nextPMin = generatorsData.getOrDefault(generatorConstraints.getGeneratorId(), new TemporalDataImpl<>()).getData(nextTimestamp).orElseThrow().pMin(); + double nextPMin = getMinP(generatorConstraints.getGeneratorId(), networks.getData(nextTimestamp).orElseThrow()); OpenRaoMPVariable offOnTransitionVariable = linearProblem.getGeneratorStateTransitionVariable( generatorConstraints.getGeneratorId(), timestamp, LinearProblem.GeneratorState.OFF, LinearProblem.GeneratorState.ON ); @@ -371,8 +365,8 @@ private void addShutDownProhibitedConstraint(LinearProblem linearProblem, String * ON(t) = 1 on first timestamp when P(t) >= Pmin *
*/ - private void addShutDownProhibitedOnFirstTimestampConstraint(LinearProblem linearProblem, String generatorId, OffsetDateTime timestamp) { - if (generatorsData.getOrDefault(generatorId, new TemporalDataImpl<>()).getData(timestamp).orElseThrow().p() >= generatorsData.getOrDefault(generatorId, new TemporalDataImpl<>()).getData(timestamp).orElseThrow().pMin()) { + private void addShutDownProhibitedOnFirstTimestampConstraint(LinearProblem linearProblem, String generatorId, OffsetDateTime timestamp, Network network) { + if (getP(generatorId, network) >= getMinP(generatorId, network)) { OpenRaoMPConstraint shutDownOnFirstTimestampProhibitedConstraint = linearProblem.addGeneratorShutDownOnFirstTimestampProhibitedConstraint(generatorId, timestamp); shutDownOnFirstTimestampProhibitedConstraint.setCoefficient(linearProblem.getGeneratorStateVariable(generatorId, timestamp, LinearProblem.GeneratorState.ON), 1.0); } @@ -395,8 +389,8 @@ private void addStartUpProhibitedConstraint(LinearProblem linearProblem, String * OFF(t) = 1 on first timestamp when P(t) < Pmin *
*/ - private void addStartUpProhibitedOnFirstTimestampConstraint(LinearProblem linearProblem, String generatorId, OffsetDateTime timestamp) { - if (generatorsData.getOrDefault(generatorId, new TemporalDataImpl<>()).getData(timestamp).orElseThrow().p() < generatorsData.getOrDefault(generatorId, new TemporalDataImpl<>()).getData(timestamp).orElseThrow().pMin()) { + private void addStartUpProhibitedOnFirstTimestampConstraint(LinearProblem linearProblem, String generatorId, OffsetDateTime timestamp, Network network) { + if (getP(generatorId, network) < getMinP(generatorId, network)) { OpenRaoMPConstraint startUpOnFirstTimestampProhibitedConstraint = linearProblem.addGeneratorStartUpOnFirstTimestampProhibitedConstraint(generatorId, timestamp); startUpOnFirstTimestampProhibitedConstraint.setCoefficient(linearProblem.getGeneratorStateVariable(generatorId, timestamp, LinearProblem.GeneratorState.OFF), 1.0); } @@ -483,26 +477,4 @@ private Optional addLeadAndLag(Optional lead, Optional l public void updateBetweenMipIteration(LinearProblem linearProblem, RangeActionActivationResult rangeActionActivationResult) { // nothing to do } - - private static Map> processGenerators(TemporalData lazyNetworks) { - Map> dataPerGeneratorPerTimestamp = new HashMap<>(); - for (OffsetDateTime timestamp : lazyNetworks.getTimestamps()) { - try (LazyNetwork lazyNetwork = lazyNetworks.getData(timestamp).orElseThrow()) { - for (Generator generator : lazyNetwork.getGenerators()) { - dataPerGeneratorPerTimestamp.computeIfAbsent(generator.getId(), k -> new HashMap<>()).put( - timestamp, new GeneratorData(generator.getTargetP(), generator.getMinP(), generator.getMaxP()) - ); - } - } catch (Exception e) { - throw new OpenRaoException(e); - } - } - Map> globalGeneratorData = new HashMap<>(); - dataPerGeneratorPerTimestamp.forEach((generatorId, dataPerTimestamp) -> globalGeneratorData.put(generatorId, new TemporalDataImpl<>(dataPerTimestamp))); - return globalGeneratorData; - } - - private record GeneratorData(double p, double pMin, double pMax) { - public static GeneratorData DEFAULT = new GeneratorData(0.0, 0.0, Double.MAX_VALUE); - } } From c4955b5c287ad7ee148162b5e4074c7295df0f45 Mon Sep 17 00:00:00 2001 From: Godelaine de Montmorillon Date: Mon, 13 Apr 2026 14:53:43 +0200 Subject: [PATCH 60/64] add UT for p0RespectsConstraints --- .../data/icsimporter/IcsDataImporter.java | 22 +- .../data/icsimporter/IcsDataImporterTest.java | 141 ++- .../openrao/data/icsimporter/IcsDataTest.java | 2 +- .../src/test/resources/crac/crac-0030.json | 81 -- .../src/test/resources/crac/crac-0130.json | 81 -- .../src/test/resources/crac/crac-0230.json | 81 -- .../src/test/resources/crac/crac-0330.json | 81 -- .../src/test/resources/crac/crac-0430.json | 81 -- .../src/test/resources/crac/crac-0530.json | 81 -- .../src/test/resources/crac/crac-0630.json | 81 -- .../src/test/resources/crac/crac-0730.json | 81 -- .../src/test/resources/ics/static.csv | 2 +- .../src/test/resources/ics/static2.csv | 2 + .../openrao/searchtreerao/marmot/Marmot.java | 2 +- .../features/4_time_coupled/US93_5.feature | 865 ------------------ tests/src/test/resources/logback.xml | 9 +- 16 files changed, 154 insertions(+), 1539 deletions(-) delete mode 100644 data/ics-importer/src/test/resources/crac/crac-0030.json delete mode 100644 data/ics-importer/src/test/resources/crac/crac-0130.json delete mode 100644 data/ics-importer/src/test/resources/crac/crac-0230.json delete mode 100644 data/ics-importer/src/test/resources/crac/crac-0330.json delete mode 100644 data/ics-importer/src/test/resources/crac/crac-0430.json delete mode 100644 data/ics-importer/src/test/resources/crac/crac-0530.json delete mode 100644 data/ics-importer/src/test/resources/crac/crac-0630.json delete mode 100644 data/ics-importer/src/test/resources/crac/crac-0730.json create mode 100644 data/ics-importer/src/test/resources/ics/static2.csv delete mode 100644 tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java index 1a077602a8..b9c91c0d33 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java @@ -288,15 +288,19 @@ private static boolean p0RespectsConstraints(CSVRecord staticRecord, Map dateTimeIterator = dateTimes.iterator(); OffsetDateTime currentDateTime = dateTimeIterator.next(); + boolean allValuesAreNullSinceFirstP0 = parseDoubleWithPossibleCommas(seriesRecord.get(P0).get(currentDateTime.getHour() + OFFSET)) < ON_POWER_THRESHOLD; + while (dateTimeIterator.hasNext()) { OffsetDateTime nextDateTime = dateTimeIterator.next(); double nextP0 = parseDoubleWithPossibleCommas(seriesRecord.get(P0).get(nextDateTime.getHour() + OFFSET)); double currentP0 = parseDoubleWithPossibleCommas(seriesRecord.get(P0).get(currentDateTime.getHour() + OFFSET)); Optional pMinRD = parseValue(seriesRecord, P_MIN_RD, currentDateTime, 1); double pMin = pMinRD.orElse(ON_POWER_THRESHOLD); + Optional nextPminPD = parseValue(seriesRecord, P_MIN_RD, nextDateTime, 1); + double nextPmin = nextPminPD.orElse(ON_POWER_THRESHOLD); // 1- Check gradients - if (!areGradientsRespected(staticRecord, nextP0, currentP0, maxGradient, minGradient, currentDateTime)) { + if (!areGradientsRespected(staticRecord, nextP0, currentP0, pMin, nextPmin, maxGradient, minGradient, currentDateTime)) { return false; } @@ -311,12 +315,14 @@ private static boolean p0RespectsConstraints(CSVRecord staticRecord, Map= pMin) { if (!isStartUpRespected(staticRecord, startUpAllowed, currentDateTime)) { return false; } - if (!isLeadTimeRespected(staticRecord, lead, countConsecutiveNullValues, currentDateTime)) { + if (!isLeadTimeRespected(staticRecord, lead, allValuesAreNullSinceFirstP0, countConsecutiveNullValues, currentDateTime)) { return false; } if (countLag) { @@ -371,7 +377,11 @@ private static boolean isLeadTimeAndLagTimeRespected(CSVRecord staticRecord, int return true; } - private static boolean isLeadTimeRespected(CSVRecord staticRecord, Optional lead, int countConsecutiveNullValues, OffsetDateTime currentDateTime) { + private static boolean isLeadTimeRespected(CSVRecord staticRecord, Optional lead, boolean allValuesAreNullSinceFirstP0, int countConsecutiveNullValues, OffsetDateTime currentDateTime) { + // The generator is initially oFF and we don't know when it was last shut down> no lead check. + if (allValuesAreNullSinceFirstP0) { + return true; + } if (lead.isPresent() && countConsecutiveNullValues < lead.get()) { BUSINESS_WARNS.warn("Redispatching action {} is not imported (hour {}): leadTime ({}) not respected. RA was OFF before start up for only {} timestamps", staticRecord.get(0), currentDateTime.getHour(), lead.get(), countConsecutiveNullValues); @@ -389,9 +399,9 @@ private static boolean isStartUpRespected(CSVRecord staticRecord, Boolean startU return true; } - private static boolean areGradientsRespected(CSVRecord staticRecord, double nextP0, double currentP0, double maxGradient, double minGradient, OffsetDateTime currentDateTime) { + private static boolean areGradientsRespected(CSVRecord staticRecord, double nextP0, double currentP0, double pMin, double nextPmin, double maxGradient, double minGradient, OffsetDateTime currentDateTime) { double diff = nextP0 - currentP0; - if (diff > maxGradient || diff < minGradient) { + if (currentP0 >= pMin && nextP0 >= nextPmin && diff > maxGradient || diff < minGradient) { BUSINESS_WARNS.warn( "Redispatching action {} is not imported (hour {}): does not respect power gradients : min/max/diff = {} / {} / {}", staticRecord.get(0), currentDateTime.getHour(), minGradient, maxGradient, diff @@ -427,7 +437,7 @@ private static boolean rangeIsOkay(Map seriesPerType, List rangeIsNotOkayCases() { return Stream.of( Arguments.of(negativeRdpPlusCsv, - "Redispatching action Redispatching_RA is not imported: RDP+ -1.0 or RDP- 35.0 is negative for datetime 2025-02-13T01:30Z"), + "Redispatching action Redispatching_RA is not imported (hour 1): RDP+ -1.0 or RDP- 35.0 is negative for datetime 2025-02-13T01:30Z"), Arguments.of(negativeRdpMinusCsv, - "Redispatching action Redispatching_RA is not imported: RDP+ 1.0 or RDP- -35.0 is negative for datetime 2025-02-13T01:30Z"), + "Redispatching action Redispatching_RA is not imported (hour 1): RDP+ 1.0 or RDP- -35.0 is negative for datetime 2025-02-13T01:30Z"), Arguments.of(tooSmallRangeCsv, "Redispatching action Redispatching_RA is not imported: max range in the day 0.0 MW is too small") ); } - private static Stream gradientNotRespectCsvCases() { + private static Stream gradientNotRespectedCsvCases() { String header = """ RA RD ID;Type of timeseries;00:30;01:30;02:30 """; @@ -284,7 +284,7 @@ private static Stream gradientNotRespectCsvCases() { String tooHighGradientCsv = header + """ Redispatching_RA;RDP-;35;35;35 Redispatching_RA;RDP+;43;43;43 - Redispatching_RA;P0;116;150;117 + Redispatching_RA;P0;116;150;132 Redispatching_RA;Pmin_RD;10;15;20 """; @@ -308,16 +308,139 @@ private static Stream gradientNotRespectCsvCases() { ); } + private static Stream pMinNotRespectedCsvCases() { + String baseHeader = """ + RA RD ID;Type of timeseries;00:30;01:30;02:30 + """; + + String pMinNotRespectedsv = baseHeader + """ + Redispatching_RA;RDP-;7;7;7 + Redispatching_RA;RDP+;7;7;7 + Redispatching_RA;P0;15;5;15 + Redispatching_RA;Pmin_RD;10;10;10 + """; + return Stream.of( + Arguments.of(pMinNotRespectedsv, + "Redispatching action Redispatching_RA is not imported (hour 1): does not respect Pmin : P0 is 5.0 and Pmin at 10.0") + ); + } + + private static Stream startUpShutDownNotRespectedCsvCases() { + String baseHeader = """ + RA RD ID;Type of timeseries;00:30;01:30;02:30 + """; + + String startUpNotRespectedCsv = baseHeader + """ + Redispatching_RA;RDP-;7;7;7 + Redispatching_RA;RDP+;7;7;7 + Redispatching_RA;P0;0;15;15 + Redispatching_RA;Pmin_RD;10;10;10 + """; + + String shutDownNotRespectedCsv = baseHeader + """ + Redispatching_RA;RDP-;7;7;7 + Redispatching_RA;RDP+;7;7;7 + Redispatching_RA;P0;15;15;0 + Redispatching_RA;Pmin_RD;10;10;10 + """; + return Stream.of( + Arguments.of(startUpNotRespectedCsv, + "Redispatching action Redispatching_RA is not imported (hour 0): start up prohibited"), + Arguments.of(shutDownNotRespectedCsv, + "Redispatching action Redispatching_RA is not imported (hour 1): shut down prohibited") + + ); + } + + private static Stream leadAndLagNotRespectedCsvCases() { + String header = """ + RA RD ID;Type of timeseries;00:30;01:30;02:30 + """; + + String leadNotRespected = header + """ + Redispatching_RA;RDP-;35;35;35;35 + Redispatching_RA;RDP+;43;43;43;43 + Redispatching_RA;P0;11;0;21;21 + Redispatching_RA;Pmin_RD;10;15;20;20 + """; + + String leadAndLagNotRespected = header + """ + Redispatching_RA;RDP-;35;35;35;35 + Redispatching_RA;RDP+;43;43;43;43 + Redispatching_RA;P0;11;0;0;21 + Redispatching_RA;Pmin_RD;10;15;20;20 + """; + + return Stream.of( + Arguments.of( + leadNotRespected, + "Redispatching action Redispatching_RA is not imported (hour 1): leadTime (2) not respected. RA was OFF before start up for only 1 timestamps" + ), + Arguments.of( + leadAndLagNotRespected, + "Redispatching action Redispatching_RA is not imported (hour 2): lagTime + leadTime (3) not respected. RA was OFF after shut down for only 2 timestamps" + ) + ); + } + @ParameterizedTest - @MethodSource("gradientNotRespectCsvCases") + @MethodSource("gradientNotRespectedCsvCases") @MethodSource("rangeIsNotOkayCases") @MethodSource("seriesCsvWithMissingSeriesTypeCases") + @MethodSource("pMinNotRespectedCsvCases") void testSeriesCsv(String seriesCsv, String expectedLogMessage) throws IOException { IcsData icsData = IcsDataImporter.read( - getClass().getResourceAsStream("/ics/static.csv"), - new ByteArrayInputStream(seriesCsv.getBytes(StandardCharsets.UTF_8)), - getClass().getResourceAsStream("/glsk/gsk.csv"), - generateOffsetDateTimeList(3)); + getClass().getResourceAsStream("/ics/static.csv"), + new ByteArrayInputStream(seriesCsv.getBytes(StandardCharsets.UTF_8)), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList(3)); + + assertEquals(0, icsData.getRedispatchingActions().size()); + assertEquals(expectedLogMessage, logsList.get(0).getFormattedMessage()); + } + + @ParameterizedTest + @MethodSource("startUpShutDownNotRespectedCsvCases") + void testSeriesWithStatic2Csv(String seriesCsv, String expectedLogMessage) throws IOException { + IcsData icsData = IcsDataImporter.read( + getClass().getResourceAsStream("/ics/static2.csv"), + new ByteArrayInputStream(seriesCsv.getBytes(StandardCharsets.UTF_8)), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList(3)); + + assertEquals(0, icsData.getRedispatchingActions().size()); + assertEquals(expectedLogMessage, logsList.get(0).getFormattedMessage()); + } + + @Test + void testGradientNotTakenIntoAccountBelowPmin() throws IOException { + String header = """ + RA RD ID;Type of timeseries;00:30;01:30;02:30 + """; + + String belowPminCsv = header + """ + Redispatching_RA;RDP-;35;35;35 + Redispatching_RA;RDP+;43;43;43 + Redispatching_RA;P0;0;150;132 + Redispatching_RA;Pmin_RD;10;15;20 + """; + IcsData icsData = IcsDataImporter.read( + getClass().getResourceAsStream("/ics/static.csv"), + new ByteArrayInputStream(belowPminCsv.getBytes(StandardCharsets.UTF_8)), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList(3)); + + assertEquals(1, icsData.getRedispatchingActions().size()); + } + + @ParameterizedTest + @MethodSource("leadAndLagNotRespectedCsvCases") + void testLongSeriesCsv(String seriesCsv, String expectedLogMessage) throws IOException { + IcsData icsData = IcsDataImporter.read( + getClass().getResourceAsStream("/ics/static.csv"), + new ByteArrayInputStream(seriesCsv.getBytes(StandardCharsets.UTF_8)), + getClass().getResourceAsStream("/glsk/gsk.csv"), + generateOffsetDateTimeList(4)); assertEquals(0, icsData.getRedispatchingActions().size()); assertEquals(expectedLogMessage, logsList.get(0).getFormattedMessage()); diff --git a/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataTest.java b/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataTest.java index 785c2fe9f8..359d793f1a 100644 --- a/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataTest.java +++ b/data/ics-importer/src/test/java/com/powsybl/openrao/data/icsimporter/IcsDataTest.java @@ -116,7 +116,7 @@ void testCreateGeneratorConstraintRaDefinedOnANode() throws IOException { assertTrue(generatorConstraints.getUpwardPowerGradient().isPresent()); assertEquals(20., generatorConstraints.getUpwardPowerGradient().get(), DOUBLE_EPSILON); assertTrue(generatorConstraints.getLeadTime().isPresent()); - assertEquals(1.0, generatorConstraints.getLeadTime().get(), DOUBLE_EPSILON); + assertEquals(2.0, generatorConstraints.getLeadTime().get(), DOUBLE_EPSILON); assertTrue(generatorConstraints.getLagTime().isPresent()); assertEquals(1.0, generatorConstraints.getLagTime().get(), DOUBLE_EPSILON); assertTrue(generatorConstraints.isShutDownAllowed()); diff --git a/data/ics-importer/src/test/resources/crac/crac-0030.json b/data/ics-importer/src/test/resources/crac/crac-0030.json deleted file mode 100644 index 9c211f799c..0000000000 --- a/data/ics-importer/src/test/resources/crac/crac-0030.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "type": "CRAC", - "version": "2.7", - "info": "Generated by PowSyBl OpenRAO https://powsybl.readthedocs.io/projects/openrao/", - "id": "crac-20250213", - "name": "crac-20250213", - "timestamp": "2025-02-13T00:30:00Z", - "instants": [ - { - "id": "preventive", - "kind": "PREVENTIVE" - }, - { - "id": "outage", - "kind": "OUTAGE" - }, - { - "id": "curative", - "kind": "CURATIVE" - } - ], - "networkElementsNamePerId": {}, - "flowCnecs": [ - { - "id": "cnecBeFrPreventive", - "name": "cnecBeFrPreventive", - "networkElementId": "BBE1AA1 FFR1AA1 1", - "operator": "FR", - "instant": "preventive", - "contingencyId": null, - "optimized": true, - "monitored": false, - "iMax": [ - NaN - ], - "nominalV": [ - 400.0 - ], - "thresholds": [ - { - "unit": "megawatt", - "min": -300.0, - "max": 300.0, - "side": 1 - }, - { - "unit": "megawatt", - "min": -300.0, - "max": 300.0, - "side": 2 - } - ] - } - ], - "pstRangeActions": [ - { - "id": "pstBeFr2", - "name": "pstBeFr2", - "operator": "BE", - "activationCost": 5.0, - "variationCosts": { - "up": 10.0, - "down": 10.0 - }, - "onInstantUsageRules": [ - { - "instant": "preventive", - "usageMethod": "available" - } - ], - "networkElementId": "BBE1AA1 FFR1AA1 2", - "ranges": [ - { - "min": -16, - "max": 16, - "rangeType": "absolute" - } - ] - } - ] -} \ No newline at end of file diff --git a/data/ics-importer/src/test/resources/crac/crac-0130.json b/data/ics-importer/src/test/resources/crac/crac-0130.json deleted file mode 100644 index b4b166104c..0000000000 --- a/data/ics-importer/src/test/resources/crac/crac-0130.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "type": "CRAC", - "version": "2.7", - "info": "Generated by PowSyBl OpenRAO https://powsybl.readthedocs.io/projects/openrao/", - "id": "crac-20250214", - "name": "crac-20250214", - "timestamp": "2025-02-13T01:30:00Z", - "instants": [ - { - "id": "preventive", - "kind": "PREVENTIVE" - }, - { - "id": "outage", - "kind": "OUTAGE" - }, - { - "id": "curative", - "kind": "CURATIVE" - } - ], - "networkElementsNamePerId": {}, - "flowCnecs": [ - { - "id": "cnecBeFrPreventive", - "name": "cnecBeFrPreventive", - "networkElementId": "BBE1AA1 FFR1AA1 1", - "operator": "FR", - "instant": "preventive", - "contingencyId": null, - "optimized": true, - "monitored": false, - "iMax": [ - NaN - ], - "nominalV": [ - 400.0 - ], - "thresholds": [ - { - "unit": "megawatt", - "min": -300.0, - "max": 300.0, - "side": 1 - }, - { - "unit": "megawatt", - "min": -300.0, - "max": 300.0, - "side": 2 - } - ] - } - ], - "pstRangeActions": [ - { - "id": "pstBeFr2", - "name": "pstBeFr2", - "operator": "BE", - "activationCost": 5.0, - "variationCosts": { - "up": 10.0, - "down": 10.0 - }, - "onInstantUsageRules": [ - { - "instant": "preventive", - "usageMethod": "available" - } - ], - "networkElementId": "BBE1AA1 FFR1AA1 2", - "ranges": [ - { - "min": -16, - "max": 16, - "rangeType": "absolute" - } - ] - } - ] -} \ No newline at end of file diff --git a/data/ics-importer/src/test/resources/crac/crac-0230.json b/data/ics-importer/src/test/resources/crac/crac-0230.json deleted file mode 100644 index 28653c5195..0000000000 --- a/data/ics-importer/src/test/resources/crac/crac-0230.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "type": "CRAC", - "version": "2.7", - "info": "Generated by PowSyBl OpenRAO https://powsybl.readthedocs.io/projects/openrao/", - "id": "crac-20250215", - "name": "crac-20250215", - "timestamp": "2025-02-13T02:30:00Z", - "instants": [ - { - "id": "preventive", - "kind": "PREVENTIVE" - }, - { - "id": "outage", - "kind": "OUTAGE" - }, - { - "id": "curative", - "kind": "CURATIVE" - } - ], - "networkElementsNamePerId": {}, - "flowCnecs": [ - { - "id": "cnecBeFrPreventive", - "name": "cnecBeFrPreventive", - "networkElementId": "BBE1AA1 FFR1AA1 1", - "operator": "FR", - "instant": "preventive", - "contingencyId": null, - "optimized": true, - "monitored": false, - "iMax": [ - NaN - ], - "nominalV": [ - 400.0 - ], - "thresholds": [ - { - "unit": "megawatt", - "min": -300.0, - "max": 300.0, - "side": 1 - }, - { - "unit": "megawatt", - "min": -300.0, - "max": 300.0, - "side": 2 - } - ] - } - ], - "pstRangeActions": [ - { - "id": "pstBeFr2", - "name": "pstBeFr2", - "operator": "BE", - "activationCost": 5.0, - "variationCosts": { - "up": 10.0, - "down": 10.0 - }, - "onInstantUsageRules": [ - { - "instant": "preventive", - "usageMethod": "available" - } - ], - "networkElementId": "BBE1AA1 FFR1AA1 2", - "ranges": [ - { - "min": -16, - "max": 16, - "rangeType": "absolute" - } - ] - } - ] -} \ No newline at end of file diff --git a/data/ics-importer/src/test/resources/crac/crac-0330.json b/data/ics-importer/src/test/resources/crac/crac-0330.json deleted file mode 100644 index 995949c68a..0000000000 --- a/data/ics-importer/src/test/resources/crac/crac-0330.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "type": "CRAC", - "version": "2.7", - "info": "Generated by PowSyBl OpenRAO https://powsybl.readthedocs.io/projects/openrao/", - "id": "crac-20250216", - "name": "crac-20250216", - "timestamp": "2025-02-13T03:30:00Z", - "instants": [ - { - "id": "preventive", - "kind": "PREVENTIVE" - }, - { - "id": "outage", - "kind": "OUTAGE" - }, - { - "id": "curative", - "kind": "CURATIVE" - } - ], - "networkElementsNamePerId": {}, - "flowCnecs": [ - { - "id": "cnecBeFrPreventive", - "name": "cnecBeFrPreventive", - "networkElementId": "BBE1AA1 FFR1AA1 1", - "operator": "FR", - "instant": "preventive", - "contingencyId": null, - "optimized": true, - "monitored": false, - "iMax": [ - NaN - ], - "nominalV": [ - 400.0 - ], - "thresholds": [ - { - "unit": "megawatt", - "min": -300.0, - "max": 300.0, - "side": 1 - }, - { - "unit": "megawatt", - "min": -300.0, - "max": 300.0, - "side": 2 - } - ] - } - ], - "pstRangeActions": [ - { - "id": "pstBeFr2", - "name": "pstBeFr2", - "operator": "BE", - "activationCost": 5.0, - "variationCosts": { - "up": 10.0, - "down": 10.0 - }, - "onInstantUsageRules": [ - { - "instant": "preventive", - "usageMethod": "available" - } - ], - "networkElementId": "BBE1AA1 FFR1AA1 2", - "ranges": [ - { - "min": -16, - "max": 16, - "rangeType": "absolute" - } - ] - } - ] -} \ No newline at end of file diff --git a/data/ics-importer/src/test/resources/crac/crac-0430.json b/data/ics-importer/src/test/resources/crac/crac-0430.json deleted file mode 100644 index 8c9791907f..0000000000 --- a/data/ics-importer/src/test/resources/crac/crac-0430.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "type": "CRAC", - "version": "2.7", - "info": "Generated by PowSyBl OpenRAO https://powsybl.readthedocs.io/projects/openrao/", - "id": "crac-20250217", - "name": "crac-20250217", - "timestamp": "2025-02-13T04:30:00Z", - "instants": [ - { - "id": "preventive", - "kind": "PREVENTIVE" - }, - { - "id": "outage", - "kind": "OUTAGE" - }, - { - "id": "curative", - "kind": "CURATIVE" - } - ], - "networkElementsNamePerId": {}, - "flowCnecs": [ - { - "id": "cnecBeFrPreventive", - "name": "cnecBeFrPreventive", - "networkElementId": "BBE1AA1 FFR1AA1 1", - "operator": "FR", - "instant": "preventive", - "contingencyId": null, - "optimized": true, - "monitored": false, - "iMax": [ - NaN - ], - "nominalV": [ - 400.0 - ], - "thresholds": [ - { - "unit": "megawatt", - "min": -300.0, - "max": 300.0, - "side": 1 - }, - { - "unit": "megawatt", - "min": -300.0, - "max": 300.0, - "side": 2 - } - ] - } - ], - "pstRangeActions": [ - { - "id": "pstBeFr2", - "name": "pstBeFr2", - "operator": "BE", - "activationCost": 5.0, - "variationCosts": { - "up": 10.0, - "down": 10.0 - }, - "onInstantUsageRules": [ - { - "instant": "preventive", - "usageMethod": "available" - } - ], - "networkElementId": "BBE1AA1 FFR1AA1 2", - "ranges": [ - { - "min": -16, - "max": 16, - "rangeType": "absolute" - } - ] - } - ] -} \ No newline at end of file diff --git a/data/ics-importer/src/test/resources/crac/crac-0530.json b/data/ics-importer/src/test/resources/crac/crac-0530.json deleted file mode 100644 index eee4d889a2..0000000000 --- a/data/ics-importer/src/test/resources/crac/crac-0530.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "type": "CRAC", - "version": "2.7", - "info": "Generated by PowSyBl OpenRAO https://powsybl.readthedocs.io/projects/openrao/", - "id": "crac-20250218", - "name": "crac-20250218", - "timestamp": "2025-02-13T05:30:00Z", - "instants": [ - { - "id": "preventive", - "kind": "PREVENTIVE" - }, - { - "id": "outage", - "kind": "OUTAGE" - }, - { - "id": "curative", - "kind": "CURATIVE" - } - ], - "networkElementsNamePerId": {}, - "flowCnecs": [ - { - "id": "cnecBeFrPreventive", - "name": "cnecBeFrPreventive", - "networkElementId": "BBE1AA1 FFR1AA1 1", - "operator": "FR", - "instant": "preventive", - "contingencyId": null, - "optimized": true, - "monitored": false, - "iMax": [ - NaN - ], - "nominalV": [ - 400.0 - ], - "thresholds": [ - { - "unit": "megawatt", - "min": -300.0, - "max": 300.0, - "side": 1 - }, - { - "unit": "megawatt", - "min": -300.0, - "max": 300.0, - "side": 2 - } - ] - } - ], - "pstRangeActions": [ - { - "id": "pstBeFr2", - "name": "pstBeFr2", - "operator": "BE", - "activationCost": 5.0, - "variationCosts": { - "up": 10.0, - "down": 10.0 - }, - "onInstantUsageRules": [ - { - "instant": "preventive", - "usageMethod": "available" - } - ], - "networkElementId": "BBE1AA1 FFR1AA1 2", - "ranges": [ - { - "min": -16, - "max": 16, - "rangeType": "absolute" - } - ] - } - ] -} \ No newline at end of file diff --git a/data/ics-importer/src/test/resources/crac/crac-0630.json b/data/ics-importer/src/test/resources/crac/crac-0630.json deleted file mode 100644 index 94a0355e27..0000000000 --- a/data/ics-importer/src/test/resources/crac/crac-0630.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "type": "CRAC", - "version": "2.7", - "info": "Generated by PowSyBl OpenRAO https://powsybl.readthedocs.io/projects/openrao/", - "id": "crac-20250219", - "name": "crac-20250219", - "timestamp": "2025-02-13T06:30:00Z", - "instants": [ - { - "id": "preventive", - "kind": "PREVENTIVE" - }, - { - "id": "outage", - "kind": "OUTAGE" - }, - { - "id": "curative", - "kind": "CURATIVE" - } - ], - "networkElementsNamePerId": {}, - "flowCnecs": [ - { - "id": "cnecBeFrPreventive", - "name": "cnecBeFrPreventive", - "networkElementId": "BBE1AA1 FFR1AA1 1", - "operator": "FR", - "instant": "preventive", - "contingencyId": null, - "optimized": true, - "monitored": false, - "iMax": [ - NaN - ], - "nominalV": [ - 400.0 - ], - "thresholds": [ - { - "unit": "megawatt", - "min": -300.0, - "max": 300.0, - "side": 1 - }, - { - "unit": "megawatt", - "min": -300.0, - "max": 300.0, - "side": 2 - } - ] - } - ], - "pstRangeActions": [ - { - "id": "pstBeFr2", - "name": "pstBeFr2", - "operator": "BE", - "activationCost": 5.0, - "variationCosts": { - "up": 10.0, - "down": 10.0 - }, - "onInstantUsageRules": [ - { - "instant": "preventive", - "usageMethod": "available" - } - ], - "networkElementId": "BBE1AA1 FFR1AA1 2", - "ranges": [ - { - "min": -16, - "max": 16, - "rangeType": "absolute" - } - ] - } - ] -} \ No newline at end of file diff --git a/data/ics-importer/src/test/resources/crac/crac-0730.json b/data/ics-importer/src/test/resources/crac/crac-0730.json deleted file mode 100644 index c619037447..0000000000 --- a/data/ics-importer/src/test/resources/crac/crac-0730.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "type": "CRAC", - "version": "2.7", - "info": "Generated by PowSyBl OpenRAO https://powsybl.readthedocs.io/projects/openrao/", - "id": "crac-20250220", - "name": "crac-20250220", - "timestamp": "2025-02-13T07:30:00Z", - "instants": [ - { - "id": "preventive", - "kind": "PREVENTIVE" - }, - { - "id": "outage", - "kind": "OUTAGE" - }, - { - "id": "curative", - "kind": "CURATIVE" - } - ], - "networkElementsNamePerId": {}, - "flowCnecs": [ - { - "id": "cnecBeFrPreventive", - "name": "cnecBeFrPreventive", - "networkElementId": "BBE1AA1 FFR1AA1 1", - "operator": "FR", - "instant": "preventive", - "contingencyId": null, - "optimized": true, - "monitored": false, - "iMax": [ - NaN - ], - "nominalV": [ - 400.0 - ], - "thresholds": [ - { - "unit": "megawatt", - "min": -300.0, - "max": 300.0, - "side": 1 - }, - { - "unit": "megawatt", - "min": -300.0, - "max": 300.0, - "side": 2 - } - ] - } - ], - "pstRangeActions": [ - { - "id": "pstBeFr2", - "name": "pstBeFr2", - "operator": "BE", - "activationCost": 5.0, - "variationCosts": { - "up": 10.0, - "down": 10.0 - }, - "onInstantUsageRules": [ - { - "instant": "preventive", - "usageMethod": "available" - } - ], - "networkElementId": "BBE1AA1 FFR1AA1 2", - "ranges": [ - { - "min": -16, - "max": 16, - "rangeType": "absolute" - } - ] - } - ] -} \ No newline at end of file diff --git a/data/ics-importer/src/test/resources/ics/static.csv b/data/ics-importer/src/test/resources/ics/static.csv index 129c323405..acb66851aa 100644 --- a/data/ics-importer/src/test/resources/ics/static.csv +++ b/data/ics-importer/src/test/resources/ics/static.csv @@ -1,2 +1,2 @@ RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed -Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;20;20;1;1;TRUE;TRUE \ No newline at end of file +Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;20;20;2;1;TRUE;TRUE \ No newline at end of file diff --git a/data/ics-importer/src/test/resources/ics/static2.csv b/data/ics-importer/src/test/resources/ics/static2.csv new file mode 100644 index 0000000000..c3b3686ecf --- /dev/null +++ b/data/ics-importer/src/test/resources/ics/static2.csv @@ -0,0 +1,2 @@ +RA RD ID;TSO;Preventive;Curative;Time From;Time To;Generator Name;RD description mode;UCT Node or GSK ID;Minimum Redispatch [MW];Fuel type;Minimum up-time [h];Minimum down-time [h];Maximum positive power gradient [MW/h];Maximum negative power gradient [MW/h];Lead time [h];Lag time [h];Startup allowed;Shutdown allowed +Redispatching_RA;FR;TRUE;FALSE;00:00;24:00:00;Generator_Name;Node;BBE1AA1;50;Coal;2;2;20;20;2;1;FALSE;FALSE \ No newline at end of file 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 3d872c91c6..3cdcfc83ac 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 @@ -88,7 +88,7 @@ public class Marmot implements TimeCoupledRaoProvider { private static final String MIP_SCENARIO = "MipScenario"; private static final String MIN_MARGIN_VIOLATION_EVALUATOR = "min-margin-violation-evaluator"; - private static final int PARALLELISM = 12; // TODO: configure as a parameter in MARMOT extension + private static final int PARALLELISM = 4; // TODO: configure as a parameter in MARMOT extension @Override public CompletableFuture run(TimeCoupledRaoInput timeCoupledRaoInput, RaoParameters raoParameters) { diff --git a/tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature b/tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature deleted file mode 100644 index eab587c716..0000000000 --- a/tests/src/test/resources/com/powsybl/openrao/tests/features/4_time_coupled/US93_5.feature +++ /dev/null @@ -1,865 +0,0 @@ -# Copyright (c) 2025, 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/. - -Feature: US 93.3: CORE IDCC data - - @slow @rao @20240122 - Scenario: US 93.3.1: 20240122 - Given network files are in folder "idcc/20240122-FID2-734-v1-10V1001C--00264T-to-10V1001C--00085T" - Given crac file is "idcc/20240122-FSC-ID2-CB-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given ics static file is "_110V1001C--00200I_CSA-COMRA-RDSTATIC-D_CORE-20240122-V001_.csv" - Given ics series file is "_110V1001C--00200I_CSA-COMRA-RDSERIES-D_CORE-20240122-V001_.csv" - Given ics gsk file is "_110V1001C--00200I_CSA-COMRA-GSK-D_CORE-20240122-V001_.csv" - Given configuration file is "idcc/RAOParameters_minCost_megawatt_dc.json" - Given time-coupled RefProg file is "idcc/20240122-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" - Given time-coupled rao inputs for CORE are: - | Timestamp | Network | - | 2024-01-22 00:30 | 20240122_0030_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-22 01:30 | 20240122_0130_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-22 02:30 | 20240122_0230_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-22 03:30 | 20240122_0330_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-22 04:30 | 20240122_0430_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-22 05:30 | 20240122_0530_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-22 06:30 | 20240122_0630_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-22 07:30 | 20240122_0730_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-22 08:30 | 20240122_0830_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-22 09:30 | 20240122_0930_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-22 10:30 | 20240122_1030_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-22 11:30 | 20240122_1130_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-22 12:30 | 20240122_1230_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-22 13:30 | 20240122_1330_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-22 14:30 | 20240122_1430_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-22 15:30 | 20240122_1530_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-22 16:30 | 20240122_1630_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-22 17:30 | 20240122_1730_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-22 18:30 | 20240122_1830_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-22 19:30 | 20240122_1930_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-22 20:30 | 20240122_2030_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-22 21:30 | 20240122_2130_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-22 22:30 | 20240122_2230_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-22 23:30 | 20240122_2330_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - When I launch marmot - When I export marmot results to "raoresults/results_20240122.zip" - When I export RefProg after redispatching to "generatedRefProgs/20240122-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" based on raoResults zip "raoresults/results_20240122.zip" - When I export networks with PRAs to "generatedNetworkWithPras/networkWithPras_20240122.zip" based on raoResults zip "raoresults/results_20240122.zip" - When I export F711 for business date "20240122" based on raoResults zip "raoresults/results_20240122.zip" - - @slow @rao @20240131 - Scenario: US 93.3.2: 20240131 - Given network files are in folder "idcc/20240131-FID2-734-v1-10V1001C--00264T-to-10V1001C--00085T" - Given crac file is "idcc/20240131-FSC-ID2-CB-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given ics static file is "_110V1001C--00200I_CSA-COMRA-RDSTATIC-D_CORE-20240131-V001_.csv" - Given ics series file is "_110V1001C--00200I_CSA-COMRA-RDSERIES-D_CORE-20240131-V001_.csv" - Given ics gsk file is "_110V1001C--00200I_CSA-COMRA-GSK-D_CORE-20240131-V001_.csv" - Given configuration file is "idcc/RAOParameters_minCost_megawatt_dc.json" - Given time-coupled RefProg file is "idcc/20240131-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" - Given time-coupled rao inputs for CORE are: - | Timestamp | Network | - | 2024-01-31 00:30 | 20240131_0030_2D3_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-31 01:30 | 20240131_0130_2D3_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-31 02:30 | 20240131_0230_2D3_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-31 03:30 | 20240131_0330_2D3_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-31 04:30 | 20240131_0430_2D3_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-31 05:30 | 20240131_0530_2D3_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-31 06:30 | 20240131_0630_2D3_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-31 07:30 | 20240131_0730_2D3_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-31 08:30 | 20240131_0830_2D3_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-31 09:30 | 20240131_0930_2D3_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-31 10:30 | 20240131_1030_2D3_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-31 11:30 | 20240131_1130_2D3_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-31 12:30 | 20240131_1230_2D3_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-31 13:30 | 20240131_1330_2D3_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-31 14:30 | 20240131_1430_2D3_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-31 15:30 | 20240131_1530_2D3_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-31 16:30 | 20240131_1630_2D3_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-31 17:30 | 20240131_1730_2D3_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-31 18:30 | 20240131_1830_2D3_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-31 19:30 | 20240131_1930_2D3_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-31 20:30 | 20240131_2030_2D3_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-31 21:30 | 20240131_2130_2D3_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-31 22:30 | 20240131_2230_2D3_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-01-31 23:30 | 20240131_2330_2D3_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - When I launch marmot - When I export marmot results to "raoresults/results_20240131.zip" - When I export RefProg after redispatching to "generatedRefProgs/20240131-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" based on raoResults zip "raoresults/results_20240131.zip" - When I export networks with PRAs to "raoresults/networkWithPras_20240131.zip" based on raoResults zip "raoresults/results_20240131.zip" - When I export F711 for business date "20240131" based on raoResults zip "raoresults/results_20240131.zip" - - @slow @rao @20240222 - Scenario: US 93.3.3: 20240222 - Given network files are in folder "idcc/20240222-FID2-734-v1-10V1001C--00264T-to-10V1001C--00085T" - Given crac file is "idcc/20240222-FSC-ID2-CB-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given ics static file is "_110V1001C--00200I_CSA-COMRA-RDSTATIC-D_CORE-20240222-V001_.csv" - Given ics series file is "_110V1001C--00200I_CSA-COMRA-RDSERIES-D_CORE-20240222-V001_.csv" - Given ics gsk file is "_110V1001C--00200I_CSA-COMRA-GSK-D_CORE-20240222-V001_.csv" - Given configuration file is "idcc/RAOParameters_minCost_megawatt_dc.json" - Given time-coupled RefProg file is "idcc/20240222-FID2-632-v3-10V1001C--00264T-to-10V1001C--00085T.xml" - Given time-coupled rao inputs for CORE are: - | Timestamp | Network | - | 2024-02-22 00:30 | 20240222_0030_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-22 01:30 | 20240222_0130_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-22 02:30 | 20240222_0230_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-22 03:30 | 20240222_0330_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-22 04:30 | 20240222_0430_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-22 05:30 | 20240222_0530_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-22 06:30 | 20240222_0630_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-22 07:30 | 20240222_0730_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-22 08:30 | 20240222_0830_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-22 09:30 | 20240222_0930_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-22 10:30 | 20240222_1030_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-22 11:30 | 20240222_1130_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-22 12:30 | 20240222_1230_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-22 13:30 | 20240222_1330_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-22 14:30 | 20240222_1430_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-22 15:30 | 20240222_1530_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-22 16:30 | 20240222_1630_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-22 17:30 | 20240222_1730_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-22 18:30 | 20240222_1830_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-22 19:30 | 20240222_1930_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-22 20:30 | 20240222_2030_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-22 21:30 | 20240222_2130_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-22 22:30 | 20240222_2230_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-22 23:30 | 20240222_2330_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - When I launch marmot - When I export marmot results to "raoresults/results_20240222.zip" - When I export RefProg after redispatching to "generatedRefProgs/20240222-FID2-632-v3-10V1001C--00264T-to-10V1001C--00085T.xml" based on raoResults zip "raoresults/results_20240222.zip" - When I export networks with PRAs to "raoresults/networkWithPras_20240222.zip" based on raoResults zip "raoresults/results_20240222.zip" - When I export F711 for business date "20240222" based on raoResults zip "raoresults/results_20240222.zip" - - @slow @rao @20240224 - Scenario: US 93.3.4: 20240224 - Given network files are in folder "idcc/20240224-FID2-734-v1-10V1001C--00264T-to-10V1001C--00085T" - Given crac file is "idcc/20240224-FSC-ID2-CB-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given ics static file is "idcc/_110V1001C--00200I_CSA-COMRA-RDSTATIC-D_CORE-20240224-V001_.csv" - Given ics series file is "idcc/_110V1001C--00200I_CSA-COMRA-RDSERIES-D_CORE-20240224-V001_.csv" - Given ics gsk file is "idcc/_10V1001C--00200I_CSA-INDRA-GSK-D_D7-20240224-V001_.csv" - Given configuration file is "idcc/RaoParameters_minCost_megawatt_dc.json" - Given time-coupled RefProg file is "idcc/20240224-FID2-632-v3-10V1001C--00264T-to-10V1001C--00085T.xml" - Given time-coupled rao inputs for CORE are: - | Timestamp | Network | - | 2024-02-24 00:30 | 20240224_0030_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 01:30 | 20240224_0130_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 02:30 | 20240224_0230_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 03:30 | 20240224_0330_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 04:30 | 20240224_0430_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 05:30 | 20240224_0530_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 06:30 | 20240224_0630_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 07:30 | 20240224_0730_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 08:30 | 20240224_0830_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 09:30 | 20240224_0930_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 10:30 | 20240224_1030_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 11:30 | 20240224_1130_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 12:30 | 20240224_1230_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 13:30 | 20240224_1330_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 14:30 | 20240224_1430_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 15:30 | 20240224_1530_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 16:30 | 20240224_1630_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 17:30 | 20240224_1730_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 18:30 | 20240224_1830_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 19:30 | 20240224_1930_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 20:30 | 20240224_2030_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 21:30 | 20240224_2130_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 22:30 | 20240224_2230_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-02-24 23:30 | 20240224_2330_2D6_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - When I launch marmot - When I export marmot results to "raoresults/results_20240224.zip" - - @slow @rao @20240502 - Scenario: US 93.3.5: 20240502 - Given network files are in folder "idcc/20240502-FID2-734-v1-10V1001C--00264T-to-10V1001C--00085T" - Given crac file is "idcc/20240502-FSC-ID2-CB-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given ics static file is "_10V1001C--00275O_CSA-COMRA-RDSTATIC-D_CORE-20240502-V001_.csv" - Given ics series file is "_10V1001C--00275O_CSA-COMRA-RDSERIES-D_CORE-20240502-V001_.csv" - Given ics gsk file is "_10V1001C--00200I_CSA-INDRA-GSK-D_D7-20240502-V001_.csv" - Given configuration file is "idcc/RAOParameters_minCost_megawatt_dc.json" - Given time-coupled RefProg file is "idcc/20240502-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" - Given time-coupled rao inputs for CORE are: - | Timestamp | Network | - | 2024-05-02 00:30 | 20240502_0030_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-02 01:30 | 20240502_0130_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-02 02:30 | 20240502_0230_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-02 03:30 | 20240502_0330_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-02 04:30 | 20240502_0430_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-02 05:30 | 20240502_0530_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-02 06:30 | 20240502_0630_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-02 07:30 | 20240502_0730_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-02 08:30 | 20240502_0830_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-02 09:30 | 20240502_0930_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-02 10:30 | 20240502_1030_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-02 11:30 | 20240502_1130_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-02 12:30 | 20240502_1230_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-02 13:30 | 20240502_1330_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-02 14:30 | 20240502_1430_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-02 15:30 | 20240502_1530_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-02 16:30 | 20240502_1630_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-02 17:30 | 20240502_1730_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-02 18:30 | 20240502_1830_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-02 19:30 | 20240502_1930_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-02 20:30 | 20240502_2030_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-02 21:30 | 20240502_2130_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-02 22:30 | 20240502_2230_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-02 23:30 | 20240502_2330_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - When I launch marmot - When I export marmot results to "raoresults/results_20240502.zip" - When I export RefProg after redispatching to "generatedRefProgs/20240502-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" based on raoResults zip "raoresults/results_20240502.zip" - When I export networks with PRAs to "raoresults/networkWithPras_20240502.zip" based on raoResults zip "raoresults/results_20240502.zip" - When I export F711 for business date "20240502" based on raoResults zip "raoresults/results_20240502.zip" - - @slow @rao @20240526 - Scenario: US 93.3.6: 20240526 - Given network files are in folder "idcc/20240526-FID2-734-v1-10V1001C--00264T-to-10V1001C--00085T" - Given crac file is "idcc/20240526-FSC-ID2-CB-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given ics static file is "_10V1001C--00275O_CSA-COMRA-RDSTATIC-D_CORE-20240526-V001_.csv" - Given ics series file is "_10V1001C--00275O_CSA-COMRA-RDSERIES-D_CORE-20240526-V001_.csv" - Given ics gsk file is "_10V1001C--00275O_CSA-INDRA-GSK-D_D7-20240526-V001_.csv" - Given configuration file is "idcc/RAOParameters_minCost_megawatt_dc.json" - Given time-coupled RefProg file is "idcc/20240526-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" - Given time-coupled rao inputs for CORE are: - | Timestamp | Network | - | 2024-05-26 00:30 | 20240526_0030_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-26 01:30 | 20240526_0130_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-26 02:30 | 20240526_0230_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-26 03:30 | 20240526_0330_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-26 04:30 | 20240526_0430_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-26 05:30 | 20240526_0530_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-26 06:30 | 20240526_0630_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-26 07:30 | 20240526_0730_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-26 08:30 | 20240526_0830_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-26 09:30 | 20240526_0930_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-26 10:30 | 20240526_1030_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-26 11:30 | 20240526_1130_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-26 12:30 | 20240526_1230_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-26 13:30 | 20240526_1330_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-26 14:30 | 20240526_1430_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-26 15:30 | 20240526_1530_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-26 16:30 | 20240526_1630_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-26 17:30 | 20240526_1730_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-26 18:30 | 20240526_1830_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-26 19:30 | 20240526_1930_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-26 20:30 | 20240526_2030_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-26 21:30 | 20240526_2130_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-26 22:30 | 20240526_2230_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-05-26 23:30 | 20240526_2330_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - When I launch marmot - When I export marmot results to "raoresults/results_20240526.zip" - When I export RefProg after redispatching to "generatedRefProgs/20240526-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" based on raoResults zip "raoresults/results_20240526.zip" - When I export networks with PRAs to "raoresults/networkWithPras_20240526.zip" based on raoResults zip "raoresults/results_20240526.zip" - When I export F711 for business date "20240526" based on raoResults zip "raoresults/results_20240526.zip" - - @slow @rao @20240602 - Scenario: US 93.3.7: 20240602 - Given network files are in folder "idcc/20240602-FID2-734-v1-10V1001C--00264T-to-10V1001C--00085T" - Given crac file is "idcc/20240602-FSC-ID2-CB-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given ics static file is "_10V1001C--00275O_CSA-COMRA-RDSTATIC-D_CORE-20240602-V001_.csv" - Given ics series file is "_10V1001C--00275O_CSA-COMRA-RDSERIES-D_CORE-20240602-V001_.csv" - Given ics gsk file is "_10V1001C--00275O_CSA-INDRA-GSK-D_D7-20240602-V001_.csv" - Given configuration file is "idcc/RAOParameters_minCost_megawatt_dc.json" - Given time-coupled RefProg file is "idcc/20240602-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" - Given time-coupled rao inputs for CORE are: - | Timestamp | Network | - | 2024-06-02 00:30 | 20240602_0030_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-02 01:30 | 20240602_0130_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-02 02:30 | 20240602_0230_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-02 03:30 | 20240602_0330_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-02 04:30 | 20240602_0430_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-02 05:30 | 20240602_0530_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-02 06:30 | 20240602_0630_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-02 07:30 | 20240602_0730_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-02 08:30 | 20240602_0830_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-02 09:30 | 20240602_0930_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-02 10:30 | 20240602_1030_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-02 11:30 | 20240602_1130_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-02 12:30 | 20240602_1230_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-02 13:30 | 20240602_1330_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-02 14:30 | 20240602_1430_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-02 15:30 | 20240602_1530_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-02 16:30 | 20240602_1630_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-02 17:30 | 20240602_1730_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-02 18:30 | 20240602_1830_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-02 19:30 | 20240602_1930_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-02 20:30 | 20240602_2030_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-02 21:30 | 20240602_2130_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-02 22:30 | 20240602_2230_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-02 23:30 | 20240602_2330_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - When I launch marmot - When I export marmot results to "raoresults/results_20240602.zip" - When I export RefProg after redispatching to "generatedRefProgs/20240602-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" based on raoResults zip "raoresults/results_20240602.zip" - When I export networks with PRAs to "raoresults/networkWithPras_20240602.zip" based on raoResults zip "raoresults/results_20240602.zip" - When I export F711 for business date "20240602" based on raoResults zip "raoresults/results_20240602.zip" - - @slow @rao @20240606 - Scenario: US 93.3.8: 20240606 - Given network files are in folder "idcc/20240606-FID2-734-v1-10V1001C--00264T-to-10V1001C--00085T" - Given crac file is "idcc/20240606-FSC-ID2-CB-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given ics static file is "_10V1001C--00275O_CSA-COMRA-RDSTATIC-D_CORE-20240606-V001_.csv" - Given ics series file is "_10V1001C--00275O_CSA-COMRA-RDSERIES-D_CORE-20240606-V001_.csv" - Given ics gsk file is "_10V1001C--00275O_CSA-INDRA-GSK-D_D7-20240606-V001_.csv" - Given configuration file is "idcc/RAOParameters_minCost_megawatt_dc.json" - Given time-coupled RefProg file is "idcc/20240606-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" - Given time-coupled rao inputs for CORE are: - | Timestamp | Network | - | 2024-06-06 00:30 | 20240606_0030_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-06 01:30 | 20240606_0130_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-06 02:30 | 20240606_0230_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-06 03:30 | 20240606_0330_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-06 04:30 | 20240606_0430_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-06 05:30 | 20240606_0530_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-06 06:30 | 20240606_0630_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-06 07:30 | 20240606_0730_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-06 08:30 | 20240606_0830_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-06 09:30 | 20240606_0930_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-06 10:30 | 20240606_1030_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-06 11:30 | 20240606_1130_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-06 12:30 | 20240606_1230_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-06 13:30 | 20240606_1330_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-06 14:30 | 20240606_1430_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-06 15:30 | 20240606_1530_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-06 16:30 | 20240606_1630_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-06 17:30 | 20240606_1730_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-06 18:30 | 20240606_1830_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-06 19:30 | 20240606_1930_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-06 20:30 | 20240606_2030_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-06 21:30 | 20240606_2130_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-06 22:30 | 20240606_2230_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-06 23:30 | 20240606_2330_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - When I launch marmot - When I export marmot results to "raoresults/results_20240606.zip" - When I export RefProg after redispatching to "generatedRefProgs/20240606-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" based on raoResults zip "raoresults/results_20240606.zip" - When I export networks with PRAs to "raoresults/networkWithPras_20240606.zip" based on raoResults zip "raoresults/results_20240606.zip" - When I export F711 for business date "20240606" based on raoResults zip "raoresults/results_20240606.zip" - - @slow @rao @20240628 - Scenario: US 93.3.9: 20240628 - Given network files are in folder "idcc/20240628-FID2-734-v1-10V1001C--00264T-to-10V1001C--00085T" - Given crac file is "idcc/20240628-FSC-ID2-CB-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given ics static file is "_10V1001C--00275O_CSA-COMRA-RDSTATIC-D_CORE-20240628-V001_.csv" - Given ics series file is "_10V1001C--00275O_CSA-COMRA-RDSERIES-D_CORE-20240628-V001_.csv" - Given ics gsk file is "_10V1001C--00275O_CSA-INDRA-GSK-D_D7-20240628-V001_.csv" - Given configuration file is "idcc/RAOParameters_minCost_megawatt_dc.json" - Given time-coupled RefProg file is "idcc/20240628-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" - Given time-coupled rao inputs for CORE are: - | Timestamp | Network | - | 2024-06-28 00:30 | 20240628_0030_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-28 01:30 | 20240628_0130_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-28 02:30 | 20240628_0230_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-28 03:30 | 20240628_0330_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-28 04:30 | 20240628_0430_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-28 05:30 | 20240628_0530_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-28 06:30 | 20240628_0630_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-28 07:30 | 20240628_0730_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-28 08:30 | 20240628_0830_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-28 09:30 | 20240628_0930_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-28 10:30 | 20240628_1030_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-28 11:30 | 20240628_1130_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-28 12:30 | 20240628_1230_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-28 13:30 | 20240628_1330_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-28 14:30 | 20240628_1430_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-28 15:30 | 20240628_1530_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-28 16:30 | 20240628_1630_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-28 17:30 | 20240628_1730_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-28 18:30 | 20240628_1830_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-28 19:30 | 20240628_1930_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-28 20:30 | 20240628_2030_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-28 21:30 | 20240628_2130_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-28 22:30 | 20240628_2230_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-06-28 23:30 | 20240628_2330_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - When I launch marmot - When I export marmot results to "raoresults/results_20240628.zip" - When I export RefProg after redispatching to "generatedRefProgs/20240628-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" based on raoResults zip "raoresults/results_20240628.zip" - When I export networks with PRAs to "raoresults/networkWithPras_20240628.zip" based on raoResults zip "raoresults/results_20240628.zip" - When I export F711 for business date "20240628" based on raoResults zip "raoresults/results_20240628.zip" - - @slow @rao @20240704 - Scenario: US 93.3.10: 20240704 - Given network files are in folder "idcc/20240704-FID2-734-v1-10V1001C--00264T-to-10V1001C--00085T" - Given crac file is "idcc/20240704-FSC-ID2-CB-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given ics static file is "_10V1001C--00275O_CSA-COMRA-RDSTATIC-D_CORE-20240704-V001_.csv" - Given ics series file is "_10V1001C--00275O_CSA-COMRA-RDSERIES-D_CORE-20240704-V001_.csv" - Given ics gsk file is "_10V1001C--00275O_CSA-INDRA-GSK-D_D7-20240704-V001_.csv" - Given configuration file is "idcc/RAOParameters_minCost_megawatt_dc.json" - Given time-coupled RefProg file is "idcc/20240704-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" - Given time-coupled rao inputs for CORE are: - | Timestamp | Network | - | 2024-07-04 00:30 | 20240704_0030_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-04 01:30 | 20240704_0130_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-04 02:30 | 20240704_0230_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-04 03:30 | 20240704_0330_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-04 04:30 | 20240704_0430_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-04 05:30 | 20240704_0530_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-04 06:30 | 20240704_0630_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-04 07:30 | 20240704_0730_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-04 08:30 | 20240704_0830_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-04 09:30 | 20240704_0930_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-04 10:30 | 20240704_1030_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-04 11:30 | 20240704_1130_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-04 12:30 | 20240704_1230_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-04 13:30 | 20240704_1330_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-04 14:30 | 20240704_1430_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-04 15:30 | 20240704_1530_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-04 16:30 | 20240704_1630_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-04 17:30 | 20240704_1730_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-04 18:30 | 20240704_1830_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-04 19:30 | 20240704_1930_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-04 20:30 | 20240704_2030_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-04 21:30 | 20240704_2130_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-04 22:30 | 20240704_2230_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-04 23:30 | 20240704_2330_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - When I launch marmot - When I export marmot results to "raoresults/results_20240704.zip" - When I export RefProg after redispatching to "generatedRefProgs/20240704-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" based on raoResults zip "raoresults/results_20240704.zip" - When I export networks with PRAs to "raoresults/networkWithPras_20240704.zip" based on raoResults zip "raoresults/results_20240704.zip" - When I export F711 for business date "20240704" based on raoResults zip "raoresults/results_20240704.zip" - - @slow @rao @20240711 - Scenario: US 93.3.11: 20240711 - Given network files are in folder "idcc/20240711-FID2-734-v1-10V1001C--00264T-to-10V1001C--00085T 1" - Given crac file is "idcc/20240711-FSC-ID2-CB-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given ics static file is "_10V1001C--00275O_CSA-COMRA-RDSTATIC-D_CORE-20240711-V001_.csv" - Given ics series file is "_10V1001C--00275O_CSA-COMRA-RDSERIES-D_CORE-20240711-V001_.csv" - Given ics gsk file is "_10V1001C--00275O_CSA-INDRA-GSK-D_D7-20240711-V001_.csv" - Given configuration file is "idcc/RAOParameters_minCost_megawatt_dc.json" - Given time-coupled RefProg file is "idcc/20240711-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" - Given time-coupled rao inputs for CORE are: - | Timestamp | Network | - | 2024-07-11 00:30 | 20240711_0030_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-11 01:30 | 20240711_0130_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-11 02:30 | 20240711_0230_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-11 03:30 | 20240711_0330_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-11 04:30 | 20240711_0430_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-11 05:30 | 20240711_0530_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-11 06:30 | 20240711_0630_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-11 07:30 | 20240711_0730_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-11 08:30 | 20240711_0830_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-11 09:30 | 20240711_0930_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-11 10:30 | 20240711_1030_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-11 11:30 | 20240711_1130_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-11 12:30 | 20240711_1230_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-11 13:30 | 20240711_1330_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-11 14:30 | 20240711_1430_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-11 15:30 | 20240711_1530_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-11 16:30 | 20240711_1630_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-11 17:30 | 20240711_1730_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-11 18:30 | 20240711_1830_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-11 19:30 | 20240711_1930_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-11 20:30 | 20240711_2030_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-11 21:30 | 20240711_2130_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-11 22:30 | 20240711_2230_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-11 23:30 | 20240711_2330_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - When I launch marmot - When I export marmot results to "raoresults/results_20240711.zip" - When I export RefProg after redispatching to "generatedRefProgs/20240711-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" based on raoResults zip "raoresults/results_20240711.zip" - When I export networks with PRAs to "raoresults/networkWithPras_20240711.zip" based on raoResults zip "raoresults/results_20240711.zip" - When I export F711 for business date "20240711" based on raoResults zip "raoresults/results_20240711.zip" - - @slow @rao @20240712 - Scenario: US 93.3.12: 20240712 - Given network files are in folder "idcc/20240712-FID2-734-v1-10V1001C--00264T-to-10V1001C--00085T 1" - Given crac file is "idcc/20240712-FSC-ID2-CB-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given ics static file is "_10V1001C--00275O_CSA-COMRA-RDSTATIC-D_CORE-20240712-V001_.csv" - Given ics series file is "_10V1001C--00275O_CSA-COMRA-RDSERIES-D_CORE-20240712-V001_.csv" - Given ics gsk file is "_10V1001C--00275O_CSA-INDRA-GSK-D_D7-20240712-V001_.csv" - Given configuration file is "idcc/RAOParameters_minCost_megawatt_dc.json" - Given time-coupled RefProg file is "idcc/20240712-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" - Given time-coupled rao inputs for CORE are: - | Timestamp | Network | - | 2024-07-12 00:30 | 20240712_0030_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-12 01:30 | 20240712_0130_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-12 02:30 | 20240712_0230_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-12 03:30 | 20240712_0330_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-12 04:30 | 20240712_0430_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-12 05:30 | 20240712_0530_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-12 06:30 | 20240712_0630_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-12 07:30 | 20240712_0730_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-12 08:30 | 20240712_0830_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-12 09:30 | 20240712_0930_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-12 10:30 | 20240712_1030_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-12 11:30 | 20240712_1130_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-12 12:30 | 20240712_1230_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-12 13:30 | 20240712_1330_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-12 14:30 | 20240712_1430_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-12 15:30 | 20240712_1530_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-12 16:30 | 20240712_1630_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-12 17:30 | 20240712_1730_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-12 18:30 | 20240712_1830_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-12 19:30 | 20240712_1930_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-12 20:30 | 20240712_2030_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-12 21:30 | 20240712_2130_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-12 22:30 | 20240712_2230_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-12 23:30 | 20240712_2330_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - When I launch marmot - When I export marmot results to "raoresults/results_20240712.zip" - When I export RefProg after redispatching to "generatedRefProgs/20240712-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" based on raoResults zip "raoresults/results_20240712.zip" - When I export networks with PRAs to "raoresults/networkWithPras_20240712.zip" based on raoResults zip "raoresults/results_20240712.zip" - When I export F711 for business date "20240712" based on raoResults zip "raoresults/results_20240712.zip" - - @slow @rao @20240716 - Scenario: US 93.3.13: 20240716 - Given network files are in folder "idcc/20240716-FID2-734-v1-10V1001C--00264T-to-10V1001C--00085T 1" - Given crac file is "idcc/20240716-FSC-ID2-CB-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given ics static file is "_10V1001C--00275O_CSA-COMRA-RDSTATIC-D_CORE-20240716-V001_.csv" - Given ics series file is "_10V1001C--00275O_CSA-COMRA-RDSERIES-D_CORE-20240716-V001_.csv" - Given ics gsk file is "_10V1001C--00275O_CSA-INDRA-GSK-D_D7-20240716-V001_.csv" - Given configuration file is "idcc/RAOParameters_minCost_megawatt_dc.json" - Given time-coupled RefProg file is "idcc/20240716-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" - Given time-coupled rao inputs for CORE are: - | Timestamp | Network | - | 2024-07-16 00:30 | 20240716_0030_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-16 01:30 | 20240716_0130_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-16 02:30 | 20240716_0230_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-16 03:30 | 20240716_0330_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-16 04:30 | 20240716_0430_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-16 05:30 | 20240716_0530_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-16 06:30 | 20240716_0630_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-16 07:30 | 20240716_0730_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-16 08:30 | 20240716_0830_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-16 09:30 | 20240716_0930_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-16 10:30 | 20240716_1030_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-16 11:30 | 20240716_1130_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-16 12:30 | 20240716_1230_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-16 13:30 | 20240716_1330_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-16 14:30 | 20240716_1430_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-16 15:30 | 20240716_1530_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-16 16:30 | 20240716_1630_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-16 17:30 | 20240716_1730_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-16 18:30 | 20240716_1830_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-16 19:30 | 20240716_1930_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-16 20:30 | 20240716_2030_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-16 21:30 | 20240716_2130_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-16 22:30 | 20240716_2230_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-16 23:30 | 20240716_2330_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - When I launch marmot - When I export marmot results to "raoresults/results_20240716.zip" - When I export RefProg after redispatching to "generatedRefProgs/20240716-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" based on raoResults zip "raoresults/results_20240716.zip" - When I export networks with PRAs to "raoresults/networkWithPras_20240716.zip" based on raoResults zip "raoresults/results_20240716.zip" - When I export F711 for business date "20240716" based on raoResults zip "raoresults/results_20240716.zip" - - @slow @rao @20240718 - Scenario: US 93.3.14: 20240718 - Given network files are in folder "idcc/20240718-FID2-734-v1-10V1001C--00264T-to-10V1001C--00085T" - Given crac file is "idcc/20240718-FSC-ID2-CB-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given ics static file is "_10V1001C--00275O_CSA-COMRA-RDSTATIC-D_CORE-20240718-V001_.csv" - Given ics series file is "_10V1001C--00275O_CSA-COMRA-RDSERIES-D_CORE-20240718-V001_.csv" - Given ics gsk file is "_10V1001C--00275O_CSA-INDRA-GSK-D_D7-20240718-V001_.csv" - Given configuration file is "idcc/RAOParameters_minCost_megawatt_dc.json" - Given time-coupled RefProg file is "idcc/20240718-FSC-ID2-REFPROG-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given time-coupled rao inputs for CORE are: - | Timestamp | Network | - | 2024-07-18 00:30 | 20240718_0030_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-18 01:30 | 20240718_0130_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-18 02:30 | 20240718_0230_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-18 03:30 | 20240718_0330_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-18 04:30 | 20240718_0430_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-18 05:30 | 20240718_0530_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-18 06:30 | 20240718_0630_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-18 07:30 | 20240718_0730_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-18 08:30 | 20240718_0830_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-18 09:30 | 20240718_0930_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-18 10:30 | 20240718_1030_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-18 11:30 | 20240718_1130_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-18 12:30 | 20240718_1230_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-18 13:30 | 20240718_1330_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-18 14:30 | 20240718_1430_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-18 15:30 | 20240718_1530_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-18 16:30 | 20240718_1630_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-18 17:30 | 20240718_1730_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-18 18:30 | 20240718_1830_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-18 19:30 | 20240718_1930_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-18 20:30 | 20240718_2030_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-18 21:30 | 20240718_2130_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-18 22:30 | 20240718_2230_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-18 23:30 | 20240718_2330_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - When I launch marmot - When I export marmot results to "raoresults/results_20240718.zip" - When I export RefProg after redispatching to "generatedRefProgs/20240718-FSC-ID2-REFPROG-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" based on raoResults zip "raoresults/results_20240718.zip" - When I export networks with PRAs to "raoresults/networkWithPras_20240718.zip" based on raoResults zip "raoresults/results_20240718.zip" - When I export F711 for business date "20240718" based on raoResults zip "raoresults/results_20240718.zip" - - - @slow @rao @20240730 - Scenario: US 93.3.16: 20240730 - Given network files are in folder "idcc/20240730-FID2-734-v1-10V1001C--00264T-to-10V1001C--00085T 1" - Given crac file is "idcc/20240730-FSC-ID2-CB-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given ics static file is "_10V1001C--00275O_CSA-COMRA-RDSTATIC-D_CORE-20240730-V001_.csv" - Given ics series file is "_10V1001C--00275O_CSA-COMRA-RDSERIES-D_CORE-20240730-V001_.csv" - Given ics gsk file is "_10V1001C--00275O_CSA-INDRA-GSK-D_D7-20240730-V001_.csv" - Given configuration file is "idcc/RAOParameters_minCost_megawatt_dc.json" - Given time-coupled RefProg file is "idcc/20240730-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" - Given time-coupled rao inputs for CORE are: - | Timestamp | Network | - | 2024-07-30 00:30 | 20240730_0030_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-30 01:30 | 20240730_0130_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-30 02:30 | 20240730_0230_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-30 03:30 | 20240730_0330_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-30 04:30 | 20240730_0430_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-30 05:30 | 20240730_0530_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-30 06:30 | 20240730_0630_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-30 07:30 | 20240730_0730_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-30 08:30 | 20240730_0830_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-30 09:30 | 20240730_0930_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-30 10:30 | 20240730_1030_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-30 11:30 | 20240730_1130_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-30 12:30 | 20240730_1230_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-30 13:30 | 20240730_1330_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-30 14:30 | 20240730_1430_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-30 15:30 | 20240730_1530_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-30 16:30 | 20240730_1630_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-30 17:30 | 20240730_1730_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-30 18:30 | 20240730_1830_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-30 19:30 | 20240730_1930_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-30 20:30 | 20240730_2030_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-30 21:30 | 20240730_2130_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-30 22:30 | 20240730_2230_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-07-30 23:30 | 20240730_2330_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - When I launch marmot - When I export marmot results to "raoresults/results_20240730.zip" - When I export RefProg after redispatching to "generatedRefProgs/20240730-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" based on raoResults zip "raoresults/results_20240730.zip" - When I export networks with PRAs to "raoresults/networkWithPras_20240730.zip" based on raoResults zip "raoresults/results_20240730.zip" - When I export F711 for business date "20240730" based on raoResults zip "raoresults/results_20240730.zip" - - @slow @rao @20240826 - Scenario: US 93.3.17: 20240826 - Given network files are in folder "idcc/20240826-FID2-734-v1-10V1001C--00264T-to-10V1001C--00085T 1" - Given crac file is "idcc/20240826-FSC-ID2-CB-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given ics static file is "_10V1001C--00275O_CSA-COMRA-RDSTATIC-D_CORE-20240826-V001_.csv" - Given ics series file is "_10V1001C--00275O_CSA-COMRA-RDSERIES-D_CORE-20240826-V001_.csv" - Given ics gsk file is "_10V1001C--00275O_CSA-INDRA-GSK-D_D7-20240826-V001_.csv" - Given configuration file is "idcc/RAOParameters_minCost_megawatt_dc.json" - Given time-coupled RefProg file is "idcc/20240826-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" - Given time-coupled rao inputs for CORE are: - | Timestamp | Network | - | 2024-08-26 00:30 | 20240826_0030_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-08-26 01:30 | 20240826_0130_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-08-26 02:30 | 20240826_0230_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-08-26 03:30 | 20240826_0330_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-08-26 04:30 | 20240826_0430_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-08-26 05:30 | 20240826_0530_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-08-26 06:30 | 20240826_0630_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-08-26 07:30 | 20240826_0730_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-08-26 08:30 | 20240826_0830_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-08-26 09:30 | 20240826_0930_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-08-26 10:30 | 20240826_1030_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-08-26 11:30 | 20240826_1130_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-08-26 12:30 | 20240826_1230_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-08-26 13:30 | 20240826_1330_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-08-26 14:30 | 20240826_1430_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-08-26 15:30 | 20240826_1530_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-08-26 16:30 | 20240826_1630_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-08-26 17:30 | 20240826_1730_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-08-26 18:30 | 20240826_1830_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-08-26 19:30 | 20240826_1930_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-08-26 20:30 | 20240826_2030_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-08-26 21:30 | 20240826_2130_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-08-26 22:30 | 20240826_2230_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-08-26 23:30 | 20240826_2330_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - When I launch marmot - When I export marmot results to "raoresults/results_20240826.zip" - When I export RefProg after redispatching to "generatedRefProgs/20240826-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" based on raoResults zip "raoresults/results_20240826.zip" - When I export networks with PRAs to "raoresults/networkWithPras_20240826.zip" based on raoResults zip "raoresults/results_20240826.zip" - When I export F711 for business date "20240826" based on raoResults zip "raoresults/results_20240826.zip" - - @slow @rao @20240906 - Scenario: US 93.3.18: 20240906 - Given network files are in folder "idcc/20240906-FID2-734-v1-10V1001C--00264T-to-10V1001C--00085T 1" - Given crac file is "idcc/20240906-FSC-ID2-CB-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given ics static file is "_10V1001C--00275O_CSA-COMRA-RDSTATIC-D_CORE-20240906-V001_.csv" - Given ics series file is "_10V1001C--00275O_CSA-COMRA-RDSERIES-D_CORE-20240906-V001_.csv" - Given ics gsk file is "_10V1001C--00275O_CSA-INDRA-GSK-D_D7-20240906-V001_.csv" - Given configuration file is "idcc/RAOParameters_minCost_megawatt_dc.json" - Given time-coupled RefProg file is "idcc/20240906-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" - Given time-coupled rao inputs for CORE are: - | Timestamp | Network | - | 2024-09-06 00:30 | 20240906_0030_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-06 01:30 | 20240906_0130_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-06 02:30 | 20240906_0230_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-06 03:30 | 20240906_0330_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-06 04:30 | 20240906_0430_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-06 05:30 | 20240906_0530_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-06 06:30 | 20240906_0630_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-06 07:30 | 20240906_0730_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-06 08:30 | 20240906_0830_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-06 09:30 | 20240906_0930_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-06 10:30 | 20240906_1030_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-06 11:30 | 20240906_1130_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-06 12:30 | 20240906_1230_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-06 13:30 | 20240906_1330_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-06 14:30 | 20240906_1430_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-06 15:30 | 20240906_1530_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-06 16:30 | 20240906_1630_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-06 17:30 | 20240906_1730_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-06 18:30 | 20240906_1830_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-06 19:30 | 20240906_1930_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-06 20:30 | 20240906_2030_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-06 21:30 | 20240906_2130_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-06 22:30 | 20240906_2230_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-06 23:30 | 20240906_2330_2D5_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - When I launch marmot - When I export marmot results to "raoresults/results_20240906.zip" - When I export RefProg after redispatching to "generatedRefProgs/20240906-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" based on raoResults zip "raoresults/results_20240906.zip" - When I export networks with PRAs to "raoresults/networkWithPras_20240906.zip" based on raoResults zip "raoresults/results_20240906.zip" - When I export F711 for business date "20240906" based on raoResults zip "raoresults/results_20240906.zip" - - @slow @rao @20240910 - Scenario: US 93.3.19: 20240910 - Given network files are in folder "idcc/20240910-FID2-734-v1-10V1001C--00264T-to-10V1001C--00085T 1" - Given crac file is "idcc/20240910-FSC-ID2-CB-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given ics static file is "_10V1001C--00275O_CSA-COMRA-RDSTATIC-D_CORE-20240910-V001_.csv" - Given ics series file is "_10V1001C--00275O_CSA-COMRA-RDSERIES-D_CORE-20240910-V001_.csv" - Given ics gsk file is "_10V1001C--00275O_CSA-INDRA-GSK-D_D7-20240910-V001_.csv" - Given configuration file is "idcc/RAOParameters_minCost_megawatt_dc.json" - Given time-coupled RefProg file is "idcc/20240910-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" - Given time-coupled rao inputs for CORE are: - | Timestamp | Network | - | 2024-09-10 00:30 | 20240910_0030_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-10 01:30 | 20240910_0130_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-10 02:30 | 20240910_0230_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-10 03:30 | 20240910_0330_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-10 04:30 | 20240910_0430_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-10 05:30 | 20240910_0530_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-10 06:30 | 20240910_0630_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-10 07:30 | 20240910_0730_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-10 08:30 | 20240910_0830_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-10 09:30 | 20240910_0930_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-10 10:30 | 20240910_1030_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-10 11:30 | 20240910_1130_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-10 12:30 | 20240910_1230_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-10 13:30 | 20240910_1330_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-10 14:30 | 20240910_1430_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-10 15:30 | 20240910_1530_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-10 16:30 | 20240910_1630_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-10 17:30 | 20240910_1730_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-10 18:30 | 20240910_1830_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-10 19:30 | 20240910_1930_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-10 20:30 | 20240910_2030_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-10 21:30 | 20240910_2130_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-10 22:30 | 20240910_2230_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-10 23:30 | 20240910_2330_2D2_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - When I launch marmot - When I export marmot results to "raoresults/results_20240910.zip" - When I export RefProg after redispatching to "generatedRefProgs/20240910-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" based on raoResults zip "raoresults/results_20240910.zip" - When I export networks with PRAs to "raoresults/networkWithPras_20240910.zip" based on raoResults zip "raoresults/results_20240910.zip" - When I export F711 for business date "20240910" based on raoResults zip "raoresults/results_20240910.zip" - - @slow @rao @20240926 - Scenario: US 93.3.20: 20240926 - Given network files are in folder "idcc/20240926-FID2-734-v1-10V1001C--00264T-to-10V1001C--00085T 1" - Given crac file is "idcc/20240926-FSC-ID2-CB-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given ics static file is "_10V1001C--00275O_CSA-COMRA-RDSTATIC-D_CORE-20240926-V001_.csv" - Given ics series file is "_10V1001C--00275O_CSA-COMRA-RDSERIES-D_CORE-20240926-V001_.csv" - Given ics gsk file is "_10V1001C--00275O_CSA-INDRA-GSK-D_D7-20240926-V001_.csv" - Given configuration file is "idcc/RAOParameters_minCost_megawatt_dc.json" - Given time-coupled RefProg file is "idcc/20240926-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" - Given time-coupled rao inputs for CORE are: - | Timestamp | Network | - | 2024-09-26 00:30 | 20240926_0030_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-26 01:30 | 20240926_0130_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-26 02:30 | 20240926_0230_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-26 03:30 | 20240926_0330_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-26 04:30 | 20240926_0430_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-26 05:30 | 20240926_0530_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-26 06:30 | 20240926_0630_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-26 07:30 | 20240926_0730_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-26 08:30 | 20240926_0830_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-26 09:30 | 20240926_0930_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-26 10:30 | 20240926_1030_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-26 11:30 | 20240926_1130_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-26 12:30 | 20240926_1230_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-26 13:30 | 20240926_1330_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-26 14:30 | 20240926_1430_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-26 15:30 | 20240926_1530_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-26 16:30 | 20240926_1630_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-26 17:30 | 20240926_1730_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-26 18:30 | 20240926_1830_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-26 19:30 | 20240926_1930_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-26 20:30 | 20240926_2030_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-26 21:30 | 20240926_2130_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-26 22:30 | 20240926_2230_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-09-26 23:30 | 20240926_2330_2D4_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - When I launch marmot - When I export marmot results to "raoresults/results_20240926.zip" - When I export RefProg after redispatching to "generatedRefProgs/20240926-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" based on raoResults zip "raoresults/results_20240926.zip" - When I export networks with PRAs to "raoresults/networkWithPras_20240926.zip" based on raoResults zip "raoresults/results_20240926.zip" - When I export F711 for business date "20240926" based on raoResults zip "raoresults/results_20240926.zip" - - @slow @rao @20241013 - Scenario: US 93.3.21: 20241013 - Given network files are in folder "idcc/20241013-FID2-734-v1-10V1001C--00264T-to-10V1001C--00085T 1" - Given crac file is "idcc/20241013-FSC-ID2-CB-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given ics static file is "_10V1001C--00275O_CSA-COMRA-RDSTATIC-D_CORE-20241013-V001_.csv" - Given ics series file is "_10V1001C--00275O_CSA-COMRA-RDSERIES-D_CORE-20241013-V001_.csv" - Given ics gsk file is "_10V1001C--00275O_CSA-INDRA-GSK-D_D7-20241013-V001_.csv" - Given configuration file is "idcc/RAOParameters_minCost_megawatt_dc.json" - Given time-coupled RefProg file is "idcc/20241013-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" - Given time-coupled rao inputs for CORE are: - | Timestamp | Network | - | 2024-10-13 00:30 | 20241013_0030_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-13 01:30 | 20241013_0130_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-13 02:30 | 20241013_0230_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-13 03:30 | 20241013_0330_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-13 04:30 | 20241013_0430_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-13 05:30 | 20241013_0530_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-13 06:30 | 20241013_0630_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-13 07:30 | 20241013_0730_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-13 08:30 | 20241013_0830_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-13 09:30 | 20241013_0930_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-13 10:30 | 20241013_1030_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-13 11:30 | 20241013_1130_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-13 12:30 | 20241013_1230_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-13 13:30 | 20241013_1330_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-13 14:30 | 20241013_1430_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-13 15:30 | 20241013_1530_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-13 16:30 | 20241013_1630_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-13 17:30 | 20241013_1730_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-13 18:30 | 20241013_1830_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-13 19:30 | 20241013_1930_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-13 20:30 | 20241013_2030_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-13 21:30 | 20241013_2130_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-13 22:30 | 20241013_2230_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-13 23:30 | 20241013_2330_2D7_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - When I launch marmot - When I export marmot results to "raoresults/results_20241013.zip" - When I export RefProg after redispatching to "generatedRefProgs/20241013-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" based on raoResults zip "raoresults/results_20241013.zip" - When I export networks with PRAs to "raoresults/networkWithPras_20241013.zip" based on raoResults zip "raoresults/results_20241013.zip" - When I export F711 for business date "20241013" based on raoResults zip "raoresults/results_20241013.zip" - - @slow @rao @20241028 - Scenario: US 93.3.22: 20241028 - Given network files are in folder "idcc/20241028-FID2-734-v1-10V1001C--00264T-to-10V1001C--00085T 1" - Given crac file is "idcc/20241028-FSC-ID2-CB-v1-10V1001C--00264T-to-10XFR-RTE------Q.xml" - Given ics static file is "_10V1001C--00275O_CSA-COMRA-RDSTATIC-D_CORE-20241028-V001_.csv" - Given ics series file is "_10V1001C--00275O_CSA-COMRA-RDSERIES-D_CORE-20241028-V001_.csv" - Given ics gsk file is "_10V1001C--00275O_CSA-INDRA-GSK-D_D7-20241028-V001_.csv" - Given configuration file is "idcc/RAOParameters_minCost_megawatt_dc.json" - Given time-coupled RefProg file is "idcc/20241028-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" - Given time-coupled rao inputs for CORE are: - | Timestamp | Network | - | 2024-10-28 00:30 | 20241028_0030_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-28 01:30 | 20241028_0130_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-28 02:30 | 20241028_0230_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-28 03:30 | 20241028_0330_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-28 04:30 | 20241028_0430_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-28 05:30 | 20241028_0530_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-28 06:30 | 20241028_0630_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-28 07:30 | 20241028_0730_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-28 08:30 | 20241028_0830_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-28 09:30 | 20241028_0930_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-28 10:30 | 20241028_1030_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-28 11:30 | 20241028_1130_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-28 12:30 | 20241028_1230_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-28 13:30 | 20241028_1330_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-28 14:30 | 20241028_1430_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-28 15:30 | 20241028_1530_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-28 16:30 | 20241028_1630_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-28 17:30 | 20241028_1730_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-28 18:30 | 20241028_1830_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-28 19:30 | 20241028_1930_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-28 20:30 | 20241028_2030_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-28 21:30 | 20241028_2130_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-28 22:30 | 20241028_2230_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - | 2024-10-28 23:30 | 20241028_2330_2D1_UX2_FINIT_EXPORTGRIDMODEL_DC_CGM_10V1001C--00264T.uct | - When I launch marmot - When I export marmot results to "raoresults/results_20241028.zip" - When I export RefProg after redispatching to "generatedRefProgs/20241028-FID2-632-v2-10V1001C--00264T-to-10V1001C--00085T.xml" based on raoResults zip "raoresults/results_20241028.zip" - When I export networks with PRAs to "raoresults/networkWithPras_20241028.zip" based on raoResults zip "raoresults/results_20241028.zip" - When I export F711 for business date "20241028" based on raoResults zip "raoresults/results_20241028.zip" diff --git a/tests/src/test/resources/logback.xml b/tests/src/test/resources/logback.xml index 44af834f8c..a248fbc015 100644 --- a/tests/src/test/resources/logback.xml +++ b/tests/src/test/resources/logback.xml @@ -4,19 +4,12 @@ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - /tmp/RAO_LOGS/openrao.log - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - + From e9f6422eba188ebb59802da4efc10e8452984c81 Mon Sep 17 00:00:00 2001 From: Godelaine de Montmorillon Date: Mon, 13 Apr 2026 15:03:19 +0200 Subject: [PATCH 61/64] revert deleting cracs --- .../src/test/resources/crac/crac-0030.json | 81 +++++++++++++++++++ .../src/test/resources/crac/crac-0130.json | 81 +++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 data/ics-importer/src/test/resources/crac/crac-0030.json create mode 100644 data/ics-importer/src/test/resources/crac/crac-0130.json diff --git a/data/ics-importer/src/test/resources/crac/crac-0030.json b/data/ics-importer/src/test/resources/crac/crac-0030.json new file mode 100644 index 0000000000..9c211f799c --- /dev/null +++ b/data/ics-importer/src/test/resources/crac/crac-0030.json @@ -0,0 +1,81 @@ +{ + "type": "CRAC", + "version": "2.7", + "info": "Generated by PowSyBl OpenRAO https://powsybl.readthedocs.io/projects/openrao/", + "id": "crac-20250213", + "name": "crac-20250213", + "timestamp": "2025-02-13T00:30:00Z", + "instants": [ + { + "id": "preventive", + "kind": "PREVENTIVE" + }, + { + "id": "outage", + "kind": "OUTAGE" + }, + { + "id": "curative", + "kind": "CURATIVE" + } + ], + "networkElementsNamePerId": {}, + "flowCnecs": [ + { + "id": "cnecBeFrPreventive", + "name": "cnecBeFrPreventive", + "networkElementId": "BBE1AA1 FFR1AA1 1", + "operator": "FR", + "instant": "preventive", + "contingencyId": null, + "optimized": true, + "monitored": false, + "iMax": [ + NaN + ], + "nominalV": [ + 400.0 + ], + "thresholds": [ + { + "unit": "megawatt", + "min": -300.0, + "max": 300.0, + "side": 1 + }, + { + "unit": "megawatt", + "min": -300.0, + "max": 300.0, + "side": 2 + } + ] + } + ], + "pstRangeActions": [ + { + "id": "pstBeFr2", + "name": "pstBeFr2", + "operator": "BE", + "activationCost": 5.0, + "variationCosts": { + "up": 10.0, + "down": 10.0 + }, + "onInstantUsageRules": [ + { + "instant": "preventive", + "usageMethod": "available" + } + ], + "networkElementId": "BBE1AA1 FFR1AA1 2", + "ranges": [ + { + "min": -16, + "max": 16, + "rangeType": "absolute" + } + ] + } + ] +} \ No newline at end of file diff --git a/data/ics-importer/src/test/resources/crac/crac-0130.json b/data/ics-importer/src/test/resources/crac/crac-0130.json new file mode 100644 index 0000000000..b4b166104c --- /dev/null +++ b/data/ics-importer/src/test/resources/crac/crac-0130.json @@ -0,0 +1,81 @@ +{ + "type": "CRAC", + "version": "2.7", + "info": "Generated by PowSyBl OpenRAO https://powsybl.readthedocs.io/projects/openrao/", + "id": "crac-20250214", + "name": "crac-20250214", + "timestamp": "2025-02-13T01:30:00Z", + "instants": [ + { + "id": "preventive", + "kind": "PREVENTIVE" + }, + { + "id": "outage", + "kind": "OUTAGE" + }, + { + "id": "curative", + "kind": "CURATIVE" + } + ], + "networkElementsNamePerId": {}, + "flowCnecs": [ + { + "id": "cnecBeFrPreventive", + "name": "cnecBeFrPreventive", + "networkElementId": "BBE1AA1 FFR1AA1 1", + "operator": "FR", + "instant": "preventive", + "contingencyId": null, + "optimized": true, + "monitored": false, + "iMax": [ + NaN + ], + "nominalV": [ + 400.0 + ], + "thresholds": [ + { + "unit": "megawatt", + "min": -300.0, + "max": 300.0, + "side": 1 + }, + { + "unit": "megawatt", + "min": -300.0, + "max": 300.0, + "side": 2 + } + ] + } + ], + "pstRangeActions": [ + { + "id": "pstBeFr2", + "name": "pstBeFr2", + "operator": "BE", + "activationCost": 5.0, + "variationCosts": { + "up": 10.0, + "down": 10.0 + }, + "onInstantUsageRules": [ + { + "instant": "preventive", + "usageMethod": "available" + } + ], + "networkElementId": "BBE1AA1 FFR1AA1 2", + "ranges": [ + { + "min": -16, + "max": 16, + "rangeType": "absolute" + } + ] + } + ] +} \ No newline at end of file From a75403645a706f8ca08f828cf3d44174ab17c637 Mon Sep 17 00:00:00 2001 From: Godelaine Date: Mon, 13 Apr 2026 15:28:25 +0200 Subject: [PATCH 62/64] sonar Signed-off-by: Godelaine de Montmorillon --- .../main/java/com/powsybl/openrao/commons/TemporalDataImpl.java | 1 + .../com/powsybl/openrao/data/icsimporter/IcsDataImporter.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/commons/src/main/java/com/powsybl/openrao/commons/TemporalDataImpl.java b/commons/src/main/java/com/powsybl/openrao/commons/TemporalDataImpl.java index 168814f3dc..d25e12f1d3 100644 --- a/commons/src/main/java/com/powsybl/openrao/commons/TemporalDataImpl.java +++ b/commons/src/main/java/com/powsybl/openrao/commons/TemporalDataImpl.java @@ -67,6 +67,7 @@ public TemporalData mapMultiThreading(Function function, int parall return new TemporalDataImpl<>(result); } catch (InterruptedException | ExecutionException e) { + Thread.currentThread().interrupt(); throw new OpenRaoException(e); } finally { executor.shutdown(); diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java index b9c91c0d33..1ebd352730 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java @@ -369,7 +369,7 @@ private static boolean isShutDownRespected(CSVRecord staticRecord, Boolean shutD } private static boolean isLeadTimeAndLagTimeRespected(CSVRecord staticRecord, int countConsecutiveNullValues, Optional lagAndLead, OffsetDateTime currentDateTime) { - if (countConsecutiveNullValues < lagAndLead.get()) { + if (lagAndLead.isPresent() && countConsecutiveNullValues < lagAndLead.get()) { BUSINESS_WARNS.warn("Redispatching action {} is not imported (hour {}): lagTime + leadTime ({}) not respected. RA was OFF after shut down for only {} timestamps", staticRecord.get(0), currentDateTime.getHour(), lagAndLead.get(), countConsecutiveNullValues); return false; From 8a38a3efb86898a23ae0af36204087e06c9cfd53 Mon Sep 17 00:00:00 2001 From: Godelaine de Montmorillon Date: Thu, 16 Apr 2026 09:53:29 +0200 Subject: [PATCH 63/64] set PARALLELISM to 1 for Azure test Signed-off-by: Godelaine de Montmorillon --- .../java/com/powsybl/openrao/searchtreerao/marmot/Marmot.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3cdcfc83ac..12f09e2db6 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 @@ -88,7 +88,7 @@ public class Marmot implements TimeCoupledRaoProvider { private static final String MIP_SCENARIO = "MipScenario"; private static final String MIN_MARGIN_VIOLATION_EVALUATOR = "min-margin-violation-evaluator"; - private static final int PARALLELISM = 4; // TODO: configure as a parameter in MARMOT extension + private static final int PARALLELISM = 1; // TODO: configure as a parameter in MARMOT extension @Override public CompletableFuture run(TimeCoupledRaoInput timeCoupledRaoInput, RaoParameters raoParameters) { From 98f3083c56e463d510b31c0cbbbe403ec72de651 Mon Sep 17 00:00:00 2001 From: Peter Mitri Date: Fri, 17 Apr 2026 10:25:04 +0200 Subject: [PATCH 64/64] fix gradients check Signed-off-by: Peter Mitri --- .../com/powsybl/openrao/data/icsimporter/IcsDataImporter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java index 1ebd352730..0aed5a4b58 100644 --- a/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java +++ b/data/ics-importer/src/main/java/com/powsybl/openrao/data/icsimporter/IcsDataImporter.java @@ -401,7 +401,7 @@ private static boolean isStartUpRespected(CSVRecord staticRecord, Boolean startU private static boolean areGradientsRespected(CSVRecord staticRecord, double nextP0, double currentP0, double pMin, double nextPmin, double maxGradient, double minGradient, OffsetDateTime currentDateTime) { double diff = nextP0 - currentP0; - if (currentP0 >= pMin && nextP0 >= nextPmin && diff > maxGradient || diff < minGradient) { + if (currentP0 >= pMin && nextP0 >= nextPmin && (diff > maxGradient || diff < minGradient)) { BUSINESS_WARNS.warn( "Redispatching action {} is not imported (hour {}): does not respect power gradients : min/max/diff = {} / {} / {}", staticRecord.get(0), currentDateTime.getHour(), minGradient, maxGradient, diff