From 15fa62b46d8ce626ccc07e6b597e2a25f06f2541 Mon Sep 17 00:00:00 2001 From: basseche Date: Wed, 4 Feb 2026 08:57:15 +0100 Subject: [PATCH 01/11] Add check on Power values for : Modification by formula, by table Signed-off-by: basseche --- .../AbstractModificationByAssignment.java | 25 ++++++++ .../byfilter/ByFormulaModification.java | 3 + .../byfilter/ModificationByAssignment.java | 3 + .../modification/utils/ModificationUtils.java | 61 +++++++++++++++++++ .../gridsuite/modification/reports.properties | 2 + 5 files changed, 94 insertions(+) diff --git a/src/main/java/org/gridsuite/modification/modifications/byfilter/AbstractModificationByAssignment.java b/src/main/java/org/gridsuite/modification/modifications/byfilter/AbstractModificationByAssignment.java index f7732a15..71666d0b 100644 --- a/src/main/java/org/gridsuite/modification/modifications/byfilter/AbstractModificationByAssignment.java +++ b/src/main/java/org/gridsuite/modification/modifications/byfilter/AbstractModificationByAssignment.java @@ -9,9 +9,11 @@ import com.powsybl.commons.report.ReportNode; import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.Generator; import com.powsybl.iidm.network.Identifiable; import com.powsybl.iidm.network.IdentifiableType; import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.extensions.GeneratorStartup; import org.apache.commons.lang3.StringUtils; import org.gridsuite.modification.IFilterService; import org.gridsuite.modification.ILoadFlowService; @@ -34,6 +36,7 @@ import static org.gridsuite.modification.dto.byfilter.equipmentfield.FieldUtils.getFieldValue; import static org.gridsuite.modification.dto.byfilter.equipmentfield.FieldUtils.setFieldValue; +import static org.gridsuite.modification.dto.byfilter.equipmentfield.GeneratorField.*; import static org.gridsuite.modification.utils.ModificationUtils.*; /** @@ -42,6 +45,9 @@ public abstract class AbstractModificationByAssignment extends AbstractModification { public static final String VALUE_KEY_FILTER_NAME = "filterName"; public static final String VALUE_KEY_FIELD_NAME = "fieldName"; + public static final String VALUE_KEY_FIELD2_NAME = "field2Name"; + public static final String VALUE_KEY_FIELD_VALUE = "fieldValue"; + public static final String VALUE_KEY_FIELD2_VALUE = "field2Value"; public static final String VALUE_KEY_EQUIPMENT_NAME = "equipmentName"; public static final String VALUE_KEY_EQUIPMENT_TYPE = "equipmentType"; public static final String VALUE_KEY_EQUIPMENT_COUNT = "equipmentCount"; @@ -57,6 +63,8 @@ public abstract class AbstractModificationByAssignment extends AbstractModificat public static final String VALUE_KEY_ARROW_VALUE = "→"; public static final String REPORT_KEY_EQUIPMENT_MODIFIED_ERROR_ZERO = "network.modification.equipmentModifiedError.zero"; public static final String REPORT_KEY_EQUIPMENT_MODIFIED_ERROR_NULL = "network.modification.equipmentModifiedError.null"; + public static final String REPORT_KEY_GENERATOR_FIELD1_GREATER_FIELD2 = "network.modification.generator.generatorField1GreaterField2"; + public static final String REPORT_KEY_GENERATOR_FIELD1_SMALLER_FIELD2 = "network.modification.generator.generatorField1SmallerField2"; public static final String REPORT_KEY_BY_FILTER_MODIFICATION_SOME = "network.modification.byFilterModificationSome"; public static final String REPORT_KEY_BY_FILTER_MODIFICATION_FAILED = "network.modification.byFilterModificationFailed"; public static final String REPORT_KEY_BY_FILTER_MODIFICATION_SUCCESS = "network.modification.byFilterModificationSuccess"; @@ -103,6 +111,23 @@ protected abstract boolean preCheckValue(Identifiable equipment, protected abstract String getNewValue(Identifiable equipment, AbstractAssignmentInfos abstractAssignmentInfos); + protected boolean checkGeneratorsPowerValues(Identifiable equipment, AbstractAssignmentInfos abstractAssignmentInfos, List reports) { + if (equipment.getType() == IdentifiableType.GENERATOR) { + Generator generator = (Generator) equipment; + double newValue = Double.parseDouble(getNewValue(equipment, abstractAssignmentInfos)); + GeneratorStartup generatorStartup = generator.getExtension(GeneratorStartup.class); + + if (abstractAssignmentInfos.getEditedField().equals(PLANNED_ACTIVE_POWER_SET_POINT.name()) && generatorStartup != null) { + return validatePlannedActivePowerSetPoint(generator, reports, newValue); + } else if (abstractAssignmentInfos.getEditedField().equals(MINIMUM_ACTIVE_POWER.name())) { + return validateMinimumActivePower(generator, generatorStartup, reports, newValue); + } else if (abstractAssignmentInfos.getEditedField().equals(MAXIMUM_ACTIVE_POWER.name())) { + return validateMaximumActivePower(generator, generatorStartup, reports, newValue); + } + } + return true; + } + protected String getOldValue(Identifiable equipment, AbstractAssignmentInfos abstractAssignmentInfos) { return getFieldValue(equipment, abstractAssignmentInfos.getEditedField()); } diff --git a/src/main/java/org/gridsuite/modification/modifications/byfilter/ByFormulaModification.java b/src/main/java/org/gridsuite/modification/modifications/byfilter/ByFormulaModification.java index e0f69fd0..d25b38be 100644 --- a/src/main/java/org/gridsuite/modification/modifications/byfilter/ByFormulaModification.java +++ b/src/main/java/org/gridsuite/modification/modifications/byfilter/ByFormulaModification.java @@ -91,6 +91,9 @@ protected boolean preCheckValue(Identifiable equipment, AbstractAssignmentInf .build()); return false; } + if (equipment.getType() == IdentifiableType.GENERATOR) { + return checkGeneratorsPowerValues(equipment, abstractAssignmentInfos, reports); + } return true; } diff --git a/src/main/java/org/gridsuite/modification/modifications/byfilter/ModificationByAssignment.java b/src/main/java/org/gridsuite/modification/modifications/byfilter/ModificationByAssignment.java index 97b5cdd4..deae7f9d 100644 --- a/src/main/java/org/gridsuite/modification/modifications/byfilter/ModificationByAssignment.java +++ b/src/main/java/org/gridsuite/modification/modifications/byfilter/ModificationByAssignment.java @@ -78,6 +78,9 @@ protected boolean isEquipmentEditable(Identifiable equipment, AbstractAssignm @Override protected boolean preCheckValue(Identifiable equipment, AbstractAssignmentInfos abstractAssignmentInfos, List reports, List notEditableEquipments) { + if (equipment.getType() == IdentifiableType.GENERATOR) { + return checkGeneratorsPowerValues(equipment, abstractAssignmentInfos, reports); + } return true; } diff --git a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java index 4c39ff2c..9e7c39df 100644 --- a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java +++ b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java @@ -37,6 +37,7 @@ import static org.gridsuite.modification.dto.OperationalLimitsGroupInfos.Applicability.SIDE1; import static org.gridsuite.modification.dto.OperationalLimitsGroupInfos.Applicability.SIDE2; import static org.gridsuite.modification.modifications.AbstractBranchModification.*; +import static org.gridsuite.modification.modifications.byfilter.AbstractModificationByAssignment.*; /** * @author Slimane Amar @@ -71,6 +72,9 @@ 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"; + public static final String FIELD_MAX_ACTIVE_POWER = "Maximum active power"; + public static final String FIELD_MIN_ACTIVE_POWER = "Minimum active power"; + public static final String FIELD_PLANNED_ACTIVE_POWER_SET_POINT = "Planned active power set point"; public static String applicabilityToString(OperationalLimitsGroupInfos.Applicability applicability) { return switch (applicability) { @@ -1921,6 +1925,63 @@ public static void checkLimitsGroupExist(String errorMessage, String limitsGroup } } + public static boolean validatePlannedActivePowerSetPoint(Generator generator, List reports, double newValue) { + if (newValue > generator.getMaxP()) { + addWarningReportForFieldComparison(reports, generator.getId(), FIELD_PLANNED_ACTIVE_POWER_SET_POINT, newValue, FIELD_MAX_ACTIVE_POWER, generator.getMaxP(), true); + return false; + } + if (newValue < generator.getMinP()) { + addWarningReportForFieldComparison(reports, generator.getId(), FIELD_PLANNED_ACTIVE_POWER_SET_POINT, newValue, FIELD_MIN_ACTIVE_POWER, generator.getMinP(), false); + return false; + } + return true; + } + + public static boolean validateMinimumActivePower(Generator generator, GeneratorStartup generatorStartup, List reports, double newValue) { + if (generatorStartup != null && !Double.isNaN(generatorStartup.getPlannedActivePowerSetpoint()) && newValue > generatorStartup.getPlannedActivePowerSetpoint()) { + addWarningReportForFieldComparison(reports, generator.getId(), FIELD_MIN_ACTIVE_POWER, newValue, FIELD_PLANNED_ACTIVE_POWER_SET_POINT, generatorStartup.getPlannedActivePowerSetpoint(), true); + return false; + } + if (newValue > generator.getMaxP()) { + addWarningReportForFieldComparison(reports, generator.getId(), FIELD_MIN_ACTIVE_POWER, newValue, FIELD_MAX_ACTIVE_POWER, generator.getMaxP(), true); + return false; + } + return true; + } + + public static boolean validateMaximumActivePower(Generator generator, GeneratorStartup generatorStartup, List reports, double newValue) { + if (generatorStartup != null && !Double.isNaN(generatorStartup.getPlannedActivePowerSetpoint()) && newValue < generatorStartup.getPlannedActivePowerSetpoint()) { + addWarningReportForFieldComparison(reports, generator.getId(), FIELD_MAX_ACTIVE_POWER, newValue, FIELD_PLANNED_ACTIVE_POWER_SET_POINT, generatorStartup.getPlannedActivePowerSetpoint(), false); + return false; + } + if (newValue < generator.getMinP()) { + addWarningReportForFieldComparison(reports, generator.getId(), FIELD_MAX_ACTIVE_POWER, newValue, FIELD_MIN_ACTIVE_POWER, generator.getMinP(), false); + return false; + } + return true; + } + + private static void addWarningReportForFieldComparison(List reports, + String equipmentName, + String field1Name, + double field1Value, + String field2Name, + double field2Value, + boolean smaller) { + + String message = smaller ? REPORT_KEY_GENERATOR_FIELD1_SMALLER_FIELD2 : REPORT_KEY_GENERATOR_FIELD1_GREATER_FIELD2; + reports.add(ReportNode.newRootReportNode() + .withResourceBundles(NetworkModificationReportResourceBundle.BASE_NAME) + .withMessageTemplate(message) + .withUntypedValue(VALUE_KEY_EQUIPMENT_NAME, equipmentName) + .withUntypedValue(VALUE_KEY_FIELD_NAME, field1Name) + .withUntypedValue(VALUE_KEY_FIELD_VALUE, field1Value) + .withUntypedValue(VALUE_KEY_FIELD2_NAME, field2Name) + .withUntypedValue(VALUE_KEY_FIELD2_VALUE, field2Value) + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); + } + public static List getOperationalLimitsGroupsOnSide(List operationalLimitsGroupInfos, OperationalLimitsGroupInfos.Applicability applicability) { if (operationalLimitsGroupInfos == null || operationalLimitsGroupInfos.isEmpty()) { diff --git a/src/main/resources/org/gridsuite/modification/reports.properties b/src/main/resources/org/gridsuite/modification/reports.properties index fc3f9203..7144f53f 100644 --- a/src/main/resources/org/gridsuite/modification/reports.properties +++ b/src/main/resources/org/gridsuite/modification/reports.properties @@ -169,6 +169,8 @@ network.modification.generatorNotFound.generatorsFrequencyReserve = Frequency re network.modification.generatorNotFound.generatorsWithFixedSupply = Generators with fixed active power: Cannot find generator ${notFoundGeneratorId} in filter ${filterName} network.modification.generatorNotFound.generatorsWithoutOutage = Generators without outage simulation: Cannot find generator ${notFoundGeneratorId} in filter ${filterName} network.modification.generatorScaling = Generator scaling +network.modification.generator.generatorField1GreaterField2 = Generator ${equipmentName} : value ${fieldValue} in field ${fieldName} should be greater or equal to value in ${field2Name} field ${field2Value}. +network.modification.generator.generatorField1SmallerField2 = Generator ${equipmentName} : value ${fieldValue} in field ${fieldName} should be smaller or equal to value in ${field2Name} field ${field2Value}. network.modification.groovyScript = Apply groovy script network.modification.groovyScriptApplied = Groovy script applied network.modification.invalidFilters = ${errorType}: There is no valid equipment ID among the provided filter(s) From 71f7b6ce2da3b5c08206166efca9749f1342a4f7 Mon Sep 17 00:00:00 2001 From: basseche Date: Wed, 4 Feb 2026 12:26:46 +0100 Subject: [PATCH 02/11] Add check on active power target --- .../AbstractModificationByAssignment.java | 4 +++- .../modification/utils/ModificationUtils.java | 15 ++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/modifications/byfilter/AbstractModificationByAssignment.java b/src/main/java/org/gridsuite/modification/modifications/byfilter/AbstractModificationByAssignment.java index 71666d0b..4723ce16 100644 --- a/src/main/java/org/gridsuite/modification/modifications/byfilter/AbstractModificationByAssignment.java +++ b/src/main/java/org/gridsuite/modification/modifications/byfilter/AbstractModificationByAssignment.java @@ -118,11 +118,13 @@ protected boolean checkGeneratorsPowerValues(Identifiable equipment, Abstract GeneratorStartup generatorStartup = generator.getExtension(GeneratorStartup.class); if (abstractAssignmentInfos.getEditedField().equals(PLANNED_ACTIVE_POWER_SET_POINT.name()) && generatorStartup != null) { - return validatePlannedActivePowerSetPoint(generator, reports, newValue); + return validateActivePowerValue(generator, FIELD_PLANNED_ACTIVE_POWER_SET_POINT, reports, newValue); } else if (abstractAssignmentInfos.getEditedField().equals(MINIMUM_ACTIVE_POWER.name())) { return validateMinimumActivePower(generator, generatorStartup, reports, newValue); } else if (abstractAssignmentInfos.getEditedField().equals(MAXIMUM_ACTIVE_POWER.name())) { return validateMaximumActivePower(generator, generatorStartup, reports, newValue); + } else if (abstractAssignmentInfos.getEditedField().equals(ACTIVE_POWER_SET_POINT.name())) { + return validateActivePowerValue(generator, FIELD_ACTIVE_POWER_TARGET, reports, newValue); } } return true; diff --git a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java index 9e7c39df..4cf1fcf7 100644 --- a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java +++ b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java @@ -75,6 +75,7 @@ public final class ModificationUtils { public static final String FIELD_MAX_ACTIVE_POWER = "Maximum active power"; public static final String FIELD_MIN_ACTIVE_POWER = "Minimum active power"; public static final String FIELD_PLANNED_ACTIVE_POWER_SET_POINT = "Planned active power set point"; + public static final String FIELD_ACTIVE_POWER_TARGET = "Active power target"; public static String applicabilityToString(OperationalLimitsGroupInfos.Applicability applicability) { return switch (applicability) { @@ -1925,13 +1926,13 @@ public static void checkLimitsGroupExist(String errorMessage, String limitsGroup } } - public static boolean validatePlannedActivePowerSetPoint(Generator generator, List reports, double newValue) { + public static boolean validateActivePowerValue(Generator generator, String fieldName, List reports, double newValue) { if (newValue > generator.getMaxP()) { - addWarningReportForFieldComparison(reports, generator.getId(), FIELD_PLANNED_ACTIVE_POWER_SET_POINT, newValue, FIELD_MAX_ACTIVE_POWER, generator.getMaxP(), true); + addWarningReportForFieldComparison(reports, generator.getId(), fieldName, newValue, FIELD_MAX_ACTIVE_POWER, generator.getMaxP(), true); return false; } if (newValue < generator.getMinP()) { - addWarningReportForFieldComparison(reports, generator.getId(), FIELD_PLANNED_ACTIVE_POWER_SET_POINT, newValue, FIELD_MIN_ACTIVE_POWER, generator.getMinP(), false); + addWarningReportForFieldComparison(reports, generator.getId(), fieldName, newValue, FIELD_MIN_ACTIVE_POWER, generator.getMinP(), false); return false; } return true; @@ -1942,6 +1943,10 @@ public static boolean validateMinimumActivePower(Generator generator, GeneratorS addWarningReportForFieldComparison(reports, generator.getId(), FIELD_MIN_ACTIVE_POWER, newValue, FIELD_PLANNED_ACTIVE_POWER_SET_POINT, generatorStartup.getPlannedActivePowerSetpoint(), true); return false; } + if (newValue > generator.getTargetP()) { + addWarningReportForFieldComparison(reports, generator.getId(), FIELD_MIN_ACTIVE_POWER, newValue, FIELD_ACTIVE_POWER_TARGET, generator.getTargetP(), true); + return false; + } if (newValue > generator.getMaxP()) { addWarningReportForFieldComparison(reports, generator.getId(), FIELD_MIN_ACTIVE_POWER, newValue, FIELD_MAX_ACTIVE_POWER, generator.getMaxP(), true); return false; @@ -1954,6 +1959,10 @@ public static boolean validateMaximumActivePower(Generator generator, GeneratorS addWarningReportForFieldComparison(reports, generator.getId(), FIELD_MAX_ACTIVE_POWER, newValue, FIELD_PLANNED_ACTIVE_POWER_SET_POINT, generatorStartup.getPlannedActivePowerSetpoint(), false); return false; } + if (newValue < generator.getTargetP()) { + addWarningReportForFieldComparison(reports, generator.getId(), FIELD_MAX_ACTIVE_POWER, newValue, FIELD_ACTIVE_POWER_TARGET, generator.getTargetP(), false); + return false; + } if (newValue < generator.getMinP()) { addWarningReportForFieldComparison(reports, generator.getId(), FIELD_MAX_ACTIVE_POWER, newValue, FIELD_MIN_ACTIVE_POWER, generator.getMinP(), false); return false; From 62738178506177492ad7b2c07abffd956e837fb5 Mon Sep 17 00:00:00 2001 From: basseche Date: Wed, 4 Feb 2026 15:29:35 +0100 Subject: [PATCH 03/11] Add check to GeneratorCreation and GeneratorModification (for tabular creation and modification) --- .../modifications/GeneratorCreation.java | 2 + .../modifications/GeneratorModification.java | 6 + .../AbstractModificationByAssignment.java | 15 +-- .../modification/utils/ModificationUtils.java | 121 +++++++++++------- .../gridsuite/modification/reports.properties | 5 +- 5 files changed, 94 insertions(+), 55 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/modifications/GeneratorCreation.java b/src/main/java/org/gridsuite/modification/modifications/GeneratorCreation.java index 335e8a9a..c9c4a4df 100644 --- a/src/main/java/org/gridsuite/modification/modifications/GeneratorCreation.java +++ b/src/main/java/org/gridsuite/modification/modifications/GeneratorCreation.java @@ -69,6 +69,8 @@ public void check(Network network) throws NetworkModificationException { checkIsNotNegativeValue(errorMessage, modificationInfos.getTargetV(), CREATE_GENERATOR_ERROR, "Target Voltage"); checkIsPercentage(errorMessage, modificationInfos.getDroop(), CREATE_GENERATOR_ERROR, "Droop"); checkIsNotNegativeValue(errorMessage, modificationInfos.getRatedS(), CREATE_GENERATOR_ERROR, "Rated apparent power"); + checkPowerValues(errorMessage, modificationInfos.getMinP(), modificationInfos.getMaxP(), modificationInfos.getTargetP(), + modificationInfos.getPlannedActivePowerSetPoint(), CREATE_GENERATOR_ERROR); } @Override diff --git a/src/main/java/org/gridsuite/modification/modifications/GeneratorModification.java b/src/main/java/org/gridsuite/modification/modifications/GeneratorModification.java index 040e79e0..e18ca402 100644 --- a/src/main/java/org/gridsuite/modification/modifications/GeneratorModification.java +++ b/src/main/java/org/gridsuite/modification/modifications/GeneratorModification.java @@ -72,6 +72,12 @@ public void check(Network network) throws NetworkModificationException { if (modificationInfos.getTargetV() != null) { checkIsNotNegativeValue(errorMessage, modificationInfos.getTargetV().getValue(), MODIFY_GENERATOR_ERROR, TARGET_VOLTAGE); } + GeneratorStartup generatorStartup = generator.getExtension(GeneratorStartup.class); + checkPowerValues(errorMessage, modificationInfos.getMinP().applyModification(generator.getMinP()), + modificationInfos.getMaxP().applyModification(generator.getMaxP()), + modificationInfos.getTargetP().applyModification(generator.getTargetP()), + modificationInfos.getPlannedActivePowerSetPoint().applyModification(generatorStartup != null && !Double.isNaN(generatorStartup.getPlannedActivePowerSetpoint()) ? generatorStartup.getPlannedActivePowerSetpoint() : null), + MODIFY_GENERATOR_ERROR); } private void checkActivePowerZeroOrBetweenMinAndMaxActivePowerGenerator(GeneratorModificationInfos modificationInfos, Generator generator, NetworkModificationException.Type exceptionType, String errorMessage) { diff --git a/src/main/java/org/gridsuite/modification/modifications/byfilter/AbstractModificationByAssignment.java b/src/main/java/org/gridsuite/modification/modifications/byfilter/AbstractModificationByAssignment.java index 4723ce16..95061464 100644 --- a/src/main/java/org/gridsuite/modification/modifications/byfilter/AbstractModificationByAssignment.java +++ b/src/main/java/org/gridsuite/modification/modifications/byfilter/AbstractModificationByAssignment.java @@ -13,7 +13,6 @@ import com.powsybl.iidm.network.Identifiable; import com.powsybl.iidm.network.IdentifiableType; import com.powsybl.iidm.network.Network; -import com.powsybl.iidm.network.extensions.GeneratorStartup; import org.apache.commons.lang3.StringUtils; import org.gridsuite.modification.IFilterService; import org.gridsuite.modification.ILoadFlowService; @@ -45,9 +44,10 @@ public abstract class AbstractModificationByAssignment extends AbstractModification { public static final String VALUE_KEY_FILTER_NAME = "filterName"; public static final String VALUE_KEY_FIELD_NAME = "fieldName"; - public static final String VALUE_KEY_FIELD2_NAME = "field2Name"; public static final String VALUE_KEY_FIELD_VALUE = "fieldValue"; - public static final String VALUE_KEY_FIELD2_VALUE = "field2Value"; + public static final String VALUE_KEY_MIN_VALUE = "minValue"; + public static final String VALUE_KEY_MAX_VALUE = "maxValue"; + public static final String VALUE_KEY_TARGET_VALUE = "targetValue"; public static final String VALUE_KEY_EQUIPMENT_NAME = "equipmentName"; public static final String VALUE_KEY_EQUIPMENT_TYPE = "equipmentType"; public static final String VALUE_KEY_EQUIPMENT_COUNT = "equipmentCount"; @@ -63,8 +63,6 @@ public abstract class AbstractModificationByAssignment extends AbstractModificat public static final String VALUE_KEY_ARROW_VALUE = "→"; public static final String REPORT_KEY_EQUIPMENT_MODIFIED_ERROR_ZERO = "network.modification.equipmentModifiedError.zero"; public static final String REPORT_KEY_EQUIPMENT_MODIFIED_ERROR_NULL = "network.modification.equipmentModifiedError.null"; - public static final String REPORT_KEY_GENERATOR_FIELD1_GREATER_FIELD2 = "network.modification.generator.generatorField1GreaterField2"; - public static final String REPORT_KEY_GENERATOR_FIELD1_SMALLER_FIELD2 = "network.modification.generator.generatorField1SmallerField2"; public static final String REPORT_KEY_BY_FILTER_MODIFICATION_SOME = "network.modification.byFilterModificationSome"; public static final String REPORT_KEY_BY_FILTER_MODIFICATION_FAILED = "network.modification.byFilterModificationFailed"; public static final String REPORT_KEY_BY_FILTER_MODIFICATION_SUCCESS = "network.modification.byFilterModificationSuccess"; @@ -115,14 +113,13 @@ protected boolean checkGeneratorsPowerValues(Identifiable equipment, Abstract if (equipment.getType() == IdentifiableType.GENERATOR) { Generator generator = (Generator) equipment; double newValue = Double.parseDouble(getNewValue(equipment, abstractAssignmentInfos)); - GeneratorStartup generatorStartup = generator.getExtension(GeneratorStartup.class); - if (abstractAssignmentInfos.getEditedField().equals(PLANNED_ACTIVE_POWER_SET_POINT.name()) && generatorStartup != null) { + if (abstractAssignmentInfos.getEditedField().equals(PLANNED_ACTIVE_POWER_SET_POINT.name())) { return validateActivePowerValue(generator, FIELD_PLANNED_ACTIVE_POWER_SET_POINT, reports, newValue); } else if (abstractAssignmentInfos.getEditedField().equals(MINIMUM_ACTIVE_POWER.name())) { - return validateMinimumActivePower(generator, generatorStartup, reports, newValue); + return validateMinimumActivePower(generator, reports, newValue); } else if (abstractAssignmentInfos.getEditedField().equals(MAXIMUM_ACTIVE_POWER.name())) { - return validateMaximumActivePower(generator, generatorStartup, reports, newValue); + return validateMaximumActivePower(generator, reports, newValue); } else if (abstractAssignmentInfos.getEditedField().equals(ACTIVE_POWER_SET_POINT.name())) { return validateActivePowerValue(generator, FIELD_ACTIVE_POWER_TARGET, reports, newValue); } diff --git a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java index 4cf1fcf7..f678c310 100644 --- a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java +++ b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java @@ -1926,69 +1926,102 @@ public static void checkLimitsGroupExist(String errorMessage, String limitsGroup } } - public static boolean validateActivePowerValue(Generator generator, String fieldName, List reports, double newValue) { - if (newValue > generator.getMaxP()) { - addWarningReportForFieldComparison(reports, generator.getId(), fieldName, newValue, FIELD_MAX_ACTIVE_POWER, generator.getMaxP(), true); - return false; + public static void checkActivePowerValue(String errorMessage, String fieldName, double newValue, double minP, double maxP, NetworkModificationException.Type exceptionType) throws NetworkModificationException { + if (newValue > maxP || newValue < minP) { + String message = String.format("value %.2f field %s should be inside interval [%.2f; %.2f]", newValue, fieldName, minP, maxP); + throw new NetworkModificationException(exceptionType, errorMessage + message); } - if (newValue < generator.getMinP()) { - addWarningReportForFieldComparison(reports, generator.getId(), fieldName, newValue, FIELD_MIN_ACTIVE_POWER, generator.getMinP(), false); + } + + public static boolean validateActivePowerValue(Generator generator, String fieldName, List reports, double newValue) { + if (newValue > generator.getMaxP() || newValue < generator.getMinP()) { + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate("network.modification.generator.ValueShouldBeWithinInterval") + .withUntypedValue(VALUE_KEY_EQUIPMENT_NAME, generator.getId()) + .withUntypedValue(VALUE_KEY_FIELD_NAME, fieldName) + .withUntypedValue(VALUE_KEY_FIELD_VALUE, newValue) + .withUntypedValue(VALUE_KEY_MIN_VALUE, generator.getMinP()) + .withUntypedValue(VALUE_KEY_MAX_VALUE, generator.getMaxP()) + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); return false; } return true; } - public static boolean validateMinimumActivePower(Generator generator, GeneratorStartup generatorStartup, List reports, double newValue) { - if (generatorStartup != null && !Double.isNaN(generatorStartup.getPlannedActivePowerSetpoint()) && newValue > generatorStartup.getPlannedActivePowerSetpoint()) { - addWarningReportForFieldComparison(reports, generator.getId(), FIELD_MIN_ACTIVE_POWER, newValue, FIELD_PLANNED_ACTIVE_POWER_SET_POINT, generatorStartup.getPlannedActivePowerSetpoint(), true); - return false; + public static void checkPowerValues(String errorMessage, double minP, double maxP, double targetP, Double pImp, NetworkModificationException.Type exceptionType) throws NetworkModificationException { + checkActivePowerValue(errorMessage, FIELD_ACTIVE_POWER_TARGET, targetP, minP, maxP, exceptionType); + if (pImp != null) { + checkActivePowerValue(errorMessage, FIELD_PLANNED_ACTIVE_POWER_SET_POINT, pImp, minP, maxP, exceptionType); } - if (newValue > generator.getTargetP()) { - addWarningReportForFieldComparison(reports, generator.getId(), FIELD_MIN_ACTIVE_POWER, newValue, FIELD_ACTIVE_POWER_TARGET, generator.getTargetP(), true); - return false; + checkMinimumActivePower(errorMessage, maxP, targetP, pImp, minP, exceptionType); + checkMaximumActivePower(errorMessage, minP, targetP, pImp, maxP, exceptionType); + } + + public static void checkMinimumActivePower(String errorMessage, double maxP, double targetP, Double pImp, double newValue, NetworkModificationException.Type exceptionType) throws NetworkModificationException { + double pMin = Math.min(targetP, maxP); + if (pImp != null) { + pMin = Math.min(pMin, pImp); } - if (newValue > generator.getMaxP()) { - addWarningReportForFieldComparison(reports, generator.getId(), FIELD_MIN_ACTIVE_POWER, newValue, FIELD_MAX_ACTIVE_POWER, generator.getMaxP(), true); - return false; + if (pMin < newValue) { + throw new NetworkModificationException(exceptionType, errorMessage + String.format("value %.2f of field %s should be be smaller or equal to %.2f", newValue, FIELD_MIN_ACTIVE_POWER, pMin)); } - return true; } - public static boolean validateMaximumActivePower(Generator generator, GeneratorStartup generatorStartup, List reports, double newValue) { - if (generatorStartup != null && !Double.isNaN(generatorStartup.getPlannedActivePowerSetpoint()) && newValue < generatorStartup.getPlannedActivePowerSetpoint()) { - addWarningReportForFieldComparison(reports, generator.getId(), FIELD_MAX_ACTIVE_POWER, newValue, FIELD_PLANNED_ACTIVE_POWER_SET_POINT, generatorStartup.getPlannedActivePowerSetpoint(), false); - return false; + public static void checkMaximumActivePower(String errorMessage, double minP, double targetP, Double pImp, double newValue, NetworkModificationException.Type exceptionType) throws NetworkModificationException { + double pMax = Math.max(targetP, minP); + if (pImp != null) { + pMax = Math.max(pMax, pImp); } - if (newValue < generator.getTargetP()) { - addWarningReportForFieldComparison(reports, generator.getId(), FIELD_MAX_ACTIVE_POWER, newValue, FIELD_ACTIVE_POWER_TARGET, generator.getTargetP(), false); - return false; + if (pMax > newValue) { + throw new NetworkModificationException(exceptionType, errorMessage + String.format("value %.2f of field %s should be be greater or equal to %.2f", newValue, FIELD_MAX_ACTIVE_POWER, pMax)); + } + } + + public static boolean validateMinimumActivePower(Generator generator, List reports, double newValue) { + GeneratorStartup generatorStartup = generator.getExtension(GeneratorStartup.class); + Double pImp = generatorStartup != null && !Double.isNaN(generatorStartup.getPlannedActivePowerSetpoint()) ? generatorStartup.getPlannedActivePowerSetpoint() : null; + + double minP = Math.min(generator.getTargetP(), generator.getMaxP()); + if (pImp != null) { + minP = Math.min(minP, pImp); } - if (newValue < generator.getMinP()) { - addWarningReportForFieldComparison(reports, generator.getId(), FIELD_MAX_ACTIVE_POWER, newValue, FIELD_MIN_ACTIVE_POWER, generator.getMinP(), false); + + if (minP < newValue) { + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate("network.modification.generator.ValueShouldBeSmallerThan") + .withUntypedValue(VALUE_KEY_EQUIPMENT_NAME, generator.getId()) + .withUntypedValue(VALUE_KEY_FIELD_NAME, FIELD_MIN_ACTIVE_POWER) + .withUntypedValue(VALUE_KEY_FIELD_VALUE, newValue) + .withUntypedValue(VALUE_KEY_TARGET_VALUE, minP) + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); return false; } return true; } - private static void addWarningReportForFieldComparison(List reports, - String equipmentName, - String field1Name, - double field1Value, - String field2Name, - double field2Value, - boolean smaller) { + public static boolean validateMaximumActivePower(Generator generator, List reports, double newValue) { + GeneratorStartup generatorStartup = generator.getExtension(GeneratorStartup.class); + Double pImp = generatorStartup != null && !Double.isNaN(generatorStartup.getPlannedActivePowerSetpoint()) ? generatorStartup.getPlannedActivePowerSetpoint() : null; - String message = smaller ? REPORT_KEY_GENERATOR_FIELD1_SMALLER_FIELD2 : REPORT_KEY_GENERATOR_FIELD1_GREATER_FIELD2; - reports.add(ReportNode.newRootReportNode() - .withResourceBundles(NetworkModificationReportResourceBundle.BASE_NAME) - .withMessageTemplate(message) - .withUntypedValue(VALUE_KEY_EQUIPMENT_NAME, equipmentName) - .withUntypedValue(VALUE_KEY_FIELD_NAME, field1Name) - .withUntypedValue(VALUE_KEY_FIELD_VALUE, field1Value) - .withUntypedValue(VALUE_KEY_FIELD2_NAME, field2Name) - .withUntypedValue(VALUE_KEY_FIELD2_VALUE, field2Value) - .withSeverity(TypedValue.WARN_SEVERITY) - .build()); + double maxP = Math.max(generator.getTargetP(), generator.getMinP()); + if (pImp != null) { + maxP = Math.min(maxP, pImp); + } + + if (newValue < maxP) { + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate("network.modification.generator.ValueShouldBeGreaterThan") + .withUntypedValue(VALUE_KEY_EQUIPMENT_NAME, generator.getId()) + .withUntypedValue(VALUE_KEY_FIELD_NAME, FIELD_MAX_ACTIVE_POWER) + .withUntypedValue(VALUE_KEY_FIELD_VALUE, newValue) + .withUntypedValue(VALUE_KEY_TARGET_VALUE, maxP) + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); + return false; + } + return true; } public static List getOperationalLimitsGroupsOnSide(List operationalLimitsGroupInfos, diff --git a/src/main/resources/org/gridsuite/modification/reports.properties b/src/main/resources/org/gridsuite/modification/reports.properties index 7144f53f..71e80700 100644 --- a/src/main/resources/org/gridsuite/modification/reports.properties +++ b/src/main/resources/org/gridsuite/modification/reports.properties @@ -169,8 +169,9 @@ network.modification.generatorNotFound.generatorsFrequencyReserve = Frequency re network.modification.generatorNotFound.generatorsWithFixedSupply = Generators with fixed active power: Cannot find generator ${notFoundGeneratorId} in filter ${filterName} network.modification.generatorNotFound.generatorsWithoutOutage = Generators without outage simulation: Cannot find generator ${notFoundGeneratorId} in filter ${filterName} network.modification.generatorScaling = Generator scaling -network.modification.generator.generatorField1GreaterField2 = Generator ${equipmentName} : value ${fieldValue} in field ${fieldName} should be greater or equal to value in ${field2Name} field ${field2Value}. -network.modification.generator.generatorField1SmallerField2 = Generator ${equipmentName} : value ${fieldValue} in field ${fieldName} should be smaller or equal to value in ${field2Name} field ${field2Value}. +network.modification.generator.ValueShouldBeGreaterThan = Generator ${equipmentName} : value ${fieldValue} in field ${fieldName} should be greater or equal to s{targetValue}. +network.modification.generator.ValueShouldBeSmallerThan = Generator ${equipmentName} : value ${fieldValue} in field ${fieldName} should be smaller or equal to s{targetValue}. +network.modification.generator.ValueShouldBeWithinInterval = Generator ${equipmentName} : value ${fieldValue} in field ${fieldName} should be within interval [${minValue}; ${maxValue}]. network.modification.groovyScript = Apply groovy script network.modification.groovyScriptApplied = Groovy script applied network.modification.invalidFilters = ${errorType}: There is no valid equipment ID among the provided filter(s) From 457225520933d21934b81f316437cbafe9405d66 Mon Sep 17 00:00:00 2001 From: basseche Date: Wed, 4 Feb 2026 15:59:49 +0100 Subject: [PATCH 04/11] change error message --- .../resources/org/gridsuite/modification/reports.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/org/gridsuite/modification/reports.properties b/src/main/resources/org/gridsuite/modification/reports.properties index 71e80700..f5c89486 100644 --- a/src/main/resources/org/gridsuite/modification/reports.properties +++ b/src/main/resources/org/gridsuite/modification/reports.properties @@ -169,9 +169,9 @@ network.modification.generatorNotFound.generatorsFrequencyReserve = Frequency re network.modification.generatorNotFound.generatorsWithFixedSupply = Generators with fixed active power: Cannot find generator ${notFoundGeneratorId} in filter ${filterName} network.modification.generatorNotFound.generatorsWithoutOutage = Generators without outage simulation: Cannot find generator ${notFoundGeneratorId} in filter ${filterName} network.modification.generatorScaling = Generator scaling -network.modification.generator.ValueShouldBeGreaterThan = Generator ${equipmentName} : value ${fieldValue} in field ${fieldName} should be greater or equal to s{targetValue}. -network.modification.generator.ValueShouldBeSmallerThan = Generator ${equipmentName} : value ${fieldValue} in field ${fieldName} should be smaller or equal to s{targetValue}. -network.modification.generator.ValueShouldBeWithinInterval = Generator ${equipmentName} : value ${fieldValue} in field ${fieldName} should be within interval [${minValue}; ${maxValue}]. +network.modification.generator.ValueShouldBeGreaterThan = Generator ${equipmentName} : Invalid value ${fieldValue} for field ${fieldName}. Value should be greater or equal to s{targetValue}. +network.modification.generator.ValueShouldBeSmallerThan = Generator ${equipmentName} : Invalid value ${fieldValue} for field ${fieldName}. Value should be smaller or equal to s{targetValue}. +network.modification.generator.ValueShouldBeWithinInterval = Generator ${equipmentName} : Invalid value ${fieldValue} for field ${fieldName}. Value should be within interval [${minValue}; ${maxValue}]. network.modification.groovyScript = Apply groovy script network.modification.groovyScriptApplied = Groovy script applied network.modification.invalidFilters = ${errorType}: There is no valid equipment ID among the provided filter(s) From 81126c6bcf5628740d3d1284aec8f6b70b9cdc47 Mon Sep 17 00:00:00 2001 From: basseche Date: Wed, 4 Feb 2026 17:03:26 +0100 Subject: [PATCH 05/11] Fix tests and modification --- .../modifications/GeneratorModification.java | 9 +++++---- .../AbstractModificationByAssignment.java | 10 ++++------ .../GeneratorModificationByAssignmentTest.java | 17 +++++++++-------- .../GeneratorByFormulaModificationTest.java | 3 ++- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/modifications/GeneratorModification.java b/src/main/java/org/gridsuite/modification/modifications/GeneratorModification.java index e18ca402..826dcc61 100644 --- a/src/main/java/org/gridsuite/modification/modifications/GeneratorModification.java +++ b/src/main/java/org/gridsuite/modification/modifications/GeneratorModification.java @@ -73,10 +73,11 @@ public void check(Network network) throws NetworkModificationException { checkIsNotNegativeValue(errorMessage, modificationInfos.getTargetV().getValue(), MODIFY_GENERATOR_ERROR, TARGET_VOLTAGE); } GeneratorStartup generatorStartup = generator.getExtension(GeneratorStartup.class); - checkPowerValues(errorMessage, modificationInfos.getMinP().applyModification(generator.getMinP()), - modificationInfos.getMaxP().applyModification(generator.getMaxP()), - modificationInfos.getTargetP().applyModification(generator.getTargetP()), - modificationInfos.getPlannedActivePowerSetPoint().applyModification(generatorStartup != null && !Double.isNaN(generatorStartup.getPlannedActivePowerSetpoint()) ? generatorStartup.getPlannedActivePowerSetpoint() : null), + double minP = modificationInfos.getMinP() != null ? modificationInfos.getMinP().getValue() : generator.getMinP(); + double maxP = modificationInfos.getMaxP() != null ? modificationInfos.getMaxP().getValue() : generator.getMaxP(); + double targetP = modificationInfos.getTargetP() != null ? modificationInfos.getTargetP().getValue() : generator.getTargetP(); + checkPowerValues(errorMessage, minP, maxP, targetP, + generatorStartup != null && !Double.isNaN(generatorStartup.getPlannedActivePowerSetpoint()) ? generatorStartup.getPlannedActivePowerSetpoint() : null, MODIFY_GENERATOR_ERROR); } diff --git a/src/main/java/org/gridsuite/modification/modifications/byfilter/AbstractModificationByAssignment.java b/src/main/java/org/gridsuite/modification/modifications/byfilter/AbstractModificationByAssignment.java index 95061464..7dd1932b 100644 --- a/src/main/java/org/gridsuite/modification/modifications/byfilter/AbstractModificationByAssignment.java +++ b/src/main/java/org/gridsuite/modification/modifications/byfilter/AbstractModificationByAssignment.java @@ -112,16 +112,14 @@ protected abstract boolean preCheckValue(Identifiable equipment, protected boolean checkGeneratorsPowerValues(Identifiable equipment, AbstractAssignmentInfos abstractAssignmentInfos, List reports) { if (equipment.getType() == IdentifiableType.GENERATOR) { Generator generator = (Generator) equipment; - double newValue = Double.parseDouble(getNewValue(equipment, abstractAssignmentInfos)); - if (abstractAssignmentInfos.getEditedField().equals(PLANNED_ACTIVE_POWER_SET_POINT.name())) { - return validateActivePowerValue(generator, FIELD_PLANNED_ACTIVE_POWER_SET_POINT, reports, newValue); + return validateActivePowerValue(generator, FIELD_PLANNED_ACTIVE_POWER_SET_POINT, reports, Double.parseDouble(getNewValue(equipment, abstractAssignmentInfos))); } else if (abstractAssignmentInfos.getEditedField().equals(MINIMUM_ACTIVE_POWER.name())) { - return validateMinimumActivePower(generator, reports, newValue); + return validateMinimumActivePower(generator, reports, Double.parseDouble(getNewValue(equipment, abstractAssignmentInfos))); } else if (abstractAssignmentInfos.getEditedField().equals(MAXIMUM_ACTIVE_POWER.name())) { - return validateMaximumActivePower(generator, reports, newValue); + return validateMaximumActivePower(generator, reports, Double.parseDouble(getNewValue(equipment, abstractAssignmentInfos))); } else if (abstractAssignmentInfos.getEditedField().equals(ACTIVE_POWER_SET_POINT.name())) { - return validateActivePowerValue(generator, FIELD_ACTIVE_POWER_TARGET, reports, newValue); + return validateActivePowerValue(generator, FIELD_ACTIVE_POWER_TARGET, reports, Double.parseDouble(getNewValue(equipment, abstractAssignmentInfos))); } } return true; diff --git a/src/test/java/org/gridsuite/modification/modifications/byfilter/assignment/GeneratorModificationByAssignmentTest.java b/src/test/java/org/gridsuite/modification/modifications/byfilter/assignment/GeneratorModificationByAssignmentTest.java index 954f00aa..145c0a33 100644 --- a/src/test/java/org/gridsuite/modification/modifications/byfilter/assignment/GeneratorModificationByAssignmentTest.java +++ b/src/test/java/org/gridsuite/modification/modifications/byfilter/assignment/GeneratorModificationByAssignmentTest.java @@ -88,7 +88,7 @@ protected void createEquipments() { getNetwork().getGenerator(GENERATOR_ID_2) .setTargetP(200) .setMaxP(2000) - .setMinP(50) + .setMinP(10) .setTargetV(10) .setTargetQ(20) .newExtension(GeneratorStartupAdder.class) @@ -243,7 +243,7 @@ protected List> getAssignmentInfos() { DoubleAssignmentInfos assignmentInfos10 = DoubleAssignmentInfos.builder() .editedField(GeneratorField.MAXIMUM_ACTIVE_POWER.name()) - .value(50.) + .value(300.) .filters(List.of(filter1, filter2, filter3, filter4, filter5)) .build(); @@ -338,7 +338,7 @@ protected void assertAfterNetworkModificationApplication() { assertEquals(0.1, generatorStartup1.getPlannedOutageRate(), 0); assertEquals(0.05, generatorStartup1.getForcedOutageRate(), 0); assertEquals(10, generatorStartup1.getPlannedActivePowerSetpoint(), 0); - assertEquals(50, generator1.getMaxP(), 0); + assertEquals(300., generator1.getMaxP(), 0); assertEquals(2, generator1.getMinP(), 0); assertTrue(generator1.isVoltageRegulatorOn()); ActivePowerControl activePowerControl1 = generator1.getExtension(ActivePowerControl.class); @@ -353,7 +353,7 @@ protected void assertAfterNetworkModificationApplication() { assertEquals(0.1, generatorStartup2.getPlannedOutageRate(), 0); assertEquals(0.05, generatorStartup2.getForcedOutageRate(), 0); assertEquals(10, generatorStartup2.getPlannedActivePowerSetpoint(), 0); - assertEquals(50, generator2.getMaxP(), 0); + assertEquals(300., generator2.getMaxP(), 0); assertEquals(2, generator2.getMinP(), 0); Generator generator3 = getNetwork().getGenerator(GENERATOR_ID_3); @@ -362,7 +362,7 @@ protected void assertAfterNetworkModificationApplication() { assertEquals(300, generator3.getTargetP(), 0); assertEquals(0.2, generatorShortCircuit3.getDirectTransX(), 0); assertEquals(0.3, generatorShortCircuit3.getStepUpTransformerX(), 0); - assertEquals(50, generator3.getMaxP(), 0); + assertEquals(300., generator3.getMaxP(), 0); assertEquals(2, generator3.getMinP(), 0); Generator generator4 = getNetwork().getGenerator(GENERATOR_ID_4); @@ -371,19 +371,20 @@ protected void assertAfterNetworkModificationApplication() { assertEquals(0.2, generatorShortCircuit4.getDirectTransX(), 0); assertEquals(0.3, generatorShortCircuit4.getStepUpTransformerX(), 0); assertEquals(400, generator4.getTargetP(), 0); - assertEquals(50, generator4.getMaxP(), 0); + //targetP is 400 MaxP won't change + assertEquals(700.0, generator4.getMaxP(), 0); assertEquals(2, generator4.getMinP(), 0); Generator generator5 = getNetwork().getGenerator(GENERATOR_ID_5); ActivePowerControl activePowerControl5 = generator5.getExtension(ActivePowerControl.class); assertNotNull(activePowerControl5); - assertEquals(50, generator5.getMaxP(), 0); + assertEquals(300., generator5.getMaxP(), 0); assertEquals(2, activePowerControl5.getDroop(), 0); Generator generator6 = getNetwork().getGenerator(GENERATOR_ID_6); ActivePowerControl activePowerControl6 = generator6.getExtension(ActivePowerControl.class); assertNotNull(activePowerControl6); - assertEquals(50, generator6.getMaxP(), 0); + assertEquals(300., generator6.getMaxP(), 0); assertEquals(2, activePowerControl6.getDroop(), 0); Generator generator7 = getNetwork().getGenerator(GENERATOR_ID_7); diff --git a/src/test/java/org/gridsuite/modification/modifications/byfilter/formula/GeneratorByFormulaModificationTest.java b/src/test/java/org/gridsuite/modification/modifications/byfilter/formula/GeneratorByFormulaModificationTest.java index a2a59c71..aa1cee4a 100644 --- a/src/test/java/org/gridsuite/modification/modifications/byfilter/formula/GeneratorByFormulaModificationTest.java +++ b/src/test/java/org/gridsuite/modification/modifications/byfilter/formula/GeneratorByFormulaModificationTest.java @@ -282,7 +282,8 @@ protected void assertAfterNetworkModificationApplication() { assertEquals(0.055, generatorStartup2.getForcedOutageRate(), 0); assertEquals(50, generatorStartup2.getPlannedActivePowerSetpoint(), 0); assertEquals(2002, generator2.getMaxP(), 0); - assertEquals(100, generator2.getMinP(), 0); + //value doesn't change anymore since MinP value can't be smaller than plannedActivePowerSetpoint + assertEquals(50, generator2.getMinP(), 0); Generator generator3 = getNetwork().getGenerator(GENERATOR_ID_3); GeneratorShortCircuit generatorShortCircuit3 = generator3.getExtension(GeneratorShortCircuit.class); From 04eceecc8ca477045d1844c05e9d92a5fbe2598f Mon Sep 17 00:00:00 2001 From: basseche Date: Wed, 4 Feb 2026 17:10:55 +0100 Subject: [PATCH 06/11] reorganize Signed-off-by: basseche --- .../modification/utils/ModificationUtils.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java index f678c310..54fd7a05 100644 --- a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java +++ b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java @@ -1933,22 +1933,6 @@ public static void checkActivePowerValue(String errorMessage, String fieldName, } } - public static boolean validateActivePowerValue(Generator generator, String fieldName, List reports, double newValue) { - if (newValue > generator.getMaxP() || newValue < generator.getMinP()) { - reports.add(ReportNode.newRootReportNode() - .withMessageTemplate("network.modification.generator.ValueShouldBeWithinInterval") - .withUntypedValue(VALUE_KEY_EQUIPMENT_NAME, generator.getId()) - .withUntypedValue(VALUE_KEY_FIELD_NAME, fieldName) - .withUntypedValue(VALUE_KEY_FIELD_VALUE, newValue) - .withUntypedValue(VALUE_KEY_MIN_VALUE, generator.getMinP()) - .withUntypedValue(VALUE_KEY_MAX_VALUE, generator.getMaxP()) - .withSeverity(TypedValue.WARN_SEVERITY) - .build()); - return false; - } - return true; - } - public static void checkPowerValues(String errorMessage, double minP, double maxP, double targetP, Double pImp, NetworkModificationException.Type exceptionType) throws NetworkModificationException { checkActivePowerValue(errorMessage, FIELD_ACTIVE_POWER_TARGET, targetP, minP, maxP, exceptionType); if (pImp != null) { @@ -2024,6 +2008,22 @@ public static boolean validateMaximumActivePower(Generator generator, List reports, double newValue) { + if (newValue > generator.getMaxP() || newValue < generator.getMinP()) { + reports.add(ReportNode.newRootReportNode() + .withMessageTemplate("network.modification.generator.ValueShouldBeWithinInterval") + .withUntypedValue(VALUE_KEY_EQUIPMENT_NAME, generator.getId()) + .withUntypedValue(VALUE_KEY_FIELD_NAME, fieldName) + .withUntypedValue(VALUE_KEY_FIELD_VALUE, newValue) + .withUntypedValue(VALUE_KEY_MIN_VALUE, generator.getMinP()) + .withUntypedValue(VALUE_KEY_MAX_VALUE, generator.getMaxP()) + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); + return false; + } + return true; + } + public static List getOperationalLimitsGroupsOnSide(List operationalLimitsGroupInfos, OperationalLimitsGroupInfos.Applicability applicability) { if (operationalLimitsGroupInfos == null || operationalLimitsGroupInfos.isEmpty()) { From 5a0de9f8bfa4a27451f736adf7d52b78d06da032 Mon Sep 17 00:00:00 2001 From: basseche Date: Thu, 5 Feb 2026 11:13:23 +0100 Subject: [PATCH 07/11] check only modified value in modification hypothesis --- .../modifications/GeneratorModification.java | 20 ++++++++++++++++--- .../modification/utils/ModificationUtils.java | 6 +++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/modifications/GeneratorModification.java b/src/main/java/org/gridsuite/modification/modifications/GeneratorModification.java index 826dcc61..a8ab1cd1 100644 --- a/src/main/java/org/gridsuite/modification/modifications/GeneratorModification.java +++ b/src/main/java/org/gridsuite/modification/modifications/GeneratorModification.java @@ -72,13 +72,27 @@ public void check(Network network) throws NetworkModificationException { if (modificationInfos.getTargetV() != null) { checkIsNotNegativeValue(errorMessage, modificationInfos.getTargetV().getValue(), MODIFY_GENERATOR_ERROR, TARGET_VOLTAGE); } + checkPowerValues(errorMessage, generator); + } + + private void checkPowerValues(String errorMessage, Generator generator) { GeneratorStartup generatorStartup = generator.getExtension(GeneratorStartup.class); + Double oldValue = generatorStartup != null && !Double.isNaN(generatorStartup.getPlannedActivePowerSetpoint()) + ? generatorStartup.getPlannedActivePowerSetpoint() : null; double minP = modificationInfos.getMinP() != null ? modificationInfos.getMinP().getValue() : generator.getMinP(); double maxP = modificationInfos.getMaxP() != null ? modificationInfos.getMaxP().getValue() : generator.getMaxP(); double targetP = modificationInfos.getTargetP() != null ? modificationInfos.getTargetP().getValue() : generator.getTargetP(); - checkPowerValues(errorMessage, minP, maxP, targetP, - generatorStartup != null && !Double.isNaN(generatorStartup.getPlannedActivePowerSetpoint()) ? generatorStartup.getPlannedActivePowerSetpoint() : null, - MODIFY_GENERATOR_ERROR); + Double pImp = modificationInfos.getPlannedActivePowerSetPoint() != null ? modificationInfos.getPlannedActivePowerSetPoint().applyModification(oldValue) : null; + + if (modificationInfos.getPlannedActivePowerSetPoint() != null) { + checkActivePowerValue(errorMessage, FIELD_PLANNED_ACTIVE_POWER_SET_POINT, pImp, minP, maxP, MODIFY_GENERATOR_ERROR); + } + if (modificationInfos.getMaxP() != null) { + checkMaximumActivePower(errorMessage, minP, targetP, pImp, maxP, MODIFY_GENERATOR_ERROR); + } + if (modificationInfos.getMinP() != null) { + checkMinimumActivePower(errorMessage, minP, targetP, pImp, maxP, MODIFY_GENERATOR_ERROR); + } } private void checkActivePowerZeroOrBetweenMinAndMaxActivePowerGenerator(GeneratorModificationInfos modificationInfos, Generator generator, NetworkModificationException.Type exceptionType, String errorMessage) { diff --git a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java index 54fd7a05..6f7a2d83 100644 --- a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java +++ b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java @@ -1928,7 +1928,7 @@ public static void checkLimitsGroupExist(String errorMessage, String limitsGroup public static void checkActivePowerValue(String errorMessage, String fieldName, double newValue, double minP, double maxP, NetworkModificationException.Type exceptionType) throws NetworkModificationException { if (newValue > maxP || newValue < minP) { - String message = String.format("value %.2f field %s should be inside interval [%.2f; %.2f]", newValue, fieldName, minP, maxP); + String message = String.format("Invalid value %.2f field %s should be inside interval [%.2f; %.2f]", newValue, fieldName, minP, maxP); throw new NetworkModificationException(exceptionType, errorMessage + message); } } @@ -1948,7 +1948,7 @@ public static void checkMinimumActivePower(String errorMessage, double maxP, dou pMin = Math.min(pMin, pImp); } if (pMin < newValue) { - throw new NetworkModificationException(exceptionType, errorMessage + String.format("value %.2f of field %s should be be smaller or equal to %.2f", newValue, FIELD_MIN_ACTIVE_POWER, pMin)); + throw new NetworkModificationException(exceptionType, errorMessage + String.format("Invalid value %.2f of field %s should be be smaller or equal to %.2f", newValue, FIELD_MIN_ACTIVE_POWER, pMin)); } } @@ -1958,7 +1958,7 @@ public static void checkMaximumActivePower(String errorMessage, double minP, dou pMax = Math.max(pMax, pImp); } if (pMax > newValue) { - throw new NetworkModificationException(exceptionType, errorMessage + String.format("value %.2f of field %s should be be greater or equal to %.2f", newValue, FIELD_MAX_ACTIVE_POWER, pMax)); + throw new NetworkModificationException(exceptionType, errorMessage + String.format("Invalid value %.2f of field %s should be be greater or equal to %.2f", newValue, FIELD_MAX_ACTIVE_POWER, pMax)); } } From 86c8d10a0acdbc7ae8a9a22c27ada4509590f19d Mon Sep 17 00:00:00 2001 From: basseche Date: Thu, 5 Feb 2026 11:20:51 +0100 Subject: [PATCH 08/11] fix --- .../modification/modifications/GeneratorModification.java | 2 +- .../org/gridsuite/modification/utils/ModificationUtils.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/modifications/GeneratorModification.java b/src/main/java/org/gridsuite/modification/modifications/GeneratorModification.java index a8ab1cd1..28cac6e2 100644 --- a/src/main/java/org/gridsuite/modification/modifications/GeneratorModification.java +++ b/src/main/java/org/gridsuite/modification/modifications/GeneratorModification.java @@ -91,7 +91,7 @@ private void checkPowerValues(String errorMessage, Generator generator) { checkMaximumActivePower(errorMessage, minP, targetP, pImp, maxP, MODIFY_GENERATOR_ERROR); } if (modificationInfos.getMinP() != null) { - checkMinimumActivePower(errorMessage, minP, targetP, pImp, maxP, MODIFY_GENERATOR_ERROR); + checkMinimumActivePower(errorMessage, maxP, targetP, pImp, minP, MODIFY_GENERATOR_ERROR); } } diff --git a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java index 6f7a2d83..4f654c3e 100644 --- a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java +++ b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java @@ -1928,7 +1928,7 @@ public static void checkLimitsGroupExist(String errorMessage, String limitsGroup public static void checkActivePowerValue(String errorMessage, String fieldName, double newValue, double minP, double maxP, NetworkModificationException.Type exceptionType) throws NetworkModificationException { if (newValue > maxP || newValue < minP) { - String message = String.format("Invalid value %.2f field %s should be inside interval [%.2f; %.2f]", newValue, fieldName, minP, maxP); + String message = String.format("Invalid value %.2f field %s should be within interval [%.2f; %.2f]", newValue, fieldName, minP, maxP); throw new NetworkModificationException(exceptionType, errorMessage + message); } } From aafeb9f4273336edd86f962c5f7b25544f642800 Mon Sep 17 00:00:00 2001 From: basseche Date: Tue, 10 Feb 2026 10:38:39 +0100 Subject: [PATCH 09/11] review Signed-off-by: basseche --- .../modifications/GeneratorModification.java | 9 +++++---- .../modification/utils/ModificationUtils.java | 10 +++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/modifications/GeneratorModification.java b/src/main/java/org/gridsuite/modification/modifications/GeneratorModification.java index 28cac6e2..ad945354 100644 --- a/src/main/java/org/gridsuite/modification/modifications/GeneratorModification.java +++ b/src/main/java/org/gridsuite/modification/modifications/GeneratorModification.java @@ -82,16 +82,17 @@ private void checkPowerValues(String errorMessage, Generator generator) { double minP = modificationInfos.getMinP() != null ? modificationInfos.getMinP().getValue() : generator.getMinP(); double maxP = modificationInfos.getMaxP() != null ? modificationInfos.getMaxP().getValue() : generator.getMaxP(); double targetP = modificationInfos.getTargetP() != null ? modificationInfos.getTargetP().getValue() : generator.getTargetP(); - Double pImp = modificationInfos.getPlannedActivePowerSetPoint() != null ? modificationInfos.getPlannedActivePowerSetPoint().applyModification(oldValue) : null; + Double plannedActivePowerSetPoint = modificationInfos.getPlannedActivePowerSetPoint() != null ? + modificationInfos.getPlannedActivePowerSetPoint().applyModification(oldValue) : null; if (modificationInfos.getPlannedActivePowerSetPoint() != null) { - checkActivePowerValue(errorMessage, FIELD_PLANNED_ACTIVE_POWER_SET_POINT, pImp, minP, maxP, MODIFY_GENERATOR_ERROR); + checkActivePowerValue(errorMessage, FIELD_PLANNED_ACTIVE_POWER_SET_POINT, plannedActivePowerSetPoint, minP, maxP, MODIFY_GENERATOR_ERROR); } if (modificationInfos.getMaxP() != null) { - checkMaximumActivePower(errorMessage, minP, targetP, pImp, maxP, MODIFY_GENERATOR_ERROR); + checkMaximumActivePower(errorMessage, minP, targetP, plannedActivePowerSetPoint, maxP, MODIFY_GENERATOR_ERROR); } if (modificationInfos.getMinP() != null) { - checkMinimumActivePower(errorMessage, maxP, targetP, pImp, minP, MODIFY_GENERATOR_ERROR); + checkMinimumActivePower(errorMessage, maxP, targetP, plannedActivePowerSetPoint, minP, MODIFY_GENERATOR_ERROR); } } diff --git a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java index 4f654c3e..0c2bf369 100644 --- a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java +++ b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java @@ -1933,13 +1933,13 @@ public static void checkActivePowerValue(String errorMessage, String fieldName, } } - public static void checkPowerValues(String errorMessage, double minP, double maxP, double targetP, Double pImp, NetworkModificationException.Type exceptionType) throws NetworkModificationException { + public static void checkPowerValues(String errorMessage, double minP, double maxP, double targetP, Double plannedActivePowerSetPoint, NetworkModificationException.Type exceptionType) throws NetworkModificationException { checkActivePowerValue(errorMessage, FIELD_ACTIVE_POWER_TARGET, targetP, minP, maxP, exceptionType); - if (pImp != null) { - checkActivePowerValue(errorMessage, FIELD_PLANNED_ACTIVE_POWER_SET_POINT, pImp, minP, maxP, exceptionType); + if (plannedActivePowerSetPoint != null) { + checkActivePowerValue(errorMessage, FIELD_PLANNED_ACTIVE_POWER_SET_POINT, plannedActivePowerSetPoint, minP, maxP, exceptionType); } - checkMinimumActivePower(errorMessage, maxP, targetP, pImp, minP, exceptionType); - checkMaximumActivePower(errorMessage, minP, targetP, pImp, maxP, exceptionType); + checkMinimumActivePower(errorMessage, maxP, targetP, plannedActivePowerSetPoint, minP, exceptionType); + checkMaximumActivePower(errorMessage, minP, targetP, plannedActivePowerSetPoint, maxP, exceptionType); } public static void checkMinimumActivePower(String errorMessage, double maxP, double targetP, Double pImp, double newValue, NetworkModificationException.Type exceptionType) throws NetworkModificationException { From 15b4a302dab7f1dd925bba5ae23d9f4c0e2f4377 Mon Sep 17 00:00:00 2001 From: basseche Date: Tue, 10 Feb 2026 14:26:07 +0100 Subject: [PATCH 10/11] PCons can be equal to 0 --- .../org/gridsuite/modification/utils/ModificationUtils.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java index 0c2bf369..5fa8d590 100644 --- a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java +++ b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java @@ -1934,7 +1934,9 @@ public static void checkActivePowerValue(String errorMessage, String fieldName, } public static void checkPowerValues(String errorMessage, double minP, double maxP, double targetP, Double plannedActivePowerSetPoint, NetworkModificationException.Type exceptionType) throws NetworkModificationException { - checkActivePowerValue(errorMessage, FIELD_ACTIVE_POWER_TARGET, targetP, minP, maxP, exceptionType); + if (targetP != 0) { // exception for the rule minP <= targetP <= maxP + checkActivePowerValue(errorMessage, FIELD_ACTIVE_POWER_TARGET, targetP, minP, maxP, exceptionType); + } if (plannedActivePowerSetPoint != null) { checkActivePowerValue(errorMessage, FIELD_PLANNED_ACTIVE_POWER_SET_POINT, plannedActivePowerSetPoint, minP, maxP, exceptionType); } From 1590701348d815bd723d705c4b10559d06baec48 Mon Sep 17 00:00:00 2001 From: basseche Date: Tue, 10 Feb 2026 15:32:19 +0100 Subject: [PATCH 11/11] PCons can be equal to 0 Fix --- .../AbstractModificationByAssignment.java | 5 +++- .../modification/utils/ModificationUtils.java | 24 +++++++++++-------- .../gridsuite/modification/reports.properties | 4 ++-- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/gridsuite/modification/modifications/byfilter/AbstractModificationByAssignment.java b/src/main/java/org/gridsuite/modification/modifications/byfilter/AbstractModificationByAssignment.java index 7dd1932b..6eb6e6fa 100644 --- a/src/main/java/org/gridsuite/modification/modifications/byfilter/AbstractModificationByAssignment.java +++ b/src/main/java/org/gridsuite/modification/modifications/byfilter/AbstractModificationByAssignment.java @@ -119,7 +119,10 @@ protected boolean checkGeneratorsPowerValues(Identifiable equipment, Abstract } else if (abstractAssignmentInfos.getEditedField().equals(MAXIMUM_ACTIVE_POWER.name())) { return validateMaximumActivePower(generator, reports, Double.parseDouble(getNewValue(equipment, abstractAssignmentInfos))); } else if (abstractAssignmentInfos.getEditedField().equals(ACTIVE_POWER_SET_POINT.name())) { - return validateActivePowerValue(generator, FIELD_ACTIVE_POWER_TARGET, reports, Double.parseDouble(getNewValue(equipment, abstractAssignmentInfos))); + double newValue = Double.parseDouble(getNewValue(equipment, abstractAssignmentInfos)); + if (newValue != 0) { // 0 is an exception to the rule + return validateActivePowerValue(generator, FIELD_ACTIVE_POWER_TARGET, reports, newValue); + } } } return true; diff --git a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java index 5fa8d590..0d49e487 100644 --- a/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java +++ b/src/main/java/org/gridsuite/modification/utils/ModificationUtils.java @@ -1945,7 +1945,8 @@ public static void checkPowerValues(String errorMessage, double minP, double max } public static void checkMinimumActivePower(String errorMessage, double maxP, double targetP, Double pImp, double newValue, NetworkModificationException.Type exceptionType) throws NetworkModificationException { - double pMin = Math.min(targetP, maxP); + // targetP = 0 is an exception to the rule + double pMin = targetP != 0 ? Math.min(targetP, maxP) : maxP; if (pImp != null) { pMin = Math.min(pMin, pImp); } @@ -1954,10 +1955,11 @@ public static void checkMinimumActivePower(String errorMessage, double maxP, dou } } - public static void checkMaximumActivePower(String errorMessage, double minP, double targetP, Double pImp, double newValue, NetworkModificationException.Type exceptionType) throws NetworkModificationException { - double pMax = Math.max(targetP, minP); - if (pImp != null) { - pMax = Math.max(pMax, pImp); + public static void checkMaximumActivePower(String errorMessage, double minP, double targetP, Double plannedActivePowerSetPoint, double newValue, NetworkModificationException.Type exceptionType) throws NetworkModificationException { + // targetP = 0 is an exception to the rule + double pMax = targetP != 0 ? Math.max(targetP, minP) : minP; + if (plannedActivePowerSetPoint != null) { + pMax = Math.max(pMax, plannedActivePowerSetPoint); } if (pMax > newValue) { throw new NetworkModificationException(exceptionType, errorMessage + String.format("Invalid value %.2f of field %s should be be greater or equal to %.2f", newValue, FIELD_MAX_ACTIVE_POWER, pMax)); @@ -1966,11 +1968,12 @@ public static void checkMaximumActivePower(String errorMessage, double minP, dou public static boolean validateMinimumActivePower(Generator generator, List reports, double newValue) { GeneratorStartup generatorStartup = generator.getExtension(GeneratorStartup.class); - Double pImp = generatorStartup != null && !Double.isNaN(generatorStartup.getPlannedActivePowerSetpoint()) ? generatorStartup.getPlannedActivePowerSetpoint() : null; + Double plannedActivePowerSetPoint = generatorStartup != null && !Double.isNaN(generatorStartup.getPlannedActivePowerSetpoint()) ? generatorStartup.getPlannedActivePowerSetpoint() : null; - double minP = Math.min(generator.getTargetP(), generator.getMaxP()); - if (pImp != null) { - minP = Math.min(minP, pImp); + // targetP = 0 is an exception to the rule + double minP = generator.getTargetP() != 0 ? Math.min(generator.getTargetP(), generator.getMaxP()) : generator.getMaxP(); + if (plannedActivePowerSetPoint != null) { + minP = Math.min(minP, plannedActivePowerSetPoint); } if (minP < newValue) { @@ -1991,7 +1994,8 @@ public static boolean validateMaximumActivePower(Generator generator, List