diff --git a/.gitignore b/.gitignore index 5eb70b2e24b..9e8763bdadb 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,3 @@ powsybl.log # Generated readthedocs pages build-docs/ - diff --git a/docs/grid_exchange_formats/cgmes/examples.md b/docs/grid_exchange_formats/cgmes/examples.md index 4eb1249921d..04f5ac6faff 100644 --- a/docs/grid_exchange_formats/cgmes/examples.md +++ b/docs/grid_exchange_formats/cgmes/examples.md @@ -1,3 +1,3 @@ # Examples -Have a look to the [CGMES sample files](https://www.entsoe.eu/Documents/CIM_documents/Grid_Model_CIM/TestConfigurations_packageCASv2.0.zip) -from ENTSO-E Test Configurations for Conformity Assessment Scheme v2.0. \ No newline at end of file +Have a look at the [CGMES sample files](https://www.entsoe.eu/Documents/CIM_documents/Grid_Model_CIM/TestConfigurations_packageCASv2.0.zip) +from ENTSO-E Test Configurations for Conformity Assessment Scheme v2.0. diff --git a/docs/grid_exchange_formats/cgmes/export.md b/docs/grid_exchange_formats/cgmes/export.md index 482e92c13ce..461aaec2456 100644 --- a/docs/grid_exchange_formats/cgmes/export.md +++ b/docs/grid_exchange_formats/cgmes/export.md @@ -2,97 +2,93 @@ TODO -Please note that PowSyBl always exports CGMES networks as CGMES Node/Breaker networks without considering the topology -level of the PowSyBl network. +Please note that PowSyBl only ever exports CIM-CGMES networks as CGMES Node/Breaker networks without consideration of the topology level of the PowSyBl network. ## Conversion from PowSyBl grid model to CGMES -The following sections describe in detail how each supported PowSyBl network model object is converted to CGMES network -components. +The following sections describe in detail how each supported PowSyBl network model object is converted to CGMES network components. ### Battery -PowSyBl [`Batteries`](../../grid_model/network_subnetwork.md#battery) are exported as CGMES `SynchronousMachine` with CGMES `HydroGeneratingUnits`. +PowSyBl [`Battery`](../../grid_model/network_subnetwork.md#battery) is exported as `SynchronousMachine` with `HydroGeneratingUnit`. TODO details ### BusbarSection -PowSyBl [`BusbarSections`](../../grid_model/network_subnetwork.md#busbar-section) are exported as CGMES `BusbarSections`. +PowSyBl [`BusbarSection`](../../grid_model/network_subnetwork.md#busbar-section) is exported as CGMES `BusbarSection`. TODO details ### DanglingLine -PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line) are exported as several CGMES network components. -Each dangling line will be exported as one CGMES `EquivalentInjection` and one CGMES `ACLineSegment`. +PowSyBl [`DanglingLine`](../../grid_model/network_subnetwork.md#dangling-line) is exported as several CGMES network objects. +Each dangling line will be exported as one `EquivalentInjection` and one `ACLineSegment`. TODO details ### Generator -PowSyBl [`Generators`](../../grid_model/network_subnetwork.md#generator) are exported as CGMES `SynchronousMachines`. +PowSyBl [`Generator`](../../grid_model/network_subnetwork.md#generator) is exported as CGMES `SynchronousMachine`. TODO details ### HVDC line and HVDC converter stations -A PowSyBl [`HVDCLine`](../../grid_model/network_subnetwork.md#hvdc-line) and its two [`HVDCConverterStations`](../../grid_model/network_subnetwork.md#hvdc-converter-station) -are exported as a CGMES `DCLineSegment` and two CGMES `DCConverterUnits`. +A PowSyBl [`HVDCLine`](../../grid_model/network_subnetwork.md#hvdc-line) and its two [`HVDCConverterStations`](../../grid_model/network_subnetwork.md#hvdc-converter-station) are exported as a `DCLineSegment` with two `DCConverterUnits`. TODO details ### Line -PowSyBl [`Lines`](../../grid_model/network_subnetwork.md#line) are exported as CGMES `ACLineSegment`. +PowSyBl [`Line`](../../grid_model/network_subnetwork.md#line) is exported as `ACLineSegment`. TODO details ### Load -PowSyBl [`Loads`](../../grid_model/network_subnetwork.md#load) are exported as CGMES `ConformLoads`, `NonConformLoads` or `EnergyConsumers` -depending on the extension [`LoadDetail`](../../grid_model/extensions.md#load-detail). +PowSyBl [`Load`](../../grid_model/network_subnetwork.md#load) is exported as `ConformLoad`, `NonConformLoad` or `EnergyConsumer` depending on the extension [`LoadDetail`](../../grid_model/extensions.md#load-detail). TODO details ### Shunt compensator -PowSyBl [`ShuntCompensators`](../../grid_model/network_subnetwork.md#shunt-compensator) are exported as CGMES `LinearShuntCompensator` or -`NonlinearShuntCompensator` depending on their models. +PowSyBl [`ShuntCompensator`](../../grid_model/network_subnetwork.md#shunt-compensator) is exported as `LinearShuntCompensator` or `NonlinearShuntCompensator` depending on their models. TODO details ### StaticVarCompensator -PowSyBl [`StaticVarCompensators`](../../grid_model/network_subnetwork.md#static-var-compensator) are exported as CGMES `StaticVarCompensators`. +PowSyBl [`StaticVarCompensator`](../../grid_model/network_subnetwork.md#static-var-compensator) is exported as `StaticVarCompensator`. TODO details ### Substation -PowSyBl [`Substations`](../../grid_model/network_subnetwork.md#substation) are exported as CGMES `Substations`. +PowSyBl [`Substation`](../../grid_model/network_subnetwork.md#substation) is exported as `Substation`. TODO details ### Switch -PowSyBl [`Switches`](../../grid_model/network_subnetwork.md#breakerswitch) are exported as CGMES `Breakers`, `Disconnectors` or `LoadBreakSwitches` depending on its `SwitchKind`. +PowSyBl [`Switch`](../../grid_model/network_subnetwork.md#breakerswitch) is exported as CGMES `Breaker`, `Disconnector` or `LoadBreakSwitch` depending on its `SwitchKind`. TODO details ### ThreeWindingsTransformer -PowSyBl [`ThreeWindingsTransformers`](../../grid_model/network_subnetwork.md#three-windings-transformer) are exported as CGMES `PowerTransformers` with three CGMES `PowerTransformerEnds`. +PowSyBl [`ThreeWindingsTransformer`](../../grid_model/network_subnetwork.md#three-windings-transformer) is exported as `PowerTransformer` with three `PowerTransformerEnds`. TODO details + ### TwoWindingsTransformer -PowSyBl [`TwoWindingsTransformers`](../../grid_model/network_subnetwork.md#two-windings-transformer) are exported as CGMES `PowerTransformers` with two CGMES `PowerTransformerEnds`. +PowSyBl [`TwoWindingsTransformer`](../../grid_model/network_subnetwork.md#two-windings-transformer) is exported as `PowerTransformer` with two `PowerTransformerEnds`. TODO details ### Voltage level -PowSybl [`VoltatgeLevels`](../../grid_model/network_subnetwork.md#voltage-level) are exported as CGMES `VoltageLevels`. +PowSybl [`VoltatgeLevel`](../../grid_model/network_subnetwork.md#voltage-level) is exported as `VoltageLevel`. TODO details @@ -100,7 +96,7 @@ PowSybl [`VoltatgeLevels`](../../grid_model/network_subnetwork.md#voltage-level) ### Control areas -PowSyBl [`ControlAreas`](import.md#cgmes-control-areas) are exported as CGMES `ControlAreas`. +PowSyBl [`ControlAreas`](import.md#cgmes-control-areas) are exported as several `ControlArea`. TODO details @@ -121,10 +117,12 @@ By default, the base name is the network's name if it exists, or else the networ **iidm.export.cgmes.boundary-eq-id** Optional property that defines the ID of the EQ-BD model if there is any. Its default value is `null`: we consider there is no EQ-BD model to consider. +If this property is defined, then this ID will be written in the header of the exported EQ file. **iidm.export.cgmes.boundary-tp-id** Optional property that defines the ID of the TP-BD model if there is any. Its default value is `null`: we consider there is no TP-BD model to consider. +If this property is defined, then this ID will be written in the header of the exported SV file. **iidm.export.cgmes.cim-version** Optional property that defines the CIM version number in which the user wants the CGMES files to be exported. @@ -203,3 +201,4 @@ Its default value is 1. The business process in which the export takes place. This is used to generate unique UUIDs for the EQ, TP, SSH and SV file `FullModel`. Its default value is `1D`. +Note that if you are exporting a network that does not come from CGMES, you can use the [`iidm.import.cgmes.boundary-location`](#options) property to define the location of the boundary files to use as reference. diff --git a/docs/grid_exchange_formats/cgmes/import.md b/docs/grid_exchange_formats/cgmes/import.md index f9b7380a5d9..5a3cbaf6669 100644 --- a/docs/grid_exchange_formats/cgmes/import.md +++ b/docs/grid_exchange_formats/cgmes/import.md @@ -1,8 +1,8 @@ # Import -The CGMES importer reads and converts a CGMES model to the PowSyBl grid model. The import process is performed in two steps: +The CIM-CGMES importer reads and converts a CIM-CGMES model to the PowSyBl grid model. The import process is performed in two steps: - Read input files into a triplestore -- Convert CGMES data retrieved by SPARQL requests from the created triplestore to PowSyBl grid model +- Convert CIM-CGMES data retrieved by SPARQL requests from the created triplestore to PowSyBl grid model The data in input CIM/XML files uses RDF (Resource Description Framework) syntax. In RDF, data is described making statements about resources using triplet expressions: (subject, predicate, object). To describe the conversion from CGMES to PowSyBl we first introduce some generic considerations about the level of detail of the model (node/breaker or bus/branch), the identity of the equipments and equipment containment in substations and voltage levels. After that, the conversion for every CGMES relevant class is explained. Consistency checks and validations performed during the conversion are mentioned in the corresponding sections. @@ -21,11 +21,11 @@ Using the `Node` or `Bus` information, PowSyBl creates a `Terminal` that will be Some equipment, like switches, lines or transformers, have more than one point of connection to the Network. -In PowSyBl, a `Node` can have zero or one terminal. In CGMES, the `ConnectivityNode` objects may have more than one associated terminals. To be able to represent this in PowSyBl, the conversion process will automatically create internal connections between the PowSyBl nodes that represent equipment connections and the nodes created to map CGMES `ConnectivityNode` objects. +In PowSyBl, a `Node` can have zero or one terminal. In CGMES, the `ConnectivityNode` objects may have more than one associated terminals. To be able to represent this in PowSyBl, the conversion process will automatically create internal connections between the PowSyBl nodes that represent equipment connections and the nodes created to map `ConnectivityNode` objects. ## Identity of model equipments -Almost all the equipments of the PowSyBl grid model require a unique identifier `Id` and may optionally have a human readable `Name`. Whenever possible, these attributes will be directly copied from original CGMES attributes. +Almost all the equipments of the PowSyBl grid model require a unique identifier `Id` and may optionally have a human-readable `Name`. Whenever possible, these attributes will be directly copied from original CGMES attributes. Terminals are used by CGMES and PowSyBl to define the points of connection of the equipment to the network. CGMES terminals have unique identifiers. PowSyBl does not allow terminals to have an associated identifier. Information about original CGMES terminal identifiers is stored in each PowSyBl object using aliases. @@ -37,7 +37,7 @@ The CGMES model does not guarantee these hierarchical constraints, so the first ## Conversion from CGMES to PowSyBl grid model -The following sections describe in detail how each supported CGMES network component is converted to PowSyBl network model objects. +The following sections describe in detail how each supported CGMES network object is converted to PowSyBl network model objects. ### Substation @@ -47,7 +47,7 @@ For each substation (considering only the representative substation if they are ### VoltageLevel -As in the substations, for each voltage level (considering only the representative voltage level if they are connected by switches) in the CGMES model a new voltage level is created in the PowSyBl grid model with the following attributes created as such: +As for substations, for each voltage level (considering only the representative voltage level if they are connected by switches) in the CGMES model a new voltage level is created in the PowSyBl grid model with the following attributes created as such: - `NominalV` It is copied from the `nominalVoltage` property of the CGMES voltage level. - `TopologyKind` It will be `NODE_BREAKER` or `BUS_BREAKER` depending on the level of detail of the CGMES grid model. - `LowVoltageLimit` It is copied from the `lowVoltageLimit` property. @@ -63,7 +63,7 @@ If the import option `iidm.import.cgmes.create-busbar-section-for-every-connecti ### TopologicalNode -If the CGMES model is defined at bus/branch detail, then CGMES `TopologicalNode` objects are used in the conversion, and for each of them a `Bus` is created in the PowSyBl grid model inside the corresponding voltage level container, at the PowSyBl bus/breaker topology level. The created `Bus` has the following attributes: +If the CGMES model is defined at bus/branch detail, then `TopologicalNode` objects are used in the conversion, and for each of them a `Bus` is created in the PowSyBl grid model inside the corresponding voltage level container, at the PowSyBl bus/breaker topology level. The created `Bus` has the following attributes: - Identity attributes `Id` and `Name` are copied from the `TopologicalNode`. - `V` The voltage of the `TopologicalNode` is copied if it is valid (greater than `0`). - `Angle` The angle the `TopologicalNode` is copied if the previous voltage is valid. @@ -78,12 +78,12 @@ CGMES Busbar sections are mapped to PowSyBl busbar sections only if CGMES is nod ### EnergyConsumer -Every `EnergyConsumer` object in the CGMES model creates a new `Load` in PowSyBl. The attributes are created as such: +Every `EnergyConsumer` in the CGMES model creates a new `Load` in PowSyBl. The attributes are created as such: - `P0`, `Q0` are set from CGMES values taken from `SSH`, `SV`, or `EQ` data depending on which are defined. - `LoadType` It will be `FICTITIOUS` if the `Id` of the `energyConsumer` contains the pattern `fict`. Otherwise `UNDEFINED`. - `LoadDetail` Additional information about conform and non-conform loads is added as an extension of the `Load` object (for more details about the [extension](../../grid_model/extensions.md#load-detail)). -The `LoadDetail` extension attributes depend on the `type` property of the CGMES `EnergyConsumer`. For a conform load: +The `LoadDetail` extension attributes depend on the `type` property of the `EnergyConsumer`. For a conform load: - `withFixedActivePower` is always `0`. - `withFixedReactivePower` is always `0`. - `withVariableActivePower` is set to the Load `P0`. @@ -97,7 +97,7 @@ When the type is a non-conform load: ### EnergySource -A CGMES `EnergySource` is a generic equivalent for an energy supplier, with the injection given using load sign convention. +An `EnergySource` is a generic equivalent for an energy supplier, with the injection given using load sign convention. For each `EnergySource` object in the CGMES model a new PowSyBl `Load` is created, with attributes created as such: - `P0`, `Q0` set from `SSH` or `SV` values depending on which are defined. @@ -114,14 +114,14 @@ For each `SvInjection` in the CGMES network model a new PowSyBl `Load` with attr ### EquivalentInjection -The mapping of a CGMES `EquivalentInjection` depends on its location relative to the boundary area. +The mapping of an `EquivalentInjection` depends on its location relative to the boundary area. If the `EquivalentInjection` is outside the boundary area, it will be mapped to a PowSyBl `Generator`. If the `EquivalentInjection` is at the boundary area, its regulating voltage data will be mapped to the generation data inside the PowSyBl `DanglingLine` created at the boundary point and its values for `P`, `Q` will be used to define the DanglingLine `P0`, `Q0`. Please note that the said `DanglingLine` can be created from an [`ACLineSegment`](#aclinesegment), a [`Switch`](#switch-switch-breaker-disconnector-loadbreakswitch-protectedswitch-grounddisconnector), an [`EquivalentBranch`](#equivalentbranch) or a [`PowerTransformer`](#powertransformer). -Attributes of the PowSyBl generator or of the PowSyBl dangling line's generation are created as such: +Attributes of the PowSyBl generator or of the PowSyBl dangling line generation are created as such: - `MinP`/`MaxP` are copied from CGMES `minP`/`maxP` if defined, otherwise they are set to `-Double.MAX_VALUE`/`Double.MAX_VALUE`. - `TargetP`/`TargetQ` are set from `SSH` or `SV` values depending on which are defined. CGMES values for `p`/`q` are given with load sign convention, so a change in sign is applied when copying them to `TargetP`/`TargetQ`. - `TargetV` The `regulationTarget` property is copied if it is not equal to zero. Otherwise, the nominal voltage associated to the connected terminal of the `equivalentInjection` is assigned. For CGMES Equivalent Injections the voltage regulation is allowed only at the point of connection. @@ -130,19 +130,19 @@ Attributes of the PowSyBl generator or of the PowSyBl dangling line's generation ### ACLineSegment -CGMES `ACLineSegments`' mapping depends on its location relative to the boundary area. +`ACLineSegments`' mapping depends on its location relative to the boundary area. If the `ACLineSegment` is outside the boundary area, it will be mapped to a PowSyBl [`Line`](../../grid_model/network_subnetwork.md#line). If the `ACLineSegment` is completely inside the boundary area, if the boundaries are not imported, it is ignored. Otherwise, it is mapped to a PowSyBl [`Line`](../../grid_model/network_subnetwork.md#line). -If the `ACLineSegment` has one side inside the boundary area and one side outside the boundary area, the importer checks if another `ACLineSegment` is linked to the same CGMES [`TopologicalNode`](#topologicalnode) in the boundary area. -- If it is the only one `ACLineSegment` linked to this `TopologicalNode`, it is mapped to a PowSyBl [`DanglingLine`](../../grid_model/network_subnetwork.md#dangling-line). -- If there are one or more other `ACLineSegment` linked to this `TopologicalNode` and they all are in the same `SubGeographicalRegion`, they are all mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line). -- If there is exactly one other `ACLineSegment` linked to this `TopologicalNode` in another `SubGeographicalRegion`, they are both mapped to PowSybl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line), part of the same PowSyBl [`TieLine`](../../grid_model/network_subnetwork.md#tie-line). -- If there are two or more other `ACLineSegment` linked to this `TopologicalNode` in different `SubGeographicalRegions`: - - If there are only two `ACLineSegments` with their boundary terminal connected **and** in different `SubGeographicalRegion`, they are both mapped to PowSybl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line), part of the same PowSyBl [`TieLine`](../../grid_model/network_subnetwork.md#tie-line) and all other `ACLineSegments` are mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line). - - Otherwise, they are all mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line). +If the `ACLineSegment` has one side inside the boundary area and one side outside the boundary area, the importer checks if another branch is connected to the same [`TopologicalNode`](#topologicalnode) in the boundary area. +- If there is no other branch connected to this `TopologicalNode`, it will be mapped to a PowSyBl [`DanglingLine`](../../grid_model/network_subnetwork.md#dangling-line). +- If there are one or more other branches connected to this `TopologicalNode` and they all are in the same `SubGeographicalRegion`, they will all be mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line). +- If there is exactly one other branch connected to this `TopologicalNode` in another `SubGeographicalRegion`, they will both be mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line), which are part of the same PowSyBl [`TieLine`](../../grid_model/network_subnetwork.md#tie-line). +- If there are two or more other branches connected to this `TopologicalNode` in different `SubGeographicalRegions`: + - If there are only two branches with their boundary terminal connected and in different `SubGeographicalRegion`, they will both be mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line), which are part of the same PowSyBl [`TieLine`](../../grid_model/network_subnetwork.md#tie-line) and all other `ACLineSegments` will be mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line). + - Otherwise, they will all be mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line). If the `ACLineSegment` is mapped to a PowSyBl [`Line`](../../grid_model/network_subnetwork.md#line): - `R` is copied from CGMES `r` @@ -152,39 +152,39 @@ If the `ACLineSegment` is mapped to a PowSyBl [`Line`](../../grid_model/network_ - `B1` is calculated as half of CGMES `bch` - `B2` is calculated as half of CGMES `bch` -If the `ACLineSegment` is mapped to a PowSyBl unpaired [`DanglingLine`](../../grid_model/network_subnetwork.md#dangling-line): +If the `ACLineSegment` is mapped to an unpaired PowSyBl [`DanglingLine`](../../grid_model/network_subnetwork.md#dangling-line): - `R` is copied from CGMES `r` - `X` is copied from CGMES `x` - `G` is copied from CMGES `gch` if defined, `0.0` otherwise - `B` is copied from CGMES `bch` -- `UcteXnodeCode` is copied from the name of the `TopologicalNode` or the `ConnectivityNode` (respectively in `NODE-BREAKER` or `BUS-BRANCH`) inside boundaries +- `PairingKey` is copied from the name of the `TopologicalNode` or the `ConnectivityNode` (respectively in `NODE-BREAKER` or `BUS-BRANCH`) inside boundaries - `P0` is copied from CGMES `P` of the terminal at boundary side - `Q0` is copied from CGMES `Q` of the terminal at boundary side -If the `ACLineSegment` is mapped to a PowSyBl paired [`DanglingLine`](../../grid_model/network_subnetwork.md#dangling-line): +If the `ACLineSegment` is mapped to a paired PowSyBl [`DanglingLine`](../../grid_model/network_subnetwork.md#dangling-line): - `R` is copied from CGMES `r` - `X` is copied from CGMES `x` -- `G1` is `0.0` is the Half Line is on side `ONE` of the Tie Line. If the Half Line is on side `TWO` of the Tie Line, it is copied from CGMES `gch` if defined, `0.0` otherwise. -- `G2` is `0.0` is the Half Line is on side `TWO` of the Tie Line. If the Half Line is on side `ONE` of the Tie Line, it is copied from CGMES `gch` if defined, `0.0` otherwise. -- `B1` is `0.0` is the Half Line is on side `ONE` of the Tie Line. If the Half Line is on side `TWO` of the Tie Line, it is copied from CGMES `bch`. -- `B2` is `0.0` is the Half Line is on side `TWO` of the Tie Line. If the Half Line is on side `ONE` of the Tie Line, it is copied from CGMES `bch`. -- `UcteXnodeCode` is copied from the name of the `TopologicalNode` or the `ConnectivityNode` (respectively in `NODE-BREAKER` or `BUS-BRANCH`) inside boundaries +- `G1` is `0.0` is the dangling line is on side `ONE` of the Tie Line. If the dangling line Line is on side `TWO` of the Tie Line, it is copied from CGMES `gch` if defined, `0.0` otherwise. +- `G2` is `0.0` is the dangling line is on side `TWO` of the Tie Line. If the dangling line Line is on side `ONE` of the Tie Line, it is copied from CGMES `gch` if defined, `0.0` otherwise. +- `B1` is `0.0` is the dangling line is on side `ONE` of the Tie Line. If the dangling line Line is on side `TWO` of the Tie Line, it is copied from CGMES `bch`. +- `B2` is `0.0` is the dangling line is on side `TWO` of the Tie Line. If the dangling line Line is on side `ONE` of the Tie Line, it is copied from CGMES `bch`. +- `PairingKey` is copied from the name of the `TopologicalNode` or the `ConnectivityNode` (respectively in `NODE-BREAKER` or `BUS-BRANCH`) inside boundaries ### EquivalentBranch -CGMES `EquivalentBranches`' mapping depends on its location relative to the boundary area. +Equivalent branches mapping depends on its location relative to the boundary area. If the `EquivalentBranch` is outside the boundary area, it will be mapped to a PowSyBl [`Line`](../../grid_model/network_subnetwork.md#line). If the `EquivalentBranch` is completely inside the boundary area, if the boundaries are not imported, it is ignored. Otherwise, it is mapped to a PowSyBl [`Line`](../../grid_model/network_subnetwork.md#line). -If the `EquivalentBranch` has one side inside the boundary area and one side outside the boundary area, the importer checks if another `EquivalentBranch` is linked to the same CGMES [`TopologicalNode`](#topologicalnode) in the boundary area. -- If it is the only one `EquivalentBranch` linked to this `TopologicalNode`, it is mapped to a PowSyBl [`DanglingLine`](../../grid_model/network_subnetwork.md#dangling-line). -- If there are one or more other `EquivalentBranch` linked to this `TopologicalNode` and they all are in the same `SubGeographicalRegion`, they are all mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line). -- If there is exactly one other `EquivalentBranch` linked to this `TopologicalNode` in another `SubGeographicalRegion`, they are both mapped to PowSybl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line), part of the same PowSyBl [`TieLine`](../../grid_model/network_subnetwork.md#tie-line). -- If there are two or more other `EquivalentBranches` linked to this `TopologicalNode` in different `SubGeographicalRegions`: - - If there are only two `EquivalentBranches` with their boundary terminal connected **and** in different `SubGeographicalRegion`, they are both mapped to PowSybl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line), part of the same PowSyBl [`TieLine`](../../grid_model/network_subnetwork.md#tie-line) and all other `EquivalentBranches` are mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line). - - Otherwise, they are all mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line). +If the `EquivalentBranch` has one side inside the boundary area and one side outside the boundary area, the importer checks if another branch is connected to the same [`TopologicalNode`](#topologicalnode) in the boundary area. +- If there is no other branch connected to this `TopologicalNode`, it will be mapped to a PowSyBl [`DanglingLine`](../../grid_model/network_subnetwork.md#dangling-line). +- If there are one or more other branches connected to this `TopologicalNode` and they all are in the same `SubGeographicalRegion`, they will all be mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line). +- If there is exactly one other branch connected to this `TopologicalNode` in another `SubGeographicalRegion`, they will both be mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line), which are part of the same PowSyBl [`TieLine`](../../grid_model/network_subnetwork.md#tie-line). +- If there are two or more other branches connected to this `TopologicalNode` in different `SubGeographicalRegions`: + - If there are only two branches connected with their boundary terminal connected and in different `SubGeographicalRegion`, they will both be mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line), which are part of the same PowSyBl [`TieLine`](../../grid_model/network_subnetwork.md#tie-line) and all other `EquivalentBranches` will be mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line). + - Otherwise, they will all be mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line). If the `EquivalentBranch` is mapped to a PowSyBl [`Line`](../../grid_model/network_subnetwork.md#line): - `R` is copied from CGMES `r` @@ -199,28 +199,29 @@ If the `EquivalentBranch` is mapped to a PowSyBl [`DanglingLine`](../../grid_mod - `X` is copied from CGMES `x` - `G` is `0.0` - `B` is `0.0` -- `UcteXnodeCode` is copied from the name of the `TopologicalNode` or the `ConnectivityNode` (respectively in `NODE-BREAKER` or `BUS-BRANCH`) inside boundaries +- `PairingKey` is copied from the name of the `TopologicalNode` or the `ConnectivityNode` (respectively in `NODE-BREAKER` or `BUS-BRANCH`) inside boundaries - `P0` is copied from CGMES `P` of the terminal at boundary side - `Q0` is copied from CGMES `Q` of the terminal at boundary side ### AsychronousMachine -CGMES `AsynchronousMachines` represent rotating machines whose shaft rotates asynchronously with the electrical field. +Asynchronous machines represent rotating machines whose shaft rotates asynchronously with the electrical field. It can be motor or generator; no distinction is made for the conversion of these two types. -A CGMES `AsynchronousMachine` is mapped to a PowSyBl [`Load`](../../grid_model/network_subnetwork.md#load) with attributes created as described below: +An `AsynchronousMachine` is mapped to a PowSyBl [`Load`](../../grid_model/network_subnetwork.md#load) with attributes created as described below: - `P0`, `Q0` are set from CGMES values taken from `SSH` or `SV`data depending on which are defined. If there is no defined data, it is `0.0`. -- `LoadType` is `FICTITIOUS` if the CGMES ID contains "`fict`". Otherwise, it is `UNDEFINED`. +- `LoadType` is `FICTITIOUS` if the CGMES ID contains "fict". Otherwise, it is `UNDEFINED`. ### SynchronousMachine -CGMES `SynchronousMachines` represent rotating machines whose shaft rotates synchronously with the electrical field. + +Synchronous machines represent rotating machines whose shaft rotates synchronously with the electrical field. It can be motor or generator; no distinction is made for the conversion of these two types. -A CGMES `SynchronousMachine` is mapped to a PowSyBl [`Generator`](../../grid_model/network_subnetwork.md#generator) with attributes created as described below: -- `MinP` is set from CGMES `GeneratingUnit.minOperatingP` on the `GeneratingUnit` associated with the `SynchronousMachine`. If invalid, `MinP` is `-Double.MAX_VALUE`. -- `MaxP` is set from CGMES `GeneratingUnit.maxOperatingP` on the `GeneratingUnit` associated with the `SynchronousMachine`. If invalid, `MaxP` is `Double.MAX_VALUE`. +A `SynchronousMachine` is mapped to a PowSyBl [`Generator`](../../grid_model/network_subnetwork.md#generator) with attributes created as described below: +- `MinP` is set from `GeneratingUnit.minOperatingP` on the `GeneratingUnit` associated with the `SynchronousMachine`. If invalid, `MinP` is `-Double.MAX_VALUE`. +- `MaxP` is set from `GeneratingUnit.maxOperatingP` on the `GeneratingUnit` associated with the `SynchronousMachine`. If invalid, `MaxP` is `Double.MAX_VALUE`. - `ratedS` is copied from CGMES `ratedS`. If it is strictly lower than 0, it is considered undefined. -- `EnergySource` is defined from the CGMES `GeneratingUnit` class of the `GeneratingUnit` associated with the `SynchronousMachine` +- `EnergySource` is defined from the `GeneratingUnit` class of the `GeneratingUnit` associated with the `SynchronousMachine` - If it is a `HydroGeneratingUnit`, `EnergySource` is `HYDRO` - If it is a `NuclearGeneratingUnit`, `EnergySource` is `NUCLEAR` - If it is a `ThermalGeneratingUnit`, `EnergySource` is `THERMAL` @@ -237,7 +238,7 @@ A CGMES `SynchronousMachine` is mapped to a PowSyBl [`Generator`](../../grid_mod ### EquivalentShunt -A CGMES `EquivalentShunt` is mapped to a PowSyBl linear [`ShuntCompensator`](../../grid_model/network_subnetwork.md#shunt-compensator). A linear shunt compensator has banks or sections with equal admittance values. +An `EquivalentShunt` is mapped to a PowSyBl linear [`ShuntCompensator`](../../grid_model/network_subnetwork.md#shunt-compensator). A linear shunt compensator has banks or sections with equal admittance values. Its attributes are created as described below: - `SectionCount` is `1` if the `EquivalentShunt` CGMES `Terminal` is connected, else it is `0`. - `BPerSection` is copied from CGMES `b` @@ -245,9 +246,9 @@ Its attributes are created as described below: ### ExternalNetworkInjection -CGMES `ExternalNetworkInjections` are injections representing the flows from an entire external network. +External network injections are injections representing the flows from an entire external network. -A CGMES `ExternalNetworkinjection` is mapped to a PowSyBl [`Generator`](../../grid_model/network_subnetwork.md#generator) with attributes created as described below: +An `ExternalNetworkinjection` is mapped to a PowSyBl [`Generator`](../../grid_model/network_subnetwork.md#generator) with attributes created as described below: - `MinP` is copied from CGMES `minP` - `MaxP` is copied from CGMES `maxP` - `TargetP`/`TargetQ` are set from `SSH` or `SV` values depending on which are defined. CGMES values for `p`/`q` are given with load sign convention, so a change in sign is applied when copying them to `TargetP`/`TargetQ`. If undefined, they are set to `0`. @@ -259,9 +260,9 @@ A CGMES `ExternalNetworkinjection` is mapped to a PowSyBl [`Generator`](../../gr ### LinearShuntCompensator -CGMES `LinearShuntCompensators` represent shunt compensators with banks or sections with equal admittance values. +Linear shunt compensators represent shunt compensators with banks or sections with equal admittance values. -A CGMES `LinearShuntCompensator` is mapped to a PowSybl [`ShuntCompensator`](../../grid_model/network_subnetwork.md#shunt-compensator) with `SectionCount` copied from CGMES SSH `sections` or CGMES `SvShuntCompensatorSections.sections`, depending on the import option. If none is defined, it is copied from CGMES `normalSections`. +A `LinearShuntCompensator` is mapped to a PowSybl [`ShuntCompensator`](../../grid_model/network_subnetwork.md#shunt-compensator) with `SectionCount` copied from CGMES SSH `sections` or CGMES `SvShuntCompensatorSections.sections`, depending on the import option. If none is defined, it is copied from CGMES `normalSections`. The created PowSyBl shunt compensator is linear and its attributes are defined as described below: - `BPerSection` is copied from CGMES `bPerSection` if defined. Else, it is `Float.MIN_VALUE`. - `GPerSection` is copied from CGMES `gPerSection` if defined. Else, it is left undefined. @@ -271,12 +272,12 @@ The created PowSyBl shunt compensator is linear and its attributes are defined a ### NonlinearShuntCompensator -CGMES `NonlinearShuntCompensators` represent shunt compensators with banks or section addmittance values that differs. +Non-linear shunt compensators represent shunt compensators with banks or section addmittance values that differs. -A CGMES `NonlinearShuntCompensator` is mapped to a PowSyBl [`ShuntCompensator`](../../grid_model/network_subnetwork.md#shunt-compensator) with `SectionCount` copied from CGMES SSH `sections` or CGMES `SvShuntCompensatorSections.sections`, depending on the import option. If none is defined, it is copied from CGMES `normalSections`. -The created PowSyBl shunt compensator is non linear and has as many `Sections` as there are CGMES `NonlinearShuntCompensatorPoint` associated with the CGMES `NonlinearShuntCompensator` it is mapped to. +A `NonlinearShuntCompensator` is mapped to a PowSyBl [`ShuntCompensator`](../../grid_model/network_subnetwork.md#shunt-compensator) with `SectionCount` copied from CGMES SSH `sections` or CGMES `SvShuntCompensatorSections.sections`, depending on the import option. If none is defined, it is copied from CGMES `normalSections`. +The created PowSyBl shunt compensator is non-linear and has as many `Sections` as there are `NonlinearShuntCompensatorPoint` associated with the `NonlinearShuntCompensator` it is mapped to. -Sections are created from the lowest CGMES `sectionNumber` to the highest and each section has its attributes created as describe below: +Sections are created from the lowest CGMES `sectionNumber` to the highest and each section has its attributes created as described below: - `B` is calculated as the sum of all CGMES `b` of `NonlinearShuntCompensatorPoints` with `sectionNumber` lower or equal to its `sectionNumber` - `G` is calculated as the sum of all CGMES `g` of `NonlinearShuntCompensatorPoints` with `sectionNumber` lower or equal to its `sectionNumber` @@ -286,25 +287,58 @@ Sections are created from the lowest CGMES `sectionNumber` to the highest and ea TODO ### PowerTransformer -TODO + +Power transformers represent electrical devices consisting of two or more coupled windings, each represented by a `PowerTransformerEnd`. PowSyBl only supports `PowerTransformers` with two or three windings. + +#### PowerTransformer with two PowerTransformerEnds + +If a `PowerTransformer` has two `PowerTransformerEnds`, both outside the boundary area, it is mapped to a PowSyBl [`TwoWindingsTransformer`](../../grid_model/network_subnetwork.md#two-windings-transformer). +Please note that in this case, if `PowerTransformerEnds` are in different substations, the substations are merged into one. + +If a `PowerTransformer` has two `PowerTransformerEnds`, both completely inside the boundary area, and if the boundary area is not imported, the `PowerTransformer` is ignored. Otherwise, it is mapped to a PowSyBl [`TwoWindingsTransformer`](../../grid_model/network_subnetwork.md#two-windings-transformer). + +If the `PowerTransformer` has one `PowerTransformerEnd` inside the boundary area and the other outside the boundary area, the importer checks if another branch is connected to the same [`TopologicalNode`](#topologicalnode) in the boundary area. +- If there is no other connected to this `TopologicalNode`, it is mapped to a PowSyBl [`DanglingLine`](../../grid_model/network_subnetwork.md#dangling-line). +- If there is one or more other branches connected to this `TopologicalNode` and they are all in the same `SubGeographicalRegion`, they will all be mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line). +- If there is exactly one other branch connected to this `TopologicalNode` in another `SubGeographicalRegion`, they will both be mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line), which are part of the same PowSyBl [`TieLine`](../../grid_model/network_subnetwork.md#tie-line). +- If there are two or more other branches connected to this `TopologicalNode` in different `SubGeographicalRegions`: + - If there are only two branches with their boundary terminal connected and in different `SubGeographicalRegion`, they will both be mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line), which are part of the same PowSyBl [`TieLine`](../../grid_model/network_subnetwork.md#tie-line) and all other `EquivalentBranches` will be mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line). + - Otherwise, they will all be mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line). + +In every case, a `PowerTransformer` with two `PowerTransformerEnds` is mapped to an intermediary model that corresponds to a PowSyBl [`TwoWindingsTransformer`](../../grid_model/network_subnetwork.md#two-windings-transformer). +For more information about this conversion, please look at the classes [`InterpretedT2xModel`](https://github.com/powsybl/powsybl-core/blob/main/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/transformers/InterpretedT2xModel.java) +and [`ConvertedT2xModel`](https://github.com/powsybl/powsybl-core/blob/main/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/transformers/ConvertedT2xModel.java). + +If the `PowerTransformer` is finally mapped to a PowSyBl [`DanglingLine`](../../grid_model/network_subnetwork.md#dangling-line), its structural attributes (`R`, `X`, `G` and `B`) are calculated from the intermediary model's attributes, and the ratio from its ratio tap changer and/or its phase tap changer. +`P0` and `Q0` are set from CGMES `P` and `Q` values at boundary side; `PairingKey` is copied from the name of the `TopologicalNode` or the `ConnectivityNode` (respectively in `NODE-BREAKER` or `BUS-BRANCH`) inside boundaries. + +If the `PowerTransformer` is finally mapped to a PowSyBl [`DanglingLine`](../../grid_model/network_subnetwork.md#dangling-line), its attributes are calculated using a standard $$\pi$$ model with distributed parameters. + +#### PowerTransformer with three PowerTransformerEnds + +A `PowerTransformer` with three `PowerTransformerEnds` is mapped to a PowSyBl [`ThreeWindingsTransformer`](../../grid_model/network_subnetwork.md#three-windings-transformer). +Please note that in this case, if `PowerTransformerEnds` are in different substations, the substations are merged into one. + +For more information about this conversion, please look at the classes [`InterpretedT3xModel`](https://github.com/powsybl/powsybl-core/blob/main/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/transformers/InterpretedT3xModel.java) +and [`ConvertedT3xModel`](https://github.com/powsybl/powsybl-core/blob/main/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/transformers/ConvertedT3xModel.java). ### SeriesCompensator -CGMES `SeriesCompensators` represent series capacitors or reactors or AC transmission lines without charging susceptance. +Series compensators represent series capacitors or reactors or AC transmission lines without charging susceptance. -If a CGMES `SeriesCompensator` has both its ends inside the same voltage level, it is mapped to a PowSyBl [`Switch`](../../grid_model/network_subnetwork.md#breakerswitch). In this case, +If a `SeriesCompensator` has both its ends inside the same voltage level, it is mapped to a PowSyBl [`Switch`](../../grid_model/network_subnetwork.md#breakerswitch). In this case, all its CGMES electrical attributes are ignored. It is considered as closed, fictitious and, if it is in a node-breaker voltage level, retained. Its `SwitchKind` is `BREAKER`. -If a CGMES `SeriesCompensator` has its ends inside different voltage levels, it is mapped to a PowSyBl [`Line`](../../grid_model/network_subnetwork.md#line) with attributes as described below: +If a `SeriesCompensator` has its ends inside different voltage levels, it is mapped to a PowSyBl [`Line`](../../grid_model/network_subnetwork.md#line) with attributes as described below: - `R` is copied from CGMES `r` - `X` is copied from CGMES `x` - `G1`, `G2`, `B1` and `B2` are set to `0` ### StaticVarCompensator -CGMES `StaticVarCompensators` represent a facility for providing variable and controllable shunt reactive power. +Static VAR compensators represent a facility for providing variable and controllable shunt reactive power. -A CGMES `StaticVarCompensator` is mapped to a PowSyBl [`StaticVarCompensator`](../../grid_model/network_subnetwork.md#static-var-compensator) with attributes as described below: +A `StaticVarCompensator` is mapped to a PowSyBl [`StaticVarCompensator`](../../grid_model/network_subnetwork.md#static-var-compensator) with attributes as described below: - `Bmin` is calculated from CGMES `inductiveRating`: if it is defined and not equals to `0`, `Bmin` is `1 / inductiveRating`. Else, it is `-Double.MAX_VALUE`. - `Bmax` is calculated from CGMES `capacitiveRating`: if it defined and not equals to `0`, `Bmax` is `1 / capacitiveRating`. Else, it is `Double.MAX_VALUE`. @@ -314,10 +348,10 @@ A PowSyBl [`VoltagePerReactivePowerControl`](../../grid_model/extensions.md#volt ### Switch (Switch, Breaker, Disconnector, LoadBreakSwitch, ProtectedSwitch, GroundDisconnector) -CGMES `Switches`, `Breakers`, `Disconnectors`, `LoadBreakSwitches`, `ProtectedSwitches` and `GroundDisconnectors` are -all imported in the same manner. For convenience purpose, we will now use CGMES `Switch` as a say but keep in mind that this section is valid for all these CGMES classes. +Switches, breakers, disconnectors, load break switches, protected switches and ground disconnectors are +all imported in the same manner. For convenience purposes, we will now use `Switch` as a say but keep in mind that this section is valid for all these CGMES classes. -If the CGMES `Switch` has its ends both inside the same voltage level, it is mapped to a PowSyBl [`Switch`](../../grid_model/network_subnetwork.md#breakerswitch) with attributes as described below: +If the `Switch` has its ends both inside the same voltage level, it is mapped to a PowSyBl [`Switch`](../../grid_model/network_subnetwork.md#breakerswitch) with attributes as described below: - `SwitchKind` is defined depending on the CGMES class - If it is a CGMES `Breaker`, it is `BREAKER` - If it is a CGMES `Disconnector`, it is `DISCONNECTOR` @@ -329,15 +363,23 @@ If the CGMES `Switch` has its ends both inside the same voltage level, it is map If the CGMES `Switch` has its ends in different voltage levels inside the same IGM, it is mapped to a [`Switch`](../../grid_model/network_subnetwork.md#breakerswitch) but the voltage levels, and potentially the substations, that contain its ends are merged: they are mapped to only one voltage level and/or substation. The created PowSyBl `Switch` has its attributes defined as described above. -If the CGMES `Switch` has one of its end in the boundary area, it is mapped to a PowSybl [`DanglingLine`](../../grid_model/network_subnetwork.md#dangling-line) with attributes as described below: -- `R`, `X`, `G`, `B` are `0.0`. -- `UcteXnodeCode` is copied from the name of the `TopologicalNode` or the `ConnectivityNode` (respectively in `NODE-BREAKER` or `BUS-BRANCH`) inside boundaries. -- `P0` is copied from CGMES `P` of the terminal at boundary side +If the `Switch` has one side inside the boundary area and the other side outside the boundary area, the importer checks if another branch is connected to the same CGMES [`TopologicalNode`](#topologicalnode) in the boundary area. +- If there is no other branch connected to this `TopologicalNode`, it will be mapped to a PowSyBl [`DanglingLine`](../../grid_model/network_subnetwork.md#dangling-line). +- If there are one or more other branches connected to this `TopologicalNode` and they all are in the same `SubGeographicalRegion`, they will all be mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line). +- If there is exactly one other branch connected to this `TopologicalNode` in another `SubGeographicalRegion`, they will both be mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line), which are part of the same PowSyBl [`TieLine`](../../grid_model/network_subnetwork.md#tie-line). +- If there are two or more other branches connected to this `TopologicalNode` in different `SubGeographicalRegions`: + - If there are only two branches with their boundary terminal connected and in different `SubGeographicalRegion`, they will both mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line), which are part of the same PowSyBl [`TieLine`](../../grid_model/network_subnetwork.md#tie-line) and all other `EquivalentBranches` will be mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line). + - Otherwise, they will all be mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line). + +If the CGMES `Switch` is mapped to a PowSyBl [`DanglingLine`](../../grid_model/network_subnetwork.md#dangling-line), its attributes are as described below: +- `R`, `X`, `G`, `B` are `0.0`; +- `PairingKey` is copied from the name of the `TopologicalNode` or the `ConnectivityNode` (respectively in `NODE-BREAKER` or `BUS-BRANCH`) inside boundaries; +- `P0` is copied from CGMES `P` of the terminal at boundary side; - `Q0` is copied from CGMES `Q` of the terminal at boundary side ## Extensions -The CIM-CGMES format contains more information than what the `iidm` grid model needs for calculation. The additional data, that are needed to export a network in CIM-CGMES format, are stored in several extensions. +The CIM-CGMES format contains more information than what the `iidm` grid model needs for calculation. The additional data, that are needed to export a network in CIM-CGMES format, are stored in several extensions. ### CGMES control areas @@ -401,13 +443,12 @@ This extension is provided by the `com.powsybl:powsybl-cgmes-extensions` module. ## Options -These properties can be defined in the configuration file in the [import-export-parameters-default-value](../../user/configuration/import-export-parameters-default-value.md#import-export-parameters-default-value) module. +These properties can be defined in the configuration file in the [import-export-parameters-default-value](../../user/configuration/import-export-parameters-default-value.md) module. + **iidm.import.cgmes.boundary-location** Optional property that defines the directory path where the CGMES importer can find the boundary files (`EQBD` and `TPBD` profiles) if they are not present in the imported zip file. By default, its value is `/CGMES/boundary`. - -**iidm.import.cgmes.change-sign-for-shunt-reactive-power-flow-initial-state** -Optional property that defines if the CGMES importer inverts the sign of active and reactive power flows for shunt compensators. Its default value is `false`. +This property can also be used at CGMES export if the network was not imported from a CGMES to indicate the boundary files that should be used for reference. **iidm.import.cgmes.convert-boundary** Optional property that defines if the equipment located inside the boundary are imported as part of the network. Used for debugging purposes. `false` by default. @@ -415,8 +456,8 @@ Optional property that defines if the equipment located inside the boundary are **iidm.import.cgmes.convert-sv-injections** Optional property that defines if `SvInjection` objects are converted to IIDM loads. `true` by default. -**iidm.import.cgmes.create-active-power-control-extension** -Optional property that defines if active power control extensions are created for the converted generators. `false` by default. If `true`, the extension will created for the CGMES `SynchronousMachines` with the attribute `normalPF` defined. For these generators, the `normalPF` value will be saved as the `participationFactor` and the flag `participate` set to `true`. +**iidm.import.cgmes.create-active-power-control-extension** +Optional property that defines if active power control extensions are created for the converted generators. `true` by default. If `true`, the extension will created for the CGMES `SynchronousMachines` with the attribute `normalPF` defined. For these generators, the `normalPF` value will be saved as the `participationFactor` and the flag `participate` set to `true`. **iidm.import.cgmes.create-busbar-section-for-every-connectivity-node** Optional property that defines if the CGMES importer creates an [IIDM Busbar Section](../../grid_model/network_subnetwork.md#busbar-section) for each CGMES connectivity node. Used for debugging purposes. `false` by default. @@ -428,7 +469,7 @@ Three modes are available: - `ALWAYS_EXCEPT_SWITCHES`: fictitious switches are created at every disconnected terminal that is not a terminal of a switch. - `NEVER`: no fictitious switches are created at disconnected terminals. -The default value is `ALWAYS`. +The default value is `ALWAYS`. **iidm.import.cgmes.decode-escaped-identifiers** Optional property that defines if identifiers containing escaped characters are decoded when CGMES files are read. `true` by default. @@ -463,16 +504,16 @@ Optional property that defines if the whole CGMES model is stored in the importe Optional property that defines if the CGMES conversion context will be stored as an extension of the IIDM output network. It is useful for external validation of the mapping made between CGMES and IIDM. Its default value is `false`. **iidm.import.cgmes.import-node-breaker-as-bus-breaker** -Optional property that forces CGMES model to be in topology bus/breaker in IIDM. This is a key feature when some models do not have all the breakers to connect and disconnect equipments in IIDM. In bus/breaker topology, connect and disconnect equipment only rely on terminal statuses and not on breakers. Its default value is `false`. +Optional property that forces CGMES model to be in topology bus/breaker in IIDM. This is a key feature when some models do not have all the breakers to connect and disconnect equipments in IIDM. In bus/breaker topology, connect and disconnect equipment only rely on terminal statuses and not on breakers. Its default value is `false`. **iidm.import.cgmes.disconnect-dangling-line-if-boundary-side-is-disconnected** Optional property used at CGMES import that disconnects the IIDM dangling line if in the CGMES model the line is open at the boundary side. As IIDM does not have any equivalence for that, this is an approximation. Its default value is `false`. **iidm.import.cgmes.missing-permanent-limit-percentage** -Optional property used when in operational limits, temporary limits are present and the permanent limit is missing as it is forbidden in IIDM. The missing permanent limit is equal to a percentage of the lowest temporary limit, with the percentage defined by the value of this property if present, `100` by default. +Optional property used when in operational limits, temporary limits are present and the permanent limit is missing as it is forbidden in IIDM. The missing permanent limit is equal to a percentage of the lowest temporary limit, with the percentage defined by the value of this property if present, `100` by default. **iidm.import.cgmes.cgm-with-subnetworks** -Optional property to define if subnetworks must be added to the network when importing a Common Grid Model (CGM). Each subnetwork will model an Individual Grid Model (IGM). By default `true`: subnetworks are added, and the merging is done at IIDM level, with a main IIDM network representing the CGM and containing a set of subnetworks, one for each IGM. If the value is set to `false` all the CGMES data will be flattened in a single network and information about the ownership of each equipment will be lost. +Optional property to define if subnetworks must be added to the network when importing a Common Grid Model (CGM). Each subnetwork will model an Individual Grid Model (IGM). By default `true`: subnetworks are added, and the merging is done at IIDM level, with a main IIDM network representing the CGM and containing a set of subnetworks, one for each IGM. If the value is set to `false` all the CGMES data will be flattened in a single network and information about the ownership of each equipment will be lost. **iidm.import.cgmes.cgm-with-subnetworks-defined** If `iidm.import.cgmes.cgm-with-subnetworks` is set to `true`, use this property to specify how the set of input files should be split by IGM: based on their filenames (use the value `FILENAME`) or by its modeling authority, read from the header (use the value `MODELING_AUTHORITY`). diff --git a/docs/grid_exchange_formats/cgmes/index.md b/docs/grid_exchange_formats/cgmes/index.md index 40318a69ac1..8cab44ba676 100644 --- a/docs/grid_exchange_formats/cgmes/index.md +++ b/docs/grid_exchange_formats/cgmes/index.md @@ -12,7 +12,7 @@ examples.md The CGMES (**C**ommon **G**rid **M**odel **E**xchange **S**pecification) is an IEC technical specification (TS 61970-600-1, TS 61970-600-2) based on the IEC CIM (**C**ommon **I**nformation **M**odel) family of standards. It was developed to meet necessary requirements for TSO data exchanges in the areas of system development and system operation. In this scenario the agents (the Modelling Authorities) generate their Individual Grid Models (IGM) that can be assembled to build broader Common Grid Models (CGM). Boundaries between IGMs are well defined: the boundary data is shared between the modelling agents and contain all boundary points required for a given grid model exchange. -In CGMES an electric power system model is described by data grouped in different subsets (profiles) and exchanged as CIM/XML files, with each file associated to a given profile. The profiles considered in PowSyBl are: +In CGMES, an electric power system model is described by data grouped in different subsets (profiles) and exchanged as CIM/XML files, with each file associated to a given profile. The profiles considered in PowSyBl are: - `EQ` Equipment. Contains data that describes the equipment present in the network and its physical characteristics. - `SSH` Steady State Hypothesis. Required input parameters to perform power flow analysis; e.g., energy injections and consumptions and setpoint values for regulating controls. - `TP` Topology. Describe how the equipment is electrically connected. Contains the definition of power flow buses. diff --git a/docs/grid_exchange_formats/cgmes/post_processor.md b/docs/grid_exchange_formats/cgmes/post_processor.md index 29fd336d385..e16d0fffb3e 100644 --- a/docs/grid_exchange_formats/cgmes/post_processor.md +++ b/docs/grid_exchange_formats/cgmes/post_processor.md @@ -3,7 +3,7 @@ ## CgmesDLImportPostProcessor This post-processor loads the diagram layout (DL) profile contained in the CGMES file, if available, into the triplestore. The diagram layout profile contains the data which is necessary to represent a drawing of the diagram corresponding to the CGMES file. -For instance, it contains the position of all equipments. +For instance, it contains the position of all equipment. This post-processor is enabled by adding the name `cgmesDLImport` to the list associated to `iidm.import.cgmes.post-processors` property. diff --git a/docs/grid_exchange_formats/cgmes/triple_store.md b/docs/grid_exchange_formats/cgmes/triple_store.md index d4affc99bfb..a9e18a48c18 100644 --- a/docs/grid_exchange_formats/cgmes/triple_store.md +++ b/docs/grid_exchange_formats/cgmes/triple_store.md @@ -1,13 +1,14 @@ # Triple store -A triplestore or RDF store is a purpose-built database for the storage and retrieval of triples through semantic queries. A triple is a data -entity composed of subject-predicate-object such as "Generator is in France", or in RDF/XML: +A triplestore or RDF store is a purpose-built database for the storage and retrieval of triples through semantic queries. +A triple is a data entity composed of subject-predicate-object such as "Generator is in France" or in RDF/XML: ```xml - France + France ``` -Input CGMES data read from CIM/XML files is stored natively in a purpose specific database for RDF statements (a Triplestore). There are multiple open-source implementations of Triplestore engines that could be easily plugged in PowSyBl. +Input CGMES data read from CIM/XML files is stored natively in a purpose-specific database for RDF statements (a Triplestore). +There are multiple open-source implementations of Triplestore engines that could be easily plugged in PowSyBl. The only supported Triplestore engine used by PowSyBl is [RDF4J](https://rdf4j.org/). Loading from RDF/XML files to the Triplestore is highly optimized by these engines. Furthermore, the Triplestore repository can be configured to use an in-memory store, allowing faster access to data. @@ -15,4 +16,4 @@ Loading from RDF/XML files to the Triplestore is highly optimized by these engin [Eclipse RDF4J™](https://rdf4j.org/about/) is an open source modular Java framework for working with RDF data. This includes parsing, storing, inferencing and querying of/over such data. It offers an easy-to-use API that can be connected to all leading RDF storage solutions. It allows you to connect with SPARQL endpoints and create applications that leverage the power of Linked Data and Semantic Web. -Its in-memory implementation is the default triplestore engine use by PowSyBl for CIM-CGMES import. \ No newline at end of file +Its in-memory implementation is the default triplestore engine used by PowSyBl for CIM-CGMES import. diff --git a/docs/grid_features/extraction.md b/docs/grid_features/extraction.md index c3c6a733381..4311c74f293 100644 --- a/docs/grid_features/extraction.md +++ b/docs/grid_features/extraction.md @@ -1,9 +1,13 @@ # Network reduction -The network reduction is relying on a `NetworkPredicate` instance, to define an area of interest (i.e. a list of equipments to keep in the network after the reduction). The equipments outside this area will be removed and the lines, transformers and HVDC lines connecting voltage levels inside and outside this area will be replaced by injections (loads or dangling lines, depending on the implementation). +This module is used to extract a portion of a network on an area of interest defined by the user. ## Define an area of interest +The network reduction is relying on a `NetworkPredicate` instance, to define an area of interest (i.e. a list of equipments to keep in the network after the reduction). +The equipments outside this area are removed and the lines, transformers and HVDC lines connecting voltage levels inside and outside this area will be replaced by injections (loads or dangling lines, depending on the implementation). + + Before doing the reduction, one has to define the area of interest, using the `com.powsybl.iidm.reducer.NetworkPredicate` interface. This interface declares two methods: ```java public interface NetworkPredicate { @@ -88,10 +92,31 @@ PowSyBl provides a default implementation of this interface, but you can provide ### Default implementation -The `com.powsybl.iidm.reducer.DefaultNetworkReducer` class is the PowSyBl implementation of the `NetworkReducer` interface that replaces the lines in the _border_ group by [loads](../grid_model/network_subnetwork.md#load) or [dangling lines](../grid_model/network_subnetwork.md#dangling-line) depending on the [options](#options), the two windings transformers and the HVDC lines by [loads](../grid_model/network_subnetwork.md#load). +The `com.powsybl.iidm.reducer.DefaultNetworkReducer` class is the PowSyBl implementation of the `NetworkReducer` interface. + +It replaces the lines in the _border_ group by [loads](../grid_model/network_subnetwork.md#load) or [dangling lines](../grid_model/network_subnetwork.md#dangling-line) depending on the [options](#options), the two windings transformers and the HVDC lines by [loads](../grid_model/network_subnetwork.md#load). The three windings transformers are replaced by a [load](../grid_model/network_subnetwork.md#load) if only one connected voltage level is kept. If two out of three connected voltage levels are kept, the third one is automatically added by the `DefaultNetworkReducer` to the voltage levels to keep. +#### Replacement + +##### Replacements by loads + +The load created in place of a branch has the same ID and name as the replaced branch. +The type of the load is set as `FICTITIOUS` and its $P_0$ and $Q_0$ are set to the $P$ and $Q$ of the relevant terminal, depending on which side is kept in the network. +If the branch is disconnected, $P_0$ and $Q_0$ are set to `NaN`. +The connectivity information (node or bus depending on the voltage level topology) is kept. +However, the operational limits and extensions from the original branch are not retained. + +##### Replacements by dangling lines + +The dangling line created in place of a line has the same ID and name as the replaced line. The resistance and reactance of the dangling line are equals to half of the resistance and reactance of the replaced line (we consider that the line is cut in the middle). +The conductance and susceptance are set to the $G_1$ and $B_1$ or to $G_2$ and $B_2$ depending on which side is kept in the network. + +The $P_0$ and $Q_0$ are set to the $P$ and $Q$ of the corresponding terminal, depending on which side is kept in the network. If the line is disconnected, $P_0$ and $Q_0$ are set to `NaN`. +The connectivity information (node or bus depending on the voltage level topology) is kept. +However, the operational limits and extensions from the original branch are not retained. + #### Options The network reduction can be configured by passing a `com.powsybl.iidm.reducer.ReductionOptions` instance to the `DefaultNetworkReducer` constructor. diff --git a/docs/grid_model/additional.md b/docs/grid_model/additional.md index 7d3926d0758..1b06270a317 100644 --- a/docs/grid_model/additional.md +++ b/docs/grid_model/additional.md @@ -8,9 +8,11 @@ Network elements can be described in an advanced way with reactive limits, loadi The reactive limits may be used to model limitations of the reactive power of [generators](./network_subnetwork.md#generator), [VSC converter stations](./network_subnetwork.md#vsc-converter-station) and [batteries](./network_subnetwork.md#battery). +(min-max-reactive-limits)= ### Min-Max reactive limits With the min-max reactive limits, the reactive power does not depend on the active power. For any active power value, the reactive power value is in the [minQ, maxQ] interval. +(reactive-capability-curve)= ### Reactive capability curve With the reactive capability curve limits, the reactive power limitation depends on the active power value. This dependency is based on a curve provided by the user. The curve is defined as a set of points that associate, to each active power value, a minimum and maximum reactive power value. @@ -62,6 +64,7 @@ generator.newReactiveCapabilityCurve() .add(); ``` +(loading-limits)= ## Loading Limits [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/LoadingLimits.html) @@ -86,6 +89,7 @@ Please look at this scheme to fully understand the modelling (the following exam Note that, following this modelling, in general the last temporary limit (the higher one in value) should be infinite with an acceptable duration different from zero, except for tripping current modeling where the last temporary limit is infinite with an acceptable duration equal to zero. If temporary limits are modeled, the permanent limit becomes mandatory. +(limit-group-collection)= ### Limit group collection In network development studies or in an operational context (CGMES), we can have a set of operational limits according to the season (winter vs summer for example), the time of the day (day vs night) etc. In PowSyBl, users can store a collection of limits: @@ -140,6 +144,7 @@ CurrentLimits currentLimits = network.getDanglingLine("DL").newCurrentLimits() ``` ![Current limits scheme_example2](img/currentLimitsExample2.svg) +(phase-tap-changer)= ## Phase tap changer [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/PhaseTapChanger.html) @@ -212,6 +217,7 @@ twoWindingsTransformer.newPhaseTapChanger() .add() ``` +(ratio-tap-changer)= ## Ratio tap changer [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/RatioTapChanger.html) diff --git a/docs/grid_model/extensions.md b/docs/grid_model/extensions.md index abf87945867..35b7490639a 100644 --- a/docs/grid_model/extensions.md +++ b/docs/grid_model/extensions.md @@ -1,3 +1,4 @@ +(grid-model-extensions)= # Grid model extensions The grid model contains enough data to basically describe supported components and run power flow computations, but it may not be sufficient for more complex studies. @@ -10,14 +11,17 @@ Note that some extensions provided by PowSyBl aren't supported in the [persisten Every extension is considered as serializable unless explicitly specified as non-serializable in XML-IIDM. +(active-power-control-extension)= ## Active power control This extension is used to configure the participation factor of the generator, typically in the case of a load flow computation with distributed slack enabled (with [balance type](../simulation/loadflow/configuration.md#generic-parameters) on generator). This extension is attached to a [generator](network_subnetwork.md#generator) or a [battery](network_subnetwork.md#battery). -| Attribute | Type | Unit | Required | Default value | Description | -|----------------------|---------|------------------------|----------|---------------|----------------------------------------------| -| participate | boolean | - | yes | - | The participation status | -| droop | double | None (repartition key) | no | - | The participation factor equals Pmax / droop | -| participation factor | double | None (repartition key) | no | - | Defines the participation factor explicitly | +| Attribute | Type | Unit | Required | Default value | Description | +|----------------------|---------|------------------------|----------|---------------|---------------------------------------------------------------------------------------| +| participate | boolean | - | yes | - | The participation status | +| droop | double | None (repartition key) | no | - | The participation factor equals maxP / droop | +| participation factor | double | None (repartition key) | no | - | Defines the participation factor explicitly | +| maxP override | double | MW | no | - | If defined, this limit is used for slack distribution instead of the generator's maxP | +| minP override | double | MW | no | - | if defined, this limit is used for slack distribution instead of the generator's minP | Here is how to add an active power control extension to a generator: ```java @@ -28,10 +32,11 @@ generator.newExtension(ActivePowerControlAdder.class) .add(); ``` -The participation status and the participation factor are multi-variants: they can vary from one variant to another. +The participation status, the participation factor, the maxP override and the minP override are multi-variants: they can vary from one variant to another. This extension is provided by the `com.powsybl:powsybl-iidm-extensions` module. +(branch-observability-extension)= ## Branch observability This extension models branches' flows' observability on both sides, obtained after a state estimation. @@ -54,14 +59,17 @@ This extension contains the sub-object `ObservabilityQuality`. This extension is provided by the `com.powsybl:powsybl-iidm-extensions` module. +(busbar-section-position-extension)= ## Busbar section position This extension gives positions information about a busbar section. The `busbarIndex` gives the position of the busbar section relatively to other busbars. The `sectionIndex` gives the position of the busbar section within the corresponding busbar. Note that a busbar is a set of busbar sections. Hence, the sections of a same busbar should have the same busbar index. The busbar indices induce an order of busbars within the voltage level, which usually reflects the busbars physical relative positions. Similarly, the section indices induce an order of sections of a same busbar, which usually reflects their physical relative position. +(connectable-position-extension)= ## Connectable position TODO +(coordinated-reactive-control-extension)= ## Coordinated reactive control Some generators can be coordinated to control reactive power in a point of the network. This extension is used to configure the percent of reactive coordinated control that comes from a generator. This extension is attached to a [generator](network_subnetwork.md#generator). @@ -81,6 +89,7 @@ Please note that the sum of the $qPercent$ values of the generators coordinating This extension is provided by the `com.powsybl:powsybl-iidm-extensions` module. +(discrete-measurements-extensions)= ## Discrete measurements This extension is used to store discrete measurements (such as tap positions, switch positions etc.) collected in substations. @@ -101,10 +110,12 @@ The DiscreteMeasurement class characteristics are the following: | value | Object | - | no | - | The discrete measurement value | | valid | boolean | - | no | - | The validity status (if true, the discrete measured value cannot be null) | +(entsoe-area-extension)= ## ENTSO-E area TODO +(hvdc-angle-droop-active-power-control-extension)= ## HVDC angle droop active power control This is an extension dedicated to DC line in order to model AC emulation. For a VSC converter station operating in AC emulation, its active power setpoint is given by @@ -117,14 +128,17 @@ $$P = P0 + k~(ph1 - ph2)$$ | droop | float | MW by degree | yes | - | k in the equation | | enabled | boolean | - | yes | - | if the AC emulation is active or not | +(hvdc-operator-active-power-range-extension)= ## HVDC operator active power range This extension enables to replace the operational limits of an DC line in AC emulation. In that case, the VSC converter stations min active power and max active power are not used. +(generator-enstoe-category-extension)= ## Generator ENTSO-E category TODO +(generator-short-circuit)= ## Generator short-circuit This extension models the generators data used for short-circuit calculations. Depending on the type of short-circuit study to be @@ -148,6 +162,7 @@ generator.newExtension(GeneratorShortCircuitAdder.class) .add(); ``` +(identifiable-short-circuit-extension)= ## Identifiable short-circuit This extension models the maximum and minimum short-circuit current admissible for any identifiable. @@ -168,6 +183,7 @@ bus.newExtension(IdentifiableShortCircuitAdder.class) ``` The code is similar for every identifiable. +(injection-observability-extension)= ## Injection observability This extension models injections' flows' observability, obtained after a state estimation. @@ -188,6 +204,7 @@ This extension contains the sub-object `ObservabilityQuality`. This extension is provided by the `com.powsybl:powsybl-iidm-extensions` module. +(line-position-extension)= ## Line Position This extension is attached to a Line and is used to store the geographical coordinates of the Line. @@ -217,6 +234,7 @@ line.newExtension(LinePositionAdder.class) .add(); ``` +(load-asymmetrical-extension)= ## Load asymmetrical A balanced load is described by its active power setpoint $P0$ and its reactive power setpoint $Q0$. @@ -295,6 +313,7 @@ load.newExtension(LoadAsymmetricalAdder.class) This extension is provided by the `com.powsybl:powsybl-iidm-extensions` module. +(load-detail-extension)= ## Load detail A load is described by its active power setpoint $P0$ and its reactive power setpoint $Q0$. This extension is used to detail : - In the total amount of active power what is fixed and what is time-dependant (also called variable). The time-dependant part can be adjusted for production equals consumption. @@ -321,6 +340,7 @@ All of this extension's attributes are multi-variants: they can vary from one va This extension is provided by the `com.powsybl:powsybl-iidm-extensions` module. +(measurements-extension)= ## Measurements This extension is used to store measurements collected in substations. @@ -341,6 +361,7 @@ The Measurement class characteristics are the following: | valid | boolean | - | no | - | The validity status (if true, the measured value cannot be NaN) | | side | ThreeSides | - | no | - | The equipment side associated to the measurement | +(operating-status-extension)= ## Operating status This is an extension of `Identifiable`, but it is restricted to some identifiable types: busbar sections, all branches, @@ -349,6 +370,7 @@ three-winding transformers, HVDC line and a dangling line. The status could be: - `PLANNED_OUTAGE`: outage due to an unscheduled putting out of service of the equipment. - `FORCED_OUTAGE`: outage due to a programmed taking out of service of the equipment. +(reference-priority-extension)= ## Reference Priority This extension is attached to a Generator, or a BusBarSection or a Load and is used to define the angle reference bus of @@ -373,7 +395,7 @@ int priority = ReferencePriority.get(generator); // note: returns zero if none d This extension is provided by the `com.powsybl:powsybl-iidm-api` module. - +(reference-terminals-extension)= ## Reference Terminals This extension is attached to a Network and is used to define the angle references of a Power Flow solution. @@ -397,6 +419,7 @@ ReferenceTerminals.add(terminal); This extension is provided by the `com.powsybl:powsybl-iidm-api` module. +(remote-reactive-power-control-extension)= ## Remote reactive power control This extensions is used for generators with a remote reactive control. @@ -407,6 +430,7 @@ This extensions is used for generators with a remote reactive control. | targetQ | double | MVar | yes | - | The targetQ at remote regulating terminal | | regulatingTerminal | `Terminal` | - | yes | - | The regulating terminal | +(slack-terminal-extension)= ## Slack terminal This extension is attached to a [voltage level](network_subnetwork.md#voltage-level) and is used to define the slack bus @@ -428,6 +452,7 @@ SlackTerminal.attach(bus); This extension is provided by the `com.powsybl:powsybl-iidm-api` module. +(substation-position-extension)= ## Substation Position This extension is attached to a Substation and is used to store the geographical coordinates of the Substation. @@ -456,6 +481,7 @@ station.newExtension(SubstationPositionAdder.class) .add(); ``` +(three-windings-transformer-phase-angle-clock-extension)= ## Three-windings transformer phase angle clock This extension is used to model the Vector Group of a three windings transformer. The phase angle clock could be modeled at leg 2, leg 3 or both legs 2 and 3 and of a three windings transformer (network side). The voltage phase angle displacement is represented with clock hours. The valid values are `0` to `11`. This extension is attached to a [three windings transformer](network_subnetwork.md#three-windings-transformer). @@ -474,6 +500,7 @@ transformer.newExtension(ThreeWindingsTransformerPhaseAngleClock.class) This extension is provided by the `com.powsybl:powsybl-iidm-extensions` module. +(three-windings-transformer-to-be-estimated-extension)= ## Three-windings transformer to be estimated This extension is used to indicate if a three-winding transformer tap changer is to be estimated during a state estimation, i.e. if its tap position should be an output of the state estimation. @@ -502,6 +529,7 @@ transformer.newExtension(ThreeWindingsTransformerToBeEstimatedAdder.class) .add(); ``` +(two-windings-transformer-phase-angle-clock-extension)= ## Two-windings transformer phase angle clock This extension is used to model the Vector Group of a two windings transformer. The phase angle clock is modeled at side 2 of a two windings transformer. The voltage phase angle displacement is represented with clock hours. The valid values are 0 to 11. This extension is attached to a [two windings transformer](network_subnetwork.md#two-windings-transformer). @@ -518,6 +546,7 @@ transformer.newExtension(TwoWindingsTransformerPhaseAngleClockAdder.class) This extension is provided in the module `com.powsybl:powsybl-iidm-extensions`. +(two-windings-transformer-to-be-estimated-extension)= ## Two-windings transformer to be estimated This extension is used to indicate if a two-winding transformer tap changer is to be estimated during a state estimation, i.e. if its tap position should be an output of the state estimation. @@ -548,6 +577,7 @@ transformer.newExtension(TwoWindingsTransformerToBeEstimatedAdder.class) .add(); ``` +(voltage-per-reactive-power-control-extension)= ## Voltage per reactive power control This extension is used to model voltage control of static VAR compensators. This extension is attached to a [static VAR compensator](network_subnetwork.md#static-var-compensator). diff --git a/docs/grid_model/network_subnetwork.md b/docs/grid_model/network_subnetwork.md index 2ba1d972832..0b16d86a042 100644 --- a/docs/grid_model/network_subnetwork.md +++ b/docs/grid_model/network_subnetwork.md @@ -18,10 +18,12 @@ To identify non-physical network components, one can use the fictitious property A network can contain several subnetworks. +(validation-level)= ## Validation level The validation level can be set to `EQUIPMENT` or `STEADY_STATE_HYPOTHESIS`. A network at equipment level is a network with missing steady-state hypotheses. This occurs just after SCADA systems, before any state estimation. Once all steady-state hypotheses are filled, meaning that a load flow engine has all the data needed to perform a computation, the validation level switches to `STEADY_STATE_HYPOTHESIS`. For some processes, a minimal validation level of the network is required. +(network)= ## Network [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/Network.html) @@ -39,6 +41,7 @@ The `SourceFormat` attribute is a required attribute that indicates the origin o **Available extensions** +(substation)= ## Substation [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/Substation.html) @@ -57,7 +60,8 @@ All three attributes are optional. **Available extensions** - [ENTSO-E Area](extensions.md#entso-e-area) -## Voltage Level +(voltage-level)= +## Voltage level [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/VoltageLevel.html) A voltage level contains equipment with the same nominal voltage. Two voltage levels may be connected through lines (when they belong to different substations) or through transformers (they must be located within the same substation). @@ -95,6 +99,7 @@ When defining the model, the user has to specify how the different equipment con - [Identifiable Short-Circuit](extensions.md#identifiable-short-circuit) - [Slack Terminal](extensions.md#slack-terminal) +(generator)= ## Generator [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/Generator.html) @@ -136,6 +141,7 @@ Target values for generators (`TargetP` and `TargetQ`) follow the generator sign - [Measurements](extensions.md#measurements) - [Remote Reactive Power Control](extensions.md#remote-reactive-power-control) +(load)= ## Load [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/Load.html) @@ -189,6 +195,7 @@ In the grid model, loads comprise the following metadata: - [Load Detail](extensions.md#load-detail) - [Measurements](extensions.md#measurements) +(battery)= ## Battery [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/Battery.html) @@ -212,6 +219,7 @@ A battery on the electric grid is an energy storage device that is either capabl - [Injection Observability](extensions.md#injection-observability) - [Measurements](extensions.md#measurements) +(dangling-line)= ## Dangling line [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/DanglingLine.html) @@ -271,7 +279,8 @@ are automatically computed using information from the terminal of the dangling l - [Injection Observability](extensions.md#injection-observability) - [Measurements](extensions.md#measurements) -## Shunt Compensator +(shunt-compensator)= +## Shunt compensator [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/ShuntCompensator.html) A shunt compensator represents a shunt capacitor or reactor or a set of switchable banks of shunt capacitors or reactors in the network. A section of a shunt compensator @@ -345,7 +354,8 @@ $B$ and $G$ attributes can be equal zero, but the disconnected status of the non - [Injection Observability](extensions.md#injection-observability) - [Measurements](extensions.md#measurements) -## Static VAR Compensator +(static-var-compensator)= +## Static VAR compensator [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/StaticVarCompensator.html) It may be controlled to hold a voltage or reactive setpoint somewhere in the network (not necessarily directly where it is connected). @@ -393,6 +403,7 @@ In IIDM the static VAR compensator also comprises some metadata: - [Measurements](extensions.md#measurements) - [VoltagePerReactivePowerControl](extensions.md#voltage-per-reactive-power-control) +(line)= ## Line [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/Line.html) @@ -448,13 +459,14 @@ $$ - [Connectable position](extensions.md#connectable-position) - [Branch Observability](extensions.md#branch-observability) -- [Branch Status](extensions.md#branch-status) +- [Operating Status](extensions.md#operating-status) - [CGMES Line Boundary Node](../grid_exchange_formats/cgmes/import.md#cgmes-line-boundary-node) - [Discrete Measurements](extensions.md#discrete-measurements) - [Identifiable Short-Circuit](extensions.md#identifiable-short-circuit) - [Measurements](extensions.md#measurements) -## Tie Line +(tie-line)= +## Tie line [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/TieLine.html) A tie line is an AC line sharing power between two neighbouring regional grids. @@ -478,6 +490,7 @@ A tie line is not a connectable. It is just a container of two underlying dangli ## Transformers +(two-windings-transformer)= ### Two windings transformer [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/TwoWindingsTransformer.html) @@ -538,7 +551,7 @@ $$ **Available extensions** - [Branch Observability](extensions.md#branch-observability) -- [Branch Status](extensions.md#branch-status) +- [Operating Status](extensions.md#operating-status) - [Connectable position](extensions.md#connectable-position) - [Discrete Measurements](extensions.md#discrete-measurements) - [Identifiable Short-Circuit](extensions.md#identifiable-short-circuit) @@ -546,6 +559,7 @@ $$ - [Two-windings Transformer Phase Angle Clock](extensions.md#two-windings-transformer-phase-angle-clock) - [Two-windings Transformer To Be Estimated](extensions.md#two-windings-transformer-to-be-estimated) +(three-windings-transformer)= ### Three windings transformer [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/ThreeWindingsTransformer.html) @@ -573,7 +587,7 @@ For each leg, the network bus is at side 1 and the star bus is at side 2. **Available extensions** -- [Branch Status](extensions.md#branch-status) +- [Operating Status](extensions.md#operating-status) - [Connectable position](extensions.md#connectable-position) - [Discrete Measurements](extensions.md#discrete-measurements) - [Identifiable Short-Circuit](extensions.md#identifiable-short-circuit) @@ -598,7 +612,8 @@ For each leg, the network bus is at side 1 and the star bus is at side 2. - A leg can have [loading limits](./additional.md#loading-limits). -## HVDC Line +(hvdc-line)= +## HVDC line [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/HvdcLine.html) An HVDC line is connected to the DC side of two HVDC converter stations, either an [LCC station](#lcc-converter-station) or a [VSC station](#vsc-converter-station). @@ -627,7 +642,8 @@ An HVDC line is connected to the DC side of two HVDC converter stations, either - [HVDC Angle Droop Active Power Control](extensions.md#hvdc-angle-droop-active-power-control) - [HVDC Operator Active Power Range](extensions.md#hvdc-operator-active-power-range) -## HVDC Converter Station +(hvdc-converter-station)= +## HVDC converter station An HVDC converter station converts electric power from high voltage alternating current (AC) to high-voltage direct current (HVDC), or vice versa. Electronic converters for HVDC are divided into two main categories: line-commutated converters (LCC) and voltage-sourced converters (VSC). @@ -656,7 +672,8 @@ The positive loss factor `LossFactor` is used to model the losses during the con Note that at the terminal on the AC side, $Q$ is always positive: the converter station always consumes reactive power. -### LCC Converter Station +(lcc-converter-station)= +### LCC converter station [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/LccConverterStation.html) An LCC converter station is made with electronic switches that can only be turned on (thyristors). Below are some characteristics: @@ -677,7 +694,8 @@ An LCC converter station is made with electronic switches that can only be turne - [Connectable position](extensions.md#connectable-position) -### VSC Converter Station +(vsc-converter-station)= +### VSC converter station [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/VscConverterStation.html) A VSC converter station is made with switching devices that can be turned both on and off (transistors). Below are some characteristics: @@ -708,7 +726,8 @@ A VSC converter station is made with switching devices that can be turned both o - [Connectable position](extensions.md#connectable-position) -## Busbar Section +(busbar-section)= +## Busbar section [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/BusbarSection.html)
A busbar section is a non impedant element used in a node/breaker substation topology to connect equipment. @@ -725,7 +744,8 @@ A busbar section is a non impedant element used in a node/breaker substation top - [Injection Observability](extensions.md#injection-observability) - [Measurements](extensions.md#measurements) -## Breaker/Switch +(switch)= +## Breaker/switch [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/Switch.html)
@@ -737,7 +757,8 @@ A busbar section is a non impedant element used in a node/breaker substation top - [Discrete Measurements](extensions.md#discrete-measurements) -## Internal Connection +(internal-connection)= +## Internal connection **Internal connection** An internal connection is a non-impedant connection between two components in a voltage level. diff --git a/docs/simulation/dynamic/configuration.md b/docs/simulation/dynamic/configuration.md new file mode 100644 index 00000000000..11ef2ca078c --- /dev/null +++ b/docs/simulation/dynamic/configuration.md @@ -0,0 +1,19 @@ +# Configuration + +## Implementation +If you have several implementation in your classpath, you need to choose which implementation to use in your configuration file with the `default-impl-name` property. +Each implementation is identified by its name, that may be unique in the classpath: +- use "DynaWaltz" to use powsybl-dynawo implementation + +**YAML configuration:** +```yaml +dynamic-simulation: + default-impl-name: Mock +``` + +**XML configuration:** +```xml + + Mock + +``` \ No newline at end of file diff --git a/docs/simulation/dynamic/index.md b/docs/simulation/dynamic/index.md index a86350250f3..c032bb4cc06 100644 --- a/docs/simulation/dynamic/index.md +++ b/docs/simulation/dynamic/index.md @@ -1 +1,53 @@ # Dynamic simulation + +The dynamic simulation aims at capturing the transient response of the system, and not only to compute the steady state solution. +It may or not involve the activation of events like a line disconnection for example. + +```{toctree} +--- +hidden: true +maxdepth: 1 +--- +configuration.md +parameters.md +``` + +## Inputs + +The inputs of a dynamic simulation are the following: +- a static network +- a set of dynamic models provided by the simulator +- a set of parameters associated to each dynamic model, with carefully chosen values +- a mapping between static components of the network and dynamic models +- optionally, a description of events occurring in the dynamic simulation (disconnection of a line, change of tap for a transformer, etc.) +- a set of parameters for the simulator itself (simulation start and stop time, solver parameters, etc.) +- a configuration file to configure the curves to export at the end of the simulation + +### Dynamic models mapping +For the moment, the only way to associate dynamic models to static components is through a groovy script. Note that the syntax of this script is specific to each simulator: +- [Dynawo dynamic model DSL](TODO) + +### Event models mapping +For the moment, the only way to add events to the simulation is through a groovy script. Note that the syntax of this script is specific to each simulator: +- [Dynawo event model DSL](TODO) + +### Curves configuration +For the moment, the only way to monitor dynamic variables of the simulation in order to export curves at the end of the simulation is to provide a groovy script to the simulation. Note that the syntax of this script is specific to each simulator: +- [Dynawo curves DSL](TODO) + +## Outputs + +The outputs of a dynamic simulation are: +- the updated static network (which may have been topologically modified depending on the events or automatons defined as inputs) +- the different results of the dynamic simulation: + - some curves, asked for by the user to track the evolution of specific variables throughout the simulation + - some aggregated data regarding constraints, like a security analysis output + - timelines, that contain the list of events that occurred during the dynamic simulation, be them planned beforehand through events, or not + - logs about the execution of the dynamic simulator + +## Implementations + +For the moment, the only available implementation is provided by powsybl-dynawo, which links PowSyBl with [Dynaωo](http://dynawo.org) open source suite. + +## Going further +- [Run a dynamic simulation through an iTools command](../../user/itools/dynamic-simulation.md): Learn how to perform a dynamic simulation from the command line \ No newline at end of file diff --git a/docs/simulation/dynamic/parameters.md b/docs/simulation/dynamic/parameters.md new file mode 100644 index 00000000000..ac11179cfc5 --- /dev/null +++ b/docs/simulation/dynamic/parameters.md @@ -0,0 +1,50 @@ +# Parameters +The `dynamic-simulation-default-parameters` module is used every time a dynamic-simulation is run. It defines the default values for the most common parameters a `com.powsybl.dynamicsimulation.DynamicSimulation` implementation should be able to handle. + +You may configure some generic parameters for all implementations: +```yaml +dynamic-simulation-default-parameters: + startTime: 0 + stopTime: 1 +``` + +The parameters may also be overridden with a JSON file, in which case the configuration will look like: +```json +{ + "version" : "1.0", + "startTime" : 0, + "stopTime" : 1, + "extensions" : { + ... + } +} +``` + +## Optional properties + +**startTime** +`startTime` defines when the simulation begins, in seconds. The default value of this property is `0`. + +**stopTime** +`stopTime` defines when the simulation stops, in seconds. The default value of this property is `1`. + +## Specific parameters +Some implementations use specific parameters that can be defined in the configuration file or in the JSON parameters file: +- [Dynawo](TODO) + +## Examples + +**YAML configuration:** +```yaml +dynamic-simulation-default-parameters: + startTime: 0 + stopTime: 3600 +``` + +**XML configuration:** +```xml + + 0 + 3600 + +``` diff --git a/docs/simulation/index.md b/docs/simulation/index.md index a659b3bcc63..a120e0fa25d 100644 --- a/docs/simulation/index.md +++ b/docs/simulation/index.md @@ -13,7 +13,7 @@ maxdepth: 1 loadflow/index.md security/index.md -sensitivity/sensitivity.md +sensitivity/index.md shortcircuit/index.md dynamic/index.md ``` \ No newline at end of file diff --git a/docs/simulation/loadflow/configuration.md b/docs/simulation/loadflow/configuration.md index f0e873ad7e5..97ac8f2cf96 100644 --- a/docs/simulation/loadflow/configuration.md +++ b/docs/simulation/loadflow/configuration.md @@ -1,7 +1,7 @@ # Configuration ## Implementation -If you have several implementation in your classpath, you need to choose which implementation to use in your configuration file: +If you have several implementations in your classpath, you need to choose which implementation to use in your configuration file: ```yaml load-flow: default-impl-name: "" @@ -13,6 +13,7 @@ Each implementation is identified by its name, that should be unique in the clas ## Parameters +(loadflow-generic-parameters)= ### Generic parameters You may configure some generic parameters for all load flow implementations: diff --git a/docs/simulation/security/configuration.md b/docs/simulation/security/configuration.md index 07aeccad05a..1ca9a5bf677 100644 --- a/docs/simulation/security/configuration.md +++ b/docs/simulation/security/configuration.md @@ -1,7 +1,9 @@ # Configuration +(security-generic-parameters)= ## Parameters +(violations-increase-thresholds)= ### Violations increase thresholds The user can provide parameters to define which violations must be raised after a contingency, if the violation was already present in the pre-contingency state (`IncreasedViolationsParameters`). @@ -20,6 +22,7 @@ Same as before but for high-voltage violations. **high-voltage-absolute-threshold** Same as before but for high-voltage violations. +(violation-filtering)= ### Violations filtering The violations listed in the results can be filtered to consider only certain type of violations, to consider only few voltage levels or to limit the geographical area by filtering equipments by countries. Check out the documentation of the [limit-violation-default-filter](../../user/configuration/limit-violation-default-filter.md) configuration module. diff --git a/docs/simulation/security/contingency-dsl.md b/docs/simulation/security/contingency-dsl.md index c202ccf0877..3f301c37725 100644 --- a/docs/simulation/security/contingency-dsl.md +++ b/docs/simulation/security/contingency-dsl.md @@ -1,5 +1,5 @@ # Contingency DSL -The contingency DSL is a domain specific language written in groovy for the creation of a contingency list, used in [security analyses](./index.md) or [sensitivity analyses](../sensitivity/sensitivity.md). At the moment, it's possible to simulate the loss of a generator, a static VAR compensator, a shunt, a power line, a power transformer, a HVDC line or a busbar section. +The contingency DSL is a domain specific language written in groovy for the creation of a contingency list, used in [security analyses](./index.md) or [sensitivity analyses](../sensitivity/index). At the moment, it's possible to simulate the loss of a generator, a static VAR compensator, a shunt, a power line, a power transformer, a HVDC line or a busbar section. ## N-1 contingency A N-1 contingency is a contingency that triggers a single equipment at a time. diff --git a/docs/simulation/sensitivity/configuration.md b/docs/simulation/sensitivity/configuration.md new file mode 100644 index 00000000000..67a6d712d2b --- /dev/null +++ b/docs/simulation/sensitivity/configuration.md @@ -0,0 +1,39 @@ +# Configuration + +(sensitivity-implementation-config)= +## Implementation +If you have several implementations in your classpath, you need to choose which implementation to use in your configuration file: +```yaml +sensitivity-analysis: + default-impl-name: "" +``` + +Each implementation is identified by its name, that should be unique in the classpath. +Use "OpenLoadFlow" to use PowSyBl OpenLoadFlow's sensitivity analysis implementation. + +(sensitivity-generic-parameter)= +## Parameters + +**flowFlowSensitivityValueThreshold** + +The `flowFlowSensitivityValueThreshold` is the threshold under which sensitivity values having variable type among +`INJECTION_ACTIVE_POWER`, `INJECTION_REACTIVE_POWER` and `HVDC_LINE_ACTIVE_POWER` and function type among +`BRANCH_ACTIVE_POWER_1/2/3`, `BRANCH_REACTIVE_POWER_1/2/3` and `BRANCH_CURRENT_1/2/3` will be filtered from the +analysis results. The default value is 0.0. + +**voltageVoltageSensitivityValueThreshold** + +The `voltageVoltageSensitivityValueThreshold` is the threshold under which sensitivity values having variable type +`BUS_TARGET_VOLTAGE` and function type `BUS_VOLTAGE` will be filtered from the analysis results. The default value is 0.0. + +**flowVoltageSensitivityValueThreshold** + +The `flowVoltageSensitivityValueThreshold` is the threshold under which sensitivity values having variable type among +`INJECTION_REACTIVE_POWER` and function type among `BUS_VOLTAGE`, or variable type among `BUS_TARGET_VOLTAGE` and function type among +`BRANCH_REACTIVE_POWER_1/2/3`, `BRANCH_CURRENT_1/2/3` or `BUS_REACTIVE_POWER` will be filtered from the analysis results. The default value is 0.0. + +**angleFlowSensitivityValueThreshold** + +The `angleFlowSensitivityValueThreshold` is the threshold under which sensitivity values having variable type among +`TRANSFORMER_PHASE` and `TRANSFORMER_PHASE_1/2/3` and function type among `BRANCH_ACTIVE_POWER_1/2/3`, `BRANCH_REACTIVE_POWER_1/2/3` +and `BRANCH_CURRENT_1/2/3` will be filtered from the analysis results. The default value is 0.0. \ No newline at end of file diff --git a/docs/simulation/sensitivity/index.md b/docs/simulation/sensitivity/index.md new file mode 100644 index 00000000000..9709a764ebe --- /dev/null +++ b/docs/simulation/sensitivity/index.md @@ -0,0 +1,134 @@ +# Sensitivity analysis + +```{toctree} +--- +hidden: true +maxdepth: 1 +--- +configuration.md +``` + +## Introduction + +The sensitivity analysis module is dedicated to computing the linearized impact of small network variations on the state variables of some equipments. + +A sensitivity value is the numerical estimation of the partial derivative of the observed function with respect to the variable of impact. +The sensitivity analysis can also be seen as the computation of partial derivatives on the network model. +For example, it may be used to know, among a group of selected lines, which are the most impacted by a change in a generator production or a change of tap on a phase tap changer. The user story about [RSC capacity calculation](https://www.powsybl.org/pages/documentation/user/user-stories/capacity_calculation_rsc.html) provides an example of application of the sensitivity analysis. + +(sensitivity-analysis-inputs)= +## Inputs + +### Network +The first input for the sensitivity analysis module is an IIDM network. + +(sensitivity-factors)= +### Sensitivity factors +Aside from providing an input network, it is necessary to specify which equipments are going to be studied: +- what impacted equipments are selected to be monitored (lines for example) +- according to a change on which equipment (a generator's production or a group of generator's production, or the tap position of a phase tap changer, etc.) + +It is also necessary to specify which quantity is being observed: the active power or the current on the monitored equipments, the voltage at a bus. + +This set of information constitutes the sensitivity factors (`SensitivityFactor`). These factors correspond to the definition +of the expected partial derivatives to be extracted from the input network. +A standard sensitivity analysis input thus comprises a list of sensitivity factors, each one constituted of: +- a sensitivity variable (the variable of impact) which type is defined by a `SensitivityVariableType`. +- a sensitivity function (the observed function) which type is defined by a `SensitivityFunctionType`. +- a contingency context. + Usually we compute the impact of an injection increase on a branch flow or current, the impact of a shift of a phase tap changer on a branch flow or current or the impact of a voltage target increase on a bus voltage. + +A sensitivity variable represents a change on an equipment or on a group of equipments. The supported variable types are: +- Use `INJECTION_ACTIVE_POWER` to model a change on active production of a generator or on a group of generators, on the active consumption of a load or on a group of loads or on GLSK (for Generation and Load Shift keys) that describes a linear combination of active power injection shifts on generators and loads. The variable increase is in MW. +- Use `INJECTION_REACTIVE_POWER` to model a change on reactive production of a generator or on the reactive consumption of a load. The variable increase is in MVar. +- Use `TRANSFORMER_PHASE` to model the change of the tap position of a phase tap changer of a two windings transformer. The increase is in degree. +- Use `BUS_TARGET_VOLTAGE` to model an increase of the voltage target of a generator, a static var compensator, a two or three windings transformer, a shunt compensator or a VSC converter station. The increase is in KV. +- Use `HVDC_LINE_ACTIVE_POWER` to model the change of the active power set point of an HVDC line. The increase is in MW. +- Use `TRANSFORMER_PHASE_1`, `TRANSFORMER_PHASE_2` or `TRANSFORMER_PHASE_3` to model the change of the tap position of a phase tap changer of a three windings transformer that contains several phase tap changers. + +The supported sensitivity function types, related to the equipment to monitor, are: +- Use `BRANCH_ACTIVE_POWER_1` and `BRANCH_ACTIVE_POWER_2` if you want to monitor the active power in MW of a network branch (lines, two windings transformer, dangling lines, etc.). Use 1 for side 1 and 2 for side 2. In case of a three windings transformer, use `BRANCH_ACTIVE_POWER_3` to monitor the active power in MW of the leg 3 (network side). +- Use `BRANCH_REACTIVE_POWER_1` and `BRANCH_REACTIVE_POWER_2` if you want to monitor the reactive power in MVar of a network branch (lines, two-winding transformer, dangling lines, etc.). Use 1 for side 1 and 2 for side 2. In case of a three-winding transformer, use `BRANCH_REACTIVE_POWER_3` to monitor the reactive power in MVar of the leg 3 (network side). +- Use `BRANCH_CURRENT_1` and `BRANCH_CURRENT_2` if you want to monitor the current in A of a network branch (lines, two windings transformer, dangling lines, etc.). Use 1 for side 1 and use 2 for side 2. In case of a three windings transformer, use `BRANCH_CURRENT_3` to monitor the current in A of the leg 3 (network side). +- `BUS_VOLTAGE` if you want to monitor the voltage in KV of a specific network bus. +- `BUS_REACTIVE_POWER` if you want to monitor the reactive power injection in MVar of a specific network bus. + +A sensitivity variable can group some equipments and has to be modeled as a variable set. In a `SensitivityVariableSet`, we have a list of individual variables, each one with a weight (called `WeightedSensitivityVariable`). We use variable sets to model what it commonly called GLSK. + +#### How to provide the sensitivity factors input + +The sensitivity factors may be created directly through Java code, or be provided to PowSyBl via a JSON file. This file should contain a list of JSON objects, each one representing a sensitivity factor. The example below shows how to write a JSON file to perform a sensitivity analysis on the active power through a line, with respect to a GLSK and to an injection on the network. + +```json +[ { + "functionType" : "BRANCH_ACTIVE_POWER_1", + "functionId" : "l45", + "variableType" : "INJECTION_ACTIVE_POWER", + "variableId" : "glsk", + "variableSet" : true, + "contingencyContextType" : "ALL" +}, { + "functionType" : "BRANCH_ACTIVE_POWER_1", + "functionId" : "l12", + "variableType" : "INJECTION_ACTIVE_POWER", + "variableId" : "g2", + "variableSet" : false, + "contingencyContextType" : "ALL" +} ] +``` + +### Contingencies +The sensitivity analysis may also take, optionally, a list of contingencies as an input. When contingencies are provided, the sensitivity values +shall be calculated on the network at state N, but also after the application of each contingency. The contingencies are provided in the same way than for the [security analysis](../security/index.md/). This then constitutes a systematic sensitivity analysis. + +```json +{ + "type" : "default", + "version" : "1.0", + "name" : "default", + "contingencies" : [ { + "id" : "l34", + "elements" : [ { + "id" : "l34", + "type" : "BRANCH" + } ] + } ] +} +``` + +At the moment the only available sensitivity simulator officially compatible with PowSyBl is the one available through OpenLoadFlow. In this case, the network is provided only once in state N, and then all the calculations are done successively by modifying the Jacobian matrix directly in the solver based on the contingencies input. The network is thus loaded only once, which improves performance. + +(sensitivity-analysis-outputs)= +## Outputs + +(sensitivity-values)= +### Sensitivity values +The outputs of the sensitivity analysis are called sensitivity values. A sensitivity value represents an elementary result given a sensitivity factor and a contingency, and contains: +- The actual value of the partial derivative +- The reference value of the function at linearization point in case of contingency context `NONE` or in its post-contingency state in case of a factor associated to a contingency. + +These results may be serialized in JSON format. + +### Example of interpretation +Let's imagine that one wants to compute the impact of an increase of active power generation of the +generator G on the branch B. The sensitivity analysis input will contain one sensitivity factor, with sensitivity function type `BRANCH_ACTIVE_POWER_1` and sensitivity variable type `INJECTION_ACTIVE_POWER`, and we do not provide any input contingencies. + +After the computation, let us consider that the values of the three elements of the sensitivity result are: +- a value of -0.05 for the partial derivative +- a variable reference value of 150 +- a function reference value of 265 + +This can be interpreted in the following way: +- an increase of 100 MW on generator G may be approximated on branch B as a 5MW decrease of the active flow from side 1 to side 2 +- the initial generation on generator G is 150MW +- the initial active flow on branch B is 265MW from side 1 to side 2 + +## Implementations + +The following sensitivity analysis implementations are supported: +- [PowSyBl OpenLoadFlow]() + +## Going further + +To go further about the sensitivity analysis, check the following content: +- [Sensitivity analysis tutorial](https://github.com/powsybl/powsybl-tutorials/tree/main/sensitivity) \ No newline at end of file diff --git a/docs/simulation/sensitivity/sensitivity.md b/docs/simulation/sensitivity/sensitivity.md deleted file mode 100644 index 30f248ddf6e..00000000000 --- a/docs/simulation/sensitivity/sensitivity.md +++ /dev/null @@ -1 +0,0 @@ -# Sensitivity analysis \ No newline at end of file diff --git a/docs/user/itools/dynamic-simulation.md b/docs/user/itools/dynamic-simulation.md index 966eb3c1d56..77d6cb0ca8e 100644 --- a/docs/user/itools/dynamic-simulation.md +++ b/docs/user/itools/dynamic-simulation.md @@ -1 +1,93 @@ # iTools dynamic-simulation + +The `dynamic-simulation` command loads a grid file and run a [time domain](../../simulation/dynamic/index.md) simulation. +At the end, the results and the modified network can be exported to files. + +## Usage +``` +usage: itools [OPTIONS] dynamic-simulation --case-file [--curves-file + ] --dynamic-models-file [--event-models-file ] + [--help] [-I ] [--import-parameters ] + [--output-file ] [--parameters-file ] + +Available options are: + --config-name Override configuration file name + +Available arguments are: + --case-file the case path + --curves-file curves description as Groovy file + --dynamic-models-file dynamic models description as a + Groovy file: defines the dynamic + models to be associated to chosen + equipments of the network + --event-models-file dynamic event models description + as a Groovy file: defines the + dynamic event models to be + associated to chosen equipments of + the network + --help display the help and quit + -I use value for given importer + parameter + --import-parameters the importer configuation file + --output-file dynamic simulation results output + path + --parameters-file dynamic simulation parameters as + JSON file +``` + +### Required options + +**\-\-case-file**: This option defines the path of the case file on which the simulation is run. The [supported formats](../../grid_exchange_formats/index.md) depend on the execution class path. + +**\-\-dynamic-models-file**: This option defines the path of the mapping file used to associate dynamic models to static equipments of the network. At the moment, only groovy scripts are supported. The [dynamic models DSL](../../simulation/dynamic/index.md#dynamic-models-mapping) depends on the simulator used. + +### Optional options + +**\-\-curves-file**: This option defines the path of the configuration for the curves to export at the end of the simulation. This configuration file is a groovy script that respects the [curves DSL](../../simulation/dynamic/index.md#curves-configuration) syntax. + +**\-\-event-models-file**: This option defines the path of the configuration for the events to simulate during the simulation. At the moment, only groovy scripts are supported. The [event models DSL](../../simulation/dynamic/index.md#event-models-mapping) depends on the simulator used. + +**\-\-import-parameters** +This option defines the path of the importer's configuration file. It's possible to overload one or many parameters using the `-I property=value` syntax. The list of supported properties depends on the [input format](../../grid_exchange_formats/index.md). + +**\-\-output-file** +This option defines the path where to export the [results](#results) of the simulation. + +**\-\-parameters-file** +This option defines the path of the [parameters](#parameters) file of the simulation. If this option is not used, the simulation is run with the default parameters. + +## Simulators + +The available power flow simulators implementations are described [here](../../simulation/dynamic/index.md#implementations). + +## Parameters +The available parameters are described [here](../../simulation/dynamic/index.md#parameters). + +## Results +The expected results are described in the [time domain documentation](../../simulation/dynamic/index.md#outputs) + +## Examples +The following example shows how to run a power flow simulation, using the default configuration: +``` +$> itools dynamic-simulation --case-file IEEE14.iidm --dynamic-models-file dynamicModels.groovy --curves-file curves.groovy +Loading network '/tmp/mathbagu/IEEE14.iidm' +dynamic simulation results: ++--------+ +| Result | ++--------+ +| true | ++--------+ +``` + +The following example shows how to run a time domain simulation, using a parameters file: +``` +$> itools dynamic-simulation --case-file IEEE14.iidm --dynamic-models-file dynamicModels.groovy --parameters-file dynawoParameters.json +dynamic simulation results: ++--------+ +| Result | ++--------+ +| true | ++--------+ +``` + +## See also diff --git a/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/AtLeastOneCountryCriterion.java b/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/AtLeastOneCountryCriterion.java index 309172be944..a68ef248404 100644 --- a/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/AtLeastOneCountryCriterion.java +++ b/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/AtLeastOneCountryCriterion.java @@ -17,7 +17,9 @@ import java.util.Objects; /** - *

Criterion checking that at least one side of a network element belongs to a country defined in a list.

+ *

Criterion checking that one of the sides of the network element belongs to a country defined in a list.

+ *

When {@link #filter(NetworkElement, ThreeSides)}} is called with a non-null side, only the country + * on this particular side is checked.

* @author Olivier Perrin {@literal } */ public class AtLeastOneCountryCriterion implements Criterion { @@ -40,7 +42,12 @@ public boolean filter(Identifiable identifiable, IdentifiableType type) { @Override public boolean filter(NetworkElement networkElement) { - return filterWithCountries(getCountriesToCheck(networkElement)); + return filter(networkElement, null); + } + + @Override + public boolean filter(NetworkElement networkElement, ThreeSides side) { + return filterWithCountries(getCountriesToCheck(networkElement, side)); } public List getCountries() { @@ -55,8 +62,12 @@ private List getCountriesToCheck(Identifiable identifiable, Identifi } } - private List getCountriesToCheck(NetworkElement networkElement) { - return Arrays.asList(networkElement.getCountry1().orElse(null), networkElement.getCountry2().orElse(null)); + private List getCountriesToCheck(NetworkElement networkElement, ThreeSides side) { + return side != null ? + Collections.singletonList(networkElement.getCountry(side).orElse(null)) : + Arrays.asList(networkElement.getCountry1().orElse(null), + networkElement.getCountry2().orElse(null), + networkElement.getCountry3().orElse(null)); } private boolean filterWithCountries(List countriesToCheck) { diff --git a/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/AtLeastOneNominalVoltageCriterion.java b/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/AtLeastOneNominalVoltageCriterion.java index 5d75d0892fb..e7fb50f37e7 100644 --- a/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/AtLeastOneNominalVoltageCriterion.java +++ b/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/AtLeastOneNominalVoltageCriterion.java @@ -11,6 +11,7 @@ import com.powsybl.iidm.criteria.translation.NetworkElement; import com.powsybl.iidm.network.Identifiable; import com.powsybl.iidm.network.IdentifiableType; +import com.powsybl.iidm.network.ThreeSides; import java.util.Arrays; import java.util.Collections; @@ -18,8 +19,10 @@ import java.util.Objects; /** - *

Criterion checking that one of the nominal voltages of the network element, on whichever side, + *

Criterion checking that one of the nominal voltages of the network element * is inside a {@link VoltageInterval}.

+ *

When filter is called with a non-null side, the voltage level on this particular side + * should be inside the {@link VoltageInterval}. Else, the validation is performed on whichever side.

* @author Olivier Perrin {@literal } */ public class AtLeastOneNominalVoltageCriterion implements Criterion { @@ -43,7 +46,12 @@ public boolean filter(Identifiable identifiable, IdentifiableType type) { @Override public boolean filter(NetworkElement networkElement) { - return filterNominalVoltages(getNominalVoltagesToCheck(networkElement)); + return filter(networkElement, null); + } + + @Override + public boolean filter(NetworkElement networkElement, ThreeSides side) { + return filterNominalVoltages(getNominalVoltagesToCheck(networkElement, side)); } private List getNominalVoltagesToCheck(Identifiable identifiable, IdentifiableType type) { @@ -56,10 +64,12 @@ private List getNominalVoltagesToCheck(Identifiable identifiable, Ide }; } - private List getNominalVoltagesToCheck(NetworkElement networkElement) { - return Arrays.asList(networkElement.getNominalVoltage1().orElse(null), - networkElement.getNominalVoltage2().orElse(null), - networkElement.getNominalVoltage3().orElse(null)); + private List getNominalVoltagesToCheck(NetworkElement networkElement, ThreeSides side) { + return side != null ? + Collections.singletonList(networkElement.getNominalVoltage(side).orElse(null)) : + Arrays.asList(networkElement.getNominalVoltage1().orElse(null), + networkElement.getNominalVoltage2().orElse(null), + networkElement.getNominalVoltage3().orElse(null)); } private boolean filterNominalVoltages(List nominalVoltagesToCheck) { diff --git a/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/Criterion.java b/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/Criterion.java index d7e0d3153b7..93a28b2a387 100644 --- a/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/Criterion.java +++ b/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/Criterion.java @@ -10,6 +10,7 @@ import com.powsybl.iidm.network.Identifiable; import com.powsybl.iidm.network.IdentifiableType; import com.powsybl.iidm.criteria.translation.NetworkElement; +import com.powsybl.iidm.network.ThreeSides; /** * the purpose of these class is to filter contingencies in a criterion contingency list @@ -45,4 +46,8 @@ default boolean filter(Identifiable identifiable, IdentifiableType type) { default boolean filter(NetworkElement networkElement) { return false; } + + default boolean filter(NetworkElement networkElement, ThreeSides side) { + return filter(networkElement); + } } diff --git a/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/NetworkElementVisitor.java b/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/NetworkElementVisitor.java index 3463343398f..d7297be8c6f 100644 --- a/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/NetworkElementVisitor.java +++ b/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/NetworkElementVisitor.java @@ -8,6 +8,7 @@ package com.powsybl.iidm.criteria; import com.powsybl.iidm.criteria.translation.NetworkElement; +import com.powsybl.iidm.network.ThreeSides; /** *

Visitor used to detect if a given {@link NetworkElement} validates criteria.

@@ -16,22 +17,28 @@ public class NetworkElementVisitor { private final NetworkElement networkElement; + private final ThreeSides side; public NetworkElementVisitor(NetworkElement networkElement) { + this(networkElement, null); + } + + public NetworkElementVisitor(NetworkElement networkElement, ThreeSides side) { this.networkElement = networkElement; + this.side = side; } public boolean visit(AbstractNetworkElementEquipmentCriterion c) { return networkElement.isValidFor(c.getNetworkElementCriterionType()) - && doRespectCriterion(networkElement, c.getCountryCriterion()) - && doRespectCriterion(networkElement, c.getNominalVoltageCriterion()); + && doRespectCriterion(c.getCountryCriterion()) + && doRespectCriterion(c.getNominalVoltageCriterion()); } public boolean visit(NetworkElementIdListCriterion c) { return c.getNetworkElementIds().contains(networkElement.getId()); } - private boolean doRespectCriterion(NetworkElement networkElement, Criterion criterion) { - return criterion == null || criterion.filter(networkElement); + private boolean doRespectCriterion(Criterion criterion) { + return criterion == null || criterion.filter(networkElement, side); } } diff --git a/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/translation/DefaultNetworkElementAdapter.java b/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/translation/DefaultNetworkElementAdapter.java index ed00fb4c5e8..351baae10ec 100644 --- a/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/translation/DefaultNetworkElementAdapter.java +++ b/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/translation/DefaultNetworkElementAdapter.java @@ -47,12 +47,17 @@ public String getId() { @Override public Optional getCountry1() { - return getCountry(TwoSides.ONE); + return getCountry(ThreeSides.ONE); } @Override public Optional getCountry2() { - return getCountry(TwoSides.TWO); + return getCountry(ThreeSides.TWO); + } + + @Override + public Optional getCountry3() { + return getCountry(ThreeSides.THREE); } @Override @@ -60,16 +65,17 @@ public Optional getCountry() { return getCountry1(); } - private Optional getCountry(TwoSides side) { + @Override + public Optional getCountry(ThreeSides side) { return switch (identifiable.getType()) { - case LINE, TIE_LINE -> getCountryFromTerminal(((Branch) identifiable).getTerminal(side)); - case HVDC_LINE -> getCountryFromTerminal(((HvdcLine) identifiable).getConverterStation(side).getTerminal()); + case LINE, TIE_LINE -> side != ThreeSides.THREE ? + getCountryFromTerminal(((Branch) identifiable).getTerminal(side.toTwoSides())) : Optional.empty(); + case HVDC_LINE -> side != ThreeSides.THREE ? + getCountryFromTerminal(((HvdcLine) identifiable).getConverterStation(side.toTwoSides()).getTerminal()) : Optional.empty(); case DANGLING_LINE, GENERATOR, LOAD, BATTERY, SHUNT_COMPENSATOR, STATIC_VAR_COMPENSATOR, BUSBAR_SECTION, HVDC_CONVERTER_STATION -> - side != TwoSides.ONE ? Optional.empty() : getCountryFromTerminal(((Injection) identifiable).getTerminal()); - case TWO_WINDINGS_TRANSFORMER -> side != TwoSides.ONE ? Optional.empty() : - ((TwoWindingsTransformer) identifiable).getSubstation().map(Substation::getNullableCountry); - case THREE_WINDINGS_TRANSFORMER -> side != TwoSides.ONE ? Optional.empty() : - ((ThreeWindingsTransformer) identifiable).getSubstation().map(Substation::getNullableCountry); + side != ThreeSides.ONE ? Optional.empty() : getCountryFromTerminal(((Injection) identifiable).getTerminal()); + case TWO_WINDINGS_TRANSFORMER -> ((TwoWindingsTransformer) identifiable).getSubstation().map(Substation::getNullableCountry); + case THREE_WINDINGS_TRANSFORMER -> ((ThreeWindingsTransformer) identifiable).getSubstation().map(Substation::getNullableCountry); default -> Optional.empty(); }; } @@ -99,7 +105,8 @@ public Optional getNominalVoltage() { return getNominalVoltage1(); } - private Optional getNominalVoltage(ThreeSides side) { + @Override + public Optional getNominalVoltage(ThreeSides side) { return switch (identifiable.getType()) { case DANGLING_LINE, GENERATOR, LOAD, BATTERY, SHUNT_COMPENSATOR, STATIC_VAR_COMPENSATOR, BUSBAR_SECTION, HVDC_CONVERTER_STATION -> side != ThreeSides.ONE ? Optional.empty() : diff --git a/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/translation/NetworkElement.java b/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/translation/NetworkElement.java index a6d542f8a9e..35a297c7502 100644 --- a/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/translation/NetworkElement.java +++ b/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/translation/NetworkElement.java @@ -9,6 +9,7 @@ import com.powsybl.iidm.criteria.NetworkElementCriterion; import com.powsybl.iidm.network.Country; +import com.powsybl.iidm.network.ThreeSides; import java.util.Optional; @@ -25,14 +26,32 @@ public interface NetworkElement { Optional getCountry2(); + Optional getCountry3(); + Optional getCountry(); + default Optional getCountry(ThreeSides side) { + return switch (side) { + case ONE -> getCountry1(); + case TWO -> getCountry2(); + case THREE -> getCountry3(); + }; + } + Optional getNominalVoltage1(); Optional getNominalVoltage2(); Optional getNominalVoltage3(); + default Optional getNominalVoltage(ThreeSides side) { + return switch (side) { + case ONE -> getNominalVoltage1(); + case TWO -> getNominalVoltage2(); + case THREE -> getNominalVoltage3(); + }; + } + Optional getNominalVoltage(); boolean isValidFor(NetworkElementCriterion.NetworkElementCriterionType networkElementCriterionType); diff --git a/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/DanglingLineCriterionTest.java b/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/DanglingLineCriterionTest.java index 73caf4c5df8..ab94633fb77 100644 --- a/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/DanglingLineCriterionTest.java +++ b/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/DanglingLineCriterionTest.java @@ -7,18 +7,19 @@ */ package com.powsybl.iidm.criteria; +import com.powsybl.iidm.criteria.translation.DefaultNetworkElementAdapter; import com.powsybl.iidm.criteria.translation.NetworkElement; import com.powsybl.iidm.network.Country; +import com.powsybl.iidm.network.DanglingLine; +import com.powsybl.iidm.network.ThreeSides; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import java.util.List; -import java.util.Optional; +import static com.powsybl.iidm.criteria.translation.DefaultNetworkElementAdapterTest.mockDanglingLine; import static org.junit.jupiter.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.mockito.Mockito.when; /** * @author Olivier Perrin {@literal } @@ -46,10 +47,10 @@ void typeTest() { @Test void emptyCriterionTest() { DanglingLineCriterion criterion = new DanglingLineCriterion(null, null); - assertTrue(criterion.accept(new NetworkElementVisitor(danglingLine1))); - assertTrue(criterion.accept(new NetworkElementVisitor(danglingLine2))); - assertTrue(criterion.accept(new NetworkElementVisitor(danglingLine3))); - assertTrue(criterion.accept(new NetworkElementVisitor(danglingLine4))); + assertCriterionTrue(criterion, danglingLine1); + assertCriterionTrue(criterion, danglingLine2); + assertCriterionTrue(criterion, danglingLine3); + assertCriterionTrue(criterion, danglingLine4); NetworkElement anotherTypeElement = Mockito.mock(NetworkElement.class); assertFalse(criterion.accept(new NetworkElementVisitor(anotherTypeElement))); @@ -59,25 +60,25 @@ void emptyCriterionTest() { void nominalVoltageTest() { DanglingLineCriterion criterion = new DanglingLineCriterion(null, new SingleNominalVoltageCriterion( VoltageInterval.between(40., 100., true, true))); - assertTrue(criterion.accept(new NetworkElementVisitor(danglingLine1))); - assertFalse(criterion.accept(new NetworkElementVisitor(danglingLine2))); - assertFalse(criterion.accept(new NetworkElementVisitor(danglingLine3))); - assertTrue(criterion.accept(new NetworkElementVisitor(danglingLine4))); + assertCriterionTrue(criterion, danglingLine1); + assertCriterionFalse(criterion, danglingLine2); + assertCriterionFalse(criterion, danglingLine3); + assertCriterionTrue(criterion, danglingLine4); } @Test void countryTest() { DanglingLineCriterion criterion = new DanglingLineCriterion(new SingleCountryCriterion(List.of(Country.BE)), null); - assertFalse(criterion.accept(new NetworkElementVisitor(danglingLine1))); - assertFalse(criterion.accept(new NetworkElementVisitor(danglingLine2))); - assertTrue(criterion.accept(new NetworkElementVisitor(danglingLine3))); - assertTrue(criterion.accept(new NetworkElementVisitor(danglingLine4))); + assertCriterionFalse(criterion, danglingLine1); + assertCriterionFalse(criterion, danglingLine2); + assertCriterionTrue(criterion, danglingLine3); + assertCriterionTrue(criterion, danglingLine4); criterion = new DanglingLineCriterion(new SingleCountryCriterion(List.of(Country.FR, Country.BE)), null); - assertTrue(criterion.accept(new NetworkElementVisitor(danglingLine1))); - assertTrue(criterion.accept(new NetworkElementVisitor(danglingLine2))); - assertTrue(criterion.accept(new NetworkElementVisitor(danglingLine3))); - assertTrue(criterion.accept(new NetworkElementVisitor(danglingLine4))); + assertCriterionTrue(criterion, danglingLine1); + assertCriterionTrue(criterion, danglingLine2); + assertCriterionTrue(criterion, danglingLine3); + assertCriterionTrue(criterion, danglingLine4); } @Test @@ -85,25 +86,25 @@ void mixedCriteriaTest() { DanglingLineCriterion criterion = new DanglingLineCriterion(new SingleCountryCriterion(List.of(Country.FR)), new SingleNominalVoltageCriterion( VoltageInterval.between(350., 450., true, true))); - assertFalse(criterion.accept(new NetworkElementVisitor(danglingLine1))); - assertTrue(criterion.accept(new NetworkElementVisitor(danglingLine2))); - assertFalse(criterion.accept(new NetworkElementVisitor(danglingLine3))); - assertFalse(criterion.accept(new NetworkElementVisitor(danglingLine4))); + assertCriterionFalse(criterion, danglingLine1); + assertCriterionTrue(criterion, danglingLine2); + assertCriterionFalse(criterion, danglingLine3); + assertCriterionFalse(criterion, danglingLine4); + } + + private void assertCriterionTrue(DanglingLineCriterion criterion, NetworkElement danglingLine) { + assertTrue(criterion.accept(new NetworkElementVisitor(danglingLine))); + assertTrue(criterion.accept(new NetworkElementVisitor(danglingLine, ThreeSides.ONE))); + } + + private void assertCriterionFalse(DanglingLineCriterion criterion, NetworkElement danglingLine) { + assertFalse(criterion.accept(new NetworkElementVisitor(danglingLine))); + assertFalse(criterion.accept(new NetworkElementVisitor(danglingLine, ThreeSides.ONE))); } protected static NetworkElement createDanglingLine(String id, Country country, double nominalVoltage) { - NetworkElement n = Mockito.mock(NetworkElement.class); - when(n.getId()).thenReturn(id); - when(n.getCountry()).thenReturn(Optional.of(country)); - when(n.getCountry1()).thenReturn(Optional.of(country)); - when(n.getCountry2()).thenReturn(Optional.empty()); - when(n.getNominalVoltage()).thenReturn(Optional.of(nominalVoltage)); - when(n.getNominalVoltage1()).thenReturn(Optional.of(nominalVoltage)); - when(n.getNominalVoltage2()).thenReturn(Optional.empty()); - when(n.getNominalVoltage3()).thenReturn(Optional.empty()); - when(n.isValidFor(NetworkElementCriterion.NetworkElementCriterionType.DANGLING_LINE)).thenReturn(true); - when(n.isValidFor(NetworkElementCriterion.NetworkElementCriterionType.IDENTIFIABLE)).thenReturn(true); - return n; + DanglingLine danglingLine = mockDanglingLine(id, country, nominalVoltage); + return new DefaultNetworkElementAdapter(danglingLine); } } diff --git a/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/IdentifiableCriterionTest.java b/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/IdentifiableCriterionTest.java index 7c549f46a37..6c46bffd98f 100644 --- a/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/IdentifiableCriterionTest.java +++ b/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/IdentifiableCriterionTest.java @@ -9,6 +9,7 @@ import com.powsybl.iidm.criteria.translation.NetworkElement; import com.powsybl.iidm.network.Country; +import com.powsybl.iidm.network.ThreeSides; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -75,46 +76,125 @@ void creationErrorsTest() { } @Test - void nominalVoltageTest() { + void linesNominalVoltageTest() { IdentifiableCriterion criterion = new IdentifiableCriterion(new AtLeastOneNominalVoltageCriterion( - VoltageInterval.between(40., 100., true, true))); + VoltageInterval.between(80., 100., true, true))); assertTrue(criterion.accept(new NetworkElementVisitor(danglingLine1))); + assertTrue(criterion.accept(new NetworkElementVisitor(danglingLine1, ThreeSides.ONE))); assertFalse(criterion.accept(new NetworkElementVisitor(danglingLine2))); + assertFalse(criterion.accept(new NetworkElementVisitor(danglingLine2, ThreeSides.ONE))); assertTrue(criterion.accept(new NetworkElementVisitor(line1))); + assertTrue(criterion.accept(new NetworkElementVisitor(line1, ThreeSides.ONE))); + assertTrue(criterion.accept(new NetworkElementVisitor(line1, ThreeSides.TWO))); assertFalse(criterion.accept(new NetworkElementVisitor(line2))); + assertFalse(criterion.accept(new NetworkElementVisitor(line2, ThreeSides.ONE))); + assertFalse(criterion.accept(new NetworkElementVisitor(line2, ThreeSides.TWO))); assertTrue(criterion.accept(new NetworkElementVisitor(tieLine1))); + assertTrue(criterion.accept(new NetworkElementVisitor(tieLine1, ThreeSides.ONE))); + assertTrue(criterion.accept(new NetworkElementVisitor(tieLine1, ThreeSides.TWO))); assertFalse(criterion.accept(new NetworkElementVisitor(tieLine2))); + assertFalse(criterion.accept(new NetworkElementVisitor(tieLine2, ThreeSides.ONE))); + assertFalse(criterion.accept(new NetworkElementVisitor(tieLine2, ThreeSides.TWO))); + } + + @Test + void transformersNominalVoltageTest() { + IdentifiableCriterion criterion = new IdentifiableCriterion(new AtLeastOneNominalVoltageCriterion( + VoltageInterval.between(80., 100., true, true))); assertTrue(criterion.accept(new NetworkElementVisitor(twoWt1))); + assertTrue(criterion.accept(new NetworkElementVisitor(twoWt1, ThreeSides.ONE))); + assertFalse(criterion.accept(new NetworkElementVisitor(twoWt1, ThreeSides.TWO))); assertFalse(criterion.accept(new NetworkElementVisitor(twoWt2))); + assertFalse(criterion.accept(new NetworkElementVisitor(twoWt2, ThreeSides.ONE))); + assertFalse(criterion.accept(new NetworkElementVisitor(twoWt2, ThreeSides.TWO))); assertTrue(criterion.accept(new NetworkElementVisitor(threeWt1))); + assertFalse(criterion.accept(new NetworkElementVisitor(threeWt1, ThreeSides.ONE))); + assertTrue(criterion.accept(new NetworkElementVisitor(threeWt1, ThreeSides.TWO))); + assertFalse(criterion.accept(new NetworkElementVisitor(threeWt1, ThreeSides.THREE))); assertFalse(criterion.accept(new NetworkElementVisitor(threeWt2))); + assertFalse(criterion.accept(new NetworkElementVisitor(threeWt2, ThreeSides.ONE))); + assertFalse(criterion.accept(new NetworkElementVisitor(threeWt2, ThreeSides.TWO))); + assertFalse(criterion.accept(new NetworkElementVisitor(threeWt2, ThreeSides.THREE))); } @Test - void countriesTest() { + void lineOneCountryTest() { IdentifiableCriterion criterion = new IdentifiableCriterion(new AtLeastOneCountryCriterion(List.of(Country.FR))); assertFalse(criterion.accept(new NetworkElementVisitor(danglingLine1))); + assertFalse(criterion.accept(new NetworkElementVisitor(danglingLine1, ThreeSides.ONE))); assertTrue(criterion.accept(new NetworkElementVisitor(danglingLine2))); + assertTrue(criterion.accept(new NetworkElementVisitor(danglingLine2, ThreeSides.ONE))); assertFalse(criterion.accept(new NetworkElementVisitor(line1))); + assertFalse(criterion.accept(new NetworkElementVisitor(line1, ThreeSides.ONE))); + assertFalse(criterion.accept(new NetworkElementVisitor(line1, ThreeSides.TWO))); assertTrue(criterion.accept(new NetworkElementVisitor(line2))); + assertTrue(criterion.accept(new NetworkElementVisitor(line2, ThreeSides.ONE))); + assertTrue(criterion.accept(new NetworkElementVisitor(line2, ThreeSides.TWO))); assertFalse(criterion.accept(new NetworkElementVisitor(tieLine1))); + assertFalse(criterion.accept(new NetworkElementVisitor(tieLine1, ThreeSides.ONE))); + assertFalse(criterion.accept(new NetworkElementVisitor(tieLine1, ThreeSides.TWO))); assertTrue(criterion.accept(new NetworkElementVisitor(tieLine2))); + assertFalse(criterion.accept(new NetworkElementVisitor(tieLine2, ThreeSides.ONE))); + assertTrue(criterion.accept(new NetworkElementVisitor(tieLine2, ThreeSides.TWO))); + } + + @Test + void transformersOneCountryTest() { + IdentifiableCriterion criterion = new IdentifiableCriterion(new AtLeastOneCountryCriterion(List.of(Country.FR))); assertFalse(criterion.accept(new NetworkElementVisitor(twoWt1))); + assertFalse(criterion.accept(new NetworkElementVisitor(twoWt1, ThreeSides.ONE))); + assertFalse(criterion.accept(new NetworkElementVisitor(twoWt1, ThreeSides.TWO))); assertTrue(criterion.accept(new NetworkElementVisitor(twoWt2))); + assertTrue(criterion.accept(new NetworkElementVisitor(twoWt2, ThreeSides.ONE))); + assertTrue(criterion.accept(new NetworkElementVisitor(twoWt2, ThreeSides.TWO))); assertFalse(criterion.accept(new NetworkElementVisitor(threeWt1))); + assertFalse(criterion.accept(new NetworkElementVisitor(threeWt1, ThreeSides.ONE))); + assertFalse(criterion.accept(new NetworkElementVisitor(threeWt1, ThreeSides.TWO))); + assertFalse(criterion.accept(new NetworkElementVisitor(threeWt1, ThreeSides.THREE))); assertTrue(criterion.accept(new NetworkElementVisitor(threeWt2))); + assertTrue(criterion.accept(new NetworkElementVisitor(threeWt2, ThreeSides.ONE))); + assertTrue(criterion.accept(new NetworkElementVisitor(threeWt2, ThreeSides.TWO))); + assertTrue(criterion.accept(new NetworkElementVisitor(threeWt2, ThreeSides.THREE))); + } - criterion = new IdentifiableCriterion(new AtLeastOneCountryCriterion(List.of(Country.FR, Country.BE))); + @Test + void linesTwoCountriesTest() { + IdentifiableCriterion criterion = new IdentifiableCriterion(new AtLeastOneCountryCriterion(List.of(Country.FR, Country.BE))); assertTrue(criterion.accept(new NetworkElementVisitor(danglingLine1))); + assertTrue(criterion.accept(new NetworkElementVisitor(danglingLine1, ThreeSides.ONE))); assertTrue(criterion.accept(new NetworkElementVisitor(danglingLine2))); + assertTrue(criterion.accept(new NetworkElementVisitor(danglingLine2, ThreeSides.ONE))); assertTrue(criterion.accept(new NetworkElementVisitor(line1))); + assertTrue(criterion.accept(new NetworkElementVisitor(line1, ThreeSides.ONE))); + assertTrue(criterion.accept(new NetworkElementVisitor(line1, ThreeSides.TWO))); assertTrue(criterion.accept(new NetworkElementVisitor(line2))); + assertTrue(criterion.accept(new NetworkElementVisitor(line2, ThreeSides.ONE))); + assertTrue(criterion.accept(new NetworkElementVisitor(line2, ThreeSides.TWO))); assertTrue(criterion.accept(new NetworkElementVisitor(tieLine1))); + assertTrue(criterion.accept(new NetworkElementVisitor(tieLine1, ThreeSides.ONE))); + assertFalse(criterion.accept(new NetworkElementVisitor(tieLine1, ThreeSides.TWO))); assertTrue(criterion.accept(new NetworkElementVisitor(tieLine2))); + assertFalse(criterion.accept(new NetworkElementVisitor(tieLine2, ThreeSides.ONE))); + assertTrue(criterion.accept(new NetworkElementVisitor(tieLine2, ThreeSides.TWO))); + } + + @Test + void transformersTwoCountriesTest() { + IdentifiableCriterion criterion = new IdentifiableCriterion(new AtLeastOneCountryCriterion(List.of(Country.FR, Country.BE))); assertTrue(criterion.accept(new NetworkElementVisitor(twoWt1))); + assertTrue(criterion.accept(new NetworkElementVisitor(twoWt1, ThreeSides.ONE))); + assertTrue(criterion.accept(new NetworkElementVisitor(twoWt1, ThreeSides.TWO))); assertTrue(criterion.accept(new NetworkElementVisitor(twoWt2))); + assertTrue(criterion.accept(new NetworkElementVisitor(twoWt2, ThreeSides.ONE))); + assertTrue(criterion.accept(new NetworkElementVisitor(twoWt2, ThreeSides.TWO))); assertTrue(criterion.accept(new NetworkElementVisitor(threeWt1))); + assertTrue(criterion.accept(new NetworkElementVisitor(threeWt1, ThreeSides.ONE))); + assertTrue(criterion.accept(new NetworkElementVisitor(threeWt1, ThreeSides.TWO))); + assertTrue(criterion.accept(new NetworkElementVisitor(threeWt1, ThreeSides.THREE))); assertTrue(criterion.accept(new NetworkElementVisitor(threeWt2))); + assertTrue(criterion.accept(new NetworkElementVisitor(threeWt2, ThreeSides.ONE))); + assertTrue(criterion.accept(new NetworkElementVisitor(threeWt2, ThreeSides.TWO))); + assertTrue(criterion.accept(new NetworkElementVisitor(threeWt2, ThreeSides.THREE))); } @Test @@ -129,8 +209,12 @@ void mixedCriteriaTest() { assertFalse(criterion.accept(new NetworkElementVisitor(tieLine2))); assertFalse(criterion.accept(new NetworkElementVisitor(twoWt1))); assertTrue(criterion.accept(new NetworkElementVisitor(twoWt2))); + assertTrue(criterion.accept(new NetworkElementVisitor(twoWt2, ThreeSides.ONE))); + assertFalse(criterion.accept(new NetworkElementVisitor(twoWt2, ThreeSides.TWO))); assertFalse(criterion.accept(new NetworkElementVisitor(threeWt1))); assertTrue(criterion.accept(new NetworkElementVisitor(threeWt2))); + assertFalse(criterion.accept(new NetworkElementVisitor(threeWt2, ThreeSides.ONE))); + assertTrue(criterion.accept(new NetworkElementVisitor(threeWt2, ThreeSides.TWO))); + assertFalse(criterion.accept(new NetworkElementVisitor(threeWt2, ThreeSides.THREE))); } - } diff --git a/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/LineCriterionTest.java b/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/LineCriterionTest.java index 8ac572c87ee..6fef4b3756b 100644 --- a/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/LineCriterionTest.java +++ b/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/LineCriterionTest.java @@ -7,17 +7,19 @@ */ package com.powsybl.iidm.criteria; -import com.powsybl.iidm.network.Country; +import com.powsybl.iidm.criteria.translation.DefaultNetworkElementAdapter; import com.powsybl.iidm.criteria.translation.NetworkElement; +import com.powsybl.iidm.network.Country; +import com.powsybl.iidm.network.Line; +import com.powsybl.iidm.network.ThreeSides; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import java.util.List; -import java.util.Optional; +import static com.powsybl.iidm.criteria.translation.DefaultNetworkElementAdapterTest.mockLine; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.when; /** * @author Olivier Perrin {@literal } @@ -45,13 +47,13 @@ void typeTest() { @Test void emptyCriterionTest() { LineCriterion criterion = new LineCriterion(null, null); - assertTrue(criterion.accept(new NetworkElementVisitor(line1))); - assertTrue(criterion.accept(new NetworkElementVisitor(line2))); - assertTrue(criterion.accept(new NetworkElementVisitor(line3))); - assertTrue(criterion.accept(new NetworkElementVisitor(line4))); + assertCriterionTrue(criterion, line1); + assertCriterionTrue(criterion, line2); + assertCriterionTrue(criterion, line3); + assertCriterionTrue(criterion, line4); NetworkElement anotherTypeElement = Mockito.mock(NetworkElement.class); - assertFalse(criterion.accept(new NetworkElementVisitor(anotherTypeElement))); + assertCriterionFalse(criterion, anotherTypeElement); } @Test @@ -59,25 +61,25 @@ void nominalVoltageTest() { LineCriterion criterion = new LineCriterion(null, new TwoNominalVoltageCriterion( VoltageInterval.between(40., 100., true, true), null)); - assertTrue(criterion.accept(new NetworkElementVisitor(line1))); - assertFalse(criterion.accept(new NetworkElementVisitor(line2))); - assertFalse(criterion.accept(new NetworkElementVisitor(line3))); - assertTrue(criterion.accept(new NetworkElementVisitor(line4))); + assertCriterionTrue(criterion, line1); + assertCriterionFalse(criterion, line2); + assertCriterionFalse(criterion, line3); + assertCriterionTrue(criterion, line4); } @Test void countriesTest() { LineCriterion criterion = new LineCriterion(new TwoCountriesCriterion(List.of(Country.FR), List.of(Country.BE)), null); - assertFalse(criterion.accept(new NetworkElementVisitor(line1))); - assertFalse(criterion.accept(new NetworkElementVisitor(line2))); - assertTrue(criterion.accept(new NetworkElementVisitor(line3))); - assertFalse(criterion.accept(new NetworkElementVisitor(line4))); + assertCriterionFalse(criterion, line1); + assertCriterionFalse(criterion, line2); + assertCriterionTrue(criterion, line3); + assertCriterionFalse(criterion, line4); criterion = new LineCriterion(new TwoCountriesCriterion(List.of(Country.FR, Country.BE), List.of(Country.BE)), null); - assertFalse(criterion.accept(new NetworkElementVisitor(line1))); - assertFalse(criterion.accept(new NetworkElementVisitor(line2))); - assertTrue(criterion.accept(new NetworkElementVisitor(line3))); - assertTrue(criterion.accept(new NetworkElementVisitor(line4))); + assertCriterionFalse(criterion, line1); + assertCriterionFalse(criterion, line2); + assertCriterionTrue(criterion, line3); + assertCriterionTrue(criterion, line4); } @Test @@ -86,25 +88,27 @@ void mixedCriteriaTest() { new TwoNominalVoltageCriterion( VoltageInterval.between(350., 450., true, true), null)); - assertFalse(criterion.accept(new NetworkElementVisitor(line1))); - assertTrue(criterion.accept(new NetworkElementVisitor(line2))); - assertFalse(criterion.accept(new NetworkElementVisitor(line3))); - assertFalse(criterion.accept(new NetworkElementVisitor(line4))); + assertCriterionFalse(criterion, line1); + assertCriterionTrue(criterion, line2); + assertCriterionFalse(criterion, line3); + assertCriterionFalse(criterion, line4); + } + + private void assertCriterionTrue(LineCriterion criterion, NetworkElement line) { + assertTrue(criterion.accept(new NetworkElementVisitor(line))); + assertTrue(criterion.accept(new NetworkElementVisitor(line, ThreeSides.ONE))); + assertTrue(criterion.accept(new NetworkElementVisitor(line, ThreeSides.TWO))); + } + + private void assertCriterionFalse(LineCriterion criterion, NetworkElement line) { + assertFalse(criterion.accept(new NetworkElementVisitor(line))); + assertFalse(criterion.accept(new NetworkElementVisitor(line, ThreeSides.ONE))); + assertFalse(criterion.accept(new NetworkElementVisitor(line, ThreeSides.TWO))); } protected static NetworkElement createLine(String id, Country country1, Country country2, double nominalVoltage) { - NetworkElement n = Mockito.mock(NetworkElement.class); - when(n.getId()).thenReturn(id); - when(n.getCountry()).thenReturn(Optional.of(country1)); - when(n.getCountry1()).thenReturn(Optional.of(country1)); - when(n.getCountry2()).thenReturn(Optional.of(country2)); - when(n.getNominalVoltage()).thenReturn(Optional.of(nominalVoltage)); - when(n.getNominalVoltage1()).thenReturn(Optional.of(nominalVoltage)); - when(n.getNominalVoltage2()).thenReturn(Optional.empty()); - when(n.getNominalVoltage3()).thenReturn(Optional.empty()); - when(n.isValidFor(NetworkElementCriterion.NetworkElementCriterionType.LINE)).thenReturn(true); - when(n.isValidFor(NetworkElementCriterion.NetworkElementCriterionType.IDENTIFIABLE)).thenReturn(true); - return n; + Line l = mockLine(id, country1, country2, nominalVoltage, nominalVoltage); + return new DefaultNetworkElementAdapter(l); } } diff --git a/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/ThreeWindingsTransformerCriterionTest.java b/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/ThreeWindingsTransformerCriterionTest.java index 487d5c2df16..8783ca87fee 100644 --- a/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/ThreeWindingsTransformerCriterionTest.java +++ b/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/ThreeWindingsTransformerCriterionTest.java @@ -7,17 +7,19 @@ */ package com.powsybl.iidm.criteria; -import com.powsybl.iidm.network.Country; +import com.powsybl.iidm.criteria.translation.DefaultNetworkElementAdapter; import com.powsybl.iidm.criteria.translation.NetworkElement; +import com.powsybl.iidm.network.Country; +import com.powsybl.iidm.network.ThreeSides; +import com.powsybl.iidm.network.ThreeWindingsTransformer; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import java.util.List; -import java.util.Optional; +import static com.powsybl.iidm.criteria.translation.DefaultNetworkElementAdapterTest.mockThreeWindingsTransformer; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.when; /** * @author Olivier Perrin {@literal } @@ -43,12 +45,12 @@ void typeTest() { @Test void emptyCriterionTest() { ThreeWindingsTransformerCriterion criterion = new ThreeWindingsTransformerCriterion(null, null); - assertTrue(criterion.accept(new NetworkElementVisitor(twt1))); - assertTrue(criterion.accept(new NetworkElementVisitor(twt2))); - assertTrue(criterion.accept(new NetworkElementVisitor(twt3))); + assertCriterionTrue(criterion, twt1); + assertCriterionTrue(criterion, twt2); + assertCriterionTrue(criterion, twt3); NetworkElement anotherTypeElement = Mockito.mock(NetworkElement.class); - assertFalse(criterion.accept(new NetworkElementVisitor(anotherTypeElement))); + assertCriterionFalse(criterion, anotherTypeElement); } @Test @@ -58,22 +60,22 @@ void nominalVoltagesTest() { VoltageInterval.between(80., 100., true, true), VoltageInterval.between(350., 550., true, true), VoltageInterval.between(40., 70., true, true))); - assertTrue(criterion.accept(new NetworkElementVisitor(twt1))); - assertFalse(criterion.accept(new NetworkElementVisitor(twt2))); - assertTrue(criterion.accept(new NetworkElementVisitor(twt3))); + assertCriterionTrue(criterion, twt1); + assertCriterionFalse(criterion, twt2); + assertCriterionTrue(criterion, twt3); } @Test void countryTest() { ThreeWindingsTransformerCriterion criterion = new ThreeWindingsTransformerCriterion(new SingleCountryCriterion(List.of(Country.BE)), null); - assertFalse(criterion.accept(new NetworkElementVisitor(twt1))); - assertFalse(criterion.accept(new NetworkElementVisitor(twt2))); - assertTrue(criterion.accept(new NetworkElementVisitor(twt3))); + assertCriterionFalse(criterion, twt1); + assertCriterionFalse(criterion, twt2); + assertCriterionTrue(criterion, twt3); criterion = new ThreeWindingsTransformerCriterion(new SingleCountryCriterion(List.of(Country.FR, Country.BE)), null); - assertTrue(criterion.accept(new NetworkElementVisitor(twt1))); - assertTrue(criterion.accept(new NetworkElementVisitor(twt2))); - assertTrue(criterion.accept(new NetworkElementVisitor(twt3))); + assertCriterionTrue(criterion, twt1); + assertCriterionTrue(criterion, twt2); + assertCriterionTrue(criterion, twt3); } @Test @@ -84,25 +86,29 @@ void mixedCriteriaTest() { VoltageInterval.between(80., 100., true, true), VoltageInterval.between(350., 550., true, true), VoltageInterval.between(40., 70., true, true))); - assertTrue(criterion.accept(new NetworkElementVisitor(twt1))); - assertFalse(criterion.accept(new NetworkElementVisitor(twt2))); - assertFalse(criterion.accept(new NetworkElementVisitor(twt3))); + assertCriterionTrue(criterion, twt1); + assertCriterionFalse(criterion, twt2); + assertCriterionFalse(criterion, twt3); + } + + private void assertCriterionTrue(ThreeWindingsTransformerCriterion criterion, NetworkElement twt) { + assertTrue(criterion.accept(new NetworkElementVisitor(twt))); + assertTrue(criterion.accept(new NetworkElementVisitor(twt, ThreeSides.ONE))); + assertTrue(criterion.accept(new NetworkElementVisitor(twt, ThreeSides.TWO))); + assertTrue(criterion.accept(new NetworkElementVisitor(twt, ThreeSides.THREE))); + } + + private void assertCriterionFalse(ThreeWindingsTransformerCriterion criterion, NetworkElement twt) { + assertFalse(criterion.accept(new NetworkElementVisitor(twt))); + assertFalse(criterion.accept(new NetworkElementVisitor(twt, ThreeSides.ONE))); + assertFalse(criterion.accept(new NetworkElementVisitor(twt, ThreeSides.TWO))); + assertFalse(criterion.accept(new NetworkElementVisitor(twt, ThreeSides.THREE))); } protected static NetworkElement createThreeWindingsTransformer(String id, Country country, double nominalVoltage1, double nominalVoltage2, double nominalVoltage3) { - NetworkElement n = Mockito.mock(NetworkElement.class); - when(n.getId()).thenReturn(id); - when(n.getCountry()).thenReturn(Optional.of(country)); - when(n.getCountry1()).thenReturn(Optional.of(country)); - when(n.getCountry2()).thenReturn(Optional.empty()); - when(n.getNominalVoltage()).thenReturn(Optional.of(nominalVoltage1)); - when(n.getNominalVoltage1()).thenReturn(Optional.of(nominalVoltage1)); - when(n.getNominalVoltage2()).thenReturn(Optional.of(nominalVoltage2)); - when(n.getNominalVoltage3()).thenReturn(Optional.of(nominalVoltage3)); - when(n.isValidFor(NetworkElementCriterion.NetworkElementCriterionType.THREE_WINDINGS_TRANSFORMER)).thenReturn(true); - when(n.isValidFor(NetworkElementCriterion.NetworkElementCriterionType.IDENTIFIABLE)).thenReturn(true); - return n; + ThreeWindingsTransformer twt = mockThreeWindingsTransformer(id, country, nominalVoltage1, nominalVoltage2, nominalVoltage3); + return new DefaultNetworkElementAdapter(twt); } } diff --git a/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/TieLineCriterionTest.java b/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/TieLineCriterionTest.java index f7472c023c7..b40553f8419 100644 --- a/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/TieLineCriterionTest.java +++ b/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/TieLineCriterionTest.java @@ -7,17 +7,19 @@ */ package com.powsybl.iidm.criteria; +import com.powsybl.iidm.criteria.translation.DefaultNetworkElementAdapter; import com.powsybl.iidm.criteria.translation.NetworkElement; import com.powsybl.iidm.network.Country; +import com.powsybl.iidm.network.ThreeSides; +import com.powsybl.iidm.network.TieLine; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import java.util.List; -import java.util.Optional; +import static com.powsybl.iidm.criteria.translation.DefaultNetworkElementAdapterTest.mockTieLine; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.when; /** * @author Olivier Perrin {@literal } @@ -45,13 +47,13 @@ void typeTest() { @Test void emptyCriterionTest() { TieLineCriterion criterion = new TieLineCriterion(null, null); - assertTrue(criterion.accept(new NetworkElementVisitor(tieLine1))); - assertTrue(criterion.accept(new NetworkElementVisitor(tieLine2))); - assertTrue(criterion.accept(new NetworkElementVisitor(tieLine3))); - assertTrue(criterion.accept(new NetworkElementVisitor(tieLine4))); + assertCriterionTrue(criterion, tieLine1); + assertCriterionTrue(criterion, tieLine2); + assertCriterionTrue(criterion, tieLine3); + assertCriterionTrue(criterion, tieLine4); NetworkElement anotherTypeElement = Mockito.mock(NetworkElement.class); - assertFalse(criterion.accept(new NetworkElementVisitor(anotherTypeElement))); + assertCriterionFalse(criterion, anotherTypeElement); } @Test @@ -59,25 +61,25 @@ void nominalVoltageTest() { TieLineCriterion criterion = new TieLineCriterion(null, new TwoNominalVoltageCriterion( VoltageInterval.between(40., 100., true, true), null)); - assertTrue(criterion.accept(new NetworkElementVisitor(tieLine1))); - assertFalse(criterion.accept(new NetworkElementVisitor(tieLine2))); - assertFalse(criterion.accept(new NetworkElementVisitor(tieLine3))); - assertTrue(criterion.accept(new NetworkElementVisitor(tieLine4))); + assertCriterionTrue(criterion, tieLine1); + assertCriterionFalse(criterion, tieLine2); + assertCriterionFalse(criterion, tieLine3); + assertCriterionTrue(criterion, tieLine4); } @Test void countriesTest() { TieLineCriterion criterion = new TieLineCriterion(new TwoCountriesCriterion(List.of(Country.FR), List.of(Country.BE)), null); - assertFalse(criterion.accept(new NetworkElementVisitor(tieLine1))); - assertFalse(criterion.accept(new NetworkElementVisitor(tieLine2))); - assertTrue(criterion.accept(new NetworkElementVisitor(tieLine3))); - assertFalse(criterion.accept(new NetworkElementVisitor(tieLine4))); + assertCriterionFalse(criterion, tieLine1); + assertCriterionFalse(criterion, tieLine2); + assertCriterionTrue(criterion, tieLine3); + assertCriterionFalse(criterion, tieLine4); criterion = new TieLineCriterion(new TwoCountriesCriterion(List.of(Country.FR, Country.BE), List.of(Country.BE)), null); - assertFalse(criterion.accept(new NetworkElementVisitor(tieLine1))); - assertFalse(criterion.accept(new NetworkElementVisitor(tieLine2))); - assertTrue(criterion.accept(new NetworkElementVisitor(tieLine3))); - assertTrue(criterion.accept(new NetworkElementVisitor(tieLine4))); + assertCriterionFalse(criterion, tieLine1); + assertCriterionFalse(criterion, tieLine2); + assertCriterionTrue(criterion, tieLine3); + assertCriterionTrue(criterion, tieLine4); } @Test @@ -86,25 +88,27 @@ void mixedCriteriaTest() { new TwoNominalVoltageCriterion( VoltageInterval.between(350., 450., true, true), null)); - assertFalse(criterion.accept(new NetworkElementVisitor(tieLine1))); - assertTrue(criterion.accept(new NetworkElementVisitor(tieLine2))); - assertFalse(criterion.accept(new NetworkElementVisitor(tieLine3))); - assertFalse(criterion.accept(new NetworkElementVisitor(tieLine4))); + assertCriterionFalse(criterion, tieLine1); + assertCriterionTrue(criterion, tieLine2); + assertCriterionFalse(criterion, tieLine3); + assertCriterionFalse(criterion, tieLine4); + } + + private void assertCriterionTrue(TieLineCriterion criterion, NetworkElement tieLine) { + assertTrue(criterion.accept(new NetworkElementVisitor(tieLine))); + assertTrue(criterion.accept(new NetworkElementVisitor(tieLine, ThreeSides.ONE))); + assertTrue(criterion.accept(new NetworkElementVisitor(tieLine, ThreeSides.TWO))); + } + + private void assertCriterionFalse(TieLineCriterion criterion, NetworkElement tieLine) { + assertFalse(criterion.accept(new NetworkElementVisitor(tieLine))); + assertFalse(criterion.accept(new NetworkElementVisitor(tieLine, ThreeSides.ONE))); + assertFalse(criterion.accept(new NetworkElementVisitor(tieLine, ThreeSides.TWO))); } protected static NetworkElement createTieLine(String id, Country country1, Country country2, double nominalVoltage) { - NetworkElement n = Mockito.mock(NetworkElement.class); - when(n.getId()).thenReturn(id); - when(n.getCountry()).thenReturn(Optional.of(country1)); - when(n.getCountry1()).thenReturn(Optional.of(country1)); - when(n.getCountry2()).thenReturn(Optional.of(country2)); - when(n.getNominalVoltage()).thenReturn(Optional.of(nominalVoltage)); - when(n.getNominalVoltage1()).thenReturn(Optional.of(nominalVoltage)); - when(n.getNominalVoltage2()).thenReturn(Optional.empty()); - when(n.getNominalVoltage3()).thenReturn(Optional.empty()); - when(n.isValidFor(NetworkElementCriterion.NetworkElementCriterionType.TIE_LINE)).thenReturn(true); - when(n.isValidFor(NetworkElementCriterion.NetworkElementCriterionType.IDENTIFIABLE)).thenReturn(true); - return n; + TieLine tieLine = mockTieLine(id, country1, country2, nominalVoltage, nominalVoltage); + return new DefaultNetworkElementAdapter(tieLine); } } diff --git a/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/TwoWindingsTransformerCriterionTest.java b/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/TwoWindingsTransformerCriterionTest.java index 8ae2175cdb4..3e0e377bdce 100644 --- a/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/TwoWindingsTransformerCriterionTest.java +++ b/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/TwoWindingsTransformerCriterionTest.java @@ -7,17 +7,19 @@ */ package com.powsybl.iidm.criteria; -import com.powsybl.iidm.network.Country; +import com.powsybl.iidm.criteria.translation.DefaultNetworkElementAdapter; import com.powsybl.iidm.criteria.translation.NetworkElement; +import com.powsybl.iidm.network.Country; +import com.powsybl.iidm.network.ThreeSides; +import com.powsybl.iidm.network.TwoWindingsTransformer; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import java.util.List; -import java.util.Optional; +import static com.powsybl.iidm.criteria.translation.DefaultNetworkElementAdapterTest.mockTwoWindingsTransformer; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.when; /** * @author Olivier Perrin {@literal } @@ -43,12 +45,12 @@ void typeTest() { @Test void emptyCriterionTest() { TwoWindingsTransformerCriterion criterion = new TwoWindingsTransformerCriterion(null, null); - assertTrue(criterion.accept(new NetworkElementVisitor(twt1))); - assertTrue(criterion.accept(new NetworkElementVisitor(twt2))); - assertTrue(criterion.accept(new NetworkElementVisitor(twt3))); + assertCriterionTrue(criterion, twt1); + assertCriterionTrue(criterion, twt2); + assertCriterionTrue(criterion, twt3); NetworkElement anotherTypeElement = Mockito.mock(NetworkElement.class); - assertFalse(criterion.accept(new NetworkElementVisitor(anotherTypeElement))); + assertCriterionFalse(criterion, anotherTypeElement); } @Test @@ -57,22 +59,22 @@ void nominalVoltagesTest() { new TwoNominalVoltageCriterion( VoltageInterval.between(80., 100., true, true), VoltageInterval.between(40., 70., true, true))); - assertTrue(criterion.accept(new NetworkElementVisitor(twt1))); - assertFalse(criterion.accept(new NetworkElementVisitor(twt2))); - assertTrue(criterion.accept(new NetworkElementVisitor(twt3))); + assertCriterionTrue(criterion, twt1); + assertCriterionFalse(criterion, twt2); + assertCriterionTrue(criterion, twt3); } @Test void countryTest() { TwoWindingsTransformerCriterion criterion = new TwoWindingsTransformerCriterion(new SingleCountryCriterion(List.of(Country.BE)), null); - assertFalse(criterion.accept(new NetworkElementVisitor(twt1))); - assertFalse(criterion.accept(new NetworkElementVisitor(twt2))); - assertTrue(criterion.accept(new NetworkElementVisitor(twt3))); + assertCriterionFalse(criterion, twt1); + assertCriterionFalse(criterion, twt2); + assertCriterionTrue(criterion, twt3); criterion = new TwoWindingsTransformerCriterion(new SingleCountryCriterion(List.of(Country.FR, Country.BE)), null); - assertTrue(criterion.accept(new NetworkElementVisitor(twt1))); - assertTrue(criterion.accept(new NetworkElementVisitor(twt2))); - assertTrue(criterion.accept(new NetworkElementVisitor(twt3))); + assertCriterionTrue(criterion, twt1); + assertCriterionTrue(criterion, twt2); + assertCriterionTrue(criterion, twt3); } @Test @@ -82,23 +84,26 @@ void mixedCriteriaTest() { new TwoNominalVoltageCriterion( VoltageInterval.between(80., 100., true, true), VoltageInterval.between(40., 70., true, true))); - assertTrue(criterion.accept(new NetworkElementVisitor(twt1))); - assertFalse(criterion.accept(new NetworkElementVisitor(twt2))); - assertFalse(criterion.accept(new NetworkElementVisitor(twt3))); + assertCriterionTrue(criterion, twt1); + assertCriterionFalse(criterion, twt2); + assertCriterionFalse(criterion, twt3); + } + + private void assertCriterionTrue(TwoWindingsTransformerCriterion criterion, NetworkElement twt) { + assertTrue(criterion.accept(new NetworkElementVisitor(twt))); + assertTrue(criterion.accept(new NetworkElementVisitor(twt, ThreeSides.ONE))); + assertTrue(criterion.accept(new NetworkElementVisitor(twt, ThreeSides.TWO))); + } + + private void assertCriterionFalse(TwoWindingsTransformerCriterion criterion, NetworkElement twt) { + assertFalse(criterion.accept(new NetworkElementVisitor(twt))); + assertFalse(criterion.accept(new NetworkElementVisitor(twt, ThreeSides.ONE))); + assertFalse(criterion.accept(new NetworkElementVisitor(twt, ThreeSides.TWO))); } protected static NetworkElement createTwoWindingsTransformer(String id, Country country, double nominalVoltage1, double nominalVoltage2) { - NetworkElement n = Mockito.mock(NetworkElement.class); - when(n.getId()).thenReturn(id); - when(n.getCountry()).thenReturn(Optional.of(country)); - when(n.getCountry1()).thenReturn(Optional.of(country)); - when(n.getCountry2()).thenReturn(Optional.empty()); - when(n.getNominalVoltage1()).thenReturn(Optional.of(nominalVoltage1)); - when(n.getNominalVoltage2()).thenReturn(Optional.of(nominalVoltage2)); - when(n.getNominalVoltage3()).thenReturn(Optional.empty()); - when(n.isValidFor(NetworkElementCriterion.NetworkElementCriterionType.TWO_WINDINGS_TRANSFORMER)).thenReturn(true); - when(n.isValidFor(NetworkElementCriterion.NetworkElementCriterionType.IDENTIFIABLE)).thenReturn(true); - return n; + TwoWindingsTransformer twt = mockTwoWindingsTransformer(id, country, nominalVoltage1, nominalVoltage2); + return new DefaultNetworkElementAdapter(twt); } } diff --git a/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/translation/DefaultNetworkElementAdapterTest.java b/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/translation/DefaultNetworkElementAdapterTest.java index 4a3fa3e140b..60e458a4ed1 100644 --- a/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/translation/DefaultNetworkElementAdapterTest.java +++ b/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/translation/DefaultNetworkElementAdapterTest.java @@ -19,33 +19,11 @@ /** * @author Olivier Perrin {@literal } */ -class DefaultNetworkElementAdapterTest { +public class DefaultNetworkElementAdapterTest { @Test void testLine() { - Substation substation1 = Mockito.mock(Substation.class); - Mockito.when(substation1.getNullableCountry()).thenReturn(Country.FR); - Substation substation2 = Mockito.mock(Substation.class); - Mockito.when(substation2.getNullableCountry()).thenReturn(Country.DE); - - VoltageLevel voltageLevel1 = Mockito.mock(VoltageLevel.class); - Mockito.when(voltageLevel1.getSubstation()).thenReturn(Optional.of(substation1)); - Mockito.when(voltageLevel1.getNominalV()).thenReturn(225.); - VoltageLevel voltageLevel2 = Mockito.mock(VoltageLevel.class); - Mockito.when(voltageLevel2.getSubstation()).thenReturn(Optional.of(substation2)); - Mockito.when(voltageLevel2.getNominalV()).thenReturn(226.); - - Terminal terminal1 = Mockito.mock(Terminal.class); - Mockito.when(terminal1.getVoltageLevel()).thenReturn(voltageLevel1); - Terminal terminal2 = Mockito.mock(Terminal.class); - Mockito.when(terminal2.getVoltageLevel()).thenReturn(voltageLevel2); - - Line line = Mockito.mock(Line.class); - Mockito.when(line.getType()).thenReturn(IdentifiableType.LINE); - Mockito.when(line.getId()).thenReturn("testLine"); - Mockito.when(line.getTerminal(TwoSides.ONE)).thenReturn(terminal1); - Mockito.when(line.getTerminal(TwoSides.TWO)).thenReturn(terminal2); - + Line line = mockLine("testLine", Country.FR, Country.DE, 225., 226.); DefaultNetworkElementAdapter networkElement = new DefaultNetworkElementAdapter(line); assertEquals("testLine", networkElement.getId()); assertEquals(Country.FR, networkElement.getCountry().orElseThrow()); @@ -68,28 +46,7 @@ void testLine() { @Test void testTieLine() { - Substation substation1 = Mockito.mock(Substation.class); - Mockito.when(substation1.getNullableCountry()).thenReturn(Country.FR); - Substation substation2 = Mockito.mock(Substation.class); - Mockito.when(substation2.getNullableCountry()).thenReturn(Country.DE); - - VoltageLevel voltageLevel1 = Mockito.mock(VoltageLevel.class); - Mockito.when(voltageLevel1.getSubstation()).thenReturn(Optional.of(substation1)); - Mockito.when(voltageLevel1.getNominalV()).thenReturn(225.); - VoltageLevel voltageLevel2 = Mockito.mock(VoltageLevel.class); - Mockito.when(voltageLevel2.getSubstation()).thenReturn(Optional.of(substation2)); - Mockito.when(voltageLevel2.getNominalV()).thenReturn(226.); - - Terminal terminal1 = Mockito.mock(Terminal.class); - Mockito.when(terminal1.getVoltageLevel()).thenReturn(voltageLevel1); - Terminal terminal2 = Mockito.mock(Terminal.class); - Mockito.when(terminal2.getVoltageLevel()).thenReturn(voltageLevel2); - - TieLine tieLine = Mockito.mock(TieLine.class); - Mockito.when(tieLine.getType()).thenReturn(IdentifiableType.TIE_LINE); - Mockito.when(tieLine.getId()).thenReturn("testTieLine"); - Mockito.when(tieLine.getTerminal(TwoSides.ONE)).thenReturn(terminal1); - Mockito.when(tieLine.getTerminal(TwoSides.TWO)).thenReturn(terminal2); + TieLine tieLine = mockTieLine("testTieLine", Country.FR, Country.DE, 225., 226.); DefaultNetworkElementAdapter networkElement = new DefaultNetworkElementAdapter(tieLine); assertEquals("testTieLine", networkElement.getId()); @@ -113,21 +70,7 @@ void testTieLine() { @Test void testDanglingLine() { - Substation substation1 = Mockito.mock(Substation.class); - Mockito.when(substation1.getNullableCountry()).thenReturn(Country.FR); - - VoltageLevel voltageLevel = Mockito.mock(VoltageLevel.class); - Mockito.when(voltageLevel.getSubstation()).thenReturn(Optional.of(substation1)); - Mockito.when(voltageLevel.getNominalV()).thenReturn(225.); - - Terminal terminal = Mockito.mock(Terminal.class); - Mockito.when(terminal.getVoltageLevel()).thenReturn(voltageLevel); - - DanglingLine danglingLine = Mockito.mock(DanglingLine.class); - Mockito.when(danglingLine.getType()).thenReturn(IdentifiableType.DANGLING_LINE); - Mockito.when(danglingLine.getId()).thenReturn("testDanglingLine"); - Mockito.when(danglingLine.getTerminal()).thenReturn(terminal); - + DanglingLine danglingLine = mockDanglingLine("testDanglingLine", Country.FR, 225.); DefaultNetworkElementAdapter networkElement = new DefaultNetworkElementAdapter(danglingLine); assertEquals("testDanglingLine", networkElement.getId()); assertEquals(Country.FR, networkElement.getCountry().orElseThrow()); @@ -150,27 +93,7 @@ void testDanglingLine() { @Test void testTwoWindingsTransformer() { - Substation substation = Mockito.mock(Substation.class); - Mockito.when(substation.getNullableCountry()).thenReturn(Country.FR); - - VoltageLevel voltageLevel1 = Mockito.mock(VoltageLevel.class); - Mockito.when(voltageLevel1.getSubstation()).thenReturn(Optional.of(substation)); - Mockito.when(voltageLevel1.getNominalV()).thenReturn(225.); - VoltageLevel voltageLevel2 = Mockito.mock(VoltageLevel.class); - Mockito.when(voltageLevel2.getSubstation()).thenReturn(Optional.of(substation)); - Mockito.when(voltageLevel2.getNominalV()).thenReturn(90.); - - Terminal terminal1 = Mockito.mock(Terminal.class); - Mockito.when(terminal1.getVoltageLevel()).thenReturn(voltageLevel1); - Terminal terminal2 = Mockito.mock(Terminal.class); - Mockito.when(terminal2.getVoltageLevel()).thenReturn(voltageLevel2); - - TwoWindingsTransformer twt = Mockito.mock(TwoWindingsTransformer.class); - Mockito.when(twt.getType()).thenReturn(IdentifiableType.TWO_WINDINGS_TRANSFORMER); - Mockito.when(twt.getId()).thenReturn("testTwoWindingsTransformer"); - Mockito.when(twt.getSubstation()).thenReturn(Optional.of(substation)); - Mockito.when(twt.getTerminal(TwoSides.ONE)).thenReturn(terminal1); - Mockito.when(twt.getTerminal(TwoSides.TWO)).thenReturn(terminal2); + TwoWindingsTransformer twt = mockTwoWindingsTransformer("testTwoWindingsTransformer", Country.FR, 225., 90.); DefaultNetworkElementAdapter networkElement = new DefaultNetworkElementAdapter(twt); assertEquals("testTwoWindingsTransformer", networkElement.getId()); @@ -192,18 +115,138 @@ void testTwoWindingsTransformer() { @Test void testThreeWindingsTransformer() { + ThreeWindingsTransformer twt = mockThreeWindingsTransformer("testThreeWindingsTransformer", Country.FR, 400., 225., 90.); + DefaultNetworkElementAdapter networkElement = new DefaultNetworkElementAdapter(twt); + assertEquals("testThreeWindingsTransformer", networkElement.getId()); + assertEquals(Country.FR, networkElement.getCountry().orElseThrow()); + assertEquals(400., networkElement.getNominalVoltage().orElseThrow(), 0.01); + assertEquals(400., networkElement.getNominalVoltage1().orElseThrow(), 0.01); + assertEquals(225., networkElement.getNominalVoltage2().orElseThrow(), 0.01); + assertEquals(90., networkElement.getNominalVoltage3().orElseThrow(), 0.01); + + assertFalse(networkElement.isValidFor(NetworkElementCriterionType.LINE)); + assertFalse(networkElement.isValidFor(NetworkElementCriterionType.TIE_LINE)); + assertFalse(networkElement.isValidFor(NetworkElementCriterionType.DANGLING_LINE)); + assertFalse(networkElement.isValidFor(NetworkElementCriterionType.TWO_WINDINGS_TRANSFORMER)); + assertTrue(networkElement.isValidFor(NetworkElementCriterionType.THREE_WINDINGS_TRANSFORMER)); + assertTrue(networkElement.isValidFor(NetworkElementCriterionType.IDENTIFIABLE)); + assertTrue(networkElement.isValidFor(NetworkElementCriterionType.IDENTIFIER)); + assertEquals(twt, networkElement.getIdentifiable()); + } + + public static Line mockLine(String id, Country country1, Country country2, + double nominalVoltage1, double nominalVoltage2) { + Substation substation1 = Mockito.mock(Substation.class); + Mockito.when(substation1.getNullableCountry()).thenReturn(country1); + Substation substation2 = Mockito.mock(Substation.class); + Mockito.when(substation2.getNullableCountry()).thenReturn(country2); + + VoltageLevel voltageLevel1 = Mockito.mock(VoltageLevel.class); + Mockito.when(voltageLevel1.getSubstation()).thenReturn(Optional.of(substation1)); + Mockito.when(voltageLevel1.getNominalV()).thenReturn(nominalVoltage1); + VoltageLevel voltageLevel2 = Mockito.mock(VoltageLevel.class); + Mockito.when(voltageLevel2.getSubstation()).thenReturn(Optional.of(substation2)); + Mockito.when(voltageLevel2.getNominalV()).thenReturn(nominalVoltage2); + + Terminal terminal1 = Mockito.mock(Terminal.class); + Mockito.when(terminal1.getVoltageLevel()).thenReturn(voltageLevel1); + Terminal terminal2 = Mockito.mock(Terminal.class); + Mockito.when(terminal2.getVoltageLevel()).thenReturn(voltageLevel2); + + Line line = Mockito.mock(Line.class); + Mockito.when(line.getType()).thenReturn(IdentifiableType.LINE); + Mockito.when(line.getId()).thenReturn(id); + Mockito.when(line.getTerminal(TwoSides.ONE)).thenReturn(terminal1); + Mockito.when(line.getTerminal(TwoSides.TWO)).thenReturn(terminal2); + return line; + } + + public static TieLine mockTieLine(String id, Country country1, Country country2, + double nominalVoltage1, double nominalVoltage2) { + Substation substation1 = Mockito.mock(Substation.class); + Mockito.when(substation1.getNullableCountry()).thenReturn(country1); + Substation substation2 = Mockito.mock(Substation.class); + Mockito.when(substation2.getNullableCountry()).thenReturn(country2); + + VoltageLevel voltageLevel1 = Mockito.mock(VoltageLevel.class); + Mockito.when(voltageLevel1.getSubstation()).thenReturn(Optional.of(substation1)); + Mockito.when(voltageLevel1.getNominalV()).thenReturn(nominalVoltage1); + VoltageLevel voltageLevel2 = Mockito.mock(VoltageLevel.class); + Mockito.when(voltageLevel2.getSubstation()).thenReturn(Optional.of(substation2)); + Mockito.when(voltageLevel2.getNominalV()).thenReturn(nominalVoltage2); + + Terminal terminal1 = Mockito.mock(Terminal.class); + Mockito.when(terminal1.getVoltageLevel()).thenReturn(voltageLevel1); + Terminal terminal2 = Mockito.mock(Terminal.class); + Mockito.when(terminal2.getVoltageLevel()).thenReturn(voltageLevel2); + + TieLine tieLine = Mockito.mock(TieLine.class); + Mockito.when(tieLine.getType()).thenReturn(IdentifiableType.TIE_LINE); + Mockito.when(tieLine.getId()).thenReturn(id); + Mockito.when(tieLine.getTerminal(TwoSides.ONE)).thenReturn(terminal1); + Mockito.when(tieLine.getTerminal(TwoSides.TWO)).thenReturn(terminal2); + return tieLine; + } + + public static DanglingLine mockDanglingLine(String id, Country country, double nominalVoltage) { + Substation substation1 = Mockito.mock(Substation.class); + Mockito.when(substation1.getNullableCountry()).thenReturn(country); + + VoltageLevel voltageLevel = Mockito.mock(VoltageLevel.class); + Mockito.when(voltageLevel.getSubstation()).thenReturn(Optional.of(substation1)); + Mockito.when(voltageLevel.getNominalV()).thenReturn(nominalVoltage); + + Terminal terminal = Mockito.mock(Terminal.class); + Mockito.when(terminal.getVoltageLevel()).thenReturn(voltageLevel); + + DanglingLine danglingLine = Mockito.mock(DanglingLine.class); + Mockito.when(danglingLine.getType()).thenReturn(IdentifiableType.DANGLING_LINE); + Mockito.when(danglingLine.getId()).thenReturn(id); + Mockito.when(danglingLine.getTerminal()).thenReturn(terminal); + return danglingLine; + } + + public static TwoWindingsTransformer mockTwoWindingsTransformer(String id, Country country, + double nominalVoltage1, double nominalVoltage2) { + Substation substation = Mockito.mock(Substation.class); + Mockito.when(substation.getNullableCountry()).thenReturn(country); + + VoltageLevel voltageLevel1 = Mockito.mock(VoltageLevel.class); + Mockito.when(voltageLevel1.getSubstation()).thenReturn(Optional.of(substation)); + Mockito.when(voltageLevel1.getNominalV()).thenReturn(nominalVoltage1); + VoltageLevel voltageLevel2 = Mockito.mock(VoltageLevel.class); + Mockito.when(voltageLevel2.getSubstation()).thenReturn(Optional.of(substation)); + Mockito.when(voltageLevel2.getNominalV()).thenReturn(nominalVoltage2); + + Terminal terminal1 = Mockito.mock(Terminal.class); + Mockito.when(terminal1.getVoltageLevel()).thenReturn(voltageLevel1); + Terminal terminal2 = Mockito.mock(Terminal.class); + Mockito.when(terminal2.getVoltageLevel()).thenReturn(voltageLevel2); + + TwoWindingsTransformer twt = Mockito.mock(TwoWindingsTransformer.class); + Mockito.when(twt.getType()).thenReturn(IdentifiableType.TWO_WINDINGS_TRANSFORMER); + Mockito.when(twt.getId()).thenReturn(id); + Mockito.when(twt.getSubstation()).thenReturn(Optional.of(substation)); + Mockito.when(twt.getTerminal(TwoSides.ONE)).thenReturn(terminal1); + Mockito.when(twt.getTerminal(TwoSides.TWO)).thenReturn(terminal2); + return twt; + } + + public static ThreeWindingsTransformer mockThreeWindingsTransformer(String id, Country country, + double nominalVoltage1, double nominalVoltage2, + double nominalVoltage3) { Substation substation = Mockito.mock(Substation.class); - Mockito.when(substation.getNullableCountry()).thenReturn(Country.FR); + Mockito.when(substation.getNullableCountry()).thenReturn(country); VoltageLevel voltageLevel1 = Mockito.mock(VoltageLevel.class); Mockito.when(voltageLevel1.getSubstation()).thenReturn(Optional.of(substation)); - Mockito.when(voltageLevel1.getNominalV()).thenReturn(400.); + Mockito.when(voltageLevel1.getNominalV()).thenReturn(nominalVoltage1); VoltageLevel voltageLevel2 = Mockito.mock(VoltageLevel.class); Mockito.when(voltageLevel2.getSubstation()).thenReturn(Optional.of(substation)); - Mockito.when(voltageLevel2.getNominalV()).thenReturn(225.); + Mockito.when(voltageLevel2.getNominalV()).thenReturn(nominalVoltage2); VoltageLevel voltageLevel3 = Mockito.mock(VoltageLevel.class); Mockito.when(voltageLevel3.getSubstation()).thenReturn(Optional.of(substation)); - Mockito.when(voltageLevel3.getNominalV()).thenReturn(90.); + Mockito.when(voltageLevel3.getNominalV()).thenReturn(nominalVoltage3); Terminal terminal1 = Mockito.mock(Terminal.class); Mockito.when(terminal1.getVoltageLevel()).thenReturn(voltageLevel1); @@ -214,27 +257,11 @@ void testThreeWindingsTransformer() { ThreeWindingsTransformer twt = Mockito.mock(ThreeWindingsTransformer.class); Mockito.when(twt.getType()).thenReturn(IdentifiableType.THREE_WINDINGS_TRANSFORMER); - Mockito.when(twt.getId()).thenReturn("testThreeWindingsTransformer"); + Mockito.when(twt.getId()).thenReturn(id); Mockito.when(twt.getSubstation()).thenReturn(Optional.of(substation)); Mockito.when(twt.getTerminal(ThreeSides.ONE)).thenReturn(terminal1); Mockito.when(twt.getTerminal(ThreeSides.TWO)).thenReturn(terminal2); Mockito.when(twt.getTerminal(ThreeSides.THREE)).thenReturn(terminal3); - - DefaultNetworkElementAdapter networkElement = new DefaultNetworkElementAdapter(twt); - assertEquals("testThreeWindingsTransformer", networkElement.getId()); - assertEquals(Country.FR, networkElement.getCountry().orElseThrow()); - assertEquals(400., networkElement.getNominalVoltage().orElseThrow(), 0.01); - assertEquals(400., networkElement.getNominalVoltage1().orElseThrow(), 0.01); - assertEquals(225., networkElement.getNominalVoltage2().orElseThrow(), 0.01); - assertEquals(90., networkElement.getNominalVoltage3().orElseThrow(), 0.01); - - assertFalse(networkElement.isValidFor(NetworkElementCriterionType.LINE)); - assertFalse(networkElement.isValidFor(NetworkElementCriterionType.TIE_LINE)); - assertFalse(networkElement.isValidFor(NetworkElementCriterionType.DANGLING_LINE)); - assertFalse(networkElement.isValidFor(NetworkElementCriterionType.TWO_WINDINGS_TRANSFORMER)); - assertTrue(networkElement.isValidFor(NetworkElementCriterionType.THREE_WINDINGS_TRANSFORMER)); - assertTrue(networkElement.isValidFor(NetworkElementCriterionType.IDENTIFIABLE)); - assertTrue(networkElement.isValidFor(NetworkElementCriterionType.IDENTIFIER)); - assertEquals(twt, networkElement.getIdentifiable()); + return twt; } } diff --git a/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/translation/NetworkElementTest.java b/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/translation/NetworkElementTest.java new file mode 100644 index 00000000000..e245b81106f --- /dev/null +++ b/iidm/iidm-criteria/src/test/java/com/powsybl/iidm/criteria/translation/NetworkElementTest.java @@ -0,0 +1,95 @@ +/** + * 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.iidm.criteria.translation; + +import com.powsybl.iidm.criteria.NetworkElementCriterion; +import com.powsybl.iidm.network.Country; +import com.powsybl.iidm.network.ThreeSides; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Olivier Perrin {@literal } + */ +class NetworkElementTest { + + private static NetworkElement networkElement; + + @BeforeAll + public static void init() { + networkElement = new NetworkElement() { + @Override + public String getId() { + return "id"; + } + + @Override + public Optional getCountry1() { + return Optional.of(Country.DE); + } + + @Override + public Optional getCountry2() { + return Optional.of(Country.ES); + } + + @Override + public Optional getCountry3() { + return Optional.of(Country.BE); + } + + @Override + public Optional getCountry() { + return getCountry1(); + } + + @Override + public Optional getNominalVoltage1() { + return Optional.of(400.); + } + + @Override + public Optional getNominalVoltage2() { + return Optional.of(225.); + } + + @Override + public Optional getNominalVoltage3() { + return Optional.of(90.); + } + + @Override + public Optional getNominalVoltage() { + return getNominalVoltage1(); + } + + @Override + public boolean isValidFor(NetworkElementCriterion.NetworkElementCriterionType networkElementCriterionType) { + return false; + } + }; + } + + @Test + void testCountries() { + assertEquals(Country.DE, networkElement.getCountry(ThreeSides.ONE).orElse(null)); + assertEquals(Country.ES, networkElement.getCountry(ThreeSides.TWO).orElse(null)); + assertEquals(Country.BE, networkElement.getCountry(ThreeSides.THREE).orElse(null)); + } + + @Test + void testVoltageLevels() { + assertEquals(400., networkElement.getNominalVoltage(ThreeSides.ONE).orElse(Double.NaN), 0.01); + assertEquals(225., networkElement.getNominalVoltage(ThreeSides.TWO).orElse(Double.NaN), 0.01); + assertEquals(90., networkElement.getNominalVoltage(ThreeSides.THREE).orElse(Double.NaN), 0.01); + } +} diff --git a/iidm/iidm-extensions/src/main/java/com/powsybl/iidm/network/extensions/ActivePowerControl.java b/iidm/iidm-extensions/src/main/java/com/powsybl/iidm/network/extensions/ActivePowerControl.java index 0dd18ffb477..af9dc407473 100644 --- a/iidm/iidm-extensions/src/main/java/com/powsybl/iidm/network/extensions/ActivePowerControl.java +++ b/iidm/iidm-extensions/src/main/java/com/powsybl/iidm/network/extensions/ActivePowerControl.java @@ -10,6 +10,8 @@ import com.powsybl.commons.extensions.Extension; import com.powsybl.iidm.network.Injection; +import java.util.OptionalDouble; + /** * @author Ghiles Abdellah {@literal } */ @@ -54,4 +56,25 @@ default String getName() { */ void setParticipationFactor(double participationFactor); + /** + * @return if present, provides the overridden value of minP to be used for active power control operations. + */ + OptionalDouble getMinPOverride(); + + /** + * Sets the overridden minimal active power. + * @param pMinOverride The overridden value of minP. A Nan value removes the override. + */ + void setMinPOverride(double pMinOverride); + + /** + * @return if present, provides the overridden value of maxP to be used for active power control operations. + */ + OptionalDouble getMaxPOverride(); + + /** + * Sets the overridden maximal active power. + * @param pMaxOverride The overridden value of maxP. A Nan value removes the override. + */ + void setMaxPOverride(double pMaxOverride); } diff --git a/iidm/iidm-extensions/src/main/java/com/powsybl/iidm/network/extensions/ActivePowerControlAdder.java b/iidm/iidm-extensions/src/main/java/com/powsybl/iidm/network/extensions/ActivePowerControlAdder.java index 22f64c6200f..b4d6e500b2f 100644 --- a/iidm/iidm-extensions/src/main/java/com/powsybl/iidm/network/extensions/ActivePowerControlAdder.java +++ b/iidm/iidm-extensions/src/main/java/com/powsybl/iidm/network/extensions/ActivePowerControlAdder.java @@ -23,4 +23,8 @@ default Class getExtensionClass() { ActivePowerControlAdder withDroop(double droop); ActivePowerControlAdder withParticipationFactor(double participationFactor); + + ActivePowerControlAdder withMinPOverride(double minPOverride); + + ActivePowerControlAdder withMaxPOverride(double maxPOverride); } diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/extensions/ActivePowerControlAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/extensions/ActivePowerControlAdderImpl.java index e0fae2e5f70..69235972c9e 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/extensions/ActivePowerControlAdderImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/extensions/ActivePowerControlAdderImpl.java @@ -20,6 +20,8 @@ public class ActivePowerControlAdderImpl> private double droop = Double.NaN; private double participationFactor = Double.NaN; + private double minPOverride = Double.NaN; + private double maxPOverride = Double.NaN; protected ActivePowerControlAdderImpl(I extendable) { super(extendable); @@ -27,7 +29,7 @@ protected ActivePowerControlAdderImpl(I extendable) { @Override protected ActivePowerControlImpl createExtension(I extendable) { - return new ActivePowerControlImpl<>(extendable, participate, droop, participationFactor); + return new ActivePowerControlImpl<>(extendable, participate, droop, participationFactor, minPOverride, maxPOverride); } @Override @@ -48,4 +50,16 @@ public ActivePowerControlAdder withParticipationFactor(double participationFa return this; } + @Override + public ActivePowerControlAdder withMinPOverride(double minPOverride) { + this.minPOverride = minPOverride; + return this; + } + + @Override + public ActivePowerControlAdder withMaxPOverride(double maxPOverride) { + this.maxPOverride = maxPOverride; + return this; + } + } diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/extensions/ActivePowerControlImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/extensions/ActivePowerControlImpl.java index cd085d0b9dc..c890aabe86e 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/extensions/ActivePowerControlImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/extensions/ActivePowerControlImpl.java @@ -14,6 +14,7 @@ import gnu.trove.list.array.TDoubleArrayList; import java.util.List; +import java.util.OptionalDouble; /** * @author Ghiles Abdellah {@literal } @@ -26,22 +27,38 @@ public class ActivePowerControlImpl> extends AbstractMult private final TDoubleArrayList droop; private final TDoubleArrayList participationFactor; + private final TDoubleArrayList minPOverride; + private final TDoubleArrayList maxPOverride; + private final List allTDoubleArrayLists; public ActivePowerControlImpl(T component, boolean participate, double droop, double participationFactor) { + this(component, participate, droop, participationFactor, Double.NaN, Double.NaN); + } + + public ActivePowerControlImpl(T component, + boolean participate, + double droop, + double participationFactor, + double minPOverride, + double maxPOverride) { super(component); int variantArraySize = getVariantManagerHolder().getVariantManager().getVariantArraySize(); this.participate = new TBooleanArrayList(variantArraySize); this.droop = new TDoubleArrayList(variantArraySize); this.participationFactor = new TDoubleArrayList(variantArraySize); - this.allTDoubleArrayLists = List.of(this.droop, this.participationFactor); + this.minPOverride = new TDoubleArrayList(variantArraySize); + this.maxPOverride = new TDoubleArrayList(variantArraySize); + this.allTDoubleArrayLists = List.of(this.droop, this.participationFactor, this.minPOverride, this.maxPOverride); for (int i = 0; i < variantArraySize; i++) { this.participate.add(participate); this.droop.add(droop); this.participationFactor.add(participationFactor); + this.minPOverride.add(minPOverride); + this.maxPOverride.add(maxPOverride); } } @@ -94,8 +111,29 @@ public void deleteVariantArrayElement(int index) { public void allocateVariantArrayElement(int[] indexes, int sourceIndex) { for (int index : indexes) { participate.set(index, participate.get(sourceIndex)); - droop.set(index, droop.get(sourceIndex)); allTDoubleArrayLists.forEach(dl -> dl.set(index, dl.get(sourceIndex))); } } + + @Override + public OptionalDouble getMinPOverride() { + double result = minPOverride.get(getVariantIndex()); + return Double.isNaN(result) ? OptionalDouble.empty() : OptionalDouble.of(result); + } + + @Override + public void setMinPOverride(double minPOverride) { + this.minPOverride.set(getVariantIndex(), minPOverride); + } + + @Override + public OptionalDouble getMaxPOverride() { + double result = maxPOverride.get(getVariantIndex()); + return Double.isNaN(result) ? OptionalDouble.empty() : OptionalDouble.of(result); + } + + @Override + public void setMaxPOverride(double maxPOverride) { + this.maxPOverride.set(getVariantIndex(), maxPOverride); + } } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ActivePowerControlSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ActivePowerControlSerDe.java index d70fad28cee..fc1507962ae 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ActivePowerControlSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ActivePowerControlSerDe.java @@ -45,11 +45,12 @@ public ActivePowerControlSerDe() { .put(IidmVersion.V_1_9, ImmutableSortedSet.of("1.0", "1.1")) .put(IidmVersion.V_1_10, ImmutableSortedSet.of("1.0", "1.1")) .put(IidmVersion.V_1_11, ImmutableSortedSet.of("1.0", "1.1")) - .put(IidmVersion.V_1_12, ImmutableSortedSet.of("1.0", "1.1")) + .put(IidmVersion.V_1_12, ImmutableSortedSet.of("1.0", "1.1", "1.2")) .build(), new ImmutableMap.Builder() .put("1.0", "http://www.itesla_project.eu/schema/iidm/ext/active_power_control/1_0") .put("1.1", "http://www.powsybl.org/schema/iidm/ext/active_power_control/1_1") + .put("1.2", "http://www.powsybl.org/schema/iidm/ext/active_power_control/1_2") .build()); } @@ -63,16 +64,22 @@ public void write(ActivePowerControl activePowerControl, SerializerContext co if ("1.1".compareTo(extVersionStr) <= 0) { context.getWriter().writeDoubleAttribute("participationFactor", activePowerControl.getParticipationFactor()); } + if ("1.2".compareTo(extVersionStr) <= 0) { + // not using writeOptionalDouble and trusting implementation convention: : writeDoubleAttribute does not write NaN values in human-readable formats JSON/XML + context.getWriter().writeDoubleAttribute("maxPOverride", activePowerControl.getMaxPOverride().orElse(Double.NaN)); + context.getWriter().writeDoubleAttribute("minPOverride", activePowerControl.getMinPOverride().orElse(Double.NaN)); + } } @Override public InputStream getXsdAsStream() { - return getClass().getResourceAsStream("/xsd/activePowerControl_V1_1.xsd"); + return getClass().getResourceAsStream("/xsd/activePowerControl_V1_2.xsd"); } @Override public List getXsdAsStreamList() { - return List.of(getClass().getResourceAsStream("/xsd/activePowerControl_V1_1.xsd"), + return List.of(getClass().getResourceAsStream("/xsd/activePowerControl_V1_2.xsd"), + getClass().getResourceAsStream("/xsd/activePowerControl_V1_1.xsd"), getClass().getResourceAsStream("/xsd/activePowerControl_V1_0.xsd")); } @@ -81,16 +88,25 @@ public ActivePowerControl read(T identifiable, DeserializerContext context) { boolean participate = context.getReader().readBooleanAttribute("participate"); double droop = context.getReader().readDoubleAttribute("droop"); double participationFactor = Double.NaN; + double minPOverride = Double.NaN; + double maxPOverride = Double.NaN; NetworkDeserializerContext networkContext = (NetworkDeserializerContext) context; String extVersionStr = networkContext.getExtensionVersion(this).orElseThrow(IllegalStateException::new); if ("1.1".compareTo(extVersionStr) <= 0) { participationFactor = context.getReader().readDoubleAttribute("participationFactor"); } + if ("1.2".compareTo(extVersionStr) <= 0) { + // not using readOptionalDouble and trusting implementation convention: readDoubleAttribute returns Nan if attribute is absent in human-readable formats (JSON / XML) + maxPOverride = context.getReader().readDoubleAttribute("maxPOverride"); + minPOverride = context.getReader().readDoubleAttribute("minPOverride"); + } context.getReader().readEndNode(); ActivePowerControlAdder activePowerControlAdder = identifiable.newExtension(ActivePowerControlAdder.class); return activePowerControlAdder.withParticipate(participate) .withDroop(droop) .withParticipationFactor(participationFactor) + .withMinPOverride(minPOverride) + .withMaxPOverride(maxPOverride) .add(); } } diff --git a/iidm/iidm-serde/src/main/resources/xsd/activePowerControl_V1_2.xsd b/iidm/iidm-serde/src/main/resources/xsd/activePowerControl_V1_2.xsd new file mode 100644 index 00000000000..d53fa4a9ca9 --- /dev/null +++ b/iidm/iidm-serde/src/main/resources/xsd/activePowerControl_V1_2.xsd @@ -0,0 +1,24 @@ + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/EurostagBinaryTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/EurostagBinaryTest.java index 6e24236fe07..3206071a35b 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/EurostagBinaryTest.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/EurostagBinaryTest.java @@ -10,9 +10,6 @@ import com.powsybl.commons.io.TreeDataFormat; import com.powsybl.commons.test.ComparisonUtils; import com.powsybl.iidm.network.Network; -import com.powsybl.iidm.network.extensions.ActivePowerControlAdder; -import com.powsybl.iidm.network.extensions.ConnectablePosition; -import com.powsybl.iidm.network.extensions.ConnectablePositionAdder; import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; import org.junit.jupiter.api.Test; @@ -47,20 +44,4 @@ void roundTripTest() throws IOException { roundTripVersionedJsonFromMinToCurrentVersionTest(fileName, IidmVersion.V_1_12); } - @Test - void roundTripTestWithExtension() throws IOException { - ExportOptions exportOptions = new ExportOptions().setFormat(TreeDataFormat.BIN); - ImportOptions importOptions = new ImportOptions().setFormat(TreeDataFormat.BIN); - Network network = EurostagTutorialExample1Factory.createWithLFResults(); - network.getGeneratorStream().findFirst().ifPresent(g -> g.newExtension(ActivePowerControlAdder.class).withDroop(2).withParticipate(true).add()); - network.getLoadStream().forEach(l -> l.newExtension(ConnectablePositionAdder.class).newFeeder().withDirection(ConnectablePosition.Direction.BOTTOM).add().add()); - roundTripTest(network, - (n, binFile) -> NetworkSerDe.write(n, exportOptions, binFile), - binFile -> NetworkSerDe.read(binFile, importOptions), - ComparisonUtils::assertBytesEquals, - getVersionedNetworkPath("eurostag-tutorial1-lf-extensions.bin", CURRENT_IIDM_VERSION)); - - //backward compatibility - roundTripVersionedJsonFromMinToCurrentVersionTest("eurostag-tutorial1-lf-extensions.bin", IidmVersion.V_1_12); - } } diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/ActivePowerControlXmlTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/ActivePowerControlXmlTest.java index 919deb63a95..613e0a6b5f9 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/ActivePowerControlXmlTest.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/ActivePowerControlXmlTest.java @@ -19,9 +19,12 @@ import org.junit.jupiter.api.Test; import java.io.IOException; +import java.util.OptionalDouble; import static com.powsybl.iidm.serde.IidmSerDeConstants.CURRENT_IIDM_VERSION; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author Ghiles Abdellah {@literal } @@ -44,13 +47,36 @@ public void setUp() throws IOException { } @Test - void test() throws IOException { - Network network2 = allFormatsRoundTripTest(network, "/activePowerControlRoundTripRef.xml", CURRENT_IIDM_VERSION); + void testPLimitOverride() throws IOException { + network.getGenerator("GEN").getExtension(ActivePowerControl.class).setMaxPOverride(100.); + network.getBattery("BAT").getExtension(ActivePowerControl.class).setMinPOverride(10.); + Network network2 = allFormatsRoundTripTest(network, "/activePowerControlWithLimitRoundTripRef.xml", CURRENT_IIDM_VERSION); + + Generator gen2 = network2.getGenerator("GEN"); + assertNotNull(gen2); + ActivePowerControl activePowerControl1 = gen2.getExtension(ActivePowerControl.class); + assertNotNull(activePowerControl1); + assertEquals(OptionalDouble.of(100), activePowerControl1.getMaxPOverride()); + assertTrue(activePowerControl1.getMinPOverride().isEmpty()); + + Battery bat2 = network2.getBattery("BAT"); + assertNotNull(bat2); + ActivePowerControl activePowerControl2 = bat2.getExtension(ActivePowerControl.class); + assertNotNull(activePowerControl2); + assertTrue(activePowerControl2.getMaxPOverride().isEmpty()); + assertEquals(OptionalDouble.of(10), activePowerControl2.getMinPOverride()); + } + + @Test + void testIidmV11() throws IOException { + Network network2 = allFormatsRoundTripTest(network, "/activePowerControlRoundTripRef.xml", IidmVersion.V_1_11); Battery bat2 = network2.getBattery("BAT"); assertNotNull(bat2); ActivePowerControl activePowerControl2 = bat2.getExtension(ActivePowerControl.class); assertNotNull(activePowerControl2); + assertTrue(activePowerControl2.getMaxPOverride().isEmpty()); + assertTrue(activePowerControl2.getMinPOverride().isEmpty()); } @Test diff --git a/iidm/iidm-serde/src/test/resources/V1_12/activePowerControlWithLimitRoundTripRef.xml b/iidm/iidm-serde/src/test/resources/V1_12/activePowerControlWithLimitRoundTripRef.xml new file mode 100644 index 00000000000..469356d8c44 --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/V1_12/activePowerControlWithLimitRoundTripRef.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/iidm/iidm-serde/src/test/resources/V1_12/eurostag-tutorial1-lf-extensions.bin b/iidm/iidm-serde/src/test/resources/V1_12/eurostag-tutorial1-lf-extensions.bin deleted file mode 100644 index 192b1c97131..00000000000 Binary files a/iidm/iidm-serde/src/test/resources/V1_12/eurostag-tutorial1-lf-extensions.bin and /dev/null differ diff --git a/iidm/iidm-serde/src/test/resources/V1_12/eurostag-tutorial1-lf-extensions.json b/iidm/iidm-serde/src/test/resources/V1_12/eurostag-tutorial1-lf-extensions.json index e517fec6c07..7fa2484b7ec 100644 --- a/iidm/iidm-serde/src/test/resources/V1_12/eurostag-tutorial1-lf-extensions.json +++ b/iidm/iidm-serde/src/test/resources/V1_12/eurostag-tutorial1-lf-extensions.json @@ -5,7 +5,7 @@ "version" : "1.1" }, { "extensionName" : "activePowerControl", - "version" : "1.1" + "version" : "1.2" } ], "id" : "sim1", "caseDate" : "2013-01-15T18:45:00.000+01:00", diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/extensions/AbstractActivePowerControlTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/extensions/AbstractActivePowerControlTest.java index d5b3e28ae5a..32f2b1e213f 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/extensions/AbstractActivePowerControlTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/extensions/AbstractActivePowerControlTest.java @@ -100,15 +100,103 @@ public void variantsCloneTest() { } } + @Test + public void variantsCloneTestWithOverride() { + String variant1 = "variant1"; + String variant2 = "variant2"; + String variant3 = "variant3"; + + Network network = BatteryNetworkFactory.create(); + Battery bat = network.getBattery("BAT"); + assertNotNull(bat); + bat.newExtension(ActivePowerControlAdder.class) + .withDroop(4.0) + .withParticipate(true) + .withParticipationFactor(1.2) + .withMinPOverride(10) + .withMaxPOverride(100) + .add(); + ActivePowerControl activePowerControl = bat.getExtension(ActivePowerControl.class); + assertNotNull(activePowerControl); + + // Testing variant cloning + VariantManager variantManager = network.getVariantManager(); + variantManager.cloneVariant(INITIAL_VARIANT_ID, variant1); + variantManager.cloneVariant(variant1, variant2); + variantManager.setWorkingVariant(variant1); + checkValues3(activePowerControl); + + // Testing setting different values in the cloned variant and going back to the initial one + activePowerControl.setDroop(6.0); + activePowerControl.setParticipate(false); + activePowerControl.setParticipationFactor(3.0); + activePowerControl.setMaxPOverride(110.); + activePowerControl.setMinPOverride(Double.NaN); + checkValues4(activePowerControl); + + activePowerControl.setMinPOverride(11.); + activePowerControl.setMaxPOverride(Double.NaN); + checkValues5(activePowerControl); + + variantManager.setWorkingVariant(INITIAL_VARIANT_ID); + checkValues3(activePowerControl); + + // Removes a variant then adds another variant to test variant recycling (hence calling allocateVariantArrayElement) + variantManager.removeVariant(variant1); + List targetVariantIds = Arrays.asList(variant1, variant3); + variantManager.cloneVariant(INITIAL_VARIANT_ID, targetVariantIds); + variantManager.setWorkingVariant(variant1); + checkValues3(activePowerControl); + variantManager.setWorkingVariant(variant3); + checkValues3(activePowerControl); + + // Test removing current variant + variantManager.removeVariant(variant3); + try { + activePowerControl.getDroop(); + fail(); + } catch (PowsyblException e) { + assertEquals("Variant index not set", e.getMessage()); + } + } + private static void checkValues1(ActivePowerControl activePowerControl) { assertTrue(activePowerControl.isParticipate()); assertEquals(4.0, activePowerControl.getDroop(), 0.0); assertEquals(1.2, activePowerControl.getParticipationFactor(), 0.0); + assertTrue(activePowerControl.getMaxPOverride().isEmpty()); + assertTrue(activePowerControl.getMinPOverride().isEmpty()); } private static void checkValues2(ActivePowerControl activePowerControl) { assertFalse(activePowerControl.isParticipate()); assertEquals(6.0, activePowerControl.getDroop(), 0.0); assertEquals(3.0, activePowerControl.getParticipationFactor(), 0.0); + assertTrue(activePowerControl.getMaxPOverride().isEmpty()); + assertTrue(activePowerControl.getMinPOverride().isEmpty()); + } + + private static void checkValues3(ActivePowerControl activePowerControl) { + assertTrue(activePowerControl.isParticipate()); + assertEquals(4.0, activePowerControl.getDroop(), 0.0); + assertEquals(1.2, activePowerControl.getParticipationFactor(), 0.0); + assertEquals(10, activePowerControl.getMinPOverride().getAsDouble()); + assertEquals(100, activePowerControl.getMaxPOverride().getAsDouble()); + } + + private static void checkValues4(ActivePowerControl activePowerControl) { + assertFalse(activePowerControl.isParticipate()); + assertEquals(6.0, activePowerControl.getDroop(), 0.0); + assertEquals(3.0, activePowerControl.getParticipationFactor(), 0.0); + assertTrue(activePowerControl.getMinPOverride().isEmpty()); + assertEquals(110, activePowerControl.getMaxPOverride().getAsDouble()); + } + + private static void checkValues5(ActivePowerControl activePowerControl) { + assertFalse(activePowerControl.isParticipate()); + assertEquals(6.0, activePowerControl.getDroop(), 0.0); + assertEquals(3.0, activePowerControl.getParticipationFactor(), 0.0); + assertTrue(activePowerControl.getMaxPOverride().isEmpty()); + assertEquals(11, activePowerControl.getMinPOverride().getAsDouble()); } } diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/AbstractLimitReductionsApplier.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/AbstractLimitReductionsApplier.java index fafccf451ed..b6b583e80ba 100644 --- a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/AbstractLimitReductionsApplier.java +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/AbstractLimitReductionsApplier.java @@ -61,7 +61,7 @@ protected Optional> computeUncachedLimits(P processable, Limi AbstractLimitsReducerCreator> limitsReducerCreator = Objects.requireNonNull(getLimitsReducerCreator()); NetworkElement networkElement = Objects.requireNonNull(asNetworkElement(processable)); AbstractLimitsReducer limitsReducer = limitsReducerCreator.create(networkElement.getId(), originalLimits.get()); - updateLimitReducer(limitsReducer, networkElement, limitType, monitoringOnly); + updateLimitReducer(limitsReducer, networkElement, limitType, side, monitoringOnly); LimitsContainer limitsContainer = limitsReducer.getLimits(); // Cache the value to avoid recomputing it @@ -91,11 +91,11 @@ protected Optional> computeUncachedLimits(P processable, Limi protected abstract NetworkElement asNetworkElement(P processable); private void updateLimitReducer(AbstractLimitsReducer limitsReducer, NetworkElement networkElement, - LimitType limitType, boolean monitoringOnly) { + LimitType limitType, ThreeSides side, boolean monitoringOnly) { for (LimitReduction limitReduction : reductionsForThisContingency) { if (limitReduction.getLimitType() == limitType && limitReduction.isMonitoringOnly() == monitoringOnly - && isNetworkElementAffectedByLimitReduction(networkElement, limitReduction)) { + && isNetworkElementAffectedByLimitReduction(networkElement, side, limitReduction)) { setLimitReductionsToLimitReducer(limitsReducer, limitReduction); } } @@ -138,8 +138,8 @@ protected static boolean isContingencyInContingencyContext(ContingencyContext co || contingencyContext.getContextType() == SPECIFIC && contingencyContext.getContingencyId().equals(contingencyId); } - protected static boolean isNetworkElementAffectedByLimitReduction(NetworkElement networkElement, LimitReduction limitReduction) { - NetworkElementVisitor networkElementVisitor = new NetworkElementVisitor(networkElement); + protected static boolean isNetworkElementAffectedByLimitReduction(NetworkElement networkElement, ThreeSides side, LimitReduction limitReduction) { + NetworkElementVisitor networkElementVisitor = new NetworkElementVisitor(networkElement, side); List networkElementCriteria = limitReduction.getNetworkElementCriteria(); return networkElementCriteria.isEmpty() || networkElementCriteria.stream().anyMatch(networkElementCriterion -> networkElementCriterion.accept(networkElementVisitor)); diff --git a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/limitreduction/DefaultLimitReductionsApplierTest.java b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/limitreduction/DefaultLimitReductionsApplierTest.java index dfce0cf8a2a..eb3f8e0438c 100644 --- a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/limitreduction/DefaultLimitReductionsApplierTest.java +++ b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/limitreduction/DefaultLimitReductionsApplierTest.java @@ -8,13 +8,13 @@ package com.powsybl.security.limitreduction; import com.powsybl.contingency.ContingencyContext; +import com.powsybl.iidm.criteria.AtLeastOneNominalVoltageCriterion; +import com.powsybl.iidm.criteria.IdentifiableCriterion; import com.powsybl.iidm.criteria.NetworkElementIdListCriterion; +import com.powsybl.iidm.criteria.VoltageInterval; import com.powsybl.iidm.criteria.duration.EqualityTemporaryDurationCriterion; import com.powsybl.iidm.criteria.duration.PermanentDurationCriterion; -import com.powsybl.iidm.network.LimitType; -import com.powsybl.iidm.network.LoadingLimits; -import com.powsybl.iidm.network.Network; -import com.powsybl.iidm.network.ThreeSides; +import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.limitmodification.result.LimitsContainer; import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; import org.junit.jupiter.api.BeforeAll; @@ -63,10 +63,17 @@ static void init() { .withNetworkElementCriteria(new NetworkElementIdListCriterion(Set.of("NHV1_NHV2_1"))) .withLimitDurationCriteria(new EqualityTemporaryDurationCriterion(60)) .build(); - LimitReduction reduction5 = LimitReduction.builder(LimitType.CURRENT, 0.2) + LimitReduction reduction5 = LimitReduction.builder(LimitType.CURRENT, 0.1) + .withMonitoringOnly(false) + .withContingencyContext(ContingencyContext.specificContingency("contingency5")) + // Applicable only for the 2 winding transformer NHV2_NLOAD on Side 2 + .withNetworkElementCriteria(new IdentifiableCriterion(new AtLeastOneNominalVoltageCriterion( + VoltageInterval.between(150., 160., true, true)))) + .build(); + LimitReduction reduction6 = LimitReduction.builder(LimitType.CURRENT, 0.2) .withMonitoringOnly(true) .build(); - applier = new DefaultLimitReductionsApplier(List.of(reduction1, reduction2, reduction3, reduction4, reduction5)); + applier = new DefaultLimitReductionsApplier(List.of(reduction1, reduction2, reduction3, reduction4, reduction5, reduction6)); } @Test @@ -176,6 +183,44 @@ void noLimitsToReduceTest() { assertTrue(optLimits.isEmpty()); } + @Test + void reduceOnOneSideOnlyTest() { + applier.setWorkingContingency("contingency5"); + TwoWindingsTransformer nhv2Nload = network.getTwoWindingsTransformer("NHV2_NLOAD"); + nhv2Nload.newCurrentLimits1() + .setPermanentLimit(1000.) + .beginTemporaryLimit() + .setValue(1200.) + .setAcceptableDuration(60) + .setName("60'") + .endTemporaryLimit() + .add(); + nhv2Nload.newCurrentLimits2() + .setPermanentLimit(1000.) + .beginTemporaryLimit() + .setValue(1200.) + .setAcceptableDuration(60) + .setName("60'") + .endTemporaryLimit() + .add(); + // The reduction only applies on side 2 for NHV2_NLOAD + Optional> optLimits = applier.computeLimits(nhv2Nload, + LimitType.CURRENT, ThreeSides.ONE, false); + assertTrue(optLimits.isPresent()); + assertEquals(1000., optLimits.get().getLimits().getPermanentLimit(), 0.01); + assertEquals(1200., optLimits.get().getLimits().getTemporaryLimitValue(60), 0.01); + assertFalse(optLimits.get().isDistinct()); + + // The reduction only applies on side 1 for NHV2_NLOAD + optLimits = applier.computeLimits(nhv2Nload, LimitType.CURRENT, ThreeSides.TWO, false); + assertTrue(optLimits.isPresent()); + assertEquals(1000., optLimits.get().getOriginalLimits().getPermanentLimit(), 0.01); + assertEquals(100., optLimits.get().getLimits().getPermanentLimit(), 0.01); + assertEquals(1200., optLimits.get().getOriginalLimits().getTemporaryLimitValue(60), 0.01); + assertEquals(120., optLimits.get().getLimits().getTemporaryLimitValue(60), 0.01); + assertTrue(optLimits.get().isDistinct()); + } + @ParameterizedTest(name = "{0}") @MethodSource("getNoChangesComputers") void noChangesTest(String desc, DefaultLimitReductionsApplier noChangesComputer) {