From 44bf8b8f949d7cf439542dd66dcc4beaf9ab7afc Mon Sep 17 00:00:00 2001 From: Lisrte Date: Thu, 12 Dec 2024 17:20:09 +0100 Subject: [PATCH 1/3] Remove duplicate from Curves timeSeries (#413) - Remove duplicates - Use LinkedHashMap for curves and fsv Signed-off-by: lisrte --- .../dynawo/it/DynawoSimulationTest.java | 2 +- .../dynawo/DynawoSimulationHandler.java | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/dynawo-integration-tests/src/test/java/com/powsybl/dynawo/it/DynawoSimulationTest.java b/dynawo-integration-tests/src/test/java/com/powsybl/dynawo/it/DynawoSimulationTest.java index 2281d2dbe..9d18241ed 100644 --- a/dynawo-integration-tests/src/test/java/com/powsybl/dynawo/it/DynawoSimulationTest.java +++ b/dynawo-integration-tests/src/test/java/com/powsybl/dynawo/it/DynawoSimulationTest.java @@ -72,7 +72,7 @@ void testIeee14() { assertEquals(27, result.getCurves().size()); DoubleTimeSeries ts1 = result.getCurve("_GEN____1_SM_generator_UStatorPu"); assertEquals("_GEN____1_SM_generator_UStatorPu", ts1.getMetadata().getName()); - assertEquals(585, ts1.toArray().length); + assertEquals(192, ts1.toArray().length); assertEquals(14, result.getFinalStateValues().size()); assertEquals(1.046227, result.getFinalStateValues().get("NETWORK__BUS___10_TN_Upu_value")); List timeLine = result.getTimeLine(); diff --git a/dynawo-simulation/src/main/java/com/powsybl/dynawo/DynawoSimulationHandler.java b/dynawo-simulation/src/main/java/com/powsybl/dynawo/DynawoSimulationHandler.java index a578dd973..abd1c71e8 100644 --- a/dynawo-simulation/src/main/java/com/powsybl/dynawo/DynawoSimulationHandler.java +++ b/dynawo-simulation/src/main/java/com/powsybl/dynawo/DynawoSimulationHandler.java @@ -27,10 +27,7 @@ import com.powsybl.dynawo.commons.timeline.XmlTimeLineParser; import com.powsybl.iidm.network.Network; import com.powsybl.iidm.serde.NetworkSerDe; -import com.powsybl.timeseries.DoubleTimeSeries; -import com.powsybl.timeseries.TimeSeries; -import com.powsybl.timeseries.TimeSeriesConstants; -import com.powsybl.timeseries.TimeSeriesCsvConfig; +import com.powsybl.timeseries.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -63,8 +60,8 @@ public final class DynawoSimulationHandler extends AbstractExecutionHandler timeline = new ArrayList<>(); - private final Map curves = new HashMap<>(); - private final Map fsv = new HashMap<>(); + private final Map curves = new LinkedHashMap<>(); + private final Map fsv = new LinkedHashMap<>(); private DynamicSimulationResult.Status status = DynamicSimulationResult.Status.SUCCESS; private String statusText = ""; @@ -186,7 +183,7 @@ private void setCurves(Path workingDir) { Path curvesPath = workingDir.resolve(CURVES_OUTPUT_PATH).resolve(CURVES_FILENAME); if (Files.exists(curvesPath)) { TimeSeries.parseCsv(curvesPath, new TimeSeriesCsvConfig(TimeSeriesConstants.DEFAULT_SEPARATOR, false, TimeSeries.TimeFormat.FRACTIONS_OF_SECOND)) - .values().forEach(l -> l.forEach(curve -> curves.put(curve.getMetadata().getName(), (DoubleTimeSeries) curve))); + .values().forEach(l -> l.forEach(curve -> curves.put(curve.getMetadata().getName(), sanitizeDoubleTimeSeries((DoubleTimeSeries) curve)))); } else { LOGGER.warn("Curves folder not found"); status = DynamicSimulationResult.Status.FAILURE; @@ -194,6 +191,14 @@ private void setCurves(Path workingDir) { } } + private DoubleTimeSeries sanitizeDoubleTimeSeries(DoubleTimeSeries series) { + Set times = new LinkedHashSet<>(); + double[] values = series.stream().filter(dp -> times.add(dp.getTime())).mapToDouble(DoublePoint::getValue).toArray(); + return TimeSeries.createDouble(series.getMetadata().getName(), + new IrregularTimeSeriesIndex(times.stream().mapToLong(l -> l).toArray()), + values); + } + private void setFinalStateValues(Path workingDir) { Path fsvPath = workingDir.resolve(FSV_OUTPUT_PATH).resolve(FSV_OUTPUT_FILENAME); if (Files.exists(fsvPath)) { From e226a1dfd74d0853bade27ffa9f77d99f801397a Mon Sep 17 00:00:00 2001 From: Lisrte Date: Fri, 13 Dec 2024 10:13:19 +0100 Subject: [PATCH 2/3] Add missing documentation (#410) * Add the different models suppliers definition. * Add missing event and dynamic model description. * Update of Dynaflow parameters documentation based on DynaFlowLauncherDocumentation * Reorder parameter documentation * Fix Tap changer doc * Update disconnect event documentation Signed-off-by: lisrte --- docs/dynamic_security_analysis/index.md | 2 +- docs/dynamic_simulation/configuration.md | 5 +- docs/dynamic_simulation/dynamic-models-dsl.md | 101 ---- .../dynamic-models-mapping.md | 506 ++++++++++++++++++ docs/dynamic_simulation/event-models-dsl.md | 22 - .../event-models-mapping.md | 154 ++++++ docs/dynamic_simulation/index.md | 6 +- ...les-dsl.md => output-variables-mapping.md} | 11 +- docs/load_flow/configuration.md | 61 ++- .../events/EventDisconnectionBuilder.java | 2 +- .../src/main/resources/models.json | 15 +- 11 files changed, 713 insertions(+), 172 deletions(-) delete mode 100644 docs/dynamic_simulation/dynamic-models-dsl.md create mode 100644 docs/dynamic_simulation/dynamic-models-mapping.md delete mode 100644 docs/dynamic_simulation/event-models-dsl.md create mode 100644 docs/dynamic_simulation/event-models-mapping.md rename docs/dynamic_simulation/{output-variables-dsl.md => output-variables-mapping.md} (61%) diff --git a/docs/dynamic_security_analysis/index.md b/docs/dynamic_security_analysis/index.md index 3db8e4e84..a36aad548 100644 --- a/docs/dynamic_security_analysis/index.md +++ b/docs/dynamic_security_analysis/index.md @@ -14,5 +14,5 @@ Read this [documentation page](https://dynawo.github.io/install/dynalgo) to lear ## Going further You may find an extensive documentation of the Dynawo project [here](https://github.com/dynawo/dynawo/releases/latest/download/DynawoDocumentation.pdf). -The dynamic security analysis use the same dynamic models supplier as the dynamic simulation implementation, documentation can be found [here](../dynamic_simulation/dynamic-models-dsl.md). +The dynamic security analysis uses the same dynamic models supplier as the dynamic simulation implementation, documentation can be found [here](../dynamic_simulation/dynamic-models-mapping). diff --git a/docs/dynamic_simulation/configuration.md b/docs/dynamic_simulation/configuration.md index d84827274..b884841cc 100644 --- a/docs/dynamic_simulation/configuration.md +++ b/docs/dynamic_simulation/configuration.md @@ -100,16 +100,16 @@ The default value is `1e-6`. **log.levelFilter** `log.levelFilter` defines the log level for Dynawo log. -The default value is `INFO`. The available `com.powsybl.dynawo.DynawoSimulationParameters.LogLevel` values are: - `DEBUG` - `INFO` - `WARN` - `ERROR` +The default value is `INFO`. + **log.specificLogs** `log.specificLogs` defines as a list the specifics logs to return besides the regular Dynawo log. -The default value is an empty list. The available `com.powsybl.dynawo.DynawoSimulationParameters.SpecificLog` values are: - `NETWORK` - `MODELER` @@ -117,6 +117,7 @@ The available `com.powsybl.dynawo.DynawoSimulationParameters.SpecificLog` values - `VARIABLES` - `EQUATIONS` +The default value is an empty list. **criteria.file** `criteria.file` defines the simulation criteria file. diff --git a/docs/dynamic_simulation/dynamic-models-dsl.md b/docs/dynamic_simulation/dynamic-models-dsl.md deleted file mode 100644 index c588245f6..000000000 --- a/docs/dynamic_simulation/dynamic-models-dsl.md +++ /dev/null @@ -1,101 +0,0 @@ -# Dynamic Models DSL - -The Dynamic Models DSL is a domain specific language written in groovy for the creation dynamic models used by Dynawo, most of them associated with a static equipment present in the network. -If some equipments are not configured, Dynawo would use a default model and set of parameters. - -## BlackBoxModel -All the models supported are `BlackBoxModel`. This kind of dynamic model have three attributes: -- `lib` refers to the dynamic model library used in Dynawo. -- `dynamicModelId` identifies the model. -- `parameterSetId` refers a set of parameters for this model in one of the network parameters file. - -## Equipment models -These models are `BlackBoxModel` matching an IIDM equipment (generator, load, ...). -To instantiate them with DSL, you have to use their model name as a keyword and define at least a `staticId` and a `parameterSetId`. -The `dynamicModelId` is optional and would be equal to the `staticId` if not set. - -**Example** -```groovy -GeneratorSynchronousThreeWindings { - staticId '' - dynamicModelId '' - parameterSetId '' -} -LoadAlphaBeta { - staticId '' - parameterSetId '' -} -``` - -## Automation system models -These models are `BlackBoxModel` of automation system without IIDM equipment. - -### OverloadManagementSystem -This model connect to IIDM line or two windings transformer. Besides `dynamicModelId` and `parameterSetId` it needs: -- one control branch -- one measurement branch (can be the controlled one) -- which side is monitored. - -**Example** -```groovy -OverloadManagementSystem { - dynamicModelId '' - parameterSetId '' - controlledBranch '' - iMeasurement '' - iMeasurementSide TwoSides.TWO -} -``` - -#### TODO add all automation systems - -## Supported models -Models are listed in [models.json](../../dynawo/src/main/resources/models.json). -The list is divided in categories each linked to a dedicated builder. -### Categories properties -* `defaultLib` : name of the default library -* `libs` : list of dynawo libraries supported for this category - -The list is statically loaded via [ModelConfigLoader](https://javadoc.io/doc/com.powsybl/powsybl-dynawo/latest/com/powsybl/dynawo/builders/ModelConfigLoader.html) services and thus can be extended. - -### Library properties -* `lib`: library name used in dynawo -* `alias`: name used in powsybl-dynawo instead of lib -* `properties`: dynamic model properties (synchronized, dangling, etc.) -* `internalModelPrefix`: used for dyd file creation -* `doc`: library documentation -* `minVersion`: Dynawo minimum version required -* `maxVersion`: Dynawo maximum version required -* `endCause`: explains the cause of the model ending at `maxVersion` - -## Dynamic model Builder List -Ultimately, all groovy scripts call dedicated builders that can be used directly by developers. -### Equipments -* BaseStaticVarCompensatorBuilder -* BaseShuntBuilder -* HvdcPBuilder -* HvdcVscBuilder -* BaseGeneratorBuilder -* SignalNGeneratorBuilder -* SynchronizedGeneratorBuilder -* SynchronousGeneratorBuilder -* WeccBuilder -* GridFormingConverterBuilder -* LineBuilder -* StandardBusBuilder -* InfiniteBusBuilder -* TransformerFixedRatioBuilder -* BaseLoadBuilder -* LoadOneTransformerBuilder -* LoadTwoTransformersBuilder -* LoadOneTransformerTapChangerBuilder -* LoadTwoTransformersTapChangersBuilder -### Automation Systems -* TapChangerAutomationSystemBuilder -* TapChangerBlockingAutomationSystemBuilder -* UnderVoltageAutomationSystemBuilder -* DynamicOverloadManagementSystemBuilder -* DynamicTwoLevelsOverloadManagementSystemBuilder -* PhaseShifterPAutomationSystemBuilder -* PhaseShifterIAutomationSystemBuilder -* PhaseShifterBlockingIAutomationSystemBuilder diff --git a/docs/dynamic_simulation/dynamic-models-mapping.md b/docs/dynamic_simulation/dynamic-models-mapping.md new file mode 100644 index 000000000..09d478fb3 --- /dev/null +++ b/docs/dynamic_simulation/dynamic-models-mapping.md @@ -0,0 +1,506 @@ +# Dynamic models mapping +Dynamic model mapping creates dynamic models used by Dynawo. +Most of the dynamic models are associated with a static equipment present in the network. +If some equipments are not configured, Dynawo would use a default model and set of parameters. + +## Implementation +Powsybl-Dynawo handles two methods of mapping: +- **Dynamic Models DSL**: a domain specific language written in groovy (used in iTools) +- **Dynawo Dynamic Models**: a JSON configuration file. + +**Note**: For Json configuration, all models should be written in an `models` array. +```json +{ + "models":[...] +} +``` +In the following examples the array will be omitted. + +## Dynamic Model +- All the models share three attributes: +- `modelName`: refers to the dynamic model library used in Dynawo (used as a keyword in Groovy script). +- `dynamicModelId`: identifies the model. +- `parameterSetId`: refers to a set of parameters for this model in the model parameters file. + +**Note**: In Json configuration file `parameterSetId` is replaced by the `group` attribute and can be customized with the `groupType` attribute taking one of the following value: +- `FIXED`: the `parameterSetId` equals the `group` (default value) +- `PREFIX`: the `parameterSetId` equals the `group` + `dynamicModelId` +- `SUFFIX`: the `parameterSetId` equals the `dynamicModelId` + `group` + +See examples below. + +### Equipment models +Dynamic models matching a network equipment (generator, load, ...). +With specific attribute: +- `staticId`: equipment id in the network. + +The `dynamicModelId` is optional for these models and will be equal to the `staticId` if not set. + +**Groovy script:** +```groovy +GeneratorSynchronousThreeWindings { + staticId 'GEN' + dynamicModelId 'BBM_GEN' + parameterSetId 'GSTW' +} +LoadAlphaBeta { + staticId 'LOAD' + parameterSetId 'LAB_LOAD' +} +``` +**Json configuration:** +```json +[ + { + "model":"GeneratorSynchronousThreeWindings", + "group": "GSTW", + "properties":[ + { + "name":"dynamicModelId", + "value":"LOAD", + "type":"STRING" + }, + { + "name":"staticId", + "value":"GEN", + "type":"STRING" + } + ] + }, + { + "model":"LoadAlphaBeta", + "group": "LAB", + "groupType": "PREFIX", + "properties":[ + { + "name":"staticId", + "value":"LOAD", + "type":"STRING" + } + ] + } +] +``` + +### Automation system models +Dynamic models representing an automation system without network representation. + +#### Overload Management System +Automation system which emits a specific order when the current on the monitored line exceeds a given threshold for a given duration. +With specific attributes: +- `controlledBranch` +- `iMeasurement` +- `iMeasurementSide` + +**Groovy script:** +```groovy +import com.powsybl.iidm.network.TwoSides + +OverloadManagementSystem { + dynamicModelId "OVERLOAD1" + parameterSetId "OMS" + controlledBranch "LINE1" + iMeasurement "TFO1" + iMeasurementSide TwoSides.ONE +} +``` +**Json configuration:** +```json +{ + "model":"OverloadManagementSystem", + "group": "OMS", + "properties":[ + { + "name": "dynamicModelId", + "value": "OVERLOAD1", + "type": "STRING" + }, + { + "name": "controlledBranch", + "value": "LINE1", + "type": "STRING" + }, + { + "name": "iMeasurement", + "value": "TFO1", + "type": "STRING" + }, + { + "name": "iMeasurementSide", + "value": "ONE", + "type": "TWO_SIDES" + } + ] +} +``` + +#### Two Level Overload Management System +Automation system which emits a specific order when the current on two monitored lines exceeds a given threshold for a given duration. +With specific attributes: +- `controlledBranch` +- `iMeasurement1` +- `iMeasurement1Side` +- `iMeasurement2` +- `iMeasurement2Side` + +**Groovy script:** +```groovy +import com.powsybl.iidm.network.TwoSides + +TwoLevelsOverloadManagementSystem { + dynamicModelId "OVERLOAD_TL_1" + parameterSetId "OMS_TL" + controlledBranch "LINE1" + iMeasurement1 "TFO1" + iMeasurement1Side TwoSides.TWO + iMeasurement1 "LINE2" + iMeasurement1Side TwoSides.ONE +} +``` +**Json configuration:** +```json +{ + "model":"TwoLevelsOverloadManagementSystem", + "group": "OMS_TL", + "properties":[ + { + "name": "dynamicModelId", + "value": "OVERLOAD_TL_1", + "type": "STRING" + }, + { + "name": "controlledBranch", + "value": "LINE1", + "type": "STRING" + }, + { + "name": "iMeasurement1", + "value": "TFO1", + "type": "STRING" + }, + { + "name": "iMeasurement1Side", + "value": "TWO", + "type": "TWO_SIDES" + }, + { + "name": "iMeasurement2", + "value": "LINE2", + "type": "STRING" + }, + { + "name": "iMeasurement2Side", + "value": "ONE", + "type": "TWO_SIDES" + } + ] +} +``` + +#### Under Voltage Automation System +Sends switch-off signal to the monitored generator when the voltage goes below a threshold. +With specific attribute: +- `generator`: generator static id + +**Groovy script:** +```groovy +UnderVoltage { + dynamicModelId "UV_GEN1" + parameterSetId "UV" + generator "GEN1" +} +``` +**Json configuration:** +```json +{ + "model":"UnderVoltage", + "group": "UV", + "properties":[ + { + "name": "dynamicModelId", + "value": "UV_GEN1", + "type": "STRING" + }, + { + "name": "generator", + "value": "GEN1", + "type": "STRING" + } + ] +} +``` + +#### Phase Shifter P +Phase-shifter which monitors a given active power. When the active power goes above a given threshold, taps will be changed in order to decrease it. When the active power goes below another threshold, the phase-shifter is automatically deactivated. +With specific attribute: +- `transformer`: transformer static id + +**Groovy script:** +```groovy +PhaseShifterP { + dynamicModelId "PSP" + parameterSetId "PS_PAR" + transformer "TFO1" +} +``` +**Json configuration:** +```json +{ + "model":"PhaseShifterP", + "group": "PS_PAR", + "properties":[ + { + "name": "dynamicModelId", + "value": "PSP", + "type": "STRING" + }, + { + "name": "transformer", + "value": "TFO1", + "type": "STRING" + } + ] +} +``` + +#### Phase Shifter I +Phase-shifter which monitors a given current. When the current goes above a given threshold, taps will be changed in order to decrease it. When the active power goes below another threshold, the phase-shifter is automatically deactivated. +With specific attribute: +- `transformer`: transformer static id + +**Groovy script:** +```groovy +PhaseShifterI { + dynamicModelId "PSI" + parameterSetId "PS_PAR" + transformer "TFO1" +} +``` +**Json configuration:** +```json +{ + "model":"PhaseShifterI", + "group": "PS_PAR", + "properties":[ + { + "name": "dynamicModelId", + "value": "PSI", + "type": "STRING" + }, + { + "name": "transformer", + "value": "TFO1", + "type": "STRING" + } + ] +} +``` + +#### Phase Shifter Blocking I +Blocks phase shifter I when the monitored intensity goes below a threshold. +With specific attributes: +- `phaseShifterId`: Phase shifter I dynamic model id + +**Groovy script:** +```groovy +import com.powsybl.dynawo.models.TransformerSide + +PhaseShifterBlockingI { + dynamicModelId "PSB" + parameterSetId "PSB_PAR" + phaseShifterId "PS" +} +``` +**Json configuration:** +```json +{ + "model":"PhaseShifterBlockingI", + "group": "PSB_PAR", + "properties":[ + { + "name": "dynamicModelId", + "value": "PSB", + "type": "STRING" + }, + { + "name": "phaseShifterId", + "value": "PS", + "type": "STRING" + } + ] +} +``` + +#### Tap Changer +Tap changer automation system added to a transformer. +With specific attributes: +- `staticId`: load mapped to a dynamic model with transformer +- `side`: transformer side where the tap changer is added, can be: + - `HIGH_VOLTAGE` + - `LOW_VOLTAGE` + - `NONE` (default) + +**Groovy script:** +```groovy +import com.powsybl.dynawo.models.TransformerSide + +TapChangerAutomaton { + dynamicModelId "TC" + parameterSetId "TC" + staticId "LOAD" + side TransformerSide.HIGH_VOLTAGE +} +``` +**Json configuration:** +```json +{ + "model":"TapChangerAutomaton", + "group": "TC", + "properties":[ + { + "name": "dynamicModelId", + "value": "TC", + "type": "STRING" + }, + { + "name": "staticId", + "value": "LOAD", + "type": "STRING" + }, + { + "name": "side", + "value": "HIGH_VOLTAGE", + "type": "STRING" + } + ] +} +``` + +#### Tap Changer Blocking Automaton +Blocks tap changers when one of the monitored voltages goes below a threshold. +With specific attributes: +- `transformers`: static ids handling three types of equipment: + - transformer + - load mapped to a dynamic model with transformer + - tap changer automation system (referenced by its dynamic model id) +- `uMeasurements`: up to five bus or busbar section ids, can be defined in two ways: + - a simple list of ids, one id for each measurement point + - a list of list of ids, each list containing a probable measurement point id, the first one found in the network will be used + +**Groovy script:** +```groovy +def measurementsTCB1 = ["S1VL2_BBS1", "S3VL1_B1"] +List[] measurementsTCB2 = [["OldId", "NGEN", "NHV1"], ["NHV1", "OldId"], ["NHV2"]] + +TapChangerBlockingAutomaton { + dynamicModelId "TCB1" + parameterSetId "TCB" + uMeasurements measurementsTCB1 + transformers "TWT", "LD1" +} + +TapChangerBlockingAutomaton { + dynamicModelId "TCB2" + parameterSetId "TCB" + uMeasurements measurementsTCB2 + transformers "NGEN_NHV1", "NHV2_NLOAD", "LOAD" +} +``` +**Json configuration:** +```json +[ + { + "model":"TapChangerBlockingAutomaton", + "group": "TCB", + "properties":[ + { + "name": "dynamicModelId", + "value": "TCB1", + "type": "STRING" + }, + { + "name": "transformers", + "values": ["TWT", "LD1"], + "type": "STRING" + }, + { + "name": "uMeasurements", + "values": ["S1VL2_BBS1", "S3VL1_B1"], + "type": "STRING" + } + ] + }, + { + "model":"TapChangerBlockingAutomaton", + "group": "TCB", + "properties":[ + { + "name": "dynamicModelId", + "value": "TCB2", + "type": "STRING" + }, + { + "name": "transformers", + "values": ["NGEN_NHV1", "NHV2_NLOAD", "LOAD"], + "type": "STRING" + }, + { + "name": "uMeasurements", + "arrays": [["OldId", "NGEN", "NHV1"], ["NHV1", "OldId"], ["NHV2"]], + "type": "STRING" + } + ] + } +] +``` + +## Supported models +Models are listed in [models.json](../../dynawo/src/main/resources/models.json). +The list is divided in categories each linked to a dedicated builder. +### Categories properties +* `defaultLib` : name of the default library +* `libs` : list of dynawo libraries supported for this category + +The list is statically loaded via [ModelConfigLoader](https://javadoc.io/doc/com.powsybl/powsybl-dynawo/latest/com/powsybl/dynawo/builders/ModelConfigLoader.html) services and thus can be extended. + +### Library properties +* `lib`: library name used in dynawo +* `alias`: name used in powsybl-dynawo instead of lib +* `properties`: dynamic model properties (synchronized, dangling, etc.) +* `internalModelPrefix`: used for dynamic model file creation +* `doc`: library documentation +* `minVersion`: Dynawo minimum version required +* `maxVersion`: Dynawo maximum version required +* `endCause`: explains the cause of the model ending at `maxVersion` + +## Dynamic model Builder List +Ultimately, all groovy scripts call dedicated builders that can be used directly by developers. +### Equipments +* BaseStaticVarCompensatorBuilder +* BaseShuntBuilder +* HvdcPBuilder +* HvdcVscBuilder +* BaseGeneratorBuilder +* SignalNGeneratorBuilder +* SynchronizedGeneratorBuilder +* SynchronousGeneratorBuilder +* WeccBuilder +* GridFormingConverterBuilder +* LineBuilder +* StandardBusBuilder +* InfiniteBusBuilder +* TransformerFixedRatioBuilder +* BaseLoadBuilder +* LoadOneTransformerBuilder +* LoadTwoTransformersBuilder +* LoadOneTransformerTapChangerBuilder +* LoadTwoTransformersTapChangersBuilder +### Automation Systems +* TapChangerAutomationSystemBuilder +* TapChangerBlockingAutomationSystemBuilder +* UnderVoltageAutomationSystemBuilder +* DynamicOverloadManagementSystemBuilder +* DynamicTwoLevelsOverloadManagementSystemBuilder +* PhaseShifterPAutomationSystemBuilder +* PhaseShifterIAutomationSystemBuilder +* PhaseShifterBlockingIAutomationSystemBuilder diff --git a/docs/dynamic_simulation/event-models-dsl.md b/docs/dynamic_simulation/event-models-dsl.md deleted file mode 100644 index cf4ec7557..000000000 --- a/docs/dynamic_simulation/event-models-dsl.md +++ /dev/null @@ -1,22 +0,0 @@ -# Event Models DSL - -The Event Models DSL is a domain specific language written in groovy for the simulation of events that occurs during the simulation. - -## Event Model -- All the models supported are `BlackBoxModel`. This kind of event model have three attributes: -- `lib` refers to the event model library used in Dynawo. -- `staticId` identifies the equipment affected by the event -- `startTime` defines when the event starts. - -## Disconnect -Use this event to disconnect a bus, branch, injection or an HVDC line. - -**Example** -```groovy -Disconnect { - staticId "GEN" - startTime 1 -} -``` - -### TODO add remaining events diff --git a/docs/dynamic_simulation/event-models-mapping.md b/docs/dynamic_simulation/event-models-mapping.md new file mode 100644 index 000000000..dc050804a --- /dev/null +++ b/docs/dynamic_simulation/event-models-mapping.md @@ -0,0 +1,154 @@ +# Event models mapping +Event models mapping allows the creation of events that occurs during the simulation. + +## Implementation +Powsybl-Dynawo handles two methods of mapping: +- **Event Models DSL**: a domain specific language written in groovy (used in iTools) +- **Dynawo Event Models**: a JSON configuration file. + +**Note**: For Json configuration, all models should be written in an `events` array. +```json +{ + "events":[...] +} +``` +In the following examples the array will be omitted. + +## Event Model +- All the models share three attributes: +- `modelName`: refers to the event model library used in Dynawo (used as a keyword in Groovy script). +- `staticId`: identifies the equipment affected by the event. +- `startTime`: defines when the event starts. + +### Disconnect +Disconnects a bus, a branch, an injection or an HVDC line. +With specific attributes: +- `disconnectOnly`: optional attribute that specifies the side to disconnect for Branch or HVDC (by default both sides are disconnected) + +**Groovy script:** +```groovy +import com.powsybl.iidm.network.TwoSides + +Disconnect { + staticId "LINE" + startTime 1 + disconnectOnly TwoSides.TWO +} +``` +**Json configuration:** +```json +{ + "model":"Disconnect", + "properties":[ + { + "name":"staticId", + "value":"GEN", + "type":"STRING" + }, + { + "name":"startTime", + "value":"1", + "type":"DOUBLE" + }, + { + "name": "disconnectOnly", + "value": "TWO", + "type": "TWO_SIDES" + } + ] +} +``` + +### Active Power Variation +Power variation on generator or load. +With specific attribute: +- `deltaP`: active power variation. + +**Groovy script:** +```groovy +Step { + staticId "LOAD" + startTime 2 + deltaP 0.2 +} +``` +**Json configuration:** +```json +{ + "model":"Step", + "properties":[ + { + "name":"staticId", + "value":"LOAD", + "type":"STRING" + }, + { + "name":"startTime", + "value":"2", + "type":"DOUBLE" + }, + { + "name":"deltaP", + "value":"0.2", + "type":"DOUBLE" + } + ] +} +``` + +### Node Fault +Node fault with configurable resistance, reactance and duration. +With specific attributes: +- `faultTime`: delta with `startTime` at which the event ends (must be > 0). +- `rPu`: r pu variation (must be >= 0). +- `xPu`: x pu variation (must be >= 0). + +**Groovy script:** +```groovy +NodeFault { + staticId "NGEN" + startTime 1 + faultTime 0.1 + rPu 0 + xPu 0.01 +} +``` +**Json configuration:** +```json +{ + "model":"NodeFault", + "properties":[ + { + "name":"staticId", + "value":"NGEN", + "type":"STRING" + }, + { + "name":"startTime", + "value":"1", + "type":"DOUBLE" + }, + { + "name":"faultTime", + "value":"0.1", + "type":"DOUBLE" + }, + { + "name":"rPu", + "value":"0", + "type":"DOUBLE" + }, + { + "name":"xPu", + "value":"0.01", + "type":"DOUBLE" + } + ] +} +``` + +## Event model builder list +Ultimately, all groovy scripts or Json configuration file call the dedicated builders that can be used directly by developers in order to create a custom `EventModelsSupplier`: +- EventDisconnectionBuilder +- EventActivePowerVariationBuilder +- NodeFaultEventBuilder \ No newline at end of file diff --git a/docs/dynamic_simulation/index.md b/docs/dynamic_simulation/index.md index 8bdd104b7..d6414553b 100644 --- a/docs/dynamic_simulation/index.md +++ b/docs/dynamic_simulation/index.md @@ -3,9 +3,9 @@ ```{toctree} :hidden: configuration.md -dynamic-models-dsl.md -event-models-dsl.md -output-variables-dsl.md +dynamic-models-mapping.md +event-models-mapping.md +output-variables-mapping.md ``` PowSyBl provides an implementation of the [DynamicSimulation API from powsybl-core](inv:powsyblcore:*:*#simulation/dynamic/index) with [Dynaωo](https://dynawo.github.io), a tool for long-term stability simulation. diff --git a/docs/dynamic_simulation/output-variables-dsl.md b/docs/dynamic_simulation/output-variables-mapping.md similarity index 61% rename from docs/dynamic_simulation/output-variables-dsl.md rename to docs/dynamic_simulation/output-variables-mapping.md index acd0bf178..15bab10dd 100644 --- a/docs/dynamic_simulation/output-variables-dsl.md +++ b/docs/dynamic_simulation/output-variables-mapping.md @@ -1,8 +1,8 @@ -# Output Variables DSL -The output variables domain specific language allow a user to configure the curves or final state values Dynawo will export at the end of the simulation. +# Output variable mapping +The output variable domain specific language allows a user to configure the curves or final state values Dynawo will export at the end of the simulation. This DSL defines the `curve` and `fsv`keywords. -The `curve` keyword combined with the `variable` field create a single curve for a dynamic model. One identifies a dynamic model by its ID, the same as the one used in the [Dynamic Models DSL](dynamic-models-dsl). The variable to plot is identified by its name. +The `curve` keyword combined with the `variable` field create a single curve for a dynamic model. The dynamic model is identified by its ID, the same as the one used in the [Dynamic Models DSL](dynamic-models-mapping). The variable to plot is identified by its name. ```groovy curve { dynamicModelId "dynamicId" @@ -26,7 +26,7 @@ curve { } ``` -If you want to plot several variables of the same dynamic model, you can use the `variables` field that permit limiting boilerplate code in the script. +If you want to plot several variables of the same model, you can use the `variables` field that limits boilerplate code in the script. ``` // This: curve { @@ -44,3 +44,6 @@ curve { variables "load_PPu", "load_QPu" } ``` + +## Output variables builder +Ultimately, all groovy scripts call the dedicated builder `DynawoOutputVariablesBuilder` that can be used directly by developers. \ No newline at end of file diff --git a/docs/load_flow/configuration.md b/docs/load_flow/configuration.md index c122cbed6..ce89c744d 100644 --- a/docs/load_flow/configuration.md +++ b/docs/load_flow/configuration.md @@ -38,22 +38,13 @@ The default value is `TRUE`. The default value is `45.0`. **activePowerCompensation** -`activePowerCompensation` defines which type of power compensation applies. -Available values **(TODO: describe them)**: +`activePowerCompensation` determines whether the generators participate in the active power balancing proportionally to: - `P` - `TARGET_P` - `PMAX` The default value is `PMAX`. -**settingPath** -`settingPath` is used to indicates the file which defines the model settings values. -The default value is `null`. - -**assemblingPath** -`assemblingPath` is used to indicates the file which defines the models' association. -The default value is `null`. - **startTime** `startTime` defines the simulation start time (in s). The default value is `0`. @@ -62,38 +53,46 @@ The default value is `0`. `stopTime` defines the simulation stop time (in s). The default value is `100`. -**precision** -**(TODO: description)** -The default value is `Nan`. - -**chosenOutputs** -**(TODO: description)** -Available values **(TODO: describe them)**: -- `STEADYSTATE` -- `LOSTEQ` -- `TIMELINE` -- `CONSTRAINTS` - -The default value is a list of all of them. - (timeStepDef)= **timeStep** `timeStep` defines the maximum time solver step value (in s). The default value is `10`. -**mergeLoads** -`mergeLoads` indicates if loads connected to the same bus are merged. -The default value is `TRUE`. +**chosenOutputs** +`chosenOutputs` defines which outputs DynaFlow will produce +Available values: +- `STEADYSTATE`: steady-state of the network +- `LOSTEQ`: lost equipments +- `TIMELINE`: simulation event timeline +- `CONSTRAINTS` + +The default value is a list of all values. **startingPointMode** -**(TODO: description)** -Available values **(TODO: describe them)**: -- `WARM` -- `FLAT` +`startingPointMode` indicates the starting point values considered in the simulation +Available values: +- `WARM`: starting point values for voltage, phase and injections are the ones in the network. +- `FLAT`: starting point values are the nominal value for bus voltages and the set points values for injections. The default value is `WARM`. +**precision** +`precision` defines the real number precision +The default value is `NaN`. + +**assemblingPath** +`assemblingPath` indicates the file which defines the models' association. +The default value is `null`. + +**settingPath** +`settingPath` indicates the file which defines the model settings values. +The default value is `null`. + +**mergeLoads** +`mergeLoads` indicates if loads connected to the same bus are merged. +The default value is `TRUE`. + ## Generic parameters Furthermore, DynaFlow only supports `useReactiveLimits` generic parameter, the other parameters are ignored. You may have a description of these parameters [here](inv:powsyblcore:*:*:#loadflow-generic-parameters). diff --git a/dynawo-simulation/src/main/java/com/powsybl/dynawo/models/events/EventDisconnectionBuilder.java b/dynawo-simulation/src/main/java/com/powsybl/dynawo/models/events/EventDisconnectionBuilder.java index 590243efe..e1ae2b6f1 100644 --- a/dynawo-simulation/src/main/java/com/powsybl/dynawo/models/events/EventDisconnectionBuilder.java +++ b/dynawo-simulation/src/main/java/com/powsybl/dynawo/models/events/EventDisconnectionBuilder.java @@ -20,7 +20,7 @@ */ public class EventDisconnectionBuilder extends AbstractEventModelBuilder, EventDisconnectionBuilder> { - private static final EventModelInfo MODEL_INFO = new EventModelInfo("Disconnect", "Disconnect network equipment (bus, injection, branch or hvdc)"); + private static final EventModelInfo MODEL_INFO = new EventModelInfo("Disconnect", "Disconnects a bus, a branch, an injection or an HVDC line"); private enum DisconnectionType { BUS, diff --git a/dynawo-simulation/src/main/resources/models.json b/dynawo-simulation/src/main/resources/models.json index fefbd76cc..831501351 100644 --- a/dynawo-simulation/src/main/resources/models.json +++ b/dynawo-simulation/src/main/resources/models.json @@ -687,7 +687,7 @@ { "lib": "CurrentLimitAutomaton", "alias": "OverloadManagementSystem", - "doc": "Automation system which emits a specific order when the current on the monitored line goes above a given threshold during some time" + "doc": "Automation system which emits a specific order when the current on the monitored line exceeds a given threshold for a given duration" } ] }, @@ -697,7 +697,7 @@ { "lib": "CurrentLimitAutomatonTwoLevels", "alias": "TwoLevelsOverloadManagementSystem", - "doc": "Automation system which emits a specific order when the current on two monitored lines goes above a given threshold during some time" + "doc": "Automation system which emits a specific order when the current on two monitored lines exceeds a given threshold for a given duration" } ] }, @@ -706,7 +706,7 @@ "libs": [ { "lib": "PhaseShifterI", - "doc": "Phase-shifter which monitors a given current. When the current goes above a given threshold max, taps will be changed in order to decrease it. When the active power goes below another threshold stop, the phase-shifter is automatically deactivated" + "doc": "Phase-shifter which monitors a given current. When the current goes above a given threshold, taps will be changed in order to decrease it. When the active power goes below another threshold, the phase-shifter is automatically deactivated" } ] }, @@ -715,7 +715,7 @@ "libs": [ { "lib": "PhaseShifterP", - "doc": "Phase-shifter which monitors a given active power. When the active power goes above a given threshold max, taps will be changed in order to decrease it. When the active power goes below another threshold stop, the phase-shifter is automatically deactivated" + "doc": "Phase-shifter which monitors a given active power. When the active power goes above a given threshold, taps will be changed in order to decrease it. When the active power goes below another threshold, the phase-shifter is automatically deactivated" } ] }, @@ -723,7 +723,8 @@ "defaultLib": "PhaseShifterBlockingI", "libs": [ { - "lib": "PhaseShifterBlockingI" + "lib": "PhaseShifterBlockingI", + "doc": "Blocks phase shifter I when the monitored intensity goes below a threshold" } ] }, @@ -741,7 +742,7 @@ "libs": [ { "lib": "TapChangerBlockingAutomaton", - "doc": "Block tap-changers when one of the monitored voltages goes below a threshold" + "doc": "Blocks tap changers when one of the monitored voltages goes below a threshold" } ] }, @@ -751,7 +752,7 @@ { "lib": "UnderVoltageAutomaton", "alias": "UnderVoltage", - "doc": "Send switch-off signal to the monitored generator when the voltage goes below a threshold" + "doc": "Sends switch-off signal to the monitored generator when the voltage goes below a threshold" } ] } From c16c095454a4b0ea4432c914eb7f227b74ba5e68 Mon Sep 17 00:00:00 2001 From: Lisrte Date: Fri, 13 Dec 2024 11:24:49 +0100 Subject: [PATCH 3/3] Handle sided contingencies in DynaFlow and Dynawo SA (#412) * Handle sided contingencies in DynaFlow and Dynawo SA * Add ContingencyEventModelsTest Signed-off-by: lisrte --- .../DynaFlowSecurityAnalysisHandler.java | 21 +++- .../com/powsybl/dynaflow/DynaflowReports.java | 7 ++ .../dynawo/it/DynawoSecurityAnalysisTest.java | 6 +- .../convergence/results.json | 3 +- .../divergence/results.json | 3 +- .../failed-criteria/results.json | 3 +- .../ContingencyEventModelsFactory.java | 104 ++++++++++++++++++ .../DynamicSecurityAnalysisReports.java | 8 ++ .../security/SecurityAnalysisContext.java | 45 +------- .../security/ContingencyEventModelsTest.java | 79 +++++++++++++ 10 files changed, 228 insertions(+), 51 deletions(-) create mode 100644 dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/ContingencyEventModelsFactory.java create mode 100644 dynawo-security-analysis/src/test/java/com/powsybl/dynawo/security/ContingencyEventModelsTest.java diff --git a/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowSecurityAnalysisHandler.java b/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowSecurityAnalysisHandler.java index 8bb3a2d25..e7fbf2f5e 100644 --- a/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowSecurityAnalysisHandler.java +++ b/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowSecurityAnalysisHandler.java @@ -16,7 +16,9 @@ import com.powsybl.computation.CommandExecution; import com.powsybl.computation.ExecutionReport; import com.powsybl.contingency.Contingency; +import com.powsybl.contingency.SidedContingencyElement; import com.powsybl.contingency.contingency.list.ContingencyList; +import com.powsybl.contingency.contingency.list.DefaultContingencyList; import com.powsybl.contingency.json.ContingencyJsonModule; import com.powsybl.dynaflow.json.DynaFlowConfigSerializer; import com.powsybl.dynaflow.results.ContingencyResultsUtils; @@ -33,6 +35,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.List; +import java.util.function.Predicate; import static com.powsybl.dynaflow.DynaFlowConstants.CONFIG_FILENAME; import static com.powsybl.dynaflow.SecurityAnalysisConstants.CONTINGENCIES_FILENAME; @@ -87,16 +90,30 @@ public SecurityAnalysisReport after(Path workingDir, ExecutionReport report) thr return new SecurityAnalysisReport(createSecurityAnalysisResult(network, violationFilter, workingDir, contingencies)); } - private static void writeContingencies(List contingencies, Path workingDir) throws IOException { + private void writeContingencies(List contingencies, Path workingDir) throws IOException { try (OutputStream os = Files.newOutputStream(workingDir.resolve(CONTINGENCIES_FILENAME))) { ObjectMapper mapper = JsonUtil.createObjectMapper(); ContingencyJsonModule module = new ContingencyJsonModule(); mapper.registerModule(module); ObjectWriter writer = mapper.writerWithDefaultPrettyPrinter(); - writer.writeValue(os, ContingencyList.of(contingencies.toArray(Contingency[]::new))); + writer.writeValue(os, buildContingencyList(contingencies)); } } + private ContingencyList buildContingencyList(List contingencies) { + return new DefaultContingencyList("", contingencies.stream().filter(nonSidedContingency()).toList()); + } + + private Predicate nonSidedContingency() { + return c -> { + if (c instanceof SidedContingencyElement sidedC && sidedC.getVoltageLevelId() != null) { + DynaflowReports.createSidedContingencyReportNode(reportNode, c.getId()); + return false; + } + return true; + }; + } + private static void writeParameters(SecurityAnalysisParameters securityAnalysisParameters, Path workingDir) throws IOException { // TODO(Luma) Take into account also Security Analysis parameters LoadFlowParameters loadFlowParameters = securityAnalysisParameters.getLoadFlowParameters(); diff --git a/dynaflow/src/main/java/com/powsybl/dynaflow/DynaflowReports.java b/dynaflow/src/main/java/com/powsybl/dynaflow/DynaflowReports.java index 8e5ec5bfb..de88b142f 100644 --- a/dynaflow/src/main/java/com/powsybl/dynaflow/DynaflowReports.java +++ b/dynaflow/src/main/java/com/powsybl/dynaflow/DynaflowReports.java @@ -37,4 +37,11 @@ public static ReportNode createContingenciesTimelineReportNode(ReportNode report .withUntypedValue("contingencyId", contingencyId) .add(); } + + public static void createSidedContingencyReportNode(ReportNode reportNode, String contingencyId) { + reportNode.newReportNode() + .withMessageTemplate("saContingency", "Contingency '${contingencyId}' has a voltageId information and cannot be handle by DynaFlow, the contingency will be skipped") + .withUntypedValue("contingencyId", contingencyId) + .add(); + } } diff --git a/dynawo-integration-tests/src/test/java/com/powsybl/dynawo/it/DynawoSecurityAnalysisTest.java b/dynawo-integration-tests/src/test/java/com/powsybl/dynawo/it/DynawoSecurityAnalysisTest.java index 48d24286a..60f936f72 100644 --- a/dynawo-integration-tests/src/test/java/com/powsybl/dynawo/it/DynawoSecurityAnalysisTest.java +++ b/dynawo-integration-tests/src/test/java/com/powsybl/dynawo/it/DynawoSecurityAnalysisTest.java @@ -107,15 +107,15 @@ void testIeee14DSA(String criteriaPath, List contingencies, String private static Stream provideSimulationParameter() { return Stream.of( Arguments.of("/ieee14/dynamic-security-analysis/convergence/criteria.crt", - List.of(Contingency.line("_BUS____1-BUS____5-1_AC"), + List.of(Contingency.line("_BUS____1-BUS____5-1_AC", "_BUS____5_VL"), Contingency.generator("_GEN____2_SM")), "/ieee14/dynamic-security-analysis/convergence/results.json"), Arguments.of("/ieee14/dynamic-security-analysis/failed-criteria/criteria.crt", - List.of(Contingency.line("_BUS____1-BUS____5-1_AC")), + List.of(Contingency.line("_BUS____1-BUS____5-1_AC", "_BUS____5_VL")), "/ieee14/dynamic-security-analysis/failed-criteria/results.json"), Arguments.of("/ieee14/dynamic-security-analysis/divergence/criteria.crt", List.of(Contingency.builder("Disconnect") - .addLine("_BUS____1-BUS____5-1_AC") + .addLine("_BUS____1-BUS____5-1_AC", "_BUS____5_VL") .addGenerator("_GEN____2_SM") .addBus("_BUS____1_TN") .build()), diff --git a/dynawo-integration-tests/src/test/resources/ieee14/dynamic-security-analysis/convergence/results.json b/dynawo-integration-tests/src/test/resources/ieee14/dynamic-security-analysis/convergence/results.json index 55ea04f14..48acfae24 100644 --- a/dynawo-integration-tests/src/test/resources/ieee14/dynamic-security-analysis/convergence/results.json +++ b/dynawo-integration-tests/src/test/resources/ieee14/dynamic-security-analysis/convergence/results.json @@ -26,7 +26,8 @@ "id" : "_BUS____1-BUS____5-1_AC", "elements" : [ { "id" : "_BUS____1-BUS____5-1_AC", - "type" : "LINE" + "type" : "LINE", + "voltageLevelId" : "_BUS____5_VL" } ] }, "status" : "CONVERGED", diff --git a/dynawo-integration-tests/src/test/resources/ieee14/dynamic-security-analysis/divergence/results.json b/dynawo-integration-tests/src/test/resources/ieee14/dynamic-security-analysis/divergence/results.json index 2c3b56212..477d08d3f 100644 --- a/dynawo-integration-tests/src/test/resources/ieee14/dynamic-security-analysis/divergence/results.json +++ b/dynawo-integration-tests/src/test/resources/ieee14/dynamic-security-analysis/divergence/results.json @@ -26,7 +26,8 @@ "id" : "Disconnect", "elements" : [ { "id" : "_BUS____1-BUS____5-1_AC", - "type" : "LINE" + "type" : "LINE", + "voltageLevelId" : "_BUS____5_VL" }, { "id" : "_GEN____2_SM", "type" : "GENERATOR" diff --git a/dynawo-integration-tests/src/test/resources/ieee14/dynamic-security-analysis/failed-criteria/results.json b/dynawo-integration-tests/src/test/resources/ieee14/dynamic-security-analysis/failed-criteria/results.json index 309a291b2..c02972c4f 100644 --- a/dynawo-integration-tests/src/test/resources/ieee14/dynamic-security-analysis/failed-criteria/results.json +++ b/dynawo-integration-tests/src/test/resources/ieee14/dynamic-security-analysis/failed-criteria/results.json @@ -26,7 +26,8 @@ "id" : "_BUS____1-BUS____5-1_AC", "elements" : [ { "id" : "_BUS____1-BUS____5-1_AC", - "type" : "LINE" + "type" : "LINE", + "voltageLevelId" : "_BUS____5_VL" } ] }, "status" : "FAILED", diff --git a/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/ContingencyEventModelsFactory.java b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/ContingencyEventModelsFactory.java new file mode 100644 index 000000000..33a1ef451 --- /dev/null +++ b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/ContingencyEventModelsFactory.java @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dynawo.security; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.contingency.Contingency; +import com.powsybl.contingency.ContingencyElement; +import com.powsybl.contingency.SidedContingencyElement; +import com.powsybl.dynawo.DynawoSimulationContext; +import com.powsybl.dynawo.models.BlackBoxModel; +import com.powsybl.dynawo.models.events.ContextDependentEvent; +import com.powsybl.dynawo.models.events.EventDisconnectionBuilder; +import com.powsybl.dynawo.models.macroconnections.MacroConnect; +import com.powsybl.dynawo.models.macroconnections.MacroConnectionsAdder; +import com.powsybl.dynawo.models.macroconnections.MacroConnector; +import com.powsybl.dynawo.parameters.ParametersSet; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.TwoSides; + +import java.util.*; + +/** + * @author Laurent Issertial + */ +public final class ContingencyEventModelsFactory { + + public static List createFrom(List contingencies, DynawoSimulationContext context, + MacroConnectionsAdder macroConnectionsAdder, + double contingenciesStartTime, + ReportNode reportNode) { + return contingencies.stream() + .map(c -> createFrom(c, context, macroConnectionsAdder, contingenciesStartTime, reportNode)) + .filter(Objects::nonNull) + .toList(); + } + + public static ContingencyEventModels createFrom(Contingency contingency, DynawoSimulationContext context, + MacroConnectionsAdder macroConnectionsAdder, + double contingenciesStartTime, + ReportNode reportNode) { + List eventModels = createContingencyEventModelList(contingency, context, contingenciesStartTime, reportNode); + if (eventModels.isEmpty()) { + return null; + } + Map macroConnectorsMap = new HashMap<>(); + List macroConnectList = new ArrayList<>(); + List eventParameters = new ArrayList<>(eventModels.size()); + // Set Contingencies connections and parameters + macroConnectionsAdder.setMacroConnectorAdder(macroConnectorsMap::computeIfAbsent); + macroConnectionsAdder.setMacroConnectAdder(macroConnectList::add); + eventModels.forEach(em -> { + em.createMacroConnections(macroConnectionsAdder); + em.createDynamicModelParameters(context, eventParameters::add); + }); + return new ContingencyEventModels(contingency, eventModels, macroConnectorsMap, macroConnectList, eventParameters); + } + + private static List createContingencyEventModelList(Contingency contingency, + DynawoSimulationContext context, + double contingenciesStartTime, + ReportNode reportNode) { + return contingency.getElements().stream() + .map(ce -> createContingencyEventModel(ce, context, contingenciesStartTime, reportNode)) + .filter(Objects::nonNull) + .toList(); + } + + private static BlackBoxModel createContingencyEventModel(ContingencyElement element, + DynawoSimulationContext context, + double contingenciesStartTime, + ReportNode reportNode) { + Network network = context.getNetwork(); + EventDisconnectionBuilder builder = EventDisconnectionBuilder.of(network) + .staticId(element.getId()) + .startTime(contingenciesStartTime); + if (element instanceof SidedContingencyElement sidedElement && sidedElement.getVoltageLevelId() != null) { + TwoSides side = SidedContingencyElement.getContingencySide(network, sidedElement); + if (side != null) { + builder.disconnectOnly(side); + } else { + DynamicSecurityAnalysisReports.createContingencyVoltageIdNotFoundReportNode(reportNode, + sidedElement.getId(), sidedElement.getVoltageLevelId()); + return null; + } + } + BlackBoxModel bbm = builder.build(); + if (bbm == null) { + throw new PowsyblException("Contingency element " + element.getType() + " not supported"); + } + if (bbm instanceof ContextDependentEvent cde) { + cde.setEquipmentHasDynamicModel(context); + } + return bbm; + } + + private ContingencyEventModelsFactory() { + } +} diff --git a/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/DynamicSecurityAnalysisReports.java b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/DynamicSecurityAnalysisReports.java index 591141ef3..ed00ca1da 100644 --- a/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/DynamicSecurityAnalysisReports.java +++ b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/DynamicSecurityAnalysisReports.java @@ -23,4 +23,12 @@ public static ReportNode createDynamicSecurityAnalysisReportNode(ReportNode repo .withUntypedValue("networkId", networkId) .add(); } + + public static ReportNode createContingencyVoltageIdNotFoundReportNode(ReportNode reportNode, String contingencyId, String voltageLevelId) { + return reportNode.newReportNode() + .withMessageTemplate("contingencyVlIdNotFound", "Voltage id '${voltageLevelId}' of contingency '${contingencyId}' not found, contingency will be skipped") + .withUntypedValue("voltageLevelId", voltageLevelId) + .withUntypedValue("contingencyId", contingencyId) + .add(); + } } diff --git a/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/SecurityAnalysisContext.java b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/SecurityAnalysisContext.java index 35ba0a587..ffb0ea57c 100644 --- a/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/SecurityAnalysisContext.java +++ b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/SecurityAnalysisContext.java @@ -7,25 +7,17 @@ */ package com.powsybl.dynawo.security; -import com.powsybl.commons.PowsyblException; import com.powsybl.commons.report.ReportNode; import com.powsybl.contingency.Contingency; -import com.powsybl.contingency.ContingencyElement; import com.powsybl.dynawo.DynawoSimulationContext; import com.powsybl.dynawo.DynawoSimulationParameters; import com.powsybl.dynawo.commons.DynawoConstants; import com.powsybl.dynawo.commons.DynawoVersion; import com.powsybl.dynawo.models.BlackBoxModel; -import com.powsybl.dynawo.models.events.ContextDependentEvent; -import com.powsybl.dynawo.models.events.EventDisconnectionBuilder; -import com.powsybl.dynawo.models.macroconnections.MacroConnect; -import com.powsybl.dynawo.models.macroconnections.MacroConnector; -import com.powsybl.dynawo.parameters.ParametersSet; -import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.*; import com.powsybl.security.dynamic.DynamicSecurityAnalysisParameters; import java.util.*; -import java.util.stream.Collectors; /** * @author Laurent Issertial @@ -53,40 +45,7 @@ public SecurityAnalysisContext(Network network, String workingVariantId, parameters.getDynamicSimulationParameters(), dynawoSimulationParameters, currentVersion, ReportNode.NO_OP); double contingenciesStartTime = parameters.getDynamicContingenciesParameters().getContingenciesStartTime(); this.contingencies = contingencies; - this.contingencyEventModels = contingencies.stream() - .map(c -> { - List contEventModels = c.getElements().stream() - .map(ce -> { - BlackBoxModel bbm = this.createContingencyEventModel(ce, contingenciesStartTime); - if (bbm instanceof ContextDependentEvent cde) { - cde.setEquipmentHasDynamicModel(this); - } - return bbm; - }) - .collect(Collectors.toList()); - Map macroConnectorsMap = new HashMap<>(); - List macroConnects = new ArrayList<>(); - List parametersSets = new ArrayList<>(contEventModels.size()); - macroConnectionsAdder.setMacroConnectorAdder(macroConnectorsMap::computeIfAbsent); - macroConnectionsAdder.setMacroConnectAdder(macroConnects::add); - for (BlackBoxModel bbm : contEventModels) { - bbm.createMacroConnections(macroConnectionsAdder); - bbm.createDynamicModelParameters(this, parametersSets::add); - } - return new ContingencyEventModels(c, contEventModels, macroConnectorsMap, macroConnects, parametersSets); - }) - .collect(Collectors.toList()); - } - - private BlackBoxModel createContingencyEventModel(ContingencyElement element, double contingenciesStartTime) { - BlackBoxModel bbm = EventDisconnectionBuilder.of(network) - .staticId(element.getId()) - .startTime(contingenciesStartTime) - .build(); - if (bbm == null) { - throw new PowsyblException("Contingency element " + element.getType() + " not supported"); - } - return bbm; + this.contingencyEventModels = ContingencyEventModelsFactory.createFrom(contingencies, this, macroConnectionsAdder, contingenciesStartTime, ReportNode.NO_OP); } public List getContingencies() { diff --git a/dynawo-security-analysis/src/test/java/com/powsybl/dynawo/security/ContingencyEventModelsTest.java b/dynawo-security-analysis/src/test/java/com/powsybl/dynawo/security/ContingencyEventModelsTest.java new file mode 100644 index 000000000..d046092b8 --- /dev/null +++ b/dynawo-security-analysis/src/test/java/com/powsybl/dynawo/security/ContingencyEventModelsTest.java @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dynawo.security; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.contingency.Contingency; +import com.powsybl.dynamicsimulation.DynamicSimulationParameters; +import com.powsybl.dynawo.DynawoSimulationContext; +import com.powsybl.dynawo.DynawoSimulationParameters; +import com.powsybl.dynawo.models.BlackBoxModel; +import com.powsybl.dynawo.models.generators.BaseGeneratorBuilder; +import com.powsybl.dynawo.models.macroconnections.MacroConnectionsAdder; +import com.powsybl.dynawo.models.macroconnections.MacroConnector; +import com.powsybl.dynawo.parameters.ParametersSet; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; +import org.junit.jupiter.api.Test; + +import java.util.*; + +import static com.powsybl.iidm.network.test.EurostagTutorialExample1Factory.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author Laurent Issertial {@literal } + */ +class ContingencyEventModelsTest { + + @Test + void test() { + Network network = EurostagTutorialExample1Factory.create(); + List dynamicModels = List.of( + BaseGeneratorBuilder.of(network) + .staticId("GEN") + .parameterSetId("gen") + .build()); + DynawoSimulationContext context = setupDynawoContext(network, dynamicModels); + MacroConnectionsAdder macroConnectionsAdder = new MacroConnectionsAdder(context::getDynamicModel, + context::getPureDynamicModel, + new ArrayList<>()::add, + new HashMap()::computeIfAbsent, + ReportNode.NO_OP); + List contingencies = List.of( + Contingency.load("LOAD"), + Contingency.generator("GEN"), + Contingency.line(NHV1_NHV2_1, VLHV1), + Contingency.branch(NHV1_NHV2_2, "WRONG_ID")); + + List contingencyEvents = ContingencyEventModelsFactory.createFrom(contingencies, context, + macroConnectionsAdder, 2, ReportNode.NO_OP); + assertThat(contingencyEvents).hasSize(3); + assertThat(contingencyEvents.get(0).eventModels()) + .hasSize(1) + .map(BlackBoxModel::getLib).containsExactly("EventConnectedStatus"); + assertThat(contingencyEvents.get(1).eventModels()) + .hasSize(1) + .map(BlackBoxModel::getLib).containsExactly("EventSetPointBoolean"); + assertThat(contingencyEvents.get(2).eventModels()) + .hasSize(1) + .map(BlackBoxModel::getLib).containsExactly("EventQuadripoleDisconnection"); + ParametersSet parametersSet = contingencyEvents.get(2).eventParameters().get(0); + assertTrue(parametersSet.getBool("event_disconnectOrigin")); + assertFalse(parametersSet.getBool("event_disconnectExtremity")); + } + + private DynawoSimulationContext setupDynawoContext(Network network, List dynamicModels) { + DynamicSimulationParameters parameters = DynamicSimulationParameters.load(); + DynawoSimulationParameters dynawoParameters = DynawoSimulationParameters.load(); + return new DynawoSimulationContext(network, network.getVariantManager().getWorkingVariantId(), dynamicModels, + Collections.emptyList(), Collections.emptyList(), parameters, dynawoParameters); + } +}