Skip to content

Commit 31b3a15

Browse files
authored
Add state measurements in load modification (#36)
Signed-off-by: Ayoub LABIDI <ayoub.labidi@protonmail.com>
1 parent a202f0d commit 31b3a15

File tree

4 files changed

+158
-15
lines changed

4 files changed

+158
-15
lines changed

src/main/java/org/gridsuite/modification/dto/InjectionModificationInfos.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77
package org.gridsuite.modification.dto;
88

9+
import com.fasterxml.jackson.annotation.JsonProperty;
910
import com.powsybl.iidm.network.extensions.ConnectablePosition;
1011
import io.swagger.v3.oas.annotations.media.Schema;
1112
import lombok.Getter;
@@ -41,4 +42,20 @@ public class InjectionModificationInfos extends BasicEquipmentModificationInfos
4142

4243
@Schema(description = "Connected")
4344
private AttributeModification<Boolean> terminalConnected;
45+
46+
@JsonProperty("pMeasurementValue")
47+
@Schema(description = "P measurement value")
48+
private AttributeModification<Double> pMeasurementValue;
49+
50+
@JsonProperty("pMeasurementValidity")
51+
@Schema(description = "P measurement validity")
52+
private AttributeModification<Boolean> pMeasurementValidity;
53+
54+
@JsonProperty("qMeasurementValue")
55+
@Schema(description = "Q measurement value")
56+
private AttributeModification<Double> qMeasurementValue;
57+
58+
@JsonProperty("qMeasurementValidity")
59+
@Schema(description = "Q measurement validity")
60+
private AttributeModification<Boolean> qMeasurementValidity;
4461
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/**
2+
* Copyright (c) 2025, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
package org.gridsuite.modification.modifications;
8+
9+
import com.powsybl.commons.report.ReportNode;
10+
import com.powsybl.commons.report.TypedValue;
11+
import com.powsybl.iidm.network.Injection;
12+
import com.powsybl.iidm.network.extensions.Measurement;
13+
import com.powsybl.iidm.network.extensions.Measurements;
14+
import com.powsybl.iidm.network.extensions.MeasurementsAdder;
15+
import org.gridsuite.modification.dto.InjectionModificationInfos;
16+
import org.gridsuite.modification.utils.ModificationUtils;
17+
18+
import java.util.ArrayList;
19+
import java.util.List;
20+
import java.util.UUID;
21+
22+
/**
23+
* @author Ayoub LABIDI <ayoub.labidi at rte-france.com>
24+
*/
25+
public abstract class AbstractInjectionModification extends AbstractModification {
26+
27+
private static final String VALUE = "value";
28+
private static final String VALIDITY = "validity";
29+
protected final InjectionModificationInfos modificationInfos;
30+
31+
protected AbstractInjectionModification(InjectionModificationInfos modificationInfos) {
32+
this.modificationInfos = modificationInfos;
33+
}
34+
35+
protected ReportNode updateMeasurements(Injection<?> injection, InjectionModificationInfos injectionModificationInfos, ReportNode subReportNode) {
36+
Double pValue = injectionModificationInfos.getPMeasurementValue() != null ? injectionModificationInfos.getPMeasurementValue().getValue() : null;
37+
Double qValue = injectionModificationInfos.getQMeasurementValue() != null ? injectionModificationInfos.getQMeasurementValue().getValue() : null;
38+
Boolean pValidity = injectionModificationInfos.getPMeasurementValidity() != null ? injectionModificationInfos.getPMeasurementValidity().getValue() : null;
39+
Boolean qValidity = injectionModificationInfos.getQMeasurementValidity() != null ? injectionModificationInfos.getQMeasurementValidity().getValue() : null;
40+
41+
if (pValue == null && pValidity == null && qValue == null && qValidity == null) {
42+
// no measurement modification requested
43+
return null;
44+
}
45+
Measurements<?> measurements = (Measurements<?>) injection.getExtension(Measurements.class);
46+
if (measurements == null) {
47+
MeasurementsAdder<?> measurementsAdder = injection.newExtension(MeasurementsAdder.class);
48+
measurements = measurementsAdder.add();
49+
}
50+
// measurements update
51+
List<ReportNode> reports = new ArrayList<>();
52+
upsertMeasurement(measurements, Measurement.Type.ACTIVE_POWER, pValue, pValidity, reports);
53+
upsertMeasurement(measurements, Measurement.Type.REACTIVE_POWER, qValue, qValidity, reports);
54+
// report changes
55+
ReportNode estimSubReportNode = null;
56+
if (!reports.isEmpty()) {
57+
estimSubReportNode = subReportNode.newReportNode().withMessageTemplate("StateEstimationData", "State estimation").add();
58+
ModificationUtils.getInstance().reportModifications(estimSubReportNode, reports, "measurements", " Measurements");
59+
}
60+
return estimSubReportNode;
61+
}
62+
63+
private void upsertMeasurement(Measurements<?> measurements, Measurement.Type type, Double value, Boolean validity, List<ReportNode> reports) {
64+
if (value == null && validity == null) {
65+
return;
66+
}
67+
String measurementType = (type == Measurement.Type.ACTIVE_POWER ? "Active power" : "Reactive power") + " measurement ";
68+
Measurement measurement = getExistingMeasurement(measurements, type);
69+
if (measurement != null) { // update measurement
70+
if (value != null) {
71+
double oldValue = measurement.getValue();
72+
measurement.setValue(value);
73+
reports.add(ModificationUtils.buildModificationReport(oldValue, value, measurementType + VALUE, 1, TypedValue.INFO_SEVERITY));
74+
}
75+
if (validity != null) {
76+
boolean oldValidity = measurement.isValid();
77+
measurement.setValid(validity);
78+
reports.add(ModificationUtils.buildModificationReport(oldValidity, validity, measurementType + VALIDITY, 1, TypedValue.INFO_SEVERITY));
79+
}
80+
} else {
81+
var measurementAdder = measurements.newMeasurement().setId(UUID.randomUUID().toString()).setType(type);
82+
if (value != null) {
83+
measurementAdder.setValue(value);
84+
reports.add(ModificationUtils.buildModificationReport(null, value, measurementType + VALUE, 1, TypedValue.INFO_SEVERITY));
85+
}
86+
if (validity != null) {
87+
measurementAdder.setValid(validity);
88+
reports.add(ModificationUtils.buildModificationReport(null, validity, measurementType + VALIDITY, 1, TypedValue.INFO_SEVERITY));
89+
}
90+
measurementAdder.add();
91+
}
92+
}
93+
94+
private Measurement getExistingMeasurement(Measurements<?> measurements, Measurement.Type type) {
95+
return measurements.getMeasurements(type).stream().findFirst().orElse(null);
96+
}
97+
}

src/main/java/org/gridsuite/modification/modifications/LoadModification.java

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import com.powsybl.iidm.network.Network;
1313
import com.powsybl.iidm.network.extensions.ConnectablePosition;
1414
import com.powsybl.iidm.network.extensions.ConnectablePositionAdder;
15+
1516
import org.gridsuite.modification.NetworkModificationException;
1617
import org.gridsuite.modification.dto.AttributeModification;
1718
import org.gridsuite.modification.dto.LoadModificationInfos;
@@ -23,12 +24,10 @@
2324
/**
2425
* @author Ayoub Labidi <ayoub.labidi at rte-france.com>
2526
*/
26-
public class LoadModification extends AbstractModification {
27-
28-
private final LoadModificationInfos modificationInfos;
27+
public class LoadModification extends AbstractInjectionModification {
2928

3029
public LoadModification(LoadModificationInfos modificationInfos) {
31-
this.modificationInfos = modificationInfos;
30+
super(modificationInfos);
3231
}
3332

3433
@Override
@@ -53,19 +52,22 @@ public String getName() {
5352
}
5453

5554
private void modifyLoad(Load load, ReportNode subReportNode) {
55+
LoadModificationInfos loadModificationInfos = (LoadModificationInfos) modificationInfos;
5656
subReportNode.newReportNode()
5757
.withMessageTemplate("loadModification", "Load with id=${id} modified :")
58-
.withUntypedValue("id", modificationInfos.getEquipmentId())
58+
.withUntypedValue("id", loadModificationInfos.getEquipmentId())
5959
.withSeverity(TypedValue.INFO_SEVERITY)
6060
.add();
6161

62-
ModificationUtils.getInstance().applyElementaryModifications(load::setName, () -> load.getOptionalName().orElse("No value"), modificationInfos.getEquipmentName(), subReportNode, "Name");
63-
ModificationUtils.getInstance().applyElementaryModifications(load::setLoadType, load::getLoadType, modificationInfos.getLoadType(), subReportNode, "Type");
64-
modifyP0(load, modificationInfos.getP0(), subReportNode);
65-
modifyQ0(load, modificationInfos.getQ0(), subReportNode);
66-
modifyLoadConnectivityAttributes(modificationInfos, load, subReportNode);
62+
ModificationUtils.getInstance().applyElementaryModifications(load::setName, () -> load.getOptionalName().orElse("No value"), loadModificationInfos.getEquipmentName(), subReportNode, "Name");
63+
ModificationUtils.getInstance().applyElementaryModifications(load::setLoadType, load::getLoadType, loadModificationInfos.getLoadType(), subReportNode, "Type");
64+
modifyP0(load, loadModificationInfos.getP0(), subReportNode);
65+
modifyQ0(load, loadModificationInfos.getQ0(), subReportNode);
66+
modifyLoadConnectivityAttributes(loadModificationInfos, load, subReportNode);
67+
// measurements
68+
updateMeasurements(load, loadModificationInfos, subReportNode);
6769
// properties
68-
PropertiesUtils.applyProperties(load, subReportNode, modificationInfos.getProperties(), "LoadProperties");
70+
PropertiesUtils.applyProperties(load, subReportNode, loadModificationInfos.getProperties(), "LoadProperties");
6971
}
7072

7173
public static void modifyQ0(Load load, AttributeModification<Double> q0, ReportNode subReportNode) {
@@ -76,10 +78,11 @@ public static void modifyP0(Load load, AttributeModification<Double> p0, ReportN
7678
ModificationUtils.getInstance().applyElementaryModifications(load::setP0, load::getP0, p0, subReportNode, "Constant active power");
7779
}
7880

79-
private ReportNode modifyLoadConnectivityAttributes(LoadModificationInfos modificationInfos,
81+
private ReportNode modifyLoadConnectivityAttributes(LoadModificationInfos loadModificationInfos,
8082
Load load, ReportNode subReportNode) {
8183
ConnectablePosition<Load> connectablePosition = load.getExtension(ConnectablePosition.class);
8284
ConnectablePositionAdder<Load> connectablePositionAdder = load.newExtension(ConnectablePositionAdder.class);
83-
return ModificationUtils.getInstance().modifyInjectionConnectivityAttributes(connectablePosition, connectablePositionAdder, load, modificationInfos, subReportNode);
85+
return ModificationUtils.getInstance().modifyInjectionConnectivityAttributes(connectablePosition, connectablePositionAdder, load, loadModificationInfos, subReportNode);
8486
}
87+
8588
}

src/test/java/org/gridsuite/modification/modifications/LoadModificationTest.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,32 @@
1111
import com.powsybl.iidm.network.LoadType;
1212
import com.powsybl.iidm.network.Network;
1313
import com.powsybl.iidm.network.ValidationException;
14+
import com.powsybl.iidm.network.extensions.Measurement;
15+
import com.powsybl.iidm.network.extensions.Measurements;
1416

17+
import org.apache.commons.collections4.CollectionUtils;
1518
import org.gridsuite.modification.dto.*;
1619
import org.gridsuite.modification.utils.NetworkCreation;
1720
import org.junit.jupiter.api.Test;
21+
22+
import java.util.Collection;
1823
import java.util.List;
1924
import java.util.Map;
2025
import java.util.UUID;
2126

2227
import static org.junit.jupiter.api.Assertions.*;
28+
import static org.assertj.core.api.Assertions.assertThat;
2329

2430
/**
2531
* @author Ayoub LABIDI <ayoub.labidi at rte-france.com>
2632
*/
2733
class LoadModificationTest extends AbstractInjectionModificationTest {
28-
private static String PROPERTY_NAME = "property-name";
29-
private static String PROPERTY_VALUE = "property-value";
34+
private static final String PROPERTY_NAME = "property-name";
35+
private static final String PROPERTY_VALUE = "property-value";
36+
private static final Double MEASUREMENT_P_VALUE = 10.0;
37+
private static final Double MEASUREMENT_Q_VALUE = -10.0;
38+
private static final Boolean MEASUREMENT_P_VALID = true;
39+
private static final Boolean MEASUREMENT_Q_VALID = false;
3040

3141
@Override
3242
protected Network createNetwork(UUID networkUuid) {
@@ -44,6 +54,10 @@ protected ModificationInfos buildModification() {
4454
.busOrBusbarSectionId(new AttributeModification<>("1B", OperationType.SET))
4555
.p0(new AttributeModification<>(200.0, OperationType.SET))
4656
.q0(new AttributeModification<>(30.0, OperationType.SET))
57+
.pMeasurementValue(new AttributeModification<>(MEASUREMENT_P_VALUE, OperationType.SET))
58+
.pMeasurementValidity(new AttributeModification<>(MEASUREMENT_P_VALID, OperationType.SET))
59+
.qMeasurementValue(new AttributeModification<>(MEASUREMENT_Q_VALUE, OperationType.SET))
60+
.qMeasurementValidity(new AttributeModification<>(MEASUREMENT_Q_VALID, OperationType.SET))
4761
.properties(List.of(FreePropertyInfos.builder().name(PROPERTY_NAME).value(PROPERTY_VALUE).build()))
4862
.build();
4963
}
@@ -57,6 +71,18 @@ protected void assertAfterNetworkModificationApplication() {
5771
assertEquals(30.0, modifiedLoad.getQ0(), 0.0);
5872
assertEquals("nameLoad1", modifiedLoad.getNameOrId());
5973
assertEquals(PROPERTY_VALUE, modifiedLoad.getProperty(PROPERTY_NAME));
74+
assertMeasurements(modifiedLoad);
75+
}
76+
77+
private void assertMeasurements(Load load) {
78+
Measurements<?> measurements = (Measurements<?>) load.getExtension(Measurements.class);
79+
assertNotNull(measurements);
80+
Collection<Measurement> activePowerMeasurements = measurements.getMeasurements(Measurement.Type.ACTIVE_POWER).stream().toList();
81+
assertFalse(CollectionUtils.isEmpty(activePowerMeasurements));
82+
assertThat(activePowerMeasurements).allMatch(m -> m.getValue() == MEASUREMENT_P_VALUE && m.isValid() == MEASUREMENT_P_VALID);
83+
Collection<Measurement> reactivePowerMeasurements = measurements.getMeasurements(Measurement.Type.REACTIVE_POWER).stream().toList();
84+
assertFalse(CollectionUtils.isEmpty(reactivePowerMeasurements));
85+
assertThat(reactivePowerMeasurements).allMatch(m -> m.getValue() == MEASUREMENT_Q_VALUE && m.isValid() == MEASUREMENT_Q_VALID);
6086
}
6187

6288
@Override

0 commit comments

Comments
 (0)