From 36278facfd52148b529eb648511f5d300e3385e3 Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Tue, 3 Feb 2026 16:20:00 +0100 Subject: [PATCH 01/10] enable override of 'validity' property for injections and branches (used in estim/adn-in) Signed-off-by: benrejebmoh --- .../AbstractBranchModification.java | 33 ++++++++--- .../AbstractInjectionModification.java | 33 ++++++++--- .../modifications/LineModificationTest.java | 46 +++++++++++++++ .../modifications/LoadModificationTest.java | 54 +++++++++++++++++ ...woWindingsTransformerModificationTest.java | 59 +++++++++++++++++-- 5 files changed, 205 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/modifications/AbstractBranchModification.java b/src/main/java/org/gridsuite/modification/modifications/AbstractBranchModification.java index 6fb1b5c2..b2eb9854 100644 --- a/src/main/java/org/gridsuite/modification/modifications/AbstractBranchModification.java +++ b/src/main/java/org/gridsuite/modification/modifications/AbstractBranchModification.java @@ -211,8 +211,8 @@ public ReportNode updateMeasurements(Branch branch, BranchModificationInfos b return estimSubReportNode; } - private void upsertMeasurement(Measurements measurements, Measurement.Type type, ThreeSides side, Double value, Boolean validity, List reports) { - if (value == null && validity == null) { + private void upsertMeasurement(Measurements measurements, Measurement.Type type, ThreeSides side, Double value, Boolean requestedValidity, List reports) { + if (value == null && requestedValidity == null) { return; } String measurementType = (type == Measurement.Type.ACTIVE_POWER ? "Active power" : "Reactive power") + " measurement "; @@ -223,10 +223,27 @@ private void upsertMeasurement(Measurements measurements, Measurement.Type ty m.setValue(value); reports.add(ModificationUtils.buildModificationReport(oldValue, value, measurementType + VALUE, TypedValue.INFO_SEVERITY)); } - if (validity != null) { + if (requestedValidity != null) { boolean oldValidity = m.isValid(); - m.setValid(validity); - reports.add(ModificationUtils.buildModificationReport(oldValidity, validity, measurementType + VALIDITY, TypedValue.INFO_SEVERITY)); + + if (m.getProperty("validity") == null) { + m.setValid(requestedValidity); + } else { + if (requestedValidity) { + switch (m.getProperty("validity")) { + case "1": m.putProperty("validity", "0"); break; + case "3": m.putProperty("validity", "2"); break; + default: break; + } + } else { + switch (m.getProperty("validity")) { + case "0": m.putProperty("validity", "1"); break; + case "2": m.putProperty("validity", "3"); break; + default: break; + } + } + } + reports.add(ModificationUtils.buildModificationReport(oldValidity, requestedValidity, measurementType + VALIDITY, TypedValue.INFO_SEVERITY)); } } else { // add new measurement var mAdder = measurements.newMeasurement().setId(UUID.randomUUID().toString()).setType(type).setSide(side); @@ -234,9 +251,9 @@ private void upsertMeasurement(Measurements measurements, Measurement.Type ty mAdder.setValue(value); reports.add(ModificationUtils.buildModificationReport(null, value, measurementType + VALUE, TypedValue.INFO_SEVERITY)); } - if (validity != null) { - mAdder.setValid(validity); - reports.add(ModificationUtils.buildModificationReport(null, validity, measurementType + VALIDITY, TypedValue.INFO_SEVERITY)); + if (requestedValidity != null) { + mAdder.setValid(requestedValidity); + reports.add(ModificationUtils.buildModificationReport(null, requestedValidity, measurementType + VALIDITY, TypedValue.INFO_SEVERITY)); } mAdder.add(); } diff --git a/src/main/java/org/gridsuite/modification/modifications/AbstractInjectionModification.java b/src/main/java/org/gridsuite/modification/modifications/AbstractInjectionModification.java index d3a4040f..4948de94 100644 --- a/src/main/java/org/gridsuite/modification/modifications/AbstractInjectionModification.java +++ b/src/main/java/org/gridsuite/modification/modifications/AbstractInjectionModification.java @@ -60,8 +60,8 @@ protected ReportNode updateMeasurements(Injection injection, InjectionModific return estimSubReportNode; } - private void upsertMeasurement(Measurements measurements, Measurement.Type type, Double value, Boolean validity, List reports) { - if (value == null && validity == null) { + private void upsertMeasurement(Measurements measurements, Measurement.Type type, Double value, Boolean requestedValidity, List reports) { + if (value == null && requestedValidity == null) { return; } String measurementType = (type == Measurement.Type.ACTIVE_POWER ? "Active power" : "Reactive power") + " measurement "; @@ -72,10 +72,27 @@ private void upsertMeasurement(Measurements measurements, Measurement.Type ty measurement.setValue(value); reports.add(ModificationUtils.buildModificationReport(oldValue, value, measurementType + VALUE, TypedValue.INFO_SEVERITY)); } - if (validity != null) { + if (requestedValidity != null) { boolean oldValidity = measurement.isValid(); - measurement.setValid(validity); - reports.add(ModificationUtils.buildModificationReport(oldValidity, validity, measurementType + VALIDITY, TypedValue.INFO_SEVERITY)); + + if (measurement.getProperty("validity") == null) { + measurement.setValid(requestedValidity); + } else { + if (requestedValidity) { + switch (measurement.getProperty("validity")) { + case "1": measurement.putProperty("validity", "0"); break; + case "3": measurement.putProperty("validity", "2"); break; + default: break; + } + } else { + switch (measurement.getProperty("validity")) { + case "0": measurement.putProperty("validity", "1"); break; + case "2": measurement.putProperty("validity", "3"); break; + default: break; + } + } + } + reports.add(ModificationUtils.buildModificationReport(oldValidity, requestedValidity, measurementType + VALIDITY, TypedValue.INFO_SEVERITY)); } } else { var measurementAdder = measurements.newMeasurement().setId(UUID.randomUUID().toString()).setType(type); @@ -83,9 +100,9 @@ private void upsertMeasurement(Measurements measurements, Measurement.Type ty measurementAdder.setValue(value); reports.add(ModificationUtils.buildModificationReport(null, value, measurementType + VALUE, TypedValue.INFO_SEVERITY)); } - if (validity != null) { - measurementAdder.setValid(validity); - reports.add(ModificationUtils.buildModificationReport(null, validity, measurementType + VALIDITY, TypedValue.INFO_SEVERITY)); + if (requestedValidity != null) { + measurementAdder.setValid(requestedValidity); + reports.add(ModificationUtils.buildModificationReport(null, requestedValidity, measurementType + VALIDITY, TypedValue.INFO_SEVERITY)); } measurementAdder.add(); } diff --git a/src/test/java/org/gridsuite/modification/modifications/LineModificationTest.java b/src/test/java/org/gridsuite/modification/modifications/LineModificationTest.java index 51fd53da..00aff73f 100644 --- a/src/test/java/org/gridsuite/modification/modifications/LineModificationTest.java +++ b/src/test/java/org/gridsuite/modification/modifications/LineModificationTest.java @@ -591,4 +591,50 @@ void testLimitsPropertiesModificationSameOperationalLimitsReplace() { assertEquals(OLG_PROP3_VALUE, repLimitsGroup2.getProperty(OLG_PROP3_NAME)); } + @Test + void testMeasurementValidityPropertyForLine() { + Line line = getNetwork().getLine("line1"); + Measurements measurements = (Measurements) line.getExtension(Measurements.class); + assertNotNull(measurements); + Measurement activePowerMeasurement = measurements.getMeasurements(Measurement.Type.ACTIVE_POWER).stream() + .filter(m -> m.getSide() == ThreeSides.ONE) + .findFirst() + .orElseThrow(); + + activePowerMeasurement.putProperty("validity", "1"); + LineModificationInfos modificationInfosTrueFromOne = LineModificationInfos.builder() + .equipmentId("line1") + .p1MeasurementValidity(new AttributeModification<>(true, OperationType.SET)) + .build(); + modificationInfosTrueFromOne.toModification().apply(getNetwork()); + assertEquals("0", activePowerMeasurement.getProperty("validity")); + + activePowerMeasurement.putProperty("validity", "3"); + LineModificationInfos modificationInfosTrueFromThree = LineModificationInfos.builder() + .equipmentId("line1") + .p1MeasurementValidity(new AttributeModification<>(true, OperationType.SET)) + .build(); + modificationInfosTrueFromThree.toModification().apply(getNetwork()); + assertEquals("2", activePowerMeasurement.getProperty("validity")); + + Measurement reactivePowerMeasurement = measurements.getMeasurements(Measurement.Type.REACTIVE_POWER).stream() + .filter(m -> m.getSide() == ThreeSides.ONE) + .findFirst() + .orElseThrow(); + reactivePowerMeasurement.putProperty("validity", "2"); + LineModificationInfos modificationInfosFalseFromTwo = LineModificationInfos.builder() + .equipmentId("line1") + .q1MeasurementValidity(new AttributeModification<>(false, OperationType.SET)) + .build(); + modificationInfosFalseFromTwo.toModification().apply(getNetwork()); + assertEquals("3", reactivePowerMeasurement.getProperty("validity")); + + reactivePowerMeasurement.putProperty("validity", "0"); + LineModificationInfos modificationInfosFalseFromZero = LineModificationInfos.builder() + .equipmentId("line1") + .q1MeasurementValidity(new AttributeModification<>(false, OperationType.SET)) + .build(); + modificationInfosFalseFromZero.toModification().apply(getNetwork()); + assertEquals("1", reactivePowerMeasurement.getProperty("validity")); + } } diff --git a/src/test/java/org/gridsuite/modification/modifications/LoadModificationTest.java b/src/test/java/org/gridsuite/modification/modifications/LoadModificationTest.java index a52f0a62..888cb234 100644 --- a/src/test/java/org/gridsuite/modification/modifications/LoadModificationTest.java +++ b/src/test/java/org/gridsuite/modification/modifications/LoadModificationTest.java @@ -13,6 +13,7 @@ import com.powsybl.iidm.network.ValidationException; import com.powsybl.iidm.network.extensions.Measurement; import com.powsybl.iidm.network.extensions.Measurements; +import com.powsybl.iidm.network.extensions.MeasurementsAdder; import org.apache.commons.collections4.CollectionUtils; import org.gridsuite.modification.dto.*; @@ -113,4 +114,57 @@ void testDisconnection() throws Exception { void testConnection() throws Exception { assertChangeConnectionState(getNetwork().getLoad("v1load"), true); } + + @Test + void testMeasurementUpsertCombinationsForLoad() { + Load load = getNetwork().getLoad("v1load"); + Measurements measurements = (Measurements) load.getExtension(Measurements.class); + if (measurements == null) { + measurements = (Measurements) load.newExtension(MeasurementsAdder.class).add(); + } + + Measurement activePowerMeasurement = measurements.newMeasurement() + .setId(UUID.randomUUID().toString()) + .setType(Measurement.Type.ACTIVE_POWER) + .setValue(5.0) + .setValid(true) + .add(); + activePowerMeasurement.putProperty("validity", "1"); + LoadModificationInfos updateActiveValidityTrue = LoadModificationInfos.builder() + .equipmentId("v1load") + .pMeasurementValidity(new AttributeModification<>(true, OperationType.SET)) + .build(); + updateActiveValidityTrue.toModification().apply(getNetwork()); + assertEquals("0", activePowerMeasurement.getProperty("validity")); + + activePowerMeasurement.putProperty("validity", "3"); + LoadModificationInfos updateActiveValidityTrueFromThree = LoadModificationInfos.builder() + .equipmentId("v1load") + .pMeasurementValidity(new AttributeModification<>(true, OperationType.SET)) + .build(); + updateActiveValidityTrueFromThree.toModification().apply(getNetwork()); + assertEquals("2", activePowerMeasurement.getProperty("validity")); + + Measurement reactivePowerMeasurement = measurements.newMeasurement() + .setId(UUID.randomUUID().toString()) + .setType(Measurement.Type.REACTIVE_POWER) + .setValue(-5.0) + .setValid(false) + .add(); + reactivePowerMeasurement.putProperty("validity", "0"); + LoadModificationInfos updateReactiveValidityFalse = LoadModificationInfos.builder() + .equipmentId("v1load") + .qMeasurementValidity(new AttributeModification<>(false, OperationType.SET)) + .build(); + updateReactiveValidityFalse.toModification().apply(getNetwork()); + assertEquals("1", reactivePowerMeasurement.getProperty("validity")); + + reactivePowerMeasurement.putProperty("validity", "2"); + LoadModificationInfos updateReactiveValidityFalseFromTwo = LoadModificationInfos.builder() + .equipmentId("v1load") + .qMeasurementValidity(new AttributeModification<>(false, OperationType.SET)) + .build(); + updateReactiveValidityFalseFromTwo.toModification().apply(getNetwork()); + assertEquals("3", reactivePowerMeasurement.getProperty("validity")); + } } diff --git a/src/test/java/org/gridsuite/modification/modifications/TwoWindingsTransformerModificationTest.java b/src/test/java/org/gridsuite/modification/modifications/TwoWindingsTransformerModificationTest.java index a9496128..ca9618d5 100644 --- a/src/test/java/org/gridsuite/modification/modifications/TwoWindingsTransformerModificationTest.java +++ b/src/test/java/org/gridsuite/modification/modifications/TwoWindingsTransformerModificationTest.java @@ -9,10 +9,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.powsybl.commons.report.ReportNode; import com.powsybl.iidm.network.*; -import com.powsybl.iidm.network.extensions.ConnectablePosition; -import com.powsybl.iidm.network.extensions.Measurement; -import com.powsybl.iidm.network.extensions.Measurements; -import com.powsybl.iidm.network.extensions.TwoWindingsTransformerToBeEstimated; +import com.powsybl.iidm.network.extensions.*; import org.apache.commons.collections4.CollectionUtils; import org.gridsuite.modification.NetworkModificationException; import org.gridsuite.modification.dto.*; @@ -1005,5 +1002,59 @@ void testProcessPhaseTapChangerRegulationModification() { assertEquals("MODIFY_TWO_WINDINGS_TRANSFORMER_ERROR : Regulation value must be positive when modifying, phase tap changer can not regulate", message2); } + @Test + void testMeasurementValidityPropertyForTransformer() { + TwoWindingsTransformer transformer = getNetwork().getTwoWindingsTransformer("trf1"); + Measurements measurements = (Measurements) transformer.getExtension(Measurements.class); + if (measurements == null) { + measurements = (Measurements) transformer.newExtension(MeasurementsAdder.class).add(); + } + Measurement activePowerMeasurement = measurements.newMeasurement() + .setId(UUID.randomUUID().toString()) + .setType(Measurement.Type.ACTIVE_POWER) + .setSide(ThreeSides.ONE) + .setValue(11.0) + .setValid(true) + .add(); + + activePowerMeasurement.putProperty("validity", "1"); + TwoWindingsTransformerModificationInfos modificationInfosTrueFromOne = TwoWindingsTransformerModificationInfos.builder() + .equipmentId("trf1") + .p1MeasurementValidity(new AttributeModification<>(true, OperationType.SET)) + .build(); + modificationInfosTrueFromOne.toModification().apply(getNetwork()); + assertEquals("0", activePowerMeasurement.getProperty("validity")); + + activePowerMeasurement.putProperty("validity", "3"); + TwoWindingsTransformerModificationInfos modificationInfosTrueFromThree = TwoWindingsTransformerModificationInfos.builder() + .equipmentId("trf1") + .p1MeasurementValidity(new AttributeModification<>(true, OperationType.SET)) + .build(); + modificationInfosTrueFromThree.toModification().apply(getNetwork()); + assertEquals("2", activePowerMeasurement.getProperty("validity")); + + Measurement reactivePowerMeasurement = measurements.newMeasurement() + .setId(UUID.randomUUID().toString()) + .setType(Measurement.Type.REACTIVE_POWER) + .setSide(ThreeSides.ONE) + .setValue(-11.0) + .setValid(false) + .add(); + reactivePowerMeasurement.putProperty("validity", "2"); + TwoWindingsTransformerModificationInfos modificationInfosFalseFromTwo = TwoWindingsTransformerModificationInfos.builder() + .equipmentId("trf1") + .q1MeasurementValidity(new AttributeModification<>(false, OperationType.SET)) + .build(); + modificationInfosFalseFromTwo.toModification().apply(getNetwork()); + assertEquals("3", reactivePowerMeasurement.getProperty("validity")); + + reactivePowerMeasurement.putProperty("validity", "0"); + TwoWindingsTransformerModificationInfos modificationInfosFalseFromZero = TwoWindingsTransformerModificationInfos.builder() + .equipmentId("trf1") + .q1MeasurementValidity(new AttributeModification<>(false, OperationType.SET)) + .build(); + modificationInfosFalseFromZero.toModification().apply(getNetwork()); + assertEquals("1", reactivePowerMeasurement.getProperty("validity")); + } } From a0401552b5b20ca27446a9c469edcbb45a56026c Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Tue, 3 Feb 2026 17:22:26 +0100 Subject: [PATCH 02/10] put in ModificationUtils the behaviour of 'validity' property overriding Signed-off-by: benrejebmoh --- .../AbstractBranchModification.java | 18 +-------------- .../AbstractInjectionModification.java | 18 +-------------- .../modification/utils/ModificationUtils.java | 23 ++++++++++++++++++- 3 files changed, 24 insertions(+), 35 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/modifications/AbstractBranchModification.java b/src/main/java/org/gridsuite/modification/modifications/AbstractBranchModification.java index b2eb9854..01d1b1f0 100644 --- a/src/main/java/org/gridsuite/modification/modifications/AbstractBranchModification.java +++ b/src/main/java/org/gridsuite/modification/modifications/AbstractBranchModification.java @@ -226,23 +226,7 @@ private void upsertMeasurement(Measurements measurements, Measurement.Type ty if (requestedValidity != null) { boolean oldValidity = m.isValid(); - if (m.getProperty("validity") == null) { - m.setValid(requestedValidity); - } else { - if (requestedValidity) { - switch (m.getProperty("validity")) { - case "1": m.putProperty("validity", "0"); break; - case "3": m.putProperty("validity", "2"); break; - default: break; - } - } else { - switch (m.getProperty("validity")) { - case "0": m.putProperty("validity", "1"); break; - case "2": m.putProperty("validity", "3"); break; - default: break; - } - } - } + ModificationUtils.updateMeasurementValidity(m, requestedValidity); reports.add(ModificationUtils.buildModificationReport(oldValidity, requestedValidity, measurementType + VALIDITY, TypedValue.INFO_SEVERITY)); } } else { // add new measurement diff --git a/src/main/java/org/gridsuite/modification/modifications/AbstractInjectionModification.java b/src/main/java/org/gridsuite/modification/modifications/AbstractInjectionModification.java index 4948de94..d62f1db6 100644 --- a/src/main/java/org/gridsuite/modification/modifications/AbstractInjectionModification.java +++ b/src/main/java/org/gridsuite/modification/modifications/AbstractInjectionModification.java @@ -75,23 +75,7 @@ private void upsertMeasurement(Measurements measurements, Measurement.Type ty if (requestedValidity != null) { boolean oldValidity = measurement.isValid(); - if (measurement.getProperty("validity") == null) { - measurement.setValid(requestedValidity); - } else { - if (requestedValidity) { - switch (measurement.getProperty("validity")) { - case "1": measurement.putProperty("validity", "0"); break; - case "3": measurement.putProperty("validity", "2"); break; - default: break; - } - } else { - switch (measurement.getProperty("validity")) { - case "0": measurement.putProperty("validity", "1"); break; - case "2": measurement.putProperty("validity", "3"); break; - default: break; - } - } - } + ModificationUtils.updateMeasurementValidity(measurement, requestedValidity); reports.add(ModificationUtils.buildModificationReport(oldValidity, requestedValidity, measurementType + VALIDITY, TypedValue.INFO_SEVERITY)); } } else { diff --git a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java index 4c39ff2c..f6d432fb 100644 --- a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java +++ b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java @@ -71,6 +71,7 @@ public final class ModificationUtils { private static final String COULD_NOT_ACTION_EQUIPMENT_ON_SIDE = COULD_NOT_ACTION_EQUIPMENT + " on side %s"; public static final String CONNECT = "connect"; public static final String DISCONNECT = "disconnect"; + private static final String MEASUREMENT_VALIDITY_PROPERTY = "validity"; public static String applicabilityToString(OperationalLimitsGroupInfos.Applicability applicability) { return switch (applicability) { @@ -2084,5 +2085,25 @@ public void createNewActivePowerControlForInjectionCreation(ActivePowerControlAd } } -} + public static void updateMeasurementValidity(Measurement measurement, boolean requestedValidity) { + if (measurement.getProperty(MEASUREMENT_VALIDITY_PROPERTY) == null) { + measurement.setValid(requestedValidity); + } else { + if (requestedValidity) { + switch (measurement.getProperty(MEASUREMENT_VALIDITY_PROPERTY)) { + case "1": measurement.putProperty(MEASUREMENT_VALIDITY_PROPERTY, "0"); break; + case "3": measurement.putProperty(MEASUREMENT_VALIDITY_PROPERTY, "2"); break; + default: break; + } + } else { + switch (measurement.getProperty(MEASUREMENT_VALIDITY_PROPERTY)) { + case "0": measurement.putProperty(MEASUREMENT_VALIDITY_PROPERTY, "1"); break; + case "2": measurement.putProperty(MEASUREMENT_VALIDITY_PROPERTY, "3"); break; + default: break; + } + } + } + } + +} From e9d536986ee18ecb5880d09cc8dd51805ed46884 Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Tue, 3 Feb 2026 17:29:08 +0100 Subject: [PATCH 03/10] add comments to explain 'validity' property values logic Signed-off-by: benrejebmoh --- .../org/gridsuite/modification/utils/ModificationUtils.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java index f6d432fb..4a1248bf 100644 --- a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java +++ b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java @@ -2092,12 +2092,16 @@ public static void updateMeasurementValidity(Measurement measurement, boolean re } else { if (requestedValidity) { switch (measurement.getProperty(MEASUREMENT_VALIDITY_PROPERTY)) { + //validity = 1 → TM non valid & not masked + //validity = 3 → TM non valid & masked case "1": measurement.putProperty(MEASUREMENT_VALIDITY_PROPERTY, "0"); break; case "3": measurement.putProperty(MEASUREMENT_VALIDITY_PROPERTY, "2"); break; default: break; } } else { switch (measurement.getProperty(MEASUREMENT_VALIDITY_PROPERTY)) { + //validity = 0 → TM valid & not masked + //validity = 2 → TM valid & masked case "0": measurement.putProperty(MEASUREMENT_VALIDITY_PROPERTY, "1"); break; case "2": measurement.putProperty(MEASUREMENT_VALIDITY_PROPERTY, "3"); break; default: break; From 2512afad55b014621d61593b7771dc285841129c Mon Sep 17 00:00:00 2001 From: David BRAQUART Date: Thu, 5 Feb 2026 13:03:12 +0100 Subject: [PATCH 04/10] rev remark: use ParameterizedTest to increase coverage Signed-off-by: David BRAQUART --- .../modifications/LoadModificationTest.java | 126 ++++++++++++------ 1 file changed, 88 insertions(+), 38 deletions(-) diff --git a/src/test/java/org/gridsuite/modification/modifications/LoadModificationTest.java b/src/test/java/org/gridsuite/modification/modifications/LoadModificationTest.java index 888cb234..e57d2ccb 100644 --- a/src/test/java/org/gridsuite/modification/modifications/LoadModificationTest.java +++ b/src/test/java/org/gridsuite/modification/modifications/LoadModificationTest.java @@ -19,6 +19,10 @@ import org.gridsuite.modification.dto.*; import org.gridsuite.modification.utils.NetworkCreation; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import java.util.Collection; import java.util.List; @@ -115,56 +119,102 @@ void testConnection() throws Exception { assertChangeConnectionState(getNetwork().getLoad("v1load"), true); } - @Test - void testMeasurementUpsertCombinationsForLoad() { + @MethodSource("validityExpectedValuesAfterModification") + @ParameterizedTest(name = "[{index}] Apply validity {0} on actual property {1} should give {2}") + void testUpsertMeasurementPropertyCombinations(final boolean modificationValidity, final String actualPropertyState, final String expectedPropertyState) { + Load load = getNetwork().getLoad("v1load"); + Measurements measurements = (Measurements) load.getExtension(Measurements.class); + if (measurements == null) { + measurements = (Measurements) load.newExtension(MeasurementsAdder.class).add(); + } + + // active power test + Measurement activePowerMeasurement = measurements.newMeasurement() + .setId(UUID.randomUUID().toString()) + .setType(Measurement.Type.ACTIVE_POWER) + .setValue(5.0) + .setValid(true) + .add(); + activePowerMeasurement.putProperty("validity", actualPropertyState); + LoadModificationInfos activePowerModification = LoadModificationInfos.builder() + .equipmentId("v1load") + .pMeasurementValidity(new AttributeModification<>(modificationValidity, OperationType.SET)) + .build(); + activePowerModification.toModification().apply(getNetwork()); + assertEquals(expectedPropertyState, activePowerMeasurement.getProperty("validity")); + assertEquals(modificationValidity, activePowerMeasurement.isValid()); + + // reactive power test + Measurement reactivePowerMeasurement = measurements.newMeasurement() + .setId(UUID.randomUUID().toString()) + .setType(Measurement.Type.REACTIVE_POWER) + .setValue(-5.0) + .setValid(false) + .add(); + reactivePowerMeasurement.putProperty("validity", actualPropertyState); + LoadModificationInfos reactivePowerModification = LoadModificationInfos.builder() + .equipmentId("v1load") + .qMeasurementValidity(new AttributeModification<>(modificationValidity, OperationType.SET)) + .build(); + reactivePowerModification.toModification().apply(getNetwork()); + assertEquals(expectedPropertyState, reactivePowerMeasurement.getProperty("validity")); + assertEquals(modificationValidity, activePowerMeasurement.isValid()); + } + + public static List validityExpectedValuesAfterModification() { + return List.of( + Arguments.of(true, "0", "0"), + Arguments.of(true, "1", "0"), + Arguments.of(true, "2", "2"), + Arguments.of(true, "3", "2"), + Arguments.of(false, "0", "1"), + Arguments.of(false, "1", "1"), + Arguments.of(false, "2", "3"), + Arguments.of(false, "3", "3") + ); + } + + @ValueSource(strings = {"0", "1", "2", "3"}) + @ParameterizedTest + void testUpsertMeasurementValueChangedPropertyNotChanged(final String propertyState) { Load load = getNetwork().getLoad("v1load"); Measurements measurements = (Measurements) load.getExtension(Measurements.class); if (measurements == null) { measurements = (Measurements) load.newExtension(MeasurementsAdder.class).add(); } + // Changing only the TM value will not change the validity + final double newValue = 10.; + // active power test Measurement activePowerMeasurement = measurements.newMeasurement() - .setId(UUID.randomUUID().toString()) - .setType(Measurement.Type.ACTIVE_POWER) - .setValue(5.0) - .setValid(true) - .add(); - activePowerMeasurement.putProperty("validity", "1"); + .setId(UUID.randomUUID().toString()) + .setType(Measurement.Type.ACTIVE_POWER) + .setValue(5.0) + .setValid(true) + .add(); + activePowerMeasurement.putProperty("validity", propertyState); LoadModificationInfos updateActiveValidityTrue = LoadModificationInfos.builder() - .equipmentId("v1load") - .pMeasurementValidity(new AttributeModification<>(true, OperationType.SET)) - .build(); + .equipmentId("v1load") + .pMeasurementValue(new AttributeModification<>(newValue, OperationType.SET)) + .build(); updateActiveValidityTrue.toModification().apply(getNetwork()); - assertEquals("0", activePowerMeasurement.getProperty("validity")); - - activePowerMeasurement.putProperty("validity", "3"); - LoadModificationInfos updateActiveValidityTrueFromThree = LoadModificationInfos.builder() - .equipmentId("v1load") - .pMeasurementValidity(new AttributeModification<>(true, OperationType.SET)) - .build(); - updateActiveValidityTrueFromThree.toModification().apply(getNetwork()); - assertEquals("2", activePowerMeasurement.getProperty("validity")); + assertEquals(propertyState, activePowerMeasurement.getProperty("validity")); + assertEquals(newValue, activePowerMeasurement.getValue()); + // reactive power test Measurement reactivePowerMeasurement = measurements.newMeasurement() - .setId(UUID.randomUUID().toString()) - .setType(Measurement.Type.REACTIVE_POWER) - .setValue(-5.0) - .setValid(false) - .add(); - reactivePowerMeasurement.putProperty("validity", "0"); + .setId(UUID.randomUUID().toString()) + .setType(Measurement.Type.REACTIVE_POWER) + .setValue(-5.0) + .setValid(false) + .add(); + reactivePowerMeasurement.putProperty("validity", propertyState); LoadModificationInfos updateReactiveValidityFalse = LoadModificationInfos.builder() - .equipmentId("v1load") - .qMeasurementValidity(new AttributeModification<>(false, OperationType.SET)) - .build(); + .equipmentId("v1load") + .qMeasurementValue(new AttributeModification<>(newValue, OperationType.SET)) + .build(); updateReactiveValidityFalse.toModification().apply(getNetwork()); - assertEquals("1", reactivePowerMeasurement.getProperty("validity")); - - reactivePowerMeasurement.putProperty("validity", "2"); - LoadModificationInfos updateReactiveValidityFalseFromTwo = LoadModificationInfos.builder() - .equipmentId("v1load") - .qMeasurementValidity(new AttributeModification<>(false, OperationType.SET)) - .build(); - updateReactiveValidityFalseFromTwo.toModification().apply(getNetwork()); - assertEquals("3", reactivePowerMeasurement.getProperty("validity")); + assertEquals(propertyState, reactivePowerMeasurement.getProperty("validity")); + assertEquals(newValue, activePowerMeasurement.getValue()); } } From 36c77101c3b1a24d6f7c64896973c7fc666eb9ce Mon Sep 17 00:00:00 2001 From: David BRAQUART Date: Thu, 5 Feb 2026 13:03:36 +0100 Subject: [PATCH 05/10] rev remark: always set the valid boolean Signed-off-by: David BRAQUART --- .../org/gridsuite/modification/utils/ModificationUtils.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java index 4a1248bf..442b89fc 100644 --- a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java +++ b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java @@ -2087,9 +2087,8 @@ public void createNewActivePowerControlForInjectionCreation(ActivePowerControlAd } public static void updateMeasurementValidity(Measurement measurement, boolean requestedValidity) { - if (measurement.getProperty(MEASUREMENT_VALIDITY_PROPERTY) == null) { - measurement.setValid(requestedValidity); - } else { + measurement.setValid(requestedValidity); + if (measurement.getProperty(MEASUREMENT_VALIDITY_PROPERTY) != null) { if (requestedValidity) { switch (measurement.getProperty(MEASUREMENT_VALIDITY_PROPERTY)) { //validity = 1 → TM non valid & not masked @@ -2109,5 +2108,4 @@ public static void updateMeasurementValidity(Measurement measurement, boolean re } } } - } From 9c3b97b5e280fbc4111bc60dcb6a159c59265e05 Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Wed, 11 Feb 2026 11:18:45 +0100 Subject: [PATCH 06/10] enhance coverage Signed-off-by: benrejebmoh --- .../modifications/LineModificationTest.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/test/java/org/gridsuite/modification/modifications/LineModificationTest.java b/src/test/java/org/gridsuite/modification/modifications/LineModificationTest.java index 00aff73f..de0c053a 100644 --- a/src/test/java/org/gridsuite/modification/modifications/LineModificationTest.java +++ b/src/test/java/org/gridsuite/modification/modifications/LineModificationTest.java @@ -636,5 +636,51 @@ void testMeasurementValidityPropertyForLine() { .build(); modificationInfosFalseFromZero.toModification().apply(getNetwork()); assertEquals("1", reactivePowerMeasurement.getProperty("validity")); + + // update validity on existing measurement without legacy property + reactivePowerMeasurement.removeProperty("validity"); + LineModificationInfos modificationInfosTrueWithoutProperty = LineModificationInfos.builder() + .equipmentId("line1") + .q1MeasurementValidity(new AttributeModification<>(true, OperationType.SET)) + .build(); + modificationInfosTrueWithoutProperty.toModification().apply(getNetwork()); + assertTrue(reactivePowerMeasurement.isValid()); + assertNull(reactivePowerMeasurement.getProperty("validity")); + + // add side 2 measurement by value only + assertTrue(measurements.getMeasurements(Measurement.Type.ACTIVE_POWER).stream().noneMatch(m -> m.getSide() == ThreeSides.TWO)); + LineModificationInfos addSide2ActiveByValue = LineModificationInfos.builder() + .equipmentId("line1") + .p2MeasurementValue(new AttributeModification<>(42.0, OperationType.SET)) + .build(); + addSide2ActiveByValue.toModification().apply(getNetwork()); + Measurement addedActiveSide2 = measurements.getMeasurements(Measurement.Type.ACTIVE_POWER).stream() + .filter(m -> m.getSide() == ThreeSides.TWO) + .findFirst() + .orElseThrow(); + assertEquals(42.0, addedActiveSide2.getValue()); + + // add side 2 reactive measurement by validity only then update it + assertTrue(measurements.getMeasurements(Measurement.Type.REACTIVE_POWER).stream().noneMatch(m -> m.getSide() == ThreeSides.TWO)); + LineModificationInfos addSide2ReactiveByValidity = LineModificationInfos.builder() + .equipmentId("line1") + .q2MeasurementValue(new AttributeModification<>(MEASUREMENT_Q_VALUE, OperationType.SET)) + .q2MeasurementValidity(new AttributeModification<>(false, OperationType.SET)) + .build(); + addSide2ReactiveByValidity.toModification().apply(getNetwork()); + + Measurement addedReactiveSide2 = measurements.getMeasurements(Measurement.Type.REACTIVE_POWER).stream() + .filter(m -> m.getSide() == ThreeSides.TWO) + .findFirst() + .orElseThrow(); + assertFalse(addedReactiveSide2.isValid()); + + LineModificationInfos updateSide2ReactiveValidity = LineModificationInfos.builder() + .equipmentId("line1") + .q2MeasurementValue(new AttributeModification<>(MEASUREMENT_Q_VALUE, OperationType.SET)) + .q2MeasurementValidity(new AttributeModification<>(true, OperationType.SET)) + .build(); + updateSide2ReactiveValidity.toModification().apply(getNetwork()); + assertTrue(addedReactiveSide2.isValid()); } } From de74f5c45c4338d309e5b464e95ca505d214ee07 Mon Sep 17 00:00:00 2001 From: benrejebmoh Date: Wed, 11 Feb 2026 11:41:29 +0100 Subject: [PATCH 07/10] check that upsertMeasurements creates Extension when missing Signed-off-by: benrejebmoh --- .../modifications/LineModificationTest.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/test/java/org/gridsuite/modification/modifications/LineModificationTest.java b/src/test/java/org/gridsuite/modification/modifications/LineModificationTest.java index de0c053a..4510e5f2 100644 --- a/src/test/java/org/gridsuite/modification/modifications/LineModificationTest.java +++ b/src/test/java/org/gridsuite/modification/modifications/LineModificationTest.java @@ -683,4 +683,36 @@ void testMeasurementValidityPropertyForLine() { updateSide2ReactiveValidity.toModification().apply(getNetwork()); assertTrue(addedReactiveSide2.isValid()); } + + @Test + void testUpdateMeasurementsCreatesExtensionWhenMissing() { + Line line = getNetwork().getLine("line2"); + assertNull(line.getExtension(Measurements.class)); + + LineModificationInfos lineModificationInfos = LineModificationInfos.builder() + .equipmentId("line2") + .p1MeasurementValidity(new AttributeModification<>(true, OperationType.SET)) + .p1MeasurementValue(new AttributeModification<>(MEASUREMENT_P_VALUE, OperationType.SET)) + .q1MeasurementValidity(new AttributeModification<>(false, OperationType.SET)) + .q1MeasurementValue(new AttributeModification<>(MEASUREMENT_Q_VALUE, OperationType.SET)) + .build(); + lineModificationInfos.toModification().apply(getNetwork()); + + Measurements measurements = (Measurements) line.getExtension(Measurements.class); + assertNotNull(measurements); + + Measurement p1Measurement = measurements.getMeasurements(Measurement.Type.ACTIVE_POWER).stream() + .filter(m -> m.getSide() == ThreeSides.ONE) + .findFirst() + .orElseThrow(); + assertEquals(10.0, p1Measurement.getValue()); + assertTrue(p1Measurement.isValid()); + + Measurement q1Measurement = measurements.getMeasurements(Measurement.Type.REACTIVE_POWER).stream() + .filter(m -> m.getSide() == ThreeSides.ONE) + .findFirst() + .orElseThrow(); + assertEquals(-10.0, q1Measurement.getValue()); + assertFalse(q1Measurement.isValid()); + } } From 1274e59f780fe5864ddbe66586d81571f7cbdb93 Mon Sep 17 00:00:00 2001 From: David BRAQUART Date: Wed, 11 Feb 2026 14:15:49 +0100 Subject: [PATCH 08/10] rev remark: add coverage for branch cases Signed-off-by: David BRAQUART --- .../modifications/LineModificationTest.java | 75 ++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/gridsuite/modification/modifications/LineModificationTest.java b/src/test/java/org/gridsuite/modification/modifications/LineModificationTest.java index 4510e5f2..cf69d9e2 100644 --- a/src/test/java/org/gridsuite/modification/modifications/LineModificationTest.java +++ b/src/test/java/org/gridsuite/modification/modifications/LineModificationTest.java @@ -685,7 +685,7 @@ void testMeasurementValidityPropertyForLine() { } @Test - void testUpdateMeasurementsCreatesExtensionWhenMissing() { + void testCreateMeasurementsExtensionWhenMissing() { Line line = getNetwork().getLine("line2"); assertNull(line.getExtension(Measurements.class)); @@ -715,4 +715,77 @@ void testUpdateMeasurementsCreatesExtensionWhenMissing() { assertEquals(-10.0, q1Measurement.getValue()); assertFalse(q1Measurement.isValid()); } + + @Test + void testCreateMeasurementsExtensionWhenMissingOnlyWithValue() { + Line line = getNetwork().getLine("line2"); + assertNull(line.getExtension(Measurements.class)); + + LineModificationInfos lineModificationInfos = LineModificationInfos.builder() + .equipmentId("line2") + .p1MeasurementValue(new AttributeModification<>(MEASUREMENT_P_VALUE, OperationType.SET)) + .q1MeasurementValue(new AttributeModification<>(MEASUREMENT_Q_VALUE, OperationType.SET)) + .build(); + lineModificationInfos.toModification().apply(getNetwork()); + + Measurements measurements = (Measurements) line.getExtension(Measurements.class); + assertNotNull(measurements); + + Measurement p1Measurement = measurements.getMeasurements(Measurement.Type.ACTIVE_POWER).stream() + .filter(m -> m.getSide() == ThreeSides.ONE) + .findFirst() + .orElseThrow(); + assertEquals(10.0, p1Measurement.getValue()); + assertTrue(p1Measurement.isValid()); // a Measurement is valid by default + + Measurement q1Measurement = measurements.getMeasurements(Measurement.Type.REACTIVE_POWER).stream() + .filter(m -> m.getSide() == ThreeSides.ONE) + .findFirst() + .orElseThrow(); + assertEquals(-10.0, q1Measurement.getValue()); + assertTrue(q1Measurement.isValid()); + } + + @Test + void testCreateMeasurementsExtensionWhenMissingOnlyWithFalseValidity() { + Line line = getNetwork().getLine("line2"); + assertNull(line.getExtension(Measurements.class)); + + LineModificationInfos lineModificationInfos = LineModificationInfos.builder() + .equipmentId("line2") + .p1MeasurementValidity(new AttributeModification<>(false, OperationType.SET)) + .q1MeasurementValidity(new AttributeModification<>(false, OperationType.SET)) + .build(); + lineModificationInfos.toModification().apply(getNetwork()); + + Measurements measurements = (Measurements) line.getExtension(Measurements.class); + assertNotNull(measurements); + + Measurement p1Measurement = measurements.getMeasurements(Measurement.Type.ACTIVE_POWER).stream() + .filter(m -> m.getSide() == ThreeSides.ONE) + .findFirst() + .orElseThrow(); + assertTrue(Double.isNaN(p1Measurement.getValue())); + assertFalse(p1Measurement.isValid()); + + Measurement q1Measurement = measurements.getMeasurements(Measurement.Type.REACTIVE_POWER).stream() + .filter(m -> m.getSide() == ThreeSides.ONE) + .findFirst() + .orElseThrow(); + assertTrue(Double.isNaN(q1Measurement.getValue())); + assertFalse(q1Measurement.isValid()); + } + + @Test + void testCannotCreateMeasurementsExtensionWhenMissingWithOnlyTrueValidity() { + Line line = getNetwork().getLine("line2"); + assertNull(line.getExtension(Measurements.class)); + + LineModificationInfos lineModificationInfos = LineModificationInfos.builder() + .equipmentId("line2") + .p1MeasurementValidity(new AttributeModification<>(true, OperationType.SET)) // not allowed by powsybl on extension creation + .build(); + PowsyblException e = assertThrows(PowsyblException.class, () -> lineModificationInfos.toModification().apply(getNetwork())); + assertEquals("Valid measurement cannot have an undefined value", e.getMessage()); + } } From cbbac60b28fae4610f1eabb60336acdcb2ec5a5d Mon Sep 17 00:00:00 2001 From: David BRAQUART Date: Wed, 11 Feb 2026 14:33:57 +0100 Subject: [PATCH 09/10] rev remark: add coverage for branch: only value on existing M Signed-off-by: David BRAQUART --- .../modification/modifications/LineModificationTest.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/gridsuite/modification/modifications/LineModificationTest.java b/src/test/java/org/gridsuite/modification/modifications/LineModificationTest.java index cf69d9e2..af5902f9 100644 --- a/src/test/java/org/gridsuite/modification/modifications/LineModificationTest.java +++ b/src/test/java/org/gridsuite/modification/modifications/LineModificationTest.java @@ -601,6 +601,13 @@ void testMeasurementValidityPropertyForLine() { .findFirst() .orElseThrow(); + LineModificationInfos addSide1ActiveByValue = LineModificationInfos.builder() + .equipmentId("line1") + .p1MeasurementValue(new AttributeModification<>(79.0, OperationType.SET)) + .build(); + addSide1ActiveByValue.toModification().apply(getNetwork()); + assertEquals(79.0, activePowerMeasurement.getValue()); + activePowerMeasurement.putProperty("validity", "1"); LineModificationInfos modificationInfosTrueFromOne = LineModificationInfos.builder() .equipmentId("line1") @@ -647,7 +654,7 @@ void testMeasurementValidityPropertyForLine() { assertTrue(reactivePowerMeasurement.isValid()); assertNull(reactivePowerMeasurement.getProperty("validity")); - // add side 2 measurement by value only + // add side 2 (new) measurement by value only assertTrue(measurements.getMeasurements(Measurement.Type.ACTIVE_POWER).stream().noneMatch(m -> m.getSide() == ThreeSides.TWO)); LineModificationInfos addSide2ActiveByValue = LineModificationInfos.builder() .equipmentId("line1") From 7455c5e70602aa05c3b99adcee6c99ec201d9415 Mon Sep 17 00:00:00 2001 From: David BRAQUART Date: Wed, 11 Feb 2026 15:05:38 +0100 Subject: [PATCH 10/10] rev remark: add coverage for load Signed-off-by: David BRAQUART --- .../modifications/LoadModificationTest.java | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/test/java/org/gridsuite/modification/modifications/LoadModificationTest.java b/src/test/java/org/gridsuite/modification/modifications/LoadModificationTest.java index e57d2ccb..4f9c94aa 100644 --- a/src/test/java/org/gridsuite/modification/modifications/LoadModificationTest.java +++ b/src/test/java/org/gridsuite/modification/modifications/LoadModificationTest.java @@ -217,4 +217,70 @@ void testUpsertMeasurementValueChangedPropertyNotChanged(final String propertySt assertEquals(propertyState, reactivePowerMeasurement.getProperty("validity")); assertEquals(newValue, activePowerMeasurement.getValue()); } + + @Test + void testCreateMeasurementsExtensionWhenMissingOnlyWithValue() { + Load load = getNetwork().getLoad("v2load"); + Measurements measurements = (Measurements) load.getExtension(Measurements.class); + assertNull(measurements); + + // active power test + LoadModificationInfos updateActiveValue = LoadModificationInfos.builder() + .equipmentId("v2load") + .pMeasurementValue(new AttributeModification<>(66.5, OperationType.SET)) + .build(); + updateActiveValue.toModification().apply(getNetwork()); + + measurements = (Measurements) load.getExtension(Measurements.class); + assertNotNull(measurements); + Measurement activePowerMeasurement = measurements.getMeasurements(Measurement.Type.ACTIVE_POWER).stream().findFirst().orElse(null); + assertNotNull(activePowerMeasurement); + assertEquals(66.5, activePowerMeasurement.getValue()); + assertTrue(activePowerMeasurement.isValid()); + + // reactive power test + LoadModificationInfos updateReactiveValue = LoadModificationInfos.builder() + .equipmentId("v2load") + .qMeasurementValue(new AttributeModification<>(99.5, OperationType.SET)) + .build(); + updateReactiveValue.toModification().apply(getNetwork()); + + Measurement reactivePowerMeasurement = measurements.getMeasurements(Measurement.Type.REACTIVE_POWER).stream().findFirst().orElse(null); + assertNotNull(reactivePowerMeasurement); + assertEquals(99.5, reactivePowerMeasurement.getValue()); + assertTrue(reactivePowerMeasurement.isValid()); + } + + @Test + void testCreateMeasurementsExtensionWhenMissingOnlyWithFalseValidity() { + Load load = getNetwork().getLoad("v2load"); + Measurements measurements = (Measurements) load.getExtension(Measurements.class); + assertNull(measurements); + + // active power test + LoadModificationInfos updateActiveValidity = LoadModificationInfos.builder() + .equipmentId("v2load") + .pMeasurementValidity(new AttributeModification<>(false, OperationType.SET)) + .build(); + updateActiveValidity.toModification().apply(getNetwork()); + + measurements = (Measurements) load.getExtension(Measurements.class); + assertNotNull(measurements); + Measurement activePowerMeasurement = measurements.getMeasurements(Measurement.Type.ACTIVE_POWER).stream().findFirst().orElse(null); + assertNotNull(activePowerMeasurement); + assertTrue(Double.isNaN(activePowerMeasurement.getValue())); + assertFalse(activePowerMeasurement.isValid()); + + // reactive power test + LoadModificationInfos updateReactiveValidity = LoadModificationInfos.builder() + .equipmentId("v2load") + .qMeasurementValidity(new AttributeModification<>(false, OperationType.SET)) + .build(); + updateReactiveValidity.toModification().apply(getNetwork()); + + Measurement reactivePowerMeasurement = measurements.getMeasurements(Measurement.Type.REACTIVE_POWER).stream().findFirst().orElse(null); + assertNotNull(reactivePowerMeasurement); + assertTrue(Double.isNaN(reactivePowerMeasurement.getValue())); + assertFalse(reactivePowerMeasurement.isValid()); + } }