From c77963ef8c24acc217bc06318225a8ba92fd73f8 Mon Sep 17 00:00:00 2001 From: Charly B <47325354+EstherDarkish@users.noreply.github.com> Date: Wed, 5 Oct 2022 14:46:31 +0200 Subject: [PATCH] Refactorization of Reactive Capability Curves. (#291) Signed-off-by: BOUTIER Charly --- .../store/model/BatteryAttributes.java | 2 +- .../store/model/GeneratorAttributes.java | 2 +- ...eactiveCapabilityCurvePointAttributes.java | 8 +- .../store/model/ReactiveLimitHolder.java | 17 + .../store/model/TemporaryLimitAttributes.java | 1 - .../model/VscConverterStationAttributes.java | 2 +- .../network/store/server/Mappings.java | 70 ++-- .../store/server/NetworkStoreRepository.java | 339 ++++++++++++++++-- .../network/store/server/OwnerInfo.java | 7 + .../network/store/server/QueryCatalog.java | 93 ++++- .../changelog_2022-09-28T11:30:00Z.xml | 79 ++++ .../db/changelog/db.changelog-master.yaml | 4 + .../server/NetworkStoreControllerIT.java | 8 +- .../server/NetworkStoreRepositoryTest.java | 148 +++++++- 14 files changed, 689 insertions(+), 91 deletions(-) create mode 100644 network-store-model/src/main/java/com/powsybl/network/store/model/ReactiveLimitHolder.java create mode 100644 network-store-server/src/main/resources/db/changelog/changesets/changelog_2022-09-28T11:30:00Z.xml diff --git a/network-store-model/src/main/java/com/powsybl/network/store/model/BatteryAttributes.java b/network-store-model/src/main/java/com/powsybl/network/store/model/BatteryAttributes.java index 09472bb63..fc7b56b26 100644 --- a/network-store-model/src/main/java/com/powsybl/network/store/model/BatteryAttributes.java +++ b/network-store-model/src/main/java/com/powsybl/network/store/model/BatteryAttributes.java @@ -21,7 +21,7 @@ @AllArgsConstructor @Builder @Schema(description = "Battery attributes") -public class BatteryAttributes extends AbstractAttributes implements InjectionAttributes { +public class BatteryAttributes extends AbstractAttributes implements InjectionAttributes, ReactiveLimitHolder { @Schema(description = "Voltage level ID") private String voltageLevelId; diff --git a/network-store-model/src/main/java/com/powsybl/network/store/model/GeneratorAttributes.java b/network-store-model/src/main/java/com/powsybl/network/store/model/GeneratorAttributes.java index cd530fc35..829718bbc 100644 --- a/network-store-model/src/main/java/com/powsybl/network/store/model/GeneratorAttributes.java +++ b/network-store-model/src/main/java/com/powsybl/network/store/model/GeneratorAttributes.java @@ -22,7 +22,7 @@ @AllArgsConstructor @Builder @Schema(description = "Generator attributes") -public class GeneratorAttributes extends AbstractAttributes implements InjectionAttributes { +public class GeneratorAttributes extends AbstractAttributes implements InjectionAttributes, ReactiveLimitHolder { @Schema(description = "Voltage level ID") private String voltageLevelId; diff --git a/network-store-model/src/main/java/com/powsybl/network/store/model/ReactiveCapabilityCurvePointAttributes.java b/network-store-model/src/main/java/com/powsybl/network/store/model/ReactiveCapabilityCurvePointAttributes.java index d4b72bce4..ef0c45958 100644 --- a/network-store-model/src/main/java/com/powsybl/network/store/model/ReactiveCapabilityCurvePointAttributes.java +++ b/network-store-model/src/main/java/com/powsybl/network/store/model/ReactiveCapabilityCurvePointAttributes.java @@ -6,7 +6,6 @@ */ package com.powsybl.network.store.model; -import com.fasterxml.jackson.annotation.JsonInclude; import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Builder; @@ -20,16 +19,15 @@ @NoArgsConstructor @AllArgsConstructor @Builder -@JsonInclude(JsonInclude.Include.NON_NULL) @Schema(description = "Point attributes") public class ReactiveCapabilityCurvePointAttributes { @Schema(description = "Active power value") - double p; + private double p; @Schema(description = "Reactive power minimum value") - double minQ; + private double minQ; @Schema(description = "Reactive power maximum value") - double maxQ; + private double maxQ; } diff --git a/network-store-model/src/main/java/com/powsybl/network/store/model/ReactiveLimitHolder.java b/network-store-model/src/main/java/com/powsybl/network/store/model/ReactiveLimitHolder.java new file mode 100644 index 000000000..2cb583b5c --- /dev/null +++ b/network-store-model/src/main/java/com/powsybl/network/store/model/ReactiveLimitHolder.java @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.network.store.model; + +/** + * @author Charly Boutier + */ +public interface ReactiveLimitHolder { + + ReactiveLimitsAttributes getReactiveLimits(); + + void setReactiveLimits(ReactiveLimitsAttributes limits); +} diff --git a/network-store-model/src/main/java/com/powsybl/network/store/model/TemporaryLimitAttributes.java b/network-store-model/src/main/java/com/powsybl/network/store/model/TemporaryLimitAttributes.java index 811feeb04..6f83c4f70 100644 --- a/network-store-model/src/main/java/com/powsybl/network/store/model/TemporaryLimitAttributes.java +++ b/network-store-model/src/main/java/com/powsybl/network/store/model/TemporaryLimitAttributes.java @@ -26,7 +26,6 @@ @Schema(description = "Temporary limit attributes") public class TemporaryLimitAttributes { - // TODO side and type should be in LimitAttributes @JsonIgnore @Schema(description = "Temporary limit side", required = true) private Integer side; diff --git a/network-store-model/src/main/java/com/powsybl/network/store/model/VscConverterStationAttributes.java b/network-store-model/src/main/java/com/powsybl/network/store/model/VscConverterStationAttributes.java index f94dd3de9..58367104a 100644 --- a/network-store-model/src/main/java/com/powsybl/network/store/model/VscConverterStationAttributes.java +++ b/network-store-model/src/main/java/com/powsybl/network/store/model/VscConverterStationAttributes.java @@ -21,7 +21,7 @@ @AllArgsConstructor @Builder @Schema(description = "VSC converter station attributes") -public class VscConverterStationAttributes extends AbstractAttributes implements InjectionAttributes { +public class VscConverterStationAttributes extends AbstractAttributes implements InjectionAttributes, ReactiveLimitHolder { @Schema(description = "Voltage level ID") private String voltageLevelId; diff --git a/network-store-server/src/main/java/com/powsybl/network/store/server/Mappings.java b/network-store-server/src/main/java/com/powsybl/network/store/server/Mappings.java index 1d718de7a..7e857be19 100644 --- a/network-store-server/src/main/java/com/powsybl/network/store/server/Mappings.java +++ b/network-store-server/src/main/java/com/powsybl/network/store/server/Mappings.java @@ -132,9 +132,9 @@ public class Mappings { private static final String PERMANENT_ACTIVE_POWER_LIMIT_1 = "permanentActivePowerLimit1"; private static final String PERMANENT_ACTIVE_POWER_LIMIT_2 = "permanentActivePowerLimit2"; private static final String VOLTAGE_REGULATOR_ON = "voltageRegulatorOn"; - private static final String MIN_MAX_REACIVE_LIMITS = "minMaxReactiveLimits"; - private static final String REACTIVE_CAPABILITY_CURVE = "reactiveCapabilityCurve"; private static final String REGULATION_TERMINAL = "regulatingTerminal"; + private static final String MINQ = "minQ"; + private static final String MAXQ = "maxQ"; public TableMapping getTableMapping(String table) { Objects.requireNonNull(table); @@ -270,19 +270,21 @@ private void createGeneratorMappings() { generatorMappings.addColumnMapping("targetQ", new ColumnMapping<>(Double.class, GeneratorAttributes::getTargetQ, GeneratorAttributes::setTargetQ)); generatorMappings.addColumnMapping("targetV", new ColumnMapping<>(Double.class, GeneratorAttributes::getTargetV, GeneratorAttributes::setTargetV)); generatorMappings.addColumnMapping(RATED_S, new ColumnMapping<>(Double.class, GeneratorAttributes::getRatedS, GeneratorAttributes::setRatedS)); - generatorMappings.addColumnMapping(MIN_MAX_REACIVE_LIMITS, new ColumnMapping<>(ReactiveLimitsAttributes.class, (GeneratorAttributes attributes) -> - attributes.getReactiveLimits() instanceof MinMaxReactiveLimitsAttributes ? attributes.getReactiveLimits() : null, - (GeneratorAttributes attributes, ReactiveLimitsAttributes limits) -> { - if (limits instanceof MinMaxReactiveLimitsAttributes) { - attributes.setReactiveLimits(limits); + generatorMappings.addColumnMapping(MINQ, new ColumnMapping<>(Double.class, + (GeneratorAttributes attributes) -> attributes.getReactiveLimits() instanceof MinMaxReactiveLimitsAttributes ? ((MinMaxReactiveLimitsAttributes) attributes.getReactiveLimits()).getMinQ() : null, + (GeneratorAttributes attributes, Double value) -> { + if (attributes.getReactiveLimits() == null) { + attributes.setReactiveLimits(new MinMaxReactiveLimitsAttributes()); } + ((MinMaxReactiveLimitsAttributes) attributes.getReactiveLimits()).setMinQ(value); })); - generatorMappings.addColumnMapping(REACTIVE_CAPABILITY_CURVE, new ColumnMapping<>(ReactiveLimitsAttributes.class, (GeneratorAttributes attributes) -> - attributes.getReactiveLimits() instanceof ReactiveCapabilityCurveAttributes ? attributes.getReactiveLimits() : null, - (GeneratorAttributes attributes, ReactiveLimitsAttributes limits) -> { - if (limits instanceof ReactiveCapabilityCurveAttributes) { - attributes.setReactiveLimits(limits); + generatorMappings.addColumnMapping(MAXQ, new ColumnMapping<>(Double.class, + (GeneratorAttributes attributes) -> attributes.getReactiveLimits() instanceof MinMaxReactiveLimitsAttributes ? ((MinMaxReactiveLimitsAttributes) attributes.getReactiveLimits()).getMaxQ() : null, + (GeneratorAttributes attributes, Double value) -> { + if (attributes.getReactiveLimits() == null) { + attributes.setReactiveLimits(new MinMaxReactiveLimitsAttributes()); } + ((MinMaxReactiveLimitsAttributes) attributes.getReactiveLimits()).setMaxQ(value); })); generatorMappings.addColumnMapping("activePowerControl", new ColumnMapping<>(ActivePowerControlAttributes.class, GeneratorAttributes::getActivePowerControl, GeneratorAttributes::setActivePowerControl)); generatorMappings.addColumnMapping(REGULATION_TERMINAL, new ColumnMapping<>(TerminalRefAttributes.class, GeneratorAttributes::getRegulatingTerminal, GeneratorAttributes::setRegulatingTerminal)); @@ -402,19 +404,21 @@ private void createBatteryMappings() { batteryMappings.addColumnMapping("p", new ColumnMapping<>(Double.class, BatteryAttributes::getP, BatteryAttributes::setP)); batteryMappings.addColumnMapping("q", new ColumnMapping<>(Double.class, BatteryAttributes::getQ, BatteryAttributes::setQ)); batteryMappings.addColumnMapping(FICTITIOUS, new ColumnMapping<>(Boolean.class, BatteryAttributes::isFictitious, BatteryAttributes::setFictitious)); - batteryMappings.addColumnMapping(MIN_MAX_REACIVE_LIMITS, new ColumnMapping<>(ReactiveLimitsAttributes.class, (BatteryAttributes attributes) -> - attributes.getReactiveLimits() instanceof MinMaxReactiveLimitsAttributes ? attributes.getReactiveLimits() : null, - (BatteryAttributes attributes, ReactiveLimitsAttributes limits) -> { - if (limits instanceof MinMaxReactiveLimitsAttributes) { - attributes.setReactiveLimits(limits); + batteryMappings.addColumnMapping(MINQ, new ColumnMapping<>(Double.class, + (BatteryAttributes attributes) -> attributes.getReactiveLimits() instanceof MinMaxReactiveLimitsAttributes ? ((MinMaxReactiveLimitsAttributes) attributes.getReactiveLimits()).getMinQ() : null, + (BatteryAttributes attributes, Double value) -> { + if (attributes.getReactiveLimits() == null) { + attributes.setReactiveLimits(new MinMaxReactiveLimitsAttributes()); } + ((MinMaxReactiveLimitsAttributes) attributes.getReactiveLimits()).setMinQ(value); })); - batteryMappings.addColumnMapping(REACTIVE_CAPABILITY_CURVE, new ColumnMapping<>(ReactiveLimitsAttributes.class, (BatteryAttributes attributes) -> - attributes.getReactiveLimits() instanceof ReactiveCapabilityCurveAttributes ? attributes.getReactiveLimits() : null, - (BatteryAttributes attributes, ReactiveLimitsAttributes limits) -> { - if (limits instanceof ReactiveCapabilityCurveAttributes) { - attributes.setReactiveLimits(limits); + batteryMappings.addColumnMapping(MAXQ, new ColumnMapping<>(Double.class, + (BatteryAttributes attributes) -> attributes.getReactiveLimits() instanceof MinMaxReactiveLimitsAttributes ? ((MinMaxReactiveLimitsAttributes) attributes.getReactiveLimits()).getMaxQ() : null, + (BatteryAttributes attributes, Double value) -> { + if (attributes.getReactiveLimits() == null) { + attributes.setReactiveLimits(new MinMaxReactiveLimitsAttributes()); } + ((MinMaxReactiveLimitsAttributes) attributes.getReactiveLimits()).setMaxQ(value); })); batteryMappings.addColumnMapping("activePowerControl", new ColumnMapping<>(ActivePowerControlAttributes.class, BatteryAttributes::getActivePowerControl, BatteryAttributes::setActivePowerControl)); batteryMappings.addColumnMapping("node", new ColumnMapping<>(Integer.class, BatteryAttributes::getNode, BatteryAttributes::setNode)); @@ -559,19 +563,21 @@ private void createVscConverterStationMappings() { vscConverterStationMappings.addColumnMapping("reactivePowerSetPoint", new ColumnMapping<>(Double.class, VscConverterStationAttributes::getReactivePowerSetPoint, VscConverterStationAttributes::setReactivePowerSetPoint)); vscConverterStationMappings.addColumnMapping("voltageSetPoint", new ColumnMapping<>(Double.class, VscConverterStationAttributes::getVoltageSetPoint, VscConverterStationAttributes::setVoltageSetPoint)); vscConverterStationMappings.addColumnMapping(FICTITIOUS, new ColumnMapping<>(Boolean.class, VscConverterStationAttributes::isFictitious, VscConverterStationAttributes::setFictitious)); - vscConverterStationMappings.addColumnMapping(MIN_MAX_REACIVE_LIMITS, new ColumnMapping<>(ReactiveLimitsAttributes.class, (VscConverterStationAttributes attributes) -> - attributes.getReactiveLimits() instanceof MinMaxReactiveLimitsAttributes ? attributes.getReactiveLimits() : null, - (VscConverterStationAttributes attributes, ReactiveLimitsAttributes limits) -> { - if (limits instanceof MinMaxReactiveLimitsAttributes) { - attributes.setReactiveLimits(limits); + vscConverterStationMappings.addColumnMapping(MINQ, new ColumnMapping<>(Double.class, + (VscConverterStationAttributes attributes) -> attributes.getReactiveLimits() instanceof MinMaxReactiveLimitsAttributes ? ((MinMaxReactiveLimitsAttributes) attributes.getReactiveLimits()).getMinQ() : null, + (VscConverterStationAttributes attributes, Double value) -> { + if (attributes.getReactiveLimits() == null) { + attributes.setReactiveLimits(new MinMaxReactiveLimitsAttributes()); } + ((MinMaxReactiveLimitsAttributes) attributes.getReactiveLimits()).setMinQ(value); })); - vscConverterStationMappings.addColumnMapping(REACTIVE_CAPABILITY_CURVE, new ColumnMapping<>(ReactiveLimitsAttributes.class, (VscConverterStationAttributes attributes) -> - attributes.getReactiveLimits() instanceof ReactiveCapabilityCurveAttributes ? attributes.getReactiveLimits() : null, - (VscConverterStationAttributes attributes, ReactiveLimitsAttributes limits) -> { - if (limits instanceof ReactiveCapabilityCurveAttributes) { - attributes.setReactiveLimits(limits); + vscConverterStationMappings.addColumnMapping(MAXQ, new ColumnMapping<>(Double.class, + (VscConverterStationAttributes attributes) -> attributes.getReactiveLimits() instanceof MinMaxReactiveLimitsAttributes ? ((MinMaxReactiveLimitsAttributes) attributes.getReactiveLimits()).getMaxQ() : null, + (VscConverterStationAttributes attributes, Double value) -> { + if (attributes.getReactiveLimits() == null) { + attributes.setReactiveLimits(new MinMaxReactiveLimitsAttributes()); } + ((MinMaxReactiveLimitsAttributes) attributes.getReactiveLimits()).setMaxQ(value); })); vscConverterStationMappings.addColumnMapping("node", new ColumnMapping<>(Integer.class, VscConverterStationAttributes::getNode, VscConverterStationAttributes::setNode)); vscConverterStationMappings.addColumnMapping(PROPERTIES, new ColumnMapping<>(Map.class, VscConverterStationAttributes::getProperties, VscConverterStationAttributes::setProperties)); diff --git a/network-store-server/src/main/java/com/powsybl/network/store/server/NetworkStoreRepository.java b/network-store-server/src/main/java/com/powsybl/network/store/server/NetworkStoreRepository.java index 3bc02f0cf..0f9d4f714 100644 --- a/network-store-server/src/main/java/com/powsybl/network/store/server/NetworkStoreRepository.java +++ b/network-store-server/src/main/java/com/powsybl/network/store/server/NetworkStoreRepository.java @@ -12,6 +12,7 @@ import com.google.common.collect.Lists; import com.powsybl.commons.PowsyblException; import com.powsybl.iidm.network.LimitType; +import com.powsybl.iidm.network.ReactiveLimitsKind; import com.powsybl.network.store.model.*; import com.powsybl.network.store.model.utils.VariantUtils; import com.powsybl.network.store.server.exceptions.JsonApiErrorResponseException; @@ -305,6 +306,11 @@ public void deleteNetwork(UUID uuid) { preparedStmt.setObject(1, uuid.toString()); preparedStmt.executeUpdate(); } + // Delete of the reactive capability curve points (which are not Identifiables objects) + try (var preparedStmt = connection.prepareStatement(QueryCatalog.buildDeleteReactiveCapabilityCurvePointsQuery())) { + preparedStmt.setObject(1, uuid.toString()); + preparedStmt.executeUpdate(); + } } catch (SQLException e) { throw new UncheckedSqlException(e); } @@ -336,6 +342,12 @@ public void deleteNetwork(UUID uuid, int variantNum) { preparedStmt.setInt(2, variantNum); preparedStmt.executeUpdate(); } + // Delete of the reactive capability curve points (which are not Identifiables objects) + try (var preparedStmt = connection.prepareStatement(QueryCatalog.buildDeleteReactiveCapabilityCurvePointsVariantQuery())) { + preparedStmt.setObject(1, uuid.toString()); + preparedStmt.setInt(2, variantNum); + preparedStmt.executeUpdate(); + } } catch (SQLException e) { throw new UncheckedSqlException(e); } @@ -417,6 +429,14 @@ public void cloneNetworkElements(Connection connection, UUID uuid, UUID targetUu preparedStmt.setInt(4, sourceVariantNum); preparedStmt.execute(); } + // Copy of the reactive capability curve points (which are not Identifiables objects) + try (var preparedStmt = connection.prepareStatement(QueryCatalog.buildCloneReactiveCapabilityCurvePointsQuery())) { + preparedStmt.setString(1, targetUuid.toString()); + preparedStmt.setInt(2, targetVariantNum); + preparedStmt.setString(3, uuid.toString()); + preparedStmt.setInt(4, sourceVariantNum); + preparedStmt.execute(); + } } public void cloneNetwork(UUID networkUuid, String sourceVariantId, String targetVariantId, boolean mayOverwrite) { @@ -467,13 +487,9 @@ public void insertTemporaryLimits(Map> try (var connection = dataSource.getConnection()) { try (var preparedStmt = connection.prepareStatement(QueryCatalog.buildInsertTemporaryLimitsQuery())) { List values = new ArrayList<>(10); - List>> list = - temporaryLimits.entrySet() - .stream() - .map(e -> Pair.of(e.getKey(), e.getValue())) - .collect(Collectors.toList()); - for (List>> subUnit : Lists.partition(list, BATCH_SIZE)) { - for (Pair> myPair : subUnit) { + List>> list = new ArrayList<>(temporaryLimits.entrySet()); + for (List>> subUnit : Lists.partition(list, BATCH_SIZE)) { + for (Map.Entry> myPair : subUnit) { for (TemporaryLimitAttributes temporaryLimit : myPair.getValue()) { values.clear(); // In order, from the QueryCatalog.buildInsertTemporaryLimitsQuery SQL query : @@ -704,52 +720,112 @@ public void deleteVoltageLevel(UUID networkUuid, int variantNum, String voltageL public void createGenerators(UUID networkUuid, List> resources) { createIdentifiables(networkUuid, resources, mappings.getGeneratorMappings()); + + // Now that generators are created, we will insert in the database the corresponding reactive capability curve points. + insertReactiveCapabilityCurvePoints(getReactiveCapabilityCurvePointsFromEquipments(networkUuid, resources)); } public Optional> getGenerator(UUID networkUuid, int variantNum, String generatorId) { - return getIdentifiable(networkUuid, variantNum, generatorId, mappings.getGeneratorMappings()); + Optional> generator = getIdentifiable(networkUuid, variantNum, generatorId, mappings.getGeneratorMappings()); + + generator.ifPresent(equipment -> { + Map> reactiveCapabilityCurvePoints = getReactiveCapabilityCurvePoints(networkUuid, variantNum, EQUIPMENT_ID_COLUMN, generatorId); + insertReactiveCapabilityCurvePointsInEquipments(networkUuid, List.of(equipment), reactiveCapabilityCurvePoints); + }); + return generator; } public List> getGenerators(UUID networkUuid, int variantNum) { - return getIdentifiables(networkUuid, variantNum, mappings.getGeneratorMappings()); + List> generators = getIdentifiables(networkUuid, variantNum, mappings.getGeneratorMappings()); + + Map> reactiveCapabilityCurvePoints = getReactiveCapabilityCurvePoints(networkUuid, variantNum, EQUIPMENT_TYPE_COLUMN, ResourceType.GENERATOR.toString()); + + insertReactiveCapabilityCurvePointsInEquipments(networkUuid, generators, reactiveCapabilityCurvePoints); + + return generators; } public List> getVoltageLevelGenerators(UUID networkUuid, int variantNum, String voltageLevelId) { - return getIdentifiablesInVoltageLevel(networkUuid, variantNum, voltageLevelId, mappings.getGeneratorMappings()); + List> generators = getIdentifiablesInVoltageLevel(networkUuid, variantNum, voltageLevelId, mappings.getGeneratorMappings()); + + List equipmentsIds = generators.stream().map(Resource::getId).collect(Collectors.toList()); + + Map> reactiveCapabilityCurvePoints = getReactiveCapabilityCurvePointsWithInClause(networkUuid, variantNum, EQUIPMENT_ID_COLUMN, equipmentsIds); + + insertReactiveCapabilityCurvePointsInEquipments(networkUuid, generators, reactiveCapabilityCurvePoints); + + return generators; } public void updateGenerators(UUID networkUuid, List> resources) { updateIdentifiables(networkUuid, resources, mappings.getGeneratorMappings(), VOLTAGE_LEVEL_ID_COLUMN); + + // To update the generator's reactive capability curve points, we will first delete them, then create them again. + // This is done this way to prevent issues in case the reactive capability curve point's primary key is to be + // modified because of the updated equipment's new values. + deleteReactiveCapabilityCurvePoints(networkUuid, resources); + insertReactiveCapabilityCurvePoints(getReactiveCapabilityCurvePointsFromEquipments(networkUuid, resources)); } public void deleteGenerator(UUID networkUuid, int variantNum, String generatorId) { deleteIdentifiable(networkUuid, variantNum, generatorId, GENERATOR_TABLE); + deleteReactiveCapabilityCurvePoints(networkUuid, variantNum, generatorId); } // battery public void createBatteries(UUID networkUuid, List> resources) { createIdentifiables(networkUuid, resources, mappings.getBatteryMappings()); + + // Now that batteries are created, we will insert in the database the corresponding reactive capability curve points. + insertReactiveCapabilityCurvePoints(getReactiveCapabilityCurvePointsFromEquipments(networkUuid, resources)); } public Optional> getBattery(UUID networkUuid, int variantNum, String batteryId) { - return getIdentifiable(networkUuid, variantNum, batteryId, mappings.getBatteryMappings()); + Optional> battery = getIdentifiable(networkUuid, variantNum, batteryId, mappings.getBatteryMappings()); + + battery.ifPresent(equipment -> { + Map> reactiveCapabilityCurvePoints = getReactiveCapabilityCurvePoints(networkUuid, variantNum, EQUIPMENT_ID_COLUMN, batteryId); + insertReactiveCapabilityCurvePointsInEquipments(networkUuid, List.of(equipment), reactiveCapabilityCurvePoints); + }); + return battery; } public List> getBatteries(UUID networkUuid, int variantNum) { - return getIdentifiables(networkUuid, variantNum, mappings.getBatteryMappings()); + List> batteries = getIdentifiables(networkUuid, variantNum, mappings.getBatteryMappings()); + + Map> reactiveCapabilityCurvePoints = getReactiveCapabilityCurvePoints(networkUuid, variantNum, EQUIPMENT_TYPE_COLUMN, ResourceType.BATTERY.toString()); + + insertReactiveCapabilityCurvePointsInEquipments(networkUuid, batteries, reactiveCapabilityCurvePoints); + + return batteries; } public List> getVoltageLevelBatteries(UUID networkUuid, int variantNum, String voltageLevelId) { - return getIdentifiablesInVoltageLevel(networkUuid, variantNum, voltageLevelId, mappings.getBatteryMappings()); + List> batteries = getIdentifiablesInVoltageLevel(networkUuid, variantNum, voltageLevelId, mappings.getBatteryMappings()); + + List equipmentsIds = batteries.stream().map(Resource::getId).collect(Collectors.toList()); + + Map> reactiveCapabilityCurvePoints = getReactiveCapabilityCurvePointsWithInClause(networkUuid, variantNum, EQUIPMENT_ID_COLUMN, equipmentsIds); + + insertReactiveCapabilityCurvePointsInEquipments(networkUuid, batteries, reactiveCapabilityCurvePoints); + + return batteries; } public void updateBatteries(UUID networkUuid, List> resources) { updateIdentifiables(networkUuid, resources, mappings.getBatteryMappings(), VOLTAGE_LEVEL_ID_COLUMN); + + // To update the battery's reactive capability curve points, we will first delete them, then create them again. + // This is done this way to prevent issues in case the reactive capability curve point's primary key is to be + // modified because of the updated equipment's new values. + deleteReactiveCapabilityCurvePoints(networkUuid, resources); + insertReactiveCapabilityCurvePoints(getReactiveCapabilityCurvePointsFromEquipments(networkUuid, resources)); } public void deleteBattery(UUID networkUuid, int variantNum, String batteryId) { deleteIdentifiable(networkUuid, variantNum, batteryId, BATTERY_TABLE); + deleteReactiveCapabilityCurvePoints(networkUuid, variantNum, batteryId); } // load @@ -808,26 +884,56 @@ public void deleteShuntCompensator(UUID networkUuid, int variantNum, String shun public void createVscConverterStations(UUID networkUuid, List> resources) { createIdentifiables(networkUuid, resources, mappings.getVscConverterStationMappings()); + + // Now that vsc converter stations are created, we will insert in the database the corresponding reactive capability curve points. + insertReactiveCapabilityCurvePoints(getReactiveCapabilityCurvePointsFromEquipments(networkUuid, resources)); } public Optional> getVscConverterStation(UUID networkUuid, int variantNum, String vscConverterStationId) { - return getIdentifiable(networkUuid, variantNum, vscConverterStationId, mappings.getVscConverterStationMappings()); + Optional> vscConverterStation = getIdentifiable(networkUuid, variantNum, vscConverterStationId, mappings.getVscConverterStationMappings()); + + vscConverterStation.ifPresent(equipment -> { + Map> reactiveCapabilityCurvePoints = getReactiveCapabilityCurvePoints(networkUuid, variantNum, EQUIPMENT_ID_COLUMN, vscConverterStationId); + insertReactiveCapabilityCurvePointsInEquipments(networkUuid, List.of(equipment), reactiveCapabilityCurvePoints); + }); + return vscConverterStation; } public List> getVscConverterStations(UUID networkUuid, int variantNum) { - return getIdentifiables(networkUuid, variantNum, mappings.getVscConverterStationMappings()); + List> vscConverterStations = getIdentifiables(networkUuid, variantNum, mappings.getVscConverterStationMappings()); + + Map> reactiveCapabilityCurvePoints = getReactiveCapabilityCurvePoints(networkUuid, variantNum, EQUIPMENT_TYPE_COLUMN, ResourceType.VSC_CONVERTER_STATION.toString()); + + insertReactiveCapabilityCurvePointsInEquipments(networkUuid, vscConverterStations, reactiveCapabilityCurvePoints); + + return vscConverterStations; } public List> getVoltageLevelVscConverterStations(UUID networkUuid, int variantNum, String voltageLevelId) { - return getIdentifiablesInVoltageLevel(networkUuid, variantNum, voltageLevelId, mappings.getVscConverterStationMappings()); + List> vscConverterStations = getIdentifiablesInVoltageLevel(networkUuid, variantNum, voltageLevelId, mappings.getVscConverterStationMappings()); + + List equipmentsIds = vscConverterStations.stream().map(Resource::getId).collect(Collectors.toList()); + + Map> reactiveCapabilityCurvePoints = getReactiveCapabilityCurvePointsWithInClause(networkUuid, variantNum, EQUIPMENT_ID_COLUMN, equipmentsIds); + + insertReactiveCapabilityCurvePointsInEquipments(networkUuid, vscConverterStations, reactiveCapabilityCurvePoints); + + return vscConverterStations; } public void updateVscConverterStations(UUID networkUuid, List> resources) { updateIdentifiables(networkUuid, resources, mappings.getVscConverterStationMappings(), VOLTAGE_LEVEL_ID_COLUMN); + + // To update the vscConverterStation's reactive capability curve points, we will first delete them, then create them again. + // This is done this way to prevent issues in case the reactive capability curve point's primary key is to be + // modified because of the updated equipment's new values. + deleteReactiveCapabilityCurvePoints(networkUuid, resources); + insertReactiveCapabilityCurvePoints(getReactiveCapabilityCurvePointsFromEquipments(networkUuid, resources)); } public void deleteVscConverterStation(UUID networkUuid, int variantNum, String vscConverterStationId) { deleteIdentifiable(networkUuid, variantNum, vscConverterStationId, VSC_CONVERTER_STATION_TABLE); + deleteReactiveCapabilityCurvePoints(networkUuid, variantNum, vscConverterStationId); } // LCC converter station @@ -966,8 +1072,6 @@ public List> getTwoWindingsTransforme public List> getVoltageLevelTwoWindingsTransformers(UUID networkUuid, int variantNum, String voltageLevelId) { List> twoWindingsTransformers = getIdentifiablesInVoltageLevel(networkUuid, variantNum, voltageLevelId, mappings.getTwoWindingsTransformerMappings()); - // Because there are not so many two windings transformers for a specific voltageLevelId, we can search their - // temporary limits by their IDs instead of by the two windings transformer type. List equipmentsIds = twoWindingsTransformers.stream().map(Resource::getId).collect(Collectors.toList()); Map> temporaryLimits = getTemporaryLimitsWithInClause(networkUuid, variantNum, EQUIPMENT_ID_COLUMN, equipmentsIds); @@ -1024,8 +1128,6 @@ public List> getThreeWindingsTransf public List> getVoltageLevelThreeWindingsTransformers(UUID networkUuid, int variantNum, String voltageLevelId) { List> threeWindingsTransformers = getIdentifiablesInVoltageLevel(networkUuid, variantNum, voltageLevelId, mappings.getThreeWindingsTransformerMappings()); - // Because there are not so many three windings transformers for a specific voltageLevelId, we can search their - // temporary limits by their IDs instead of by the three windings transformer type. List equipmentsIds = threeWindingsTransformers.stream().map(Resource::getId).collect(Collectors.toList()); Map> temporaryLimits = getTemporaryLimitsWithInClause(networkUuid, variantNum, EQUIPMENT_ID_COLUMN, equipmentsIds); @@ -1082,8 +1184,6 @@ public List> getLines(UUID networkUuid, int variantNum) public List> getVoltageLevelLines(UUID networkUuid, int variantNum, String voltageLevelId) { List> lines = getIdentifiablesInVoltageLevel(networkUuid, variantNum, voltageLevelId, mappings.getLineMappings()); - // Because there are not so many lines for a specific voltageLevelId, we can search their - // temporary limits by their IDs instead of by the line type. List equipmentsIds = lines.stream().map(Resource::getId).collect(Collectors.toList()); Map> temporaryLimits = getTemporaryLimitsWithInClause(networkUuid, variantNum, EQUIPMENT_ID_COLUMN, equipmentsIds); @@ -1155,8 +1255,6 @@ public Optional> getDanglingLine(UUID networkUu public List> getVoltageLevelDanglingLines(UUID networkUuid, int variantNum, String voltageLevelId) { List> danglingLines = getIdentifiablesInVoltageLevel(networkUuid, variantNum, voltageLevelId, mappings.getDanglingLineMappings()); - // Because there are not so many dangling lines for a specific voltageLevelId, we can search their - // temporary limits by their IDs instead of by the dangling line type. List equipmentsIds = danglingLines.stream().map(Resource::getId).collect(Collectors.toList()); Map> temporaryLimits = getTemporaryLimitsWithInClause(networkUuid, variantNum, EQUIPMENT_ID_COLUMN, equipmentsIds); @@ -1272,7 +1370,6 @@ public Optional> getIdentifiable(UUID networkUu } // Temporary Limits - public Map> getTemporaryLimitsWithInClause(UUID networkUuid, int variantNum, String columnNameForWhereClause, List valuesForInClause) { if (valuesForInClause.isEmpty()) { return Collections.emptyMap(); @@ -1348,21 +1445,12 @@ protected Map void insertTemporaryLimitsInEquipments(UUID networkUuid, List> equipments, Map> temporaryLimits) { - // Some equipments can have temporary limits. - // Those limits are not in the database table representation of the equipment, they are in a different - // table : "temporarylimit". - // We need to complete the equipments we get from the database by searching the corresponding temporary limits - // and inserting those limits inside the corresponding equipments. - // The choosen algorithm to do this is to retrieve all the temporary limits for a networkUuid, variantNum and - // side, then check each limit's equipment ID to see if it is the same ID as an equipment's. - // If it is, then it means the temporary limit belongs to the equipment. if (!temporaryLimits.isEmpty() && !equipments.isEmpty()) { for (Resource equipmentAttributesResource : equipments) { @@ -1424,8 +1512,189 @@ private void deleteTemporaryLimits(UUID netwo resourceIds.add(resource.getId()); } resourceIdsByVariant.put(resource.getVariantNum(), resourceIds); - } resourceIdsByVariant.forEach((k, v) -> deleteTemporaryLimits(networkUuid, k, v)); } + + public void insertReactiveCapabilityCurvePoints(Map> reactiveCapabilityCurvePoints) { + try (var connection = dataSource.getConnection()) { + try (var preparedStmt = connection.prepareStatement(QueryCatalog.buildInsertReactiveCapabilityCurvePointsQuery())) { + List values = new ArrayList<>(7); + List>> list = new ArrayList<>(reactiveCapabilityCurvePoints.entrySet()); + for (List>> subUnit : Lists.partition(list, BATCH_SIZE)) { + for (Map.Entry> myPair : subUnit) { + for (ReactiveCapabilityCurvePointAttributes reactiveCapabilityCurvePoint : myPair.getValue()) { + values.clear(); + // In order, from the QueryCatalog.buildInsertReactiveCapabilityCurvePointsQuery SQL query : + // equipmentId, equipmentType, networkUuid, variantNum, minQ, maxQ, p + values.add(myPair.getKey().getEquipmentId()); + values.add(myPair.getKey().getEquipmentType().toString()); + values.add(myPair.getKey().getNetworkUuid()); + values.add(myPair.getKey().getVariantNum()); + values.add(reactiveCapabilityCurvePoint.getMinQ()); + values.add(reactiveCapabilityCurvePoint.getMaxQ()); + values.add(reactiveCapabilityCurvePoint.getP()); + bindValues(preparedStmt, values); + preparedStmt.addBatch(); + } + } + preparedStmt.executeBatch(); + } + } + } catch (SQLException e) { + throw new UncheckedSqlException(e); + } + } + + public Map> getReactiveCapabilityCurvePointsWithInClause(UUID networkUuid, int variantNum, String columnNameForWhereClause, List valuesForInClause) { + if (valuesForInClause.isEmpty()) { + return Collections.emptyMap(); + } + try (var connection = dataSource.getConnection()) { + var preparedStmt = connection.prepareStatement(QueryCatalog.buildReactiveCapabilityCurvePointWithInClauseQuery(columnNameForWhereClause, valuesForInClause.size())); + preparedStmt.setObject(1, networkUuid.toString()); + preparedStmt.setInt(2, variantNum); + for (int i = 0; i < valuesForInClause.size(); i++) { + preparedStmt.setString(3 + i, valuesForInClause.get(i)); + } + + return innerGetReactiveCapabilityCurvePoints(preparedStmt); + } catch (SQLException e) { + throw new UncheckedSqlException(e); + } + } + + public Map> getReactiveCapabilityCurvePoints(UUID networkUuid, int variantNum, String columnNameForWhereClause, String valueForWhereClause) { + try (var connection = dataSource.getConnection()) { + var preparedStmt = connection.prepareStatement(QueryCatalog.buildReactiveCapabilityCurvePointQuery(columnNameForWhereClause)); + preparedStmt.setObject(1, networkUuid.toString()); + preparedStmt.setInt(2, variantNum); + preparedStmt.setString(3, valueForWhereClause); + + return innerGetReactiveCapabilityCurvePoints(preparedStmt); + } catch (SQLException e) { + throw new UncheckedSqlException(e); + } + } + + private Map> innerGetReactiveCapabilityCurvePoints(PreparedStatement preparedStmt) throws SQLException { + try (ResultSet resultSet = preparedStmt.executeQuery()) { + Map> map = new HashMap<>(); + while (resultSet.next()) { + + OwnerInfo owner = new OwnerInfo(); + ReactiveCapabilityCurvePointAttributes reactiveCapabilityCurvePoint = new ReactiveCapabilityCurvePointAttributes(); + // In order, from the QueryCatalog.buildReactiveCapabilityCurvePointQuery SQL query : + // equipmentId, equipmentType, networkUuid, variantNum, minQ, maxQ, p + owner.setEquipmentId(resultSet.getString(1)); + owner.setEquipmentType(ResourceType.valueOf(resultSet.getString(2))); + owner.setNetworkUuid(UUID.fromString(resultSet.getString(3))); + owner.setVariantNum(resultSet.getInt(4)); + reactiveCapabilityCurvePoint.setMinQ(resultSet.getInt(5)); + reactiveCapabilityCurvePoint.setMaxQ(resultSet.getInt(6)); + reactiveCapabilityCurvePoint.setP(resultSet.getInt(7)); + + if (!map.containsKey(owner)) { + List reactiveCapabilityCurvePointsCollection = new ArrayList<>(); + reactiveCapabilityCurvePointsCollection.add(reactiveCapabilityCurvePoint); + map.put(owner, reactiveCapabilityCurvePointsCollection); + } else { + map.get(owner).add(reactiveCapabilityCurvePoint); + } + } + return map; + } + } + + protected Map> getReactiveCapabilityCurvePointsFromEquipments(UUID networkUuid, List> resources) { + Map> map = new HashMap<>(); + + if (!resources.isEmpty()) { + for (Resource resource : resources) { + + ReactiveLimitsAttributes reactiveLimits = resource.getAttributes().getReactiveLimits(); + if (reactiveLimits != null + && reactiveLimits.getKind() == ReactiveLimitsKind.CURVE + && ((ReactiveCapabilityCurveAttributes) reactiveLimits).getPoints() != null) { + + OwnerInfo info = new OwnerInfo( + resource.getId(), + resource.getType(), + networkUuid, + resource.getVariantNum() + ); + map.put(info, new ArrayList<>(((ReactiveCapabilityCurveAttributes) reactiveLimits).getPoints().values())); + } + } + } + return map; + } + + protected void insertReactiveCapabilityCurvePointsInEquipments(UUID networkUuid, List> equipments, Map> reactiveCapabilityCurvePoints) { + + if (!reactiveCapabilityCurvePoints.isEmpty() && !equipments.isEmpty()) { + for (Resource equipmentAttributesResource : equipments) { + OwnerInfo owner = new OwnerInfo( + equipmentAttributesResource.getId(), + equipmentAttributesResource.getType(), + networkUuid, + equipmentAttributesResource.getVariantNum() + ); + if (reactiveCapabilityCurvePoints.containsKey(owner)) { + T equipment = equipmentAttributesResource.getAttributes(); + for (ReactiveCapabilityCurvePointAttributes reactiveCapabilityCurvePoint : reactiveCapabilityCurvePoints.get(owner)) { + insertReactiveCapabilityCurvePointInEquipment(equipment, reactiveCapabilityCurvePoint); + } + } + } + } + } + + private void insertReactiveCapabilityCurvePointInEquipment(T equipment, ReactiveCapabilityCurvePointAttributes reactiveCapabilityCurvePoint) { + + if (equipment.getReactiveLimits() == null) { + equipment.setReactiveLimits(new ReactiveCapabilityCurveAttributes()); + } + ReactiveLimitsAttributes reactiveLimitsAttributes = equipment.getReactiveLimits(); + if (reactiveLimitsAttributes instanceof ReactiveCapabilityCurveAttributes) { + if (((ReactiveCapabilityCurveAttributes) reactiveLimitsAttributes).getPoints() == null) { + ((ReactiveCapabilityCurveAttributes) reactiveLimitsAttributes).setPoints(new TreeMap<>()); + } + ((ReactiveCapabilityCurveAttributes) reactiveLimitsAttributes).getPoints().put(reactiveCapabilityCurvePoint.getP(), reactiveCapabilityCurvePoint); + } + } + + private void deleteReactiveCapabilityCurvePoints(UUID networkUuid, int variantNum, String equipmentId) { + deleteReactiveCapabilityCurvePoints(networkUuid, variantNum, List.of(equipmentId)); + } + + private void deleteReactiveCapabilityCurvePoints(UUID networkUuid, int variantNum, List equipmentIds) { + try (var connection = dataSource.getConnection()) { + try (var preparedStmt = connection.prepareStatement(QueryCatalog.buildDeleteReactiveCapabilityCurvePointsVariantEquipmentINQuery(equipmentIds.size()))) { + preparedStmt.setObject(1, networkUuid.toString()); + preparedStmt.setInt(2, variantNum); + for (int i = 0; i < equipmentIds.size(); i++) { + preparedStmt.setString(3 + i, equipmentIds.get(i)); + } + preparedStmt.executeUpdate(); + } + } catch (SQLException e) { + throw new UncheckedSqlException(e); + } + } + + private void deleteReactiveCapabilityCurvePoints(UUID networkUuid, List> resources) { + Map> resourceIdsByVariant = new HashMap<>(); + for (Resource resource : resources) { + List resourceIds = resourceIdsByVariant.get(resource.getVariantNum()); + if (resourceIds != null) { + resourceIds.add(resource.getId()); + } else { + resourceIds = new ArrayList<>(); + resourceIds.add(resource.getId()); + } + resourceIdsByVariant.put(resource.getVariantNum(), resourceIds); + } + resourceIdsByVariant.forEach((k, v) -> deleteReactiveCapabilityCurvePoints(networkUuid, k, v)); + } } diff --git a/network-store-server/src/main/java/com/powsybl/network/store/server/OwnerInfo.java b/network-store-server/src/main/java/com/powsybl/network/store/server/OwnerInfo.java index ce25a8e68..a3c5dbc4b 100644 --- a/network-store-server/src/main/java/com/powsybl/network/store/server/OwnerInfo.java +++ b/network-store-server/src/main/java/com/powsybl/network/store/server/OwnerInfo.java @@ -16,6 +16,13 @@ import lombok.NoArgsConstructor; /** + * Some equipments can have sub-attributes which are represented in a different table as the equipment's. + * When fetching the equipment from the database, we need to also get the sub-attributes from their respective + * table sand insert them inside the retrieved equipment. + * The sub-attributes all have in common some information about their parent equipment, which are represented + * in this OwnerInfo class. + * Each sub-attribute is supposed to be linked manually to the equipment with the help of this OwnerInfo class. + * * @author Sylvain Bouzols */ @Data diff --git a/network-store-server/src/main/java/com/powsybl/network/store/server/QueryCatalog.java b/network-store-server/src/main/java/com/powsybl/network/store/server/QueryCatalog.java index c233988e4..4f9491476 100644 --- a/network-store-server/src/main/java/com/powsybl/network/store/server/QueryCatalog.java +++ b/network-store-server/src/main/java/com/powsybl/network/store/server/QueryCatalog.java @@ -20,6 +20,8 @@ */ public final class QueryCatalog { + private static final String MINIMAL_VALUE_REQUIREMENT_ERROR = "Function should not be called without at least one value."; + static final String VARIANT_ID_COLUMN = "variantId"; static final String UUID_COLUMN = "uuid"; static final String NETWORK_UUID_COLUMN = "networkUuid"; @@ -197,13 +199,24 @@ public static String buildCloneIdentifiablesQuery(String tableName, Collection columns) { @@ -221,11 +234,12 @@ public static String buildCloneNetworksQuery(Collection columns) { ID_COLUMN + ", " + columns.stream().filter(column -> !column.equals(UUID_COLUMN) && !column.equals(VARIANT_ID_COLUMN) && !column.equals(NAME_COLUMN)).collect(Collectors.joining(",")) + " from network" + " " + - "where uuid = ? and variantNum = ?"; + "where uuid = ? and " + VARIANT_NUM_COLUMN + " = ?"; } public static String buildTemporaryLimitQuery(String columnNameForWhereClause) { - return "select equipmentId, equipmentType, " + + return "select " + EQUIPMENT_ID_COLUMN + ", " + + EQUIPMENT_TYPE_COLUMN + ", " + NETWORK_UUID_COLUMN + ", " + VARIANT_NUM_COLUMN + ", " + "side, limitType, " + @@ -239,9 +253,10 @@ public static String buildTemporaryLimitQuery(String columnNameForWhereClause) { public static String buildTemporaryLimitWithInClauseQuery(String columnNameForInClause, int numberOfValues) { if (numberOfValues < 1) { - throw new IllegalArgumentException("Function should not be called without values to insert."); + throw new IllegalArgumentException(MINIMAL_VALUE_REQUIREMENT_ERROR); } - return "select equipmentId, equipmentType, " + + return "select " + EQUIPMENT_ID_COLUMN + ", " + + EQUIPMENT_TYPE_COLUMN + ", " + NETWORK_UUID_COLUMN + ", " + VARIANT_NUM_COLUMN + ", " + "side, limitType, " + @@ -256,7 +271,7 @@ public static String buildTemporaryLimitWithInClauseQuery(String columnNameForIn public static String buildInsertTemporaryLimitsQuery() { return "insert into temporarylimit(" + - "equipmentId, equipmentType, " + + EQUIPMENT_ID_COLUMN + ", " + EQUIPMENT_TYPE_COLUMN + ", " + NETWORK_UUID_COLUMN + " ," + VARIANT_NUM_COLUMN + ", side, limitType, " + NAME_COLUMN + ", value_, acceptableDuration, fictitious)" + @@ -265,7 +280,7 @@ public static String buildInsertTemporaryLimitsQuery() { public static String buildDeleteTemporaryLimitsVariantEquipmentINQuery(int numberOfValues) { if (numberOfValues < 1) { - throw new IllegalArgumentException("Function should not be called without values to insert."); + throw new IllegalArgumentException(MINIMAL_VALUE_REQUIREMENT_ERROR); } return "delete from temporarylimit where " + NETWORK_UUID_COLUMN + " = ? and " + @@ -284,4 +299,62 @@ public static String buildDeleteTemporaryLimitsQuery() { return "delete from temporarylimit where " + NETWORK_UUID_COLUMN + " = ?"; } + + public static String buildReactiveCapabilityCurvePointQuery(String columnNameForWhereClause) { + return "select " + EQUIPMENT_ID_COLUMN + ", " + + EQUIPMENT_TYPE_COLUMN + ", " + + NETWORK_UUID_COLUMN + ", " + + VARIANT_NUM_COLUMN + ", " + + "minQ, maxQ, p " + + "from ReactiveCapabilityCurvePoint where " + + NETWORK_UUID_COLUMN + " = ? and " + + VARIANT_NUM_COLUMN + " = ? and " + + columnNameForWhereClause + " = ?"; + } + + public static String buildReactiveCapabilityCurvePointWithInClauseQuery(String columnNameForInClause, int numberOfValues) { + if (numberOfValues < 1) { + throw new IllegalArgumentException(MINIMAL_VALUE_REQUIREMENT_ERROR); + } + return "select " + EQUIPMENT_ID_COLUMN + ", " + + EQUIPMENT_TYPE_COLUMN + ", " + + NETWORK_UUID_COLUMN + ", " + + VARIANT_NUM_COLUMN + ", " + + "minQ, maxQ, p " + + "from ReactiveCapabilityCurvePoint where " + + NETWORK_UUID_COLUMN + " = ? and " + + VARIANT_NUM_COLUMN + " = ? and " + + columnNameForInClause + " in (" + + "?, ".repeat(numberOfValues - 1) + "?)"; + } + + public static String buildInsertReactiveCapabilityCurvePointsQuery() { + return "insert into ReactiveCapabilityCurvePoint(" + + EQUIPMENT_ID_COLUMN + ", " + EQUIPMENT_TYPE_COLUMN + ", " + + NETWORK_UUID_COLUMN + " ," + + VARIANT_NUM_COLUMN + ", minQ, maxQ, p)" + + " values (?, ?, ?, ?, ?, ?, ?)"; + } + + public static String buildDeleteReactiveCapabilityCurvePointsVariantEquipmentINQuery(int numberOfValues) { + if (numberOfValues < 1) { + throw new IllegalArgumentException(MINIMAL_VALUE_REQUIREMENT_ERROR); + } + return "delete from ReactiveCapabilityCurvePoint where " + + NETWORK_UUID_COLUMN + " = ? and " + + VARIANT_NUM_COLUMN + " = ? and " + + EQUIPMENT_ID_COLUMN + " in (" + + "?, ".repeat(numberOfValues - 1) + "?)"; + } + + public static String buildDeleteReactiveCapabilityCurvePointsVariantQuery() { + return "delete from ReactiveCapabilityCurvePoint where " + + NETWORK_UUID_COLUMN + " = ? and " + + VARIANT_NUM_COLUMN + " = ?"; + } + + public static String buildDeleteReactiveCapabilityCurvePointsQuery() { + return "delete from ReactiveCapabilityCurvePoint where " + + NETWORK_UUID_COLUMN + " = ?"; + } } diff --git a/network-store-server/src/main/resources/db/changelog/changesets/changelog_2022-09-28T11:30:00Z.xml b/network-store-server/src/main/resources/db/changelog/changesets/changelog_2022-09-28T11:30:00Z.xml new file mode 100644 index 000000000..56f807c3f --- /dev/null +++ b/network-store-server/src/main/resources/db/changelog/changesets/changelog_2022-09-28T11:30:00Z.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/network-store-server/src/main/resources/db/changelog/db.changelog-master.yaml b/network-store-server/src/main/resources/db/changelog/db.changelog-master.yaml index 28f2c5487..849e240d9 100644 --- a/network-store-server/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/network-store-server/src/main/resources/db/changelog/db.changelog-master.yaml @@ -7,3 +7,7 @@ databaseChangeLog: - include: file: changesets/changelog_2022-10-04T16:04:00Z.xml relativeToChangelogFile: true + + - include: + file: changesets/changelog_2022-09-28T11:30:00Z.xml + relativeToChangelogFile: true diff --git a/network-store-server/src/test/java/com/powsybl/network/store/server/NetworkStoreControllerIT.java b/network-store-server/src/test/java/com/powsybl/network/store/server/NetworkStoreControllerIT.java index 5b8a90cae..ee9143b80 100644 --- a/network-store-server/src/test/java/com/powsybl/network/store/server/NetworkStoreControllerIT.java +++ b/network-store-server/src/test/java/com/powsybl/network/store/server/NetworkStoreControllerIT.java @@ -482,7 +482,7 @@ public void test() throws Exception { generator.getAttributes().getRegulatingTerminal().setConnectableId("idEq2"); generator.getAttributes().getRegulatingTerminal().setSide("TWO"); generator.getAttributes().setReactiveLimits(ReactiveCapabilityCurveAttributes.builder() - .points(new TreeMap<>(Map.of(1., ReactiveCapabilityCurvePointAttributes.builder().p(50.).minQ(11.).maxQ(76.) + .points(new TreeMap<>(Map.of(50., ReactiveCapabilityCurvePointAttributes.builder().p(50.).minQ(11.).maxQ(76.) .build()))).build()); mvc.perform(put("/" + VERSION + "/networks/" + NETWORK_UUID + "/generators") @@ -497,9 +497,9 @@ public void test() throws Exception { .andExpect(jsonPath("data[0].attributes.regulatingTerminal.connectableId").value("idEq2")) .andExpect(jsonPath("data[0].attributes.regulatingTerminal.side").value("TWO")) .andExpect(jsonPath("data[0].attributes.reactiveLimits.kind").value("CURVE")) - .andExpect(jsonPath("data[0].attributes.reactiveLimits.points[\"1.0\"].p").value(50.)) - .andExpect(jsonPath("data[0].attributes.reactiveLimits.points[\"1.0\"].minQ").value(11.)) - .andExpect(jsonPath("data[0].attributes.reactiveLimits.points[\"1.0\"].maxQ").value(76.)); + .andExpect(jsonPath("data[0].attributes.reactiveLimits.points[\"50.0\"].p").value(50.)) + .andExpect(jsonPath("data[0].attributes.reactiveLimits.points[\"50.0\"].minQ").value(11.)) + .andExpect(jsonPath("data[0].attributes.reactiveLimits.points[\"50.0\"].maxQ").value(76.)); // battery creation and update Resource battery = Resource.batteryBuilder() diff --git a/network-store-server/src/test/java/com/powsybl/network/store/server/NetworkStoreRepositoryTest.java b/network-store-server/src/test/java/com/powsybl/network/store/server/NetworkStoreRepositoryTest.java index 20f510823..c898f444d 100644 --- a/network-store-server/src/test/java/com/powsybl/network/store/server/NetworkStoreRepositoryTest.java +++ b/network-store-server/src/test/java/com/powsybl/network/store/server/NetworkStoreRepositoryTest.java @@ -175,7 +175,153 @@ public void insertTemporaryLimitsInLinesTest() { assertNotNull(resLineB.getAttributes().getCurrentLimits1().getTemporaryLimits()); assertNull(resLineB.getAttributes().getCurrentLimits2()); assertEquals(3, resLineB.getAttributes().getCurrentLimits1().getTemporaryLimits().size()); - } + @Test + public void insertReactiveCapabilityCurvesInGeneratorsTest() { + + String equipmentIdA = "idGeneratorA"; + String equipmentIdB = "idGeneratorB"; + String equipmentIdMinMax = "idGeneratorMinMax"; + + OwnerInfo infoGeneratorA = new OwnerInfo( + equipmentIdA, + ResourceType.GENERATOR, + NETWORK_UUID, + Resource.INITIAL_VARIANT_NUM + ); + OwnerInfo infoGeneratorB = new OwnerInfo( + equipmentIdB, + ResourceType.GENERATOR, + NETWORK_UUID, + Resource.INITIAL_VARIANT_NUM + ); + OwnerInfo infoGeneratorMinMax = new OwnerInfo( + equipmentIdMinMax, + ResourceType.GENERATOR, + NETWORK_UUID, + Resource.INITIAL_VARIANT_NUM + ); + OwnerInfo infoGeneratorX = new OwnerInfo( + "badID", + ResourceType.GENERATOR, + NETWORK_UUID, + Resource.INITIAL_VARIANT_NUM + ); + + Resource resGeneratorA = Resource.generatorBuilder() + .id(equipmentIdA) + .attributes(GeneratorAttributes.builder() + .voltageLevelId("vl1") + .name("idGeneratorA") + .build()) // In this case, the reactivelimits are not initialized + .build(); + + Resource resGeneratorB = Resource.generatorBuilder() + .id(equipmentIdB) + .attributes(GeneratorAttributes.builder() + .voltageLevelId("vl2") + .name("idGeneratorB") + .reactiveLimits(ReactiveCapabilityCurveAttributes.builder().build()) // In this case, the reactivelimits are already initialized as ReactiveCapabilityCurveAttributes + .build()) + .build(); + + Resource resGeneratorMinMax = Resource.generatorBuilder() + .id(equipmentIdMinMax) + .attributes(GeneratorAttributes.builder() + .voltageLevelId("vl3") + .name("idGeneratorMinMax") + .reactiveLimits(MinMaxReactiveLimitsAttributes.builder() // In this case, the reactivelimits are already initialized as MinMaxReactiveLimitsAttributes + .maxQ(50.) + .minQ(20.) + .build()) + .build()) + .build(); + + assertEquals(resGeneratorA.getId(), infoGeneratorA.getEquipmentId()); + assertEquals(resGeneratorB.getId(), infoGeneratorB.getEquipmentId()); + assertEquals(resGeneratorMinMax.getId(), infoGeneratorMinMax.getEquipmentId()); + assertNotEquals(resGeneratorA.getId(), infoGeneratorX.getEquipmentId()); + assertNotEquals(resGeneratorB.getId(), infoGeneratorX.getEquipmentId()); + assertNotEquals(resGeneratorMinMax.getId(), infoGeneratorX.getEquipmentId()); + + ReactiveCapabilityCurvePointAttributes curvePointOka = ReactiveCapabilityCurvePointAttributes.builder() + .minQ(-100.) + .maxQ(100.) + .p(0.) + .build(); + + ReactiveCapabilityCurvePointAttributes curvePointOkb = ReactiveCapabilityCurvePointAttributes.builder() + .minQ(10.) + .maxQ(30.) + .p(20.) + .build(); + + ReactiveCapabilityCurvePointAttributes curvePointOkc = ReactiveCapabilityCurvePointAttributes.builder() + .minQ(5.) + .maxQ(25.) + .p(15.) + .build(); + + // If there are multiple instance of a curve point with the same value P, only one is kept. + ReactiveCapabilityCurvePointAttributes curvePointSameValueP = ReactiveCapabilityCurvePointAttributes.builder() + .minQ(10.) + .maxQ(30.) + .p(20.) + .build(); + + ReactiveCapabilityCurvePointAttributes curvePointWrongEquipmentId = ReactiveCapabilityCurvePointAttributes.builder() + .minQ(10.) + .maxQ(30.) + .p(20.) + .build(); + + List> generators = new ArrayList<>(); + generators.add(resGeneratorA); + generators.add(resGeneratorB); + generators.add(resGeneratorMinMax); + + List curvePointsForGeneratorA = new ArrayList<>(); + curvePointsForGeneratorA.add(curvePointOka); + curvePointsForGeneratorA.add(curvePointOkb); + curvePointsForGeneratorA.add(curvePointOkc); + curvePointsForGeneratorA.add(curvePointSameValueP); + + List curvePointsForGeneratorB = new ArrayList<>(); + curvePointsForGeneratorB.add(curvePointOka); + curvePointsForGeneratorB.add(curvePointOkb); + + List curvePointsX = new ArrayList<>(); + curvePointsX.add(curvePointWrongEquipmentId); + + Map> map = new HashMap<>(); + + map.put(infoGeneratorA, curvePointsForGeneratorA); + map.put(infoGeneratorB, curvePointsForGeneratorB); + map.put(infoGeneratorX, curvePointsX); + + assertNull(resGeneratorA.getAttributes().getReactiveLimits()); + assertTrue(resGeneratorB.getAttributes().getReactiveLimits() instanceof ReactiveCapabilityCurveAttributes); + assertNull(((ReactiveCapabilityCurveAttributes) resGeneratorB.getAttributes().getReactiveLimits()).getPoints()); + assertTrue(resGeneratorMinMax.getAttributes().getReactiveLimits() instanceof MinMaxReactiveLimitsAttributes); + + networkStoreRepository.insertReactiveCapabilityCurvePointsInEquipments(NETWORK_UUID, generators, new HashMap<>()); + + assertNull(resGeneratorA.getAttributes().getReactiveLimits()); + assertTrue(resGeneratorB.getAttributes().getReactiveLimits() instanceof ReactiveCapabilityCurveAttributes); + assertNull(((ReactiveCapabilityCurveAttributes) resGeneratorB.getAttributes().getReactiveLimits()).getPoints()); + assertTrue(resGeneratorMinMax.getAttributes().getReactiveLimits() instanceof MinMaxReactiveLimitsAttributes); + + networkStoreRepository.insertReactiveCapabilityCurvePointsInEquipments(NETWORK_UUID, generators, map); + + assertTrue(resGeneratorA.getAttributes().getReactiveLimits() instanceof ReactiveCapabilityCurveAttributes); + assertNotNull(((ReactiveCapabilityCurveAttributes) resGeneratorA.getAttributes().getReactiveLimits()).getPoints()); + assertEquals(3, ((ReactiveCapabilityCurveAttributes) resGeneratorA.getAttributes().getReactiveLimits()).getPoints().size()); + + assertTrue(resGeneratorB.getAttributes().getReactiveLimits() instanceof ReactiveCapabilityCurveAttributes); + assertNotNull(((ReactiveCapabilityCurveAttributes) resGeneratorB.getAttributes().getReactiveLimits()).getPoints()); + assertEquals(2, ((ReactiveCapabilityCurveAttributes) resGeneratorB.getAttributes().getReactiveLimits()).getPoints().size()); + + assertTrue(resGeneratorMinMax.getAttributes().getReactiveLimits() instanceof MinMaxReactiveLimitsAttributes); + } }