Repository: powsybl/powsybl-core
Pull request #3273: IIDM ValidationLevel: Add a silent option.
Author: login: marqueslanauja
Labels: name: Breaking Change
description: API is broken
name: Release 06/2025
name: 1EQ 24SSH
Created: 2025-01-14T11:43:54Z | Merged: 2025-05-19T08:55:37Z

**Please check if the PR fulfills these requirements**
<!-- please use `'[x]'` to check the checkboxes, or submit the PR and then click the checkboxes -->
- [x] The commit message follows our guidelines
- [x] Tests for the changes have been added (for bug fixes / features)
- [ ] Docs have been added / updated (for bug fixes / features)


**Does this PR already have an issue describing the problem?**
<!-- If so, link to this issue using `'Fixes #XXX'` and skip the rest -->



**What kind of change does this PR introduce?**
<!-- Bug fix, feature, docs update, ... -->
Feature
Closes #3185.

**What is the current behavior?**
<!-- You can also link to an open issue here -->
When only an `EQ` file is imported after setting the validation level to `EQUIPMENT`, the validation process generates warnings indicating that some equipment attributes are not defined. These warnings are unnecessary, as we know that only the equipment attributes are imported.


**What is the new behavior (if this is a feature change)?**

In the validation process, a new option (IGNORE) has been added. The validation level warnings are only generated when the `runValidationChecks` is executed. With this PR, it will be possible to import the `EQ` and `SSH` files separately without generating unnecessary warnings.


**Does this PR introduce a breaking change or deprecate an API?**
- [x] Yes
- [ ] No

**If yes, please check if the following requirements are fulfilled**
<!-- If no breaking changes or API deprecations were introduced, delete this section -->
- [X] The *Breaking Change* or *Deprecated* label has been added
- [X] The migration steps are described in the following section

**What changes might users need to make in their application due to this PR? (migration steps)**
<!-- If this PR introduces a breaking change, describe the migration steps -->
<!-- The content of this section will be copied in the migration guide -->

The `boolean throwException` parameter of `ValidationUtil.validate(...)` was replaced by an enum value.
If you used this method, you can use one of the following values:
- `ValidationUtil.ActionOnError.THROW_EXCEPTION`: throw an exception if an error is detected - corresponds to older `throwException` = `true`);
- `ValidationUtil.ActionOnError.LOG_ERROR`: report all the errors in the given reportNode and on the console - corresponds to older `throwException` = `false`);
- `ValidationUtil.ActionOnError.IGNORE`: ignore all the errors (no exception, no logs and no reports)


**Other information**:
<!-- if any of the questions/checkboxes don't apply, please delete them entirely -->


Linked issue #3185: Network validation: too many reported errors
### Describe the current behavior

All validation checks are performed always, independent of the minimum validation level established in the network.
Even if we have set that we accept the network to be in `EQUIPMENT` validation level, any change will emit logs and report errors related to the verification of steady state attributes.

This can be easily reproduced with the following code:

```java
Network network = Network.create("test", "iidm");
VoltageLevel vl = network.newSubstation().setId("s1").add()
        .newVoltageLevel().setId("vl1").setNominalV(100).setTopologyKind(TopologyKind.NODE_BREAKER).add();
network.setMinimumAcceptableValidationLevel(ValidationLevel.EQUIPMENT);
vl.newLoad().setId("l1").setNode(0).add();
```

The logger contents will show the following errors:

```
ERROR c.p.i.n.ValidationUtil | Load 'l1': p0 is invalid
ERROR c.p.i.n.ValidationUtil | Load 'l1': q0 is invalid
```

### Describe the expected behavior

We expect to obtain errors based in the current minimum accepted validation level. 

If we want to keep the validations for levels higher than the minimum accepted (to compute the actual validation level of the network no matter what is the minimum set), at least we should be able to configure that we do not want to emit logs/report errors for the "higher" level checks.

Currently validation checks can be called with a boolean flag that controls the behaviour of error processing: either raise an exception or simply log/report the error. This flag could be changed to an enum with a third option to run the checks _silently_ (no exception, no log, no report), just to update the validation level of the network.

### Describe the motivation

This suggestion is made in the context of CGMES import refactoring to split the conversion of networks in two stages:

1. Building the network structure from equipment (EQ) files that contain only connectivity and electric characteristics of the devices.
2. Apply information from a steady state hypothesis (SSH) file.

During the first stage we do not want errors to be emitted for missing SSH data.

### Extra Information

When we request a validation report, we miss the identification of the related equipment. In the logger, we have messages  like `Load 'l1': p0 is invalid`. In the output from reports we get only the attribute with error, but not the equipment identifier.

To check it, run the following code:

```java
Network network = Network.create("test", "iidm");
VoltageLevel vl = network.newSubstation().setId("s1").add()
        .newVoltageLevel().setId("vl1").setNominalV(100).setTopologyKind(TopologyKind.NODE_BREAKER).add();
network.setMinimumAcceptableValidationLevel(ValidationLevel.EQUIPMENT);
vl.newLoad().setId("l1").setNode(0).add();

ReportNode reportNode = ReportNode.newRootReportNode().withMessageTemplate("root", "Report").build();
network.runValidationChecks(false, reportNode);

StringWriter sw = new StringWriter();
reportNode.print(sw);
System.out.println(sw.toString());
```
It will produce this output:
```
+ Report
   + Running validation checks on IIDM network test
      p0 is invalid
      p0 is invalid
```


Diff:
diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java
index c578d297ab3..8ed63a0cae5 100644
--- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java
+++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java
@@ -23,6 +23,12 @@
  */
 public final class ValidationUtil {
 
+    public enum ActionOnError {
+        THROW_EXCEPTION,
+        LOG_ERROR,
+        IGNORE,
+    }
+
     private static final Logger LOGGER = LoggerFactory.getLogger(ValidationUtil.class);
 
     private static final String ACTIVE_POWER_SETPOINT = "active power setpoint";
@@ -63,47 +69,61 @@ private static void logError(Validable validable, String message, ReportNode rep
         LOGGER.error("{}{}", validable.getMessageHeader(), message);
     }
 
-    private static void throwExceptionOrLogError(Validable validable, String message, boolean throwException, ReportNode reportNode) {
-        if (throwException) {
+    private static void throwExceptionOrLogError(Validable validable, String message, ActionOnError actionOnError, ReportNode reportNode) {
+        if (actionOnError == ActionOnError.THROW_EXCEPTION) {
+            throw new ValidationException(validable, message);
+        }
+        if (actionOnError == ActionOnError.LOG_ERROR) {
+            logError(validable, message, reportNode);
+        }
+    }
+
+    private static void throwExceptionOrIgnore(Validable validable, String message, ActionOnError actionOnError) {
+        if (actionOnError == ActionOnError.THROW_EXCEPTION) {
             throw new ValidationException(validable, message);
         }
-        logError(validable, message, reportNode);
+    }
+
+    public static void throwExceptionOrIgnore(Validable validable, String message, ValidationLevel validationLevel) {
+        throwExceptionOrIgnore(validable, message, checkValidationActionOnError(validationLevel));
     }
 
     public static void throwExceptionOrLogError(Validable validable, String message, ValidationLevel validationLevel, ReportNode reportNode) {
-        throwExceptionOrLogError(validable, message, validationLevel == ValidationLevel.STEADY_STATE_HYPOTHESIS, reportNode);
+        throwExceptionOrLogError(validable, message, validationLevel == ValidationLevel.STEADY_STATE_HYPOTHESIS ? ActionOnError.THROW_EXCEPTION : ActionOnError.LOG_ERROR, reportNode);
     }
 
-    private static void throwExceptionOrLogErrorForInvalidValue(Validable validable, double value, String valueName, boolean throwException, ReportNode reportNode) {
-        throwExceptionOrLogErrorForInvalidValue(validable, value, valueName, null, throwException, reportNode);
+    private static void throwExceptionOrLogErrorForInvalidValue(Validable validable, double value, String valueName, ActionOnError actionOnError, ReportNode reportNode) {
+        throwExceptionOrLogErrorForInvalidValue(validable, value, valueName, null, actionOnError, reportNode);
     }
 
-    private static void throwExceptionOrLogErrorForInvalidValue(Validable validable, double value, String valueName, String reason, boolean throwException, ReportNode reportNode) {
-        if (throwException) {
+    private static void throwExceptionOrLogErrorForInvalidValue(Validable validable, double value, String valueName, String reason, ActionOnError actionOnError, ReportNode reportNode) {
+        if (actionOnError == ActionOnError.THROW_EXCEPTION) {
             throw createInvalidValueException(validable, value, valueName, reason);
         }
-        logError(validable, createInvalidValueMessage(value, valueName, reason), reportNode);
+        if (actionOnError == ActionOnError.LOG_ERROR) {
+            logError(validable, createInvalidValueMessage(value, valueName, reason), reportNode);
+        }
     }
 
     public static ValidationLevel checkActivePowerSetpoint(Validable validable, double activePowerSetpoint, ValidationLevel validationLevel, ReportNode reportNode) {
-        return checkActivePowerSetpoint(validable, activePowerSetpoint, validationLevel == ValidationLevel.STEADY_STATE_HYPOTHESIS, reportNode);
+        return checkActivePowerSetpoint(validable, activePowerSetpoint, checkValidationActionOnError(validationLevel), reportNode);
     }
 
-    private static ValidationLevel checkActivePowerSetpoint(Validable validable, double activePowerSetpoint, boolean throwException, ReportNode reportNode) {
+    private static ValidationLevel checkActivePowerSetpoint(Validable validable, double activePowerSetpoint, ActionOnError actionOnError, ReportNode reportNode) {
         if (Double.isNaN(activePowerSetpoint)) {
-            throwExceptionOrLogErrorForInvalidValue(validable, activePowerSetpoint, ACTIVE_POWER_SETPOINT, throwException, reportNode);
+            throwExceptionOrLogErrorForInvalidValue(validable, activePowerSetpoint, ACTIVE_POWER_SETPOINT, actionOnError, reportNode);
             return ValidationLevel.EQUIPMENT;
         }
         return ValidationLevel.STEADY_STATE_HYPOTHESIS;
     }
 
     public static ValidationLevel checkHvdcActivePowerSetpoint(Validable validable, double activePowerSetpoint, ValidationLevel validationLevel, ReportNode reportNode) {
-        return checkHvdcActivePowerSetpoint(validable, activePowerSetpoint, checkValidationLevel(validationLevel), reportNode);
+        return checkHvdcActivePowerSetpoint(validable, activePowerSetpoint, checkValidationActionOnError(validationLevel), reportNode);
     }
 
-    private static ValidationLevel checkHvdcActivePowerSetpoint(Validable validable, double activePowerSetpoint, boolean throwException, ReportNode reportNode) {
+    private static ValidationLevel checkHvdcActivePowerSetpoint(Validable validable, double activePowerSetpoint, ActionOnError actionOnError, ReportNode reportNode) {
         if (Double.isNaN(activePowerSetpoint)) {
-            throwExceptionOrLogErrorForInvalidValue(validable, activePowerSetpoint, ACTIVE_POWER_SETPOINT, throwException, reportNode);
+            throwExceptionOrLogErrorForInvalidValue(validable, activePowerSetpoint, ACTIVE_POWER_SETPOINT, actionOnError, reportNode);
             return ValidationLevel.EQUIPMENT;
         } else if (activePowerSetpoint < 0) {
             throw createInvalidValueException(validable, activePowerSetpoint, ACTIVE_POWER_SETPOINT, "active power setpoint should not be negative");
@@ -118,12 +138,12 @@ public static void checkActivePowerLimits(Validable validable, double minP, doub
     }
 
     public static ValidationLevel checkTargetDeadband(Validable validable, String validableType, boolean regulating, double targetDeadband, ValidationLevel validationLevel, ReportNode reportNode) {
-        return checkTargetDeadband(validable, validableType, regulating, targetDeadband, checkValidationLevel(validationLevel), reportNode);
+        return checkTargetDeadband(validable, validableType, regulating, targetDeadband, checkValidationActionOnError(validationLevel), reportNode);
     }
 
-    private static ValidationLevel checkTargetDeadband(Validable validable, String validableType, boolean regulating, double targetDeadband, boolean throwsException, ReportNode reportNode) {
+    private static ValidationLevel checkTargetDeadband(Validable validable, String validableType, boolean regulating, double targetDeadband, ActionOnError actionOnError, ReportNode reportNode) {
         if (regulating && Double.isNaN(targetDeadband)) {
-            throwExceptionOrLogError(validable, "Undefined value for target deadband of regulating " + validableType, throwsException, reportNode);
+            throwExceptionOrLogError(validable, "Undefined value for target deadband of regulating " + validableType, actionOnError, reportNode);
             return ValidationLevel.EQUIPMENT;
         }
         if (targetDeadband < 0) {
@@ -133,13 +153,13 @@ private static ValidationLevel checkTargetDeadband(Validable validable, String v
     }
 
     public static ValidationLevel checkVoltageControl(Validable validable, boolean voltageRegulatorOn, double voltageSetpoint, ValidationLevel validationLevel, ReportNode reportNode) {
-        return checkVoltageControl(validable, voltageRegulatorOn, voltageSetpoint, checkValidationLevel(validationLevel), reportNode);
+        return checkVoltageControl(validable, voltageRegulatorOn, voltageSetpoint, checkValidationActionOnError(validationLevel), reportNode);
     }
 
-    private static ValidationLevel checkVoltageControl(Validable validable, boolean voltageRegulatorOn, double voltageSetpoint, boolean throwException, ReportNode reportNode) {
+    private static ValidationLevel checkVoltageControl(Validable validable, boolean voltageRegulatorOn, double voltageSetpoint, ActionOnError actionOnError, ReportNode reportNode) {
         if (voltageRegulatorOn) {
             if (Double.isNaN(voltageSetpoint)) {
-                throwExceptionOrLogErrorForInvalidValue(validable, voltageSetpoint, VOLTAGE_SETPOINT, VOLTAGE_REGULATOR_ON, throwException, reportNode);
+                throwExceptionOrLogErrorForInvalidValue(validable, voltageSetpoint, VOLTAGE_SETPOINT, VOLTAGE_REGULATOR_ON, actionOnError, reportNode);
                 return ValidationLevel.EQUIPMENT;
             }
             if (voltageSetpoint <= 0) {
@@ -150,23 +170,23 @@ private static ValidationLevel checkVoltageControl(Validable validable, boolean
     }
 
     public static ValidationLevel checkVoltageControl(Validable validable, Boolean voltageRegulatorOn, double voltageSetpoint, double reactivePowerSetpoint, ValidationLevel validationLevel, ReportNode reportNode) {
-        return checkVoltageControl(validable, voltageRegulatorOn, voltageSetpoint, reactivePowerSetpoint, checkValidationLevel(validationLevel), reportNode);
+        return checkVoltageControl(validable, voltageRegulatorOn, voltageSetpoint, reactivePowerSetpoint, checkValidationActionOnError(validationLevel), reportNode);
     }
 
-    private static ValidationLevel checkVoltageControl(Validable validable, Boolean voltageRegulatorOn, double voltageSetpoint, double reactivePowerSetpoint, boolean throwException, ReportNode reportNode) {
+    private static ValidationLevel checkVoltageControl(Validable validable, Boolean voltageRegulatorOn, double voltageSetpoint, double reactivePowerSetpoint, ActionOnError actionOnError, ReportNode reportNode) {
         if (voltageRegulatorOn == null) {
             throw new ValidationException(validable, "voltage regulator status is not set");
         }
         if (voltageRegulatorOn) {
             if (Double.isNaN(voltageSetpoint)) {
-                throwExceptionOrLogErrorForInvalidValue(validable, voltageSetpoint, VOLTAGE_SETPOINT, VOLTAGE_REGULATOR_ON, throwException, reportNode);
+                throwExceptionOrLogErrorForInvalidValue(validable, voltageSetpoint, VOLTAGE_SETPOINT, VOLTAGE_REGULATOR_ON, actionOnError, reportNode);
                 return ValidationLevel.EQUIPMENT;
             }
             if (voltageSetpoint <= 0) {
                 throw createInvalidValueException(validable, voltageSetpoint, VOLTAGE_SETPOINT, VOLTAGE_REGULATOR_ON);
             }
         } else if (Double.isNaN(reactivePowerSetpoint)) {
-            throwExceptionOrLogErrorForInvalidValue(validable, reactivePowerSetpoint, "reactive power setpoint", "voltage regulator is off", throwException, reportNode);
+            throwExceptionOrLogErrorForInvalidValue(validable, reactivePowerSetpoint, "reactive power setpoint", "voltage regulator is off", actionOnError, reportNode);
             return ValidationLevel.EQUIPMENT;
         }
         return ValidationLevel.STEADY_STATE_HYPOTHESIS;
@@ -217,24 +237,24 @@ public static void checkLoadType(Validable validable, LoadType loadType) {
     }
 
     public static ValidationLevel checkP0(Validable validable, double p0, ValidationLevel validationLevel, ReportNode reportNode) {
-        return checkP0(validable, p0, checkValidationLevel(validationLevel), reportNode);
+        return checkP0(validable, p0, checkValidationActionOnError(validationLevel), reportNode);
     }
 
-    private static ValidationLevel checkP0(Validable validable, double p0, boolean throwException, ReportNode reportNode) {
+    private static ValidationLevel checkP0(Validable validable, double p0, ActionOnError actionOnError, ReportNode reportNode) {
         if (Double.isNaN(p0)) {
-            throwExceptionOrLogError(validable, "p0 is invalid", throwException, reportNode);
+            throwExceptionOrLogError(validable, "p0 is invalid", actionOnError, reportNode);
             return ValidationLevel.EQUIPMENT;
         }
         return ValidationLevel.STEADY_STATE_HYPOTHESIS;
     }
 
     public static ValidationLevel checkQ0(Validable validable, double q0, ValidationLevel validationLevel, ReportNode reportNode) {
-        return checkQ0(validable, q0, validationLevel == ValidationLevel.STEADY_STATE_HYPOTHESIS, reportNode);
+        return checkQ0(validable, q0, checkValidationActionOnError(validationLevel), reportNode);
     }
 
-    private static ValidationLevel checkQ0(Validable validable, double q0, boolean throwException, ReportNode reportNode) {
+    private static ValidationLevel checkQ0(Validable validable, double q0, ActionOnError actionOnError, ReportNode reportNode) {
         if (Double.isNaN(q0)) {
-            throwExceptionOrLogError(validable, "q0 is invalid", throwException, reportNode);
+            throwExceptionOrLogError(validable, "q0 is invalid", actionOnError, reportNode);
             return ValidationLevel.EQUIPMENT;
         }
         return ValidationLevel.STEADY_STATE_HYPOTHESIS;
@@ -339,13 +359,13 @@ public static void checkMaximumSectionCount(Validable validable, int maximumSect
     }
 
     public static ValidationLevel checkSections(Validable validable, Integer currentSectionCount, int maximumSectionCount, ValidationLevel validationLevel, ReportNode reportNode) {
-        return checkSections(validable, currentSectionCount, maximumSectionCount, checkValidationLevel(validationLevel), reportNode);
+        return checkSections(validable, currentSectionCount, maximumSectionCount, checkValidationActionOnError(validationLevel), reportNode);
     }
 
-    private static ValidationLevel checkSections(Validable validable, Integer currentSectionCount, int maximumSectionCount, boolean throwException, ReportNode reportNode) {
+    private static ValidationLevel checkSections(Validable validable, Integer currentSectionCount, int maximumSectionCount, ActionOnError actionOnError, ReportNode reportNode) {
         checkMaximumSectionCount(validable, maximumSectionCount);
         if (currentSectionCount == null) {
-            throwExceptionOrLogError(validable, "the current number of section is undefined", throwException, reportNode);
+            throwExceptionOrLogError(validable, "the current number of section is undefined", actionOnError, reportNode);
             return ValidationLevel.EQUIPMENT;
         } else {
             if (currentSectionCount < 0) {
@@ -377,25 +397,25 @@ public static void checkRatedU2(Validable validable, double ratedU2) {
 
     public static ValidationLevel checkSvcRegulator(Validable validable, double voltageSetpoint, double reactivePowerSetpoint,
                                                     StaticVarCompensator.RegulationMode regulationMode, ValidationLevel validationLevel, ReportNode reportNode) {
-        return checkSvcRegulator(validable, voltageSetpoint, reactivePowerSetpoint, regulationMode, checkValidationLevel(validationLevel), reportNode);
+        return checkSvcRegulator(validable, voltageSetpoint, reactivePowerSetpoint, regulationMode, checkValidationActionOnError(validationLevel), reportNode);
     }
 
     private static ValidationLevel checkSvcRegulator(Validable validable, double voltageSetpoint, double reactivePowerSetpoint,
-                                                    StaticVarCompensator.RegulationMode regulationMode, boolean throwException, ReportNode reportNode) {
+                                                     StaticVarCompensator.RegulationMode regulationMode, ActionOnError actionOnError, ReportNode reportNode) {
         if (regulationMode == null) {
-            throwExceptionOrLogError(validable, "Regulation mode is invalid", throwException, reportNode);
+            throwExceptionOrLogError(validable, "Regulation mode is invalid", actionOnError, reportNode);
             return ValidationLevel.EQUIPMENT;
         }
         switch (regulationMode) {
             case VOLTAGE -> {
                 if (Double.isNaN(voltageSetpoint)) {
-                    throwExceptionOrLogErrorForInvalidValue(validable, voltageSetpoint, VOLTAGE_SETPOINT, throwException, reportNode);
+                    throwExceptionOrLogErrorForInvalidValue(validable, voltageSetpoint, VOLTAGE_SETPOINT, actionOnError, reportNode);
                     return ValidationLevel.EQUIPMENT;
                 }
             }
             case REACTIVE_POWER -> {
                 if (Double.isNaN(reactivePowerSetpoint)) {
-                    throwExceptionOrLogErrorForInvalidValue(validable, reactivePowerSetpoint, "reactive power setpoint", throwException, reportNode);
+                    throwExceptionOrLogErrorForInvalidValue(validable, reactivePowerSetpoint, "reactive power setpoint", actionOnError, reportNode);
                     return ValidationLevel.EQUIPMENT;
                 }
             }
@@ -418,9 +438,9 @@ public static void checkBmax(Validable validable, double bMax) {
         }
     }
 
-    private static ValidationLevel errorOrWarningForRtc(Validable validable, boolean loadTapChangingCapabilities, String message, boolean throwException, ReportNode reportNode) {
+    private static ValidationLevel errorOrWarningForRtc(Validable validable, boolean loadTapChangingCapabilities, String message, ActionOnError actionOnError, ReportNode reportNode) {
         if (loadTapChangingCapabilities) {
-            throwExceptionOrLogError(validable, message, throwException, reportNode);
+            throwExceptionOrLogError(validable, message, actionOnError, reportNode);
             return ValidationLevel.EQUIPMENT;
         }
         reportNode.newReportNode()
@@ -434,26 +454,26 @@ private static ValidationLevel errorOrWarningForRtc(Validable validable, boolean
     public static ValidationLevel checkRatioTapChangerRegulation(Validable validable, boolean regulating, boolean loadTapChangingCapabilities,
                                                                  Terminal regulationTerminal, RatioTapChanger.RegulationMode regulationMode,
                                                                  double regulationValue, Network network, ValidationLevel validationLevel, ReportNode reportNode) {
-        return checkRatioTapChangerRegulation(validable, regulating, loadTapChangingCapabilities, regulationTerminal, regulationMode, regulationValue, network, checkValidationLevel(validationLevel), reportNode);
+        return checkRatioTapChangerRegulation(validable, regulating, loadTapChangingCapabilities, regulationTerminal, regulationMode, regulationValue, network, checkValidationActionOnError(validationLevel), reportNode);
     }
 
     private static ValidationLevel checkRatioTapChangerRegulation(Validable validable, boolean regulating, boolean loadTapChangingCapabilities,
                                                                  Terminal regulationTerminal, RatioTapChanger.RegulationMode regulationMode,
-                                                                 double regulationValue, Network network, boolean throwException,
+                                                                 double regulationValue, Network network, ActionOnError actionOnError,
                                                                  ReportNode reportNode) {
         ValidationLevel validationLevel = ValidationLevel.STEADY_STATE_HYPOTHESIS;
         if (regulating) {
             if (Objects.isNull(regulationMode)) {
-                validationLevel = ValidationLevel.min(validationLevel, errorOrWarningForRtc(validable, loadTapChangingCapabilities, "regulation mode of regulating ratio tap changer must be given", throwException, reportNode));
+                validationLevel = ValidationLevel.min(validationLevel, errorOrWarningForRtc(validable, loadTapChangingCapabilities, "regulation mode of regulating ratio tap changer must be given", actionOnError, reportNode));
             }
             if (Double.isNaN(regulationValue)) {
-                validationLevel = ValidationLevel.min(validationLevel, errorOrWarningForRtc(validable, loadTapChangingCapabilities, "a regulation value has to be set for a regulating ratio tap changer", throwException, reportNode));
+                validationLevel = ValidationLevel.min(validationLevel, errorOrWarningForRtc(validable, loadTapChangingCapabilities, "a regulation value has to be set for a regulating ratio tap changer", actionOnError, reportNode));
             }
             if (regulationMode == RatioTapChanger.RegulationMode.VOLTAGE && regulationValue <= 0) {
                 throw new ValidationException(validable, "bad target voltage " + regulationValue);
             }
             if (regulationTerminal == null) {
-                validationLevel = ValidationLevel.min(validationLevel, errorOrWarningForRtc(validable, loadTapChangingCapabilities, "a regulation terminal has to be set for a regulating ratio tap changer", throwException, reportNode));
+                validationLevel = ValidationLevel.min(validationLevel, errorOrWarningForRtc(validable, loadTapChangingCapabilities, "a regulation terminal has to be set for a regulating ratio tap changer", actionOnError, reportNode));
             }
         }
         if (regulationTerminal != null && regulationTerminal.getVoltageLevel().getNetwork() != network) {
@@ -466,28 +486,28 @@ public static ValidationLevel checkPhaseTapChangerRegulation(Validable validable
                                                                  double regulationValue, boolean regulating, Terminal regulationTerminal,
                                                                  Network network, ValidationLevel validationLevel, ReportNode reportNode) {
         return checkPhaseTapChangerRegulation(validable, regulationMode, regulationValue, regulating, regulationTerminal,
-                network, checkValidationLevel(validationLevel), reportNode);
+                network, checkValidationActionOnError(validationLevel), reportNode);
     }
 
     private static ValidationLevel checkPhaseTapChangerRegulation(Validable validable, PhaseTapChanger.RegulationMode regulationMode,
                                                                   double regulationValue, boolean regulating, Terminal regulationTerminal,
-                                                                  Network network, boolean throwException, ReportNode reportNode) {
+                                                                  Network network, ActionOnError actionOnError, ReportNode reportNode) {
         ValidationLevel validationLevel = ValidationLevel.STEADY_STATE_HYPOTHESIS;
         if (regulationMode == null) {
-            throwExceptionOrLogError(validable, "phase regulation mode is not set", throwException, reportNode);
+            throwExceptionOrLogError(validable, "phase regulation mode is not set", actionOnError, reportNode);
             validationLevel = ValidationLevel.min(validationLevel, ValidationLevel.EQUIPMENT);
         }
         if (regulating && regulationMode != null) {
             if (regulationMode != PhaseTapChanger.RegulationMode.FIXED_TAP && Double.isNaN(regulationValue)) {
-                throwExceptionOrLogError(validable, "phase regulation is on and threshold/setpoint value is not set", throwException, reportNode);
+                throwExceptionOrLogError(validable, "phase regulation is on and threshold/setpoint value is not set", actionOnError, reportNode);
                 validationLevel = ValidationLevel.min(validationLevel, ValidationLevel.EQUIPMENT);
             }
             if (regulationMode != PhaseTapChanger.RegulationMode.FIXED_TAP && regulationTerminal == null) {
-                throwExceptionOrLogError(validable, "phase regulation is on and regulated terminal is not set", throwException, reportNode);
+                throwExceptionOrLogError(validable, "phase regulation is on and regulated terminal is not set", actionOnError, reportNode);
                 validationLevel = ValidationLevel.min(validationLevel, ValidationLevel.EQUIPMENT);
             }
             if (regulationMode == PhaseTapChanger.RegulationMode.FIXED_TAP) {
-                throwExceptionOrLogError(validable, "phase regulation cannot be on if mode is FIXED", throwException, reportNode);
+                throwExceptionOrLogError(validable, "phase regulation cannot be on if mode is FIXED", actionOnError, reportNode);
                 validationLevel = ValidationLevel.min(validationLevel, ValidationLevel.EQUIPMENT);
             }
         }
@@ -499,13 +519,13 @@ private static ValidationLevel checkPhaseTapChangerRegulation(Validable validabl
 
     public static ValidationLevel checkOnlyOneTapChangerRegulatingEnabled(Validable validable, Set<TapChanger<?, ?, ?, ?>> tapChangersNotIncludingTheModified,
                                                                            boolean regulating, ValidationLevel validationLevel, ReportNode reportNode) {
-        return checkOnlyOneTapChangerRegulatingEnabled(validable, tapChangersNotIncludingTheModified, regulating, checkValidationLevel(validationLevel), reportNode);
+        return checkOnlyOneTapChangerRegulatingEnabled(validable, tapChangersNotIncludingTheModified, regulating, checkValidationActionOnError(validationLevel), reportNode);
     }
 
     private static ValidationLevel checkOnlyOneTapChangerRegulatingEnabled(Validable validable, Set<TapChanger<?, ?, ?, ?>> tapChangersNotIncludingTheModified,
-                                                                          boolean regulating, boolean throwException, ReportNode reportNode) {
+                                                                           boolean regulating, ActionOnError actionOnError, ReportNode reportNode) {
         if (regulating && tapChangersNotIncludingTheModified.stream().anyMatch(TapChanger::isRegulating)) {
-            throwExceptionOrLogError(validable, UNIQUE_REGULATING_TAP_CHANGER_MSG, throwException, reportNode);
+            throwExceptionOrLogError(validable, UNIQUE_REGULATING_TAP_CHANGER_MSG, actionOnError, reportNode);
             return ValidationLevel.EQUIPMENT;
         }
         return ValidationLevel.STEADY_STATE_HYPOTHESIS;
@@ -513,13 +533,13 @@ private static ValidationLevel checkOnlyOneTapChangerRegulatingEnabled(Validable
 
     public static ValidationLevel checkConvertersMode(Validable validable, HvdcLine.ConvertersMode converterMode,
                                                       ValidationLevel validationLevel, ReportNode reportNode) {
-        return checkConvertersMode(validable, converterMode, checkValidationLevel(validationLevel), reportNode);
+        return checkConvertersMode(validable, converterMode, checkValidationActionOnError(validationLevel), reportNode);
     }
 
     private static ValidationLevel checkConvertersMode(Validable validable, HvdcLine.ConvertersMode converterMode,
-                                                      boolean throwException, ReportNode reportNode) {
+                                                       ActionOnError actionOnError, ReportNode reportNode) {
         if (converterMode == null) {
-            throwExceptionOrLogError(validable, "converter mode is invalid", throwException, reportNode);
+            throwExceptionOrLogError(validable, "converter mode is invalid", actionOnError, reportNode);
             return ValidationLevel.EQUIPMENT;
         }
         return ValidationLevel.STEADY_STATE_HYPOTHESIS;
@@ -535,26 +555,26 @@ public static void checkPowerFactor(Validable validable, double powerFactor) {
 
     public static ValidationLevel checkLoadingLimits(Validable validable, double permanentLimit, Collection<LoadingLimits.TemporaryLimit> temporaryLimits,
                                                      ValidationLevel validationLevel, ReportNode reportNode) {
-        return checkLoadingLimits(validable, permanentLimit, temporaryLimits, checkValidationLevel(validationLevel), reportNode);
+        return checkLoadingLimits(validable, permanentLimit, temporaryLimits, checkValidationActionOnError(validationLevel), reportNode);
     }
 
     private static ValidationLevel checkLoadingLimits(Validable validable, double permanentLimit, Collection<LoadingLimits.TemporaryLimit> temporaryLimits,
-                                                     boolean throwException, ReportNode reportNode) {
-        ValidationLevel validationLevel = ValidationUtil.checkPermanentLimit(validable, permanentLimit, temporaryLimits, throwException, reportNode);
+                                                      ActionOnError actionOnError, ReportNode reportNode) {
+        ValidationLevel validationLevel = ValidationUtil.checkPermanentLimit(validable, permanentLimit, temporaryLimits, actionOnError, reportNode);
         ValidationUtil.checkTemporaryLimits(validable, permanentLimit, temporaryLimits);
         return validationLevel;
     }
 
     public static ValidationLevel checkPermanentLimit(Validable validable, double permanentLimit, Collection<LoadingLimits.TemporaryLimit> temporaryLimits,
                                                       ValidationLevel validationLevel, ReportNode reportNode) {
-        return checkPermanentLimit(validable, permanentLimit, temporaryLimits, checkValidationLevel(validationLevel), reportNode);
+        return checkPermanentLimit(validable, permanentLimit, temporaryLimits, checkValidationActionOnError(validationLevel), reportNode);
     }
 
     private static ValidationLevel checkPermanentLimit(Validable validable, double permanentLimit, Collection<LoadingLimits.TemporaryLimit> temporaryLimits,
-                                                      boolean throwException, ReportNode reportNode) {
+                                                       ActionOnError actionOnError, ReportNode reportNode) {
         ValidationLevel validationLevel = ValidationLevel.STEADY_STATE_HYPOTHESIS;
         if (Double.isNaN(permanentLimit) && !temporaryLimits.isEmpty()) {
-            throwExceptionOrLogError(validable, "permanent limit must be defined if temporary limits are present", throwException, reportNode);
+            throwExceptionOrLogError(validable, "permanent limit must be defined if temporary limits are present", actionOnError, reportNode);
             validationLevel = ValidationLevel.min(validationLevel, ValidationLevel.EQUIPMENT);
         }
         if (permanentLimit < 0) {
@@ -588,13 +608,13 @@ private static void checkTemporaryLimits(Validable validable, double permanentLi
     }
 
     public static ValidationLevel checkLossFactor(Validable validable, float lossFactor, ValidationLevel validationLevel, ReportNode reportNode) {
-        return checkLossFactor(validable, lossFactor, checkValidationLevel(validationLevel), reportNode);
+        return checkLossFactor(validable, lossFactor, checkValidationActionOnError(validationLevel), reportNode);
     }
 
-    private static ValidationLevel checkLossFactor(Validable validable, float lossFactor, boolean throwException, ReportNode reportNode) {
+    private static ValidationLevel checkLossFactor(Validable validable, float lossFactor, ActionOnError actionOnError, ReportNode reportNode) {
         ValidationLevel validationLevel = ValidationLevel.STEADY_STATE_HYPOTHESIS;
         if (Double.isNaN(lossFactor)) {
-            throwExceptionOrLogError(validable, "loss factor is invalid is undefined", throwException, reportNode);
+            throwExceptionOrLogError(validable, "loss factor is invalid is undefined", actionOnError, reportNode);
             validationLevel = ValidationLevel.min(validationLevel, ValidationLevel.EQUIPMENT);
         } else if (lossFactor < 0 || lossFactor > 100) {
             throw new ValidationException(validable, "loss factor must be >= 0 and <= 100");
@@ -602,36 +622,36 @@ private static ValidationLevel checkLossFactor(Validable validable, float lossFa
         return validationLevel;
     }
 
-    private static ValidationLevel checkRtc(Validable validable, RatioTapChanger rtc, Network network, boolean throwException, ReportNode reportNode) {
+    private static ValidationLevel checkRtc(Validable validable, RatioTapChanger rtc, Network network, ActionOnError actionOnError, ReportNode reportNode) {
         ValidationLevel validationLevel = ValidationLevel.STEADY_STATE_HYPOTHESIS;
         if (rtc.findTapPosition().isEmpty()) {
-            throwExceptionOrLogError(validable, "tap position is not set", throwException, reportNode);
+            throwExceptionOrLogError(validable, "tap position is not set", actionOnError, reportNode);
             validationLevel = ValidationLevel.min(validationLevel, ValidationLevel.EQUIPMENT);
         }
-        validationLevel = ValidationLevel.min(validationLevel, checkRatioTapChangerRegulation(validable, rtc.isRegulating(), rtc.hasLoadTapChangingCapabilities(), rtc.getRegulationTerminal(), rtc.getRegulationMode(), rtc.getRegulationValue(), network, throwException, reportNode));
-        validationLevel = ValidationLevel.min(validationLevel, checkTargetDeadband(validable, "ratio tap changer", rtc.isRegulating(), rtc.getTargetDeadband(), throwException, reportNode));
+        validationLevel = ValidationLevel.min(validationLevel, checkRatioTapChangerRegulation(validable, rtc.isRegulating(), rtc.hasLoadTapChangingCapabilities(), rtc.getRegulationTerminal(), rtc.getRegulationMode(), rtc.getRegulationValue(), network, actionOnError, reportNode));
+        validationLevel = ValidationLevel.min(validationLevel, checkTargetDeadband(validable, "ratio tap changer", rtc.isRegulating(), rtc.getTargetDeadband(), actionOnError, reportNode));
         return validationLevel;
     }
 
-    private static ValidationLevel checkPtc(Validable validable, PhaseTapChanger ptc, Network network, boolean throwException, ReportNode reportNode) {
+    private static ValidationLevel checkPtc(Validable validable, PhaseTapChanger ptc, Network network, ActionOnError actionOnError, ReportNode reportNode) {
         ValidationLevel validationLevel = ValidationLevel.STEADY_STATE_HYPOTHESIS;
         if (ptc.findTapPosition().isEmpty()) {
-            throwExceptionOrLogError(validable, "tap position is not set", throwException, reportNode);
+            throwExceptionOrLogError(validable, "tap position is not set", actionOnError, reportNode);
             validationLevel = ValidationLevel.min(validationLevel, ValidationLevel.EQUIPMENT);
         }
-        validationLevel = ValidationLevel.min(validationLevel, checkPhaseTapChangerRegulation(validable, ptc.getRegulationMode(), ptc.getRegulationValue(), ptc.isRegulating(), ptc.getRegulationTerminal(), network, throwException, reportNode));
-        validationLevel = ValidationLevel.min(validationLevel, checkTargetDeadband(validable, "phase tap changer", ptc.isRegulating(), ptc.getTargetDeadband(), throwException, reportNode));
+        validationLevel = ValidationLevel.min(validationLevel, checkPhaseTapChangerRegulation(validable, ptc.getRegulationMode(), ptc.getRegulationValue(), ptc.isRegulating(), ptc.getRegulationTerminal(), network, actionOnError, reportNode));
+        validationLevel = ValidationLevel.min(validationLevel, checkTargetDeadband(validable, "phase tap changer", ptc.isRegulating(), ptc.getTargetDeadband(), actionOnError, reportNode));
         return validationLevel;
     }
 
-    private static ValidationLevel checkThreeWindingsTransformer(Validable validable, ThreeWindingsTransformer twt, boolean throwException, ReportNode reportNode) {
+    private static ValidationLevel checkThreeWindingsTransformer(Validable validable, ThreeWindingsTransformer twt, ActionOnError actionOnError, ReportNode reportNode) {
         ValidationLevel validationLevel = ValidationLevel.STEADY_STATE_HYPOTHESIS;
         for (ThreeWindingsTransformer.Leg leg : twt.getLegs()) {
             if (leg.hasRatioTapChanger()) {
-                validationLevel = ValidationLevel.min(validationLevel, checkRtc(validable, leg.getRatioTapChanger(), twt.getNetwork(), throwException, reportNode));
+                validationLevel = ValidationLevel.min(validationLevel, checkRtc(validable, leg.getRatioTapChanger(), twt.getNetwork(), actionOnError, reportNode));
             }
             if (leg.hasPhaseTapChanger()) {
-                validationLevel = ValidationLevel.min(validationLevel, checkPtc(validable, leg.getPhaseTapChanger(), twt.getNetwork(), throwException, reportNode));
+                validationLevel = ValidationLevel.min(validationLevel, checkPtc(validable, leg.getPhaseTapChanger(), twt.getNetwork(), actionOnError, reportNode));
             }
         }
         long regulatingTc = twt.getLegStream()
@@ -647,75 +667,75 @@ private static ValidationLevel checkThreeWindingsTransformer(Validable validable
         if (regulatingTc > 1) {
             throw new ValidationException(validable, UNIQUE_REGULATING_TAP_CHANGER_MSG);
         }
-        validationLevel = checkOperationalLimitsGroups(validable, twt.getLeg1().getOperationalLimitsGroups(), validationLevel, throwException, reportNode);
-        validationLevel = checkOperationalLimitsGroups(validable, twt.getLeg2().getOperationalLimitsGroups(), validationLevel, throwException, reportNode);
-        validationLevel = checkOperationalLimitsGroups(validable, twt.getLeg3().getOperationalLimitsGroups(), validationLevel, throwException, reportNode);
+        validationLevel = checkOperationalLimitsGroups(validable, twt.getLeg1().getOperationalLimitsGroups(), validationLevel, actionOnError, reportNode);
+        validationLevel = checkOperationalLimitsGroups(validable, twt.getLeg2().getOperationalLimitsGroups(), validationLevel, actionOnError, reportNode);
+        validationLevel = checkOperationalLimitsGroups(validable, twt.getLeg3().getOperationalLimitsGroups(), validationLevel, actionOnError, reportNode);
         return validationLevel;
     }
 
-    private static ValidationLevel checkTwoWindingsTransformer(Validable validable, TwoWindingsTransformer twt, boolean throwException, ReportNode reportNode) {
+    private static ValidationLevel checkTwoWindingsTransformer(Validable validable, TwoWindingsTransformer twt, ActionOnError actionOnError, ReportNode reportNode) {
         ValidationLevel validationLevel = ValidationLevel.STEADY_STATE_HYPOTHESIS;
         if (twt.hasRatioTapChanger()) {
-            validationLevel = ValidationLevel.min(validationLevel, checkRtc(validable, twt.getRatioTapChanger(), twt.getNetwork(), throwException, reportNode));
+            validationLevel = ValidationLevel.min(validationLevel, checkRtc(validable, twt.getRatioTapChanger(), twt.getNetwork(), actionOnError, reportNode));
         }
         if (twt.hasPhaseTapChanger()) {
-            validationLevel = ValidationLevel.min(validationLevel, checkPtc(validable, twt.getPhaseTapChanger(), twt.getNetwork(), throwException, reportNode));
+            validationLevel = ValidationLevel.min(validationLevel, checkPtc(validable, twt.getPhaseTapChanger(), twt.getNetwork(), actionOnError, reportNode));
         }
         if (twt.getOptionalRatioTapChanger().map(TapChanger::isRegulating).orElse(false)
                 && twt.getOptionalPhaseTapChanger().map(TapChanger::isRegulating).orElse(false)) {
             throw new ValidationException(validable, UNIQUE_REGULATING_TAP_CHANGER_MSG);
         }
-        validationLevel = checkOperationalLimitsGroups(validable, twt.getOperationalLimitsGroups1(), validationLevel, throwException, reportNode);
-        validationLevel = checkOperationalLimitsGroups(validable, twt.getOperationalLimitsGroups2(), validationLevel, throwException, reportNode);
+        validationLevel = checkOperationalLimitsGroups(validable, twt.getOperationalLimitsGroups1(), validationLevel, actionOnError, reportNode);
+        validationLevel = checkOperationalLimitsGroups(validable, twt.getOperationalLimitsGroups2(), validationLevel, actionOnError, reportNode);
         return validationLevel;
     }
 
-    private static ValidationLevel checkIdentifiable(Identifiable<?> identifiable, ValidationLevel previous, boolean throwException, ReportNode reportNode) {
+    private static ValidationLevel checkIdentifiable(Identifiable<?> identifiable, ValidationLevel previous, ActionOnError actionOnError, ReportNode reportNode) {
         ValidationLevel validationLevel = previous;
         if (identifiable instanceof Validable validable) {
             if (identifiable instanceof Battery battery) {
-                validationLevel = ValidationLevel.min(validationLevel, checkP0(validable, battery.getTargetP(), throwException, reportNode));
-                validationLevel = ValidationLevel.min(validationLevel, checkQ0(validable, battery.getTargetQ(), throwException, reportNode));
+                validationLevel = ValidationLevel.min(validationLevel, checkP0(validable, battery.getTargetP(), actionOnError, reportNode));
+                validationLevel = ValidationLevel.min(validationLevel, checkQ0(validable, battery.getTargetQ(), actionOnError, reportNode));
             } else if (identifiable instanceof DanglingLine danglingLine) {
-                validationLevel = ValidationLevel.min(validationLevel, checkP0(validable, danglingLine.getP0(), throwException, reportNode));
-                validationLevel = ValidationLevel.min(validationLevel, checkQ0(validable, danglingLine.getQ0(), throwException, reportNode));
-                validationLevel = checkGenerationOnDanglingLine(validationLevel, validable, danglingLine, throwException, reportNode);
-                validationLevel = checkOperationalLimitsGroups(validable, danglingLine.getOperationalLimitsGroups(), validationLevel, throwException, reportNode);
+                validationLevel = ValidationLevel.min(validationLevel, checkP0(validable, danglingLine.getP0(), actionOnError, reportNode));
+                validationLevel = ValidationLevel.min(validationLevel, checkQ0(validable, danglingLine.getQ0(), actionOnError, reportNode));
+                validationLevel = checkGenerationOnDanglingLine(validationLevel, validable, danglingLine, actionOnError, reportNode);
+                validationLevel = checkOperationalLimitsGroups(validable, danglingLine.getOperationalLimitsGroups(), validationLevel, actionOnError, reportNode);
             } else if (identifiable instanceof Generator generator) {
-                validationLevel = ValidationLevel.min(validationLevel, checkActivePowerSetpoint(validable, generator.getTargetP(), throwException, reportNode));
-                validationLevel = ValidationLevel.min(validationLevel, checkVoltageControl(validable, generator.isVoltageRegulatorOn(), generator.getTargetV(), generator.getTargetQ(), throwException, reportNode));
+                validationLevel = ValidationLevel.min(validationLevel, checkActivePowerSetpoint(validable, generator.getTargetP(), actionOnError, reportNode));
+                validationLevel = ValidationLevel.min(validationLevel, checkVoltageControl(validable, generator.isVoltageRegulatorOn(), generator.getTargetV(), generator.getTargetQ(), actionOnError, reportNode));
             } else if (identifiable instanceof HvdcLine hvdcLine) {
-                validationLevel = ValidationLevel.min(validationLevel, checkConvertersMode(validable, hvdcLine.getConvertersMode(), throwException, reportNode));
-                validationLevel = ValidationLevel.min(validationLevel, checkHvdcActivePowerSetpoint(validable, hvdcLine.getActivePowerSetpoint(), throwException, reportNode));
+                validationLevel = ValidationLevel.min(validationLevel, checkConvertersMode(validable, hvdcLine.getConvertersMode(), actionOnError, reportNode));
+                validationLevel = ValidationLevel.min(validationLevel, checkHvdcActivePowerSetpoint(validable, hvdcLine.getActivePowerSetpoint(), actionOnError, reportNode));
             } else if (identifiable instanceof Load load) {
-                validationLevel = ValidationLevel.min(validationLevel, checkP0(validable, load.getP0(), throwException, reportNode));
-                validationLevel = ValidationLevel.min(validationLevel, checkQ0(validable, load.getQ0(), throwException, reportNode));
+                validationLevel = ValidationLevel.min(validationLevel, checkP0(validable, load.getP0(), actionOnError, reportNode));
+                validationLevel = ValidationLevel.min(validationLevel, checkQ0(validable, load.getQ0(), actionOnError, reportNode));
             } else if (identifiable instanceof ShuntCompensator shunt) {
-                validationLevel = ValidationLevel.min(validationLevel, checkVoltageControl(validable, shunt.isVoltageRegulatorOn(), shunt.getTargetV(), throwException, reportNode));
-                validationLevel = ValidationLevel.min(validationLevel, checkTargetDeadband(validable, "shunt compensator", shunt.isVoltageRegulatorOn(), shunt.getTargetDeadband(), throwException, reportNode));
-                validationLevel = ValidationLevel.min(validationLevel, checkSections(validable, getSectionCount(shunt), shunt.getMaximumSectionCount(), throwException, reportNode));
+                validationLevel = ValidationLevel.min(validationLevel, checkVoltageControl(validable, shunt.isVoltageRegulatorOn(), shunt.getTargetV(), actionOnError, reportNode));
+                validationLevel = ValidationLevel.min(validationLevel, checkTargetDeadband(validable, "shunt compensator", shunt.isVoltageRegulatorOn(), shunt.getTargetDeadband(), actionOnError, reportNode));
+                validationLevel = ValidationLevel.min(validationLevel, checkSections(validable, getSectionCount(shunt), shunt.getMaximumSectionCount(), actionOnError, reportNode));
             } else if (identifiable instanceof StaticVarCompensator svc) {
-                validationLevel = ValidationLevel.min(validationLevel, checkSvcRegulator(validable, svc.getVoltageSetpoint(), svc.getReactivePowerSetpoint(), svc.getRegulationMode(), throwException, reportNode));
+                validationLevel = ValidationLevel.min(validationLevel, checkSvcRegulator(validable, svc.getVoltageSetpoint(), svc.getReactivePowerSetpoint(), svc.getRegulationMode(), actionOnError, reportNode));
             } else if (identifiable instanceof ThreeWindingsTransformer twt) {
-                validationLevel = ValidationLevel.min(validationLevel, checkThreeWindingsTransformer(validable, twt, throwException, reportNode));
+                validationLevel = ValidationLevel.min(validationLevel, checkThreeWindingsTransformer(validable, twt, actionOnError, reportNode));
             } else if (identifiable instanceof TwoWindingsTransformer twt) {
-                validationLevel = ValidationLevel.min(validationLevel, checkTwoWindingsTransformer(validable, twt, throwException, reportNode));
+                validationLevel = ValidationLevel.min(validationLevel, checkTwoWindingsTransformer(validable, twt, actionOnError, reportNode));
             } else if (identifiable instanceof VscConverterStation converterStation) {
-                validationLevel = ValidationLevel.min(validationLevel, checkVoltageControl(validable, converterStation.isVoltageRegulatorOn(), converterStation.getVoltageSetpoint(), converterStation.getReactivePowerSetpoint(), throwException, reportNode));
+                validationLevel = ValidationLevel.min(validationLevel, checkVoltageControl(validable, converterStation.isVoltageRegulatorOn(), converterStation.getVoltageSetpoint(), converterStation.getReactivePowerSetpoint(), actionOnError, reportNode));
             } else if (identifiable instanceof Branch<?> branch) {
-                validationLevel = checkOperationalLimitsGroups(validable, branch.getOperationalLimitsGroups1(), validationLevel, throwException, reportNode);
-                validationLevel = checkOperationalLimitsGroups(validable, branch.getOperationalLimitsGroups2(), validationLevel, throwException, reportNode);
+                validationLevel = checkOperationalLimitsGroups(validable, branch.getOperationalLimitsGroups1(), validationLevel, actionOnError, reportNode);
+                validationLevel = checkOperationalLimitsGroups(validable, branch.getOperationalLimitsGroups2(), validationLevel, actionOnError, reportNode);
             }
         }
         return validationLevel;
     }
 
-    private static ValidationLevel checkGenerationOnDanglingLine(ValidationLevel previous, Validable validable, DanglingLine danglingLine, boolean throwException, ReportNode reportNode) {
+    private static ValidationLevel checkGenerationOnDanglingLine(ValidationLevel previous, Validable validable, DanglingLine danglingLine, ActionOnError actionOnError, ReportNode reportNode) {
         ValidationLevel validationLevel = previous;
         DanglingLine.Generation generation = danglingLine.getGeneration();
         if (generation != null) {
-            validationLevel = ValidationLevel.min(validationLevel, checkActivePowerSetpoint(validable, generation.getTargetP(), throwException, reportNode));
-            validationLevel = ValidationLevel.min(validationLevel, checkVoltageControl(validable, generation.isVoltageRegulationOn(), generation.getTargetV(), generation.getTargetQ(), throwException, reportNode));
+            validationLevel = ValidationLevel.min(validationLevel, checkActivePowerSetpoint(validable, generation.getTargetP(), actionOnError, reportNode));
+            validationLevel = ValidationLevel.min(validationLevel, checkVoltageControl(validable, generation.isVoltageRegulationOn(), generation.getTargetV(), generation.getTargetQ(), actionOnError, reportNode));
         }
         return validationLevel;
     }
@@ -724,28 +744,28 @@ private static Integer getSectionCount(ShuntCompensator shunt) {
         return shunt.findSectionCount().isPresent() ? shunt.getSectionCount() : null;
     }
 
-    private static ValidationLevel checkOperationalLimitsGroups(Validable validable, Collection<OperationalLimitsGroup> operationalLimitsGroupCollection, ValidationLevel previous, boolean throwException, ReportNode reportNode) {
+    private static ValidationLevel checkOperationalLimitsGroups(Validable validable, Collection<OperationalLimitsGroup> operationalLimitsGroupCollection, ValidationLevel previous, ActionOnError actionOnError, ReportNode reportNode) {
         ValidationLevel validationLevel = previous;
         for (OperationalLimitsGroup group : operationalLimitsGroupCollection) {
-            validationLevel = checkOperationalLimitsGroup(validable, group, validationLevel, throwException, reportNode);
+            validationLevel = checkOperationalLimitsGroup(validable, group, validationLevel, actionOnError, reportNode);
         }
         return validationLevel;
     }
 
-    private static ValidationLevel checkOperationalLimitsGroup(Validable validable, OperationalLimitsGroup operationalLimitsGroup, ValidationLevel previous, boolean throwException, ReportNode reportNode) {
+    private static ValidationLevel checkOperationalLimitsGroup(Validable validable, OperationalLimitsGroup operationalLimitsGroup, ValidationLevel previous, ActionOnError actionOnError, ReportNode reportNode) {
         ValidationLevel[] validationLevel = new ValidationLevel[1];
         validationLevel[0] = previous;
-        operationalLimitsGroup.getCurrentLimits().ifPresent(l -> validationLevel[0] = checkLoadingLimits(validable, l, validationLevel[0], throwException, reportNode));
-        operationalLimitsGroup.getApparentPowerLimits().ifPresent(l -> validationLevel[0] = checkLoadingLimits(validable, l, validationLevel[0], throwException, reportNode));
-        operationalLimitsGroup.getActivePowerLimits().ifPresent(l -> validationLevel[0] = checkLoadingLimits(validable, l, validationLevel[0], throwException, reportNode));
+        operationalLimitsGroup.getCurrentLimits().ifPresent(l -> validationLevel[0] = checkLoadingLimits(validable, l, validationLevel[0], actionOnError, reportNode));
+        operationalLimitsGroup.getApparentPowerLimits().ifPresent(l -> validationLevel[0] = checkLoadingLimits(validable, l, validationLevel[0], actionOnError, reportNode));
+        operationalLimitsGroup.getActivePowerLimits().ifPresent(l -> validationLevel[0] = checkLoadingLimits(validable, l, validationLevel[0], actionOnError, reportNode));
         return validationLevel[0];
     }
 
-    private static ValidationLevel checkLoadingLimits(Validable validable, LoadingLimits limits, ValidationLevel validationLevel, boolean throwException, ReportNode reportNode) {
-        return ValidationLevel.min(validationLevel, checkLoadingLimits(validable, limits.getPermanentLimit(), limits.getTemporaryLimits(), throwException, reportNode));
+    private static ValidationLevel checkLoadingLimits(Validable validable, LoadingLimits limits, ValidationLevel validationLevel, ActionOnError actionOnError, ReportNode reportNode) {
+        return ValidationLevel.min(validationLevel, checkLoadingLimits(validable, limits.getPermanentLimit(), limits.getTemporaryLimits(), actionOnError, reportNode));
     }
 
-    public static ValidationLevel validate(Collection<Identifiable<?>> identifiables, boolean allChecks, boolean throwException, ValidationLevel previous, ReportNode reportNode) {
+    public static ValidationLevel validate(Collection<Identifiable<?>> identifiables, boolean allChecks, ActionOnError actionOnError, ValidationLevel previous, ReportNode reportNode) {
         Objects.requireNonNull(identifiables);
         Objects.requireNonNull(previous);
         Objects.requireNonNull(reportNode);
@@ -754,7 +774,7 @@ public static ValidationLevel validate(Collection<Identifiable<?>> identifiables
         }
         ValidationLevel validationLevel = ValidationLevel.STEADY_STATE_HYPOTHESIS;
         for (Identifiable<?> identifiable : identifiables) {
-            validationLevel = checkIdentifiable(identifiable, validationLevel, throwException, reportNode);
+            validationLevel = checkIdentifiable(identifiable, validationLevel, actionOnError, reportNode);
             if (!allChecks && validationLevel == ValidationLevel.MINIMUM_VALUE) {
                 return validationLevel;
             }
@@ -766,4 +786,8 @@ private static boolean checkValidationLevel(ValidationLevel validationLevel) {
         return validationLevel.compareTo(ValidationLevel.STEADY_STATE_HYPOTHESIS) >= 0;
     }
 
+    private static ActionOnError checkValidationActionOnError(ValidationLevel validationLevel) {
+        return validationLevel.compareTo(ValidationLevel.STEADY_STATE_HYPOTHESIS) >= 0 ? ActionOnError.THROW_EXCEPTION : ActionOnError.IGNORE;
+    }
+
 }
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractTapChanger.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractTapChanger.java
index 84d370a7c74..dd22133b01d 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractTapChanger.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractTapChanger.java
@@ -131,7 +131,7 @@ public C setTapPosition(int tapPosition) {
 
     public C unsetTapPosition() {
         NetworkImpl n = getNetwork();
-        ValidationUtil.throwExceptionOrLogError(parent, "tap position has been unset", n.getMinValidationLevel(), n.getReportNodeContext().getReportNode());
+        ValidationUtil.throwExceptionOrIgnore(parent, "tap position has been unset", n.getMinValidationLevel());
         int variantIndex = network.get().getVariantIndex();
         Integer oldValue = this.tapPosition.set(variantIndex, null);
         String variantId = network.get().getVariantManager().getVariantId(variantIndex);
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractTapChangerAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractTapChangerAdderImpl.java
index bf05d601ae4..29e3452a169 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractTapChangerAdderImpl.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractTapChangerAdderImpl.java
@@ -79,8 +79,7 @@ NetworkImpl getNetwork() {
     public T add() {
         NetworkImpl network = getNetwork();
         if (tapPosition == null) {
-            ValidationUtil.throwExceptionOrLogError(parent, "tap position is not set", network.getMinValidationLevel(),
-                    network.getReportNodeContext().getReportNode());
+            ValidationUtil.throwExceptionOrIgnore(parent, "tap position is not set", network.getMinValidationLevel());
             network.setValidationLevelIfGreaterThan(ValidationLevel.EQUIPMENT);
         }
         if (steps.isEmpty()) {
@@ -89,9 +88,9 @@ public T add() {
         if (tapPosition != null) {
             int highTapPosition = lowTapPosition + steps.size() - 1;
             if (tapPosition < lowTapPosition || tapPosition > highTapPosition) {
-                ValidationUtil.throwExceptionOrLogError(parent, "incorrect tap position "
+                ValidationUtil.throwExceptionOrIgnore(parent, "incorrect tap position "
                         + tapPosition + " [" + lowTapPosition + ", "
-                        + highTapPosition + "]", network.getMinValidationLevel(), network.getReportNodeContext().getReportNode());
+                        + highTapPosition + "]", network.getMinValidationLevel());
                 network.setValidationLevelIfGreaterThan(ValidationLevel.EQUIPMENT);
             }
         }
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkImpl.java
index 4c7e5204b94..b0ee6d414a6 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkImpl.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkImpl.java
@@ -1245,14 +1245,14 @@ public ValidationLevel runValidationChecks(boolean throwsException) {
     public ValidationLevel runValidationChecks(boolean throwsException, ReportNode reportNode) {
         ReportNode readReportNode = NetworkReports.runIidmNetworkValidationCHecks(reportNode, id);
         validationLevel = ValidationUtil.validate(Collections.unmodifiableCollection(index.getAll()),
-                true, throwsException, validationLevel != null ? validationLevel : minValidationLevel, readReportNode);
+                true, throwsException ? ValidationUtil.ActionOnError.THROW_EXCEPTION : ValidationUtil.ActionOnError.LOG_ERROR, validationLevel != null ? validationLevel : minValidationLevel, readReportNode);
         return validationLevel;
     }
 
     @Override
     public ValidationLevel getValidationLevel() {
         if (validationLevel == null) {
-            validationLevel = ValidationUtil.validate(Collections.unmodifiableCollection(index.getAll()), false, false, minValidationLevel, ReportNode.NO_OP);
+            validationLevel = ValidationUtil.validate(Collections.unmodifiableCollection(index.getAll()), false, ValidationUtil.ActionOnError.IGNORE, minValidationLevel, ReportNode.NO_OP);
         }
         return validationLevel;
     }
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ShuntCompensatorImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ShuntCompensatorImpl.java
index f202f14dc6b..8df4e6bf258 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ShuntCompensatorImpl.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ShuntCompensatorImpl.java
@@ -102,8 +102,7 @@ public ShuntCompensatorImpl setSectionCount(int sectionCount) {
     @Override
     public ShuntCompensator unsetSectionCount() {
         NetworkImpl n = getNetwork();
-        ValidationUtil.throwExceptionOrLogError(this, "count of sections in service has been unset", n.getMinValidationLevel(),
-                n.getReportNodeContext().getReportNode());
+        ValidationUtil.throwExceptionOrIgnore(this, "count of sections in service has been unset", n.getMinValidationLevel());
         int variantIndex = network.get().getVariantIndex();
         Integer oldValue = this.sectionCount.set(variantIndex, null);
         String variantId = network.get().getVariantManager().getVariantId(variantIndex);
diff --git a/iidm/iidm-impl/src/test/java/com/powsybl/iidm/network/impl/ValidationLevelLogsTest.java b/iidm/iidm-impl/src/test/java/com/powsybl/iidm/network/impl/ValidationLevelLogsTest.java
new file mode 100644
index 00000000000..d48bd5d4a71
--- /dev/null
+++ b/iidm/iidm-impl/src/test/java/com/powsybl/iidm/network/impl/ValidationLevelLogsTest.java
@@ -0,0 +1,112 @@
+/**
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+
+package com.powsybl.iidm.network.impl;
+
+import com.powsybl.commons.report.PowsyblCoreReportResourceBundle;
+import com.powsybl.commons.report.ReportNode;
+import com.powsybl.commons.test.PowsyblCoreTestReportResourceBundle;
+import com.powsybl.commons.test.TestUtil;
+import com.powsybl.iidm.network.*;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * @author Luma Zamarreño {@literal <zamarrenolm at aia.es>}
+ * @author José Antonio Marqués {@literal <marquesja at aia.es>}
+ */
+
+class ValidationLevelLogsTest {
+
+    @Test
+    void equipmentAndSteadyStateTest() throws IOException {
+        // create a network associated with a reportNode
+        ReportNode reportNode = ReportNode.newRootReportNode()
+                .withResourceBundles(
+                        PowsyblCoreTestReportResourceBundle.TEST_BASE_NAME,
+                        PowsyblCoreReportResourceBundle.BASE_NAME,
+                        "i18n.iidm-test-reports")
+                .withMessageTemplate("testValidationLevelLogs")
+                .withUntypedValue("name", "test")
+                .build();
+
+        Network network = NetworkFactory.findDefault().createNetwork("oneLoad", "test");
+        ReportNodeContext reportNodeContext = network.getReportNodeContext();
+        reportNodeContext.pushReportNode(reportNode);
+
+        // Define the network model
+        network.setMinimumAcceptableValidationLevel(ValidationLevel.EQUIPMENT);
+        createNetworkWithEquipmentValidationLevel(network);
+
+        // We check that the SILENT option for action on error works
+        // While creating only network equipment if we have established validation level to EQUIPMENT,
+        // We should not see anything reported
+        assertTrue(checkReportNode("""
+                Test validationLevel Logs
+                """, network.getReportNodeContext().getReportNode()));
+
+        network.runValidationChecks(false, network.getReportNodeContext().getReportNode());
+
+        assertTrue(checkReportNode("""
+                + Test validationLevel Logs
+                   + Running validation checks on IIDM network oneLoad
+                      p0 is invalid
+                      p0 is invalid
+                """, network.getReportNodeContext().getReportNode()));
+
+        ValidationException e = assertThrows(ValidationException.class, () -> network.setMinimumAcceptableValidationLevel(ValidationLevel.STEADY_STATE_HYPOTHESIS));
+        assertTrue(e.getMessage().contains("Network 'oneLoad': Network should be corrected in order to correspond to validation level STEADY_STATE_HYPOTHESIS"));
+
+        // Define the steady-state attributes
+        network.getLoads().iterator().next().setP0(10.0).setQ0(-5.0);
+        network.runValidationChecks(false, network.getReportNodeContext().getReportNode());
+
+        assertTrue(checkReportNode("""
+                + Test validationLevel Logs
+                   + Running validation checks on IIDM network oneLoad
+                      p0 is invalid
+                      p0 is invalid
+                   Running validation checks on IIDM network oneLoad
+                """, network.getReportNodeContext().getReportNode()));
+
+        assertEquals(ValidationLevel.STEADY_STATE_HYPOTHESIS, network.getValidationLevel());
+    }
+
+    private static boolean checkReportNode(String expected, ReportNode reportNode) throws IOException {
+        StringWriter sw = new StringWriter();
+        reportNode.print(sw);
+        assertEquals(expected, TestUtil.normalizeLineSeparator(sw.toString()));
+        return true;
+    }
+
+    public static void createNetworkWithEquipmentValidationLevel(Network network) {
+        Substation s1 = network.newSubstation()
+                .setId("S1")
+                .setCountry(Country.FR)
+                .setTso("RTE")
+                .setGeographicalTags("A")
+                .add();
+        VoltageLevel vl1 = s1.newVoltageLevel()
+                .setId("VL1")
+                .setNominalV(24.0)
+                .setTopologyKind(TopologyKind.BUS_BREAKER)
+                .add();
+        Bus loadBus = vl1.getBusBreakerView().newBus()
+                .setId("LoadBus")
+                .add();
+        vl1.newLoad()
+                .setId("LOAD")
+                .setBus(loadBus.getId())
+                .setConnectableBus(loadBus.getId())
+                .add();
+    }
+}
diff --git a/iidm/iidm-impl/src/test/resources/i18n/iidm-test-reports.properties b/iidm/iidm-impl/src/test/resources/i18n/iidm-test-reports.properties
new file mode 100644
index 00000000000..4b86409b72c
--- /dev/null
+++ b/iidm/iidm-impl/src/test/resources/i18n/iidm-test-reports.properties
@@ -0,0 +1 @@
+testValidationLevelLogs=Test validationLevel Logs


Reviews:
user: login: zamarrenolm
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-01-21T10:48:25Z
user: login: zamarrenolm
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-01-21T10:48:28Z
user: login: zamarrenolm
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-01-21T11:03:26Z
user: login: marqueslanauja
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-01-21T11:23:49Z
user: login: marqueslanauja
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-01-21T11:24:09Z
user: login: marqueslanauja
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-01-21T11:24:20Z
user: login: zamarrenolm
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-01-23T20:54:01Z
user: login: olperr1
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-04-17T08:33:04Z
user: login: marqueslanauja
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T06:02:46Z
user: login: marqueslanauja
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T06:02:56Z
user: login: marqueslanauja
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T06:03:05Z
user: login: marqueslanauja
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T06:03:18Z
user: login: olperr1
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-05-15T07:14:19Z
user: login: marqueslanauja
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T14:32:50Z
user: login: olperr1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-16T13:18:25Z
user: login: olperr1
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-16T13:19:10Z

Review comments:
diff_hunk: @@ -23,6 +23,12 @@
  */
 public final class ValidationUtil {
 
+    public enum LogLevel {
path: iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java
user: login: zamarrenolm
body: Instead of `LogLevel`(that could be confused with the logger behavior), I prefer `ActionOnError` for this enum name.
created_at: 2025-01-21T10:48:24Z
author_association: MEMBER
diff_hunk: @@ -63,33 +69,37 @@ private static void logError(Validable validable, String message, ReportNode rep
         LOGGER.error("{}{}", validable.getMessageHeader(), message);
     }
 
-    private static void throwExceptionOrLogError(Validable validable, String message, boolean throwException, ReportNode reportNode) {
-        if (throwException) {
+    private static void throwExceptionOrLogError(Validable validable, String message, LogLevel throwException, ReportNode reportNode) {
+        if (throwException.equals(LogLevel.THROW_EXCEPTION)) {
             throw new ValidationException(validable, message);
         }
-        logError(validable, message, reportNode);
+        if (throwException.equals(LogLevel.LOG_ERROR)) {
+            logError(validable, message, reportNode);
+        }
     }
 
     public static void throwExceptionOrLogError(Validable validable, String message, ValidationLevel validationLevel, ReportNode reportNode) {
-        throwExceptionOrLogError(validable, message, validationLevel == ValidationLevel.STEADY_STATE_HYPOTHESIS, reportNode);
+        throwExceptionOrLogError(validable, message, checkValidationLogLevel(validationLevel), reportNode);
path: iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java
user: login: zamarrenolm
body: Following the proposed name change, replace `checkValidationLogLevel` by `checkValidationActionOnError`
created_at: 2025-01-21T10:48:28Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,119 @@
+/**
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+
+package com.powsybl.iidm.network.impl;
+
+import com.powsybl.commons.report.ReportNode;
+import com.powsybl.commons.test.TestUtil;
+import com.powsybl.iidm.network.*;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * @author Luma Zamarreño {@literal <zamarrenolm at aia.es>}
+ * @author José Antonio Marqués {@literal <marquesja at aia.es>}
+ */
+
+class ValidationLevelLogsTest {
+
+    @Test
+    void equipmentTest() throws IOException {
+        Network network = networkAssociatedWithReportNode();
+
+        network.setMinimumAcceptableValidationLevel(ValidationLevel.EQUIPMENT);
+        createNetworkWithEquipmentValidationLevel(network);
+
path: iidm/iidm-impl/src/test/java/com/powsybl/iidm/network/impl/ValidationLevelLogsTest.java
user: login: zamarrenolm
body: Maybe assert at this point that no validation messages have been produced. Something like:

```java
// We check that the SILENT option for action on error works
// While creating only network equipment if we have established validation level to EQUIPMENT,
// We should not see anything reported
assertTrue(checkReportNode("""
      Test validationLevel Logs
      """, network.getReportNodeContext().getReportNode()));
```
created_at: 2025-01-21T11:03:26Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,119 @@
+/**
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+
+package com.powsybl.iidm.network.impl;
+
+import com.powsybl.commons.report.ReportNode;
+import com.powsybl.commons.test.TestUtil;
+import com.powsybl.iidm.network.*;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * @author Luma Zamarreño {@literal <zamarrenolm at aia.es>}
+ * @author José Antonio Marqués {@literal <marquesja at aia.es>}
+ */
+
+class ValidationLevelLogsTest {
+
+    @Test
+    void equipmentTest() throws IOException {
+        Network network = networkAssociatedWithReportNode();
+
+        network.setMinimumAcceptableValidationLevel(ValidationLevel.EQUIPMENT);
+        createNetworkWithEquipmentValidationLevel(network);
+
path: iidm/iidm-impl/src/test/java/com/powsybl/iidm/network/impl/ValidationLevelLogsTest.java
user: login: marqueslanauja
body: Done
created_at: 2025-01-21T11:23:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -63,33 +69,37 @@ private static void logError(Validable validable, String message, ReportNode rep
         LOGGER.error("{}{}", validable.getMessageHeader(), message);
     }
 
-    private static void throwExceptionOrLogError(Validable validable, String message, boolean throwException, ReportNode reportNode) {
-        if (throwException) {
+    private static void throwExceptionOrLogError(Validable validable, String message, LogLevel throwException, ReportNode reportNode) {
+        if (throwException.equals(LogLevel.THROW_EXCEPTION)) {
             throw new ValidationException(validable, message);
         }
-        logError(validable, message, reportNode);
+        if (throwException.equals(LogLevel.LOG_ERROR)) {
+            logError(validable, message, reportNode);
+        }
     }
 
     public static void throwExceptionOrLogError(Validable validable, String message, ValidationLevel validationLevel, ReportNode reportNode) {
-        throwExceptionOrLogError(validable, message, validationLevel == ValidationLevel.STEADY_STATE_HYPOTHESIS, reportNode);
+        throwExceptionOrLogError(validable, message, checkValidationLogLevel(validationLevel), reportNode);
path: iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java
user: login: marqueslanauja
body: Done
created_at: 2025-01-21T11:24:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -23,6 +23,12 @@
  */
 public final class ValidationUtil {
 
+    public enum LogLevel {
path: iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java
user: login: marqueslanauja
body: Done
created_at: 2025-01-21T11:24:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -23,6 +23,12 @@
  */
 public final class ValidationUtil {
 
+    public enum ActionOnError {
+        THROW_EXCEPTION,
+        LOG_ERROR,
+        SILENT,
path: iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java
user: login: olperr1
body: I think it would be better to rename this option to use a verb instead, since the enum describe an "action".
We could use for instance, `IGNORE`.
created_at: 2025-04-17T08:13:35Z
author_association: MEMBER
diff_hunk: @@ -63,33 +69,37 @@ private static void logError(Validable validable, String message, ReportNode rep
         LOGGER.error("{}{}", validable.getMessageHeader(), message);
     }
 
-    private static void throwExceptionOrLogError(Validable validable, String message, boolean throwException, ReportNode reportNode) {
-        if (throwException) {
+    private static void throwExceptionOrLogError(Validable validable, String message, ActionOnError throwException, ReportNode reportNode) {
path: iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java
user: login: olperr1
body: Could you rename all the `throwException` and `throwsException` attributes to `actionOnError`?
created_at: 2025-04-17T08:19:54Z
author_association: MEMBER
diff_hunk: @@ -63,33 +69,37 @@ private static void logError(Validable validable, String message, ReportNode rep
         LOGGER.error("{}{}", validable.getMessageHeader(), message);
     }
 
-    private static void throwExceptionOrLogError(Validable validable, String message, boolean throwException, ReportNode reportNode) {
-        if (throwException) {
+    private static void throwExceptionOrLogError(Validable validable, String message, ActionOnError throwException, ReportNode reportNode) {
+        if (throwException.equals(ActionOnError.THROW_EXCEPTION)) {
path: iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java
user: login: olperr1
body: For enums comparison, you can use '==' instead of '.equals(...)'. This prevents potential NullPointerException.
created_at: 2025-04-17T08:22:26Z
author_association: MEMBER
diff_hunk: @@ -63,33 +69,37 @@ private static void logError(Validable validable, String message, ReportNode rep
         LOGGER.error("{}{}", validable.getMessageHeader(), message);
     }
 
-    private static void throwExceptionOrLogError(Validable validable, String message, boolean throwException, ReportNode reportNode) {
-        if (throwException) {
+    private static void throwExceptionOrLogError(Validable validable, String message, ActionOnError throwException, ReportNode reportNode) {
+        if (throwException.equals(ActionOnError.THROW_EXCEPTION)) {
             throw new ValidationException(validable, message);
         }
-        logError(validable, message, reportNode);
+        if (throwException.equals(ActionOnError.LOG_ERROR)) {
path: iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java
user: login: olperr1
body: Same here.
created_at: 2025-04-17T08:22:35Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,112 @@
+/**
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+
+package com.powsybl.iidm.network.impl;
+
+import com.powsybl.commons.report.PowsyblCoreReportResourceBundle;
+import com.powsybl.commons.report.ReportNode;
+import com.powsybl.commons.test.PowsyblCoreTestReportResourceBundle;
+import com.powsybl.commons.test.TestUtil;
+import com.powsybl.iidm.network.*;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * @author Luma Zamarreño {@literal <zamarrenolm at aia.es>}
+ * @author José Antonio Marqués {@literal <marquesja at aia.es>}
+ */
+
+class ValidationLevelLogsTest {
+
+    @Test
+    void equipmentAndSteadyStateTest() throws IOException {
+        // create a network associated with a reportNode
+        ReportNode reportNode = ReportNode.newRootReportNode()
+                .withResourceBundles(
+                        PowsyblCoreTestReportResourceBundle.TEST_BASE_NAME,
+                        PowsyblCoreReportResourceBundle.BASE_NAME,
+                        "i18n.iidm-test-reports")
+                .withMessageTemplate("testValidationLevelLogs")
+                .withUntypedValue("name", "test")
+                .build();
+
+        Network network = NetworkFactory.findDefault().createNetwork("oneLoad", "test");
+        ReportNodeContext reportNodeContext = network.getReportNodeContext();
+        reportNodeContext.pushReportNode(reportNode);
+
+        // Define the network model
+        network.setMinimumAcceptableValidationLevel(ValidationLevel.EQUIPMENT);
+        createNetworkWithEquipmentValidationLevel(network);
+
+        // We check that the SILENT option for action on error works
+        // While creating only network equipment if we have established validation level to EQUIPMENT,
+        // We should not see anything reported
+        assertTrue(checkReportNode("""
+                Test validationLevel Logs
+                """, network.getReportNodeContext().getReportNode()));
+
+        network.runValidationChecks(false, network.getReportNodeContext().getReportNode());
+
+        assertTrue(checkReportNode("""
+                + Test validationLevel Logs
+                   + Running validation checks on IIDM network oneLoad
+                      p0 is invalid
path: iidm/iidm-impl/src/test/java/com/powsybl/iidm/network/impl/ValidationLevelLogsTest.java
user: login: olperr1
body: As @zamarrenolm stated in the issue, not having the validable ID here is problematic since the validation is performed on the whole network.
@flo-dup is reworking the report node usage in this class regarding I18n. For now, we can keep the messages like this, but we should keep in mind that it should be changed quickly after these both works are done.
created_at: 2025-04-17T08:32:45Z
author_association: MEMBER
diff_hunk: @@ -23,6 +23,12 @@
  */
 public final class ValidationUtil {
 
+    public enum ActionOnError {
+        THROW_EXCEPTION,
+        LOG_ERROR,
+        SILENT,
path: iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java
user: login: marqueslanauja
body: Done
created_at: 2025-04-22T06:02:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -63,33 +69,37 @@ private static void logError(Validable validable, String message, ReportNode rep
         LOGGER.error("{}{}", validable.getMessageHeader(), message);
     }
 
-    private static void throwExceptionOrLogError(Validable validable, String message, boolean throwException, ReportNode reportNode) {
-        if (throwException) {
+    private static void throwExceptionOrLogError(Validable validable, String message, ActionOnError throwException, ReportNode reportNode) {
path: iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java
user: login: marqueslanauja
body: Done
created_at: 2025-04-22T06:02:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -63,33 +69,37 @@ private static void logError(Validable validable, String message, ReportNode rep
         LOGGER.error("{}{}", validable.getMessageHeader(), message);
     }
 
-    private static void throwExceptionOrLogError(Validable validable, String message, boolean throwException, ReportNode reportNode) {
-        if (throwException) {
+    private static void throwExceptionOrLogError(Validable validable, String message, ActionOnError throwException, ReportNode reportNode) {
+        if (throwException.equals(ActionOnError.THROW_EXCEPTION)) {
path: iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java
user: login: marqueslanauja
body: Done
created_at: 2025-04-22T06:03:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -63,33 +69,37 @@ private static void logError(Validable validable, String message, ReportNode rep
         LOGGER.error("{}{}", validable.getMessageHeader(), message);
     }
 
-    private static void throwExceptionOrLogError(Validable validable, String message, boolean throwException, ReportNode reportNode) {
-        if (throwException) {
+    private static void throwExceptionOrLogError(Validable validable, String message, ActionOnError throwException, ReportNode reportNode) {
+        if (throwException.equals(ActionOnError.THROW_EXCEPTION)) {
             throw new ValidationException(validable, message);
         }
-        logError(validable, message, reportNode);
+        if (throwException.equals(ActionOnError.LOG_ERROR)) {
path: iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java
user: login: marqueslanauja
body: Done
created_at: 2025-04-22T06:03:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -63,47 +69,51 @@ private static void logError(Validable validable, String message, ReportNode rep
         LOGGER.error("{}{}", validable.getMessageHeader(), message);
     }
 
-    private static void throwExceptionOrLogError(Validable validable, String message, boolean throwException, ReportNode reportNode) {
-        if (throwException) {
+    private static void throwExceptionOrLogError(Validable validable, String message, ActionOnError actionOnError, ReportNode reportNode) {
+        if (actionOnError == ActionOnError.THROW_EXCEPTION) {
             throw new ValidationException(validable, message);
         }
-        logError(validable, message, reportNode);
+        if (actionOnError == ActionOnError.LOG_ERROR) {
+            logError(validable, message, reportNode);
+        }
     }
 
     public static void throwExceptionOrLogError(Validable validable, String message, ValidationLevel validationLevel, ReportNode reportNode) {
-        throwExceptionOrLogError(validable, message, validationLevel == ValidationLevel.STEADY_STATE_HYPOTHESIS, reportNode);
+        throwExceptionOrLogError(validable, message, checkValidationActionOnError(validationLevel), reportNode);
     }
path: iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java
user: login: olperr1
body: This public method is called `throwExceptionOrLogError(...)`, but it now throws or ignores the error.

I think we should not change the behavior of this public method (throw if validationLevel is SSH, else log) but instead add a new method `throwExceptionOrIgnore(Validable, String, ValidationLevel)` (reportNode is not needed since it won't be used).
created_at: 2025-05-06T13:26:42Z
author_association: MEMBER
diff_hunk: @@ -63,47 +69,51 @@ private static void logError(Validable validable, String message, ReportNode rep
         LOGGER.error("{}{}", validable.getMessageHeader(), message);
     }
 
-    private static void throwExceptionOrLogError(Validable validable, String message, boolean throwException, ReportNode reportNode) {
-        if (throwException) {
+    private static void throwExceptionOrLogError(Validable validable, String message, ActionOnError actionOnError, ReportNode reportNode) {
+        if (actionOnError == ActionOnError.THROW_EXCEPTION) {
             throw new ValidationException(validable, message);
         }
-        logError(validable, message, reportNode);
+        if (actionOnError == ActionOnError.LOG_ERROR) {
+            logError(validable, message, reportNode);
+        }
     }
 
     public static void throwExceptionOrLogError(Validable validable, String message, ValidationLevel validationLevel, ReportNode reportNode) {
-        throwExceptionOrLogError(validable, message, validationLevel == ValidationLevel.STEADY_STATE_HYPOTHESIS, reportNode);
+        throwExceptionOrLogError(validable, message, checkValidationActionOnError(validationLevel), reportNode);
     }
path: iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java
user: login: marqueslanauja
body: Done
created_at: 2025-05-15T14:32:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -63,47 +69,51 @@ private static void logError(Validable validable, String message, ReportNode rep
         LOGGER.error("{}{}", validable.getMessageHeader(), message);
     }
 
-    private static void throwExceptionOrLogError(Validable validable, String message, boolean throwException, ReportNode reportNode) {
-        if (throwException) {
+    private static void throwExceptionOrLogError(Validable validable, String message, ActionOnError actionOnError, ReportNode reportNode) {
+        if (actionOnError == ActionOnError.THROW_EXCEPTION) {
             throw new ValidationException(validable, message);
         }
-        logError(validable, message, reportNode);
+        if (actionOnError == ActionOnError.LOG_ERROR) {
+            logError(validable, message, reportNode);
+        }
     }
 
     public static void throwExceptionOrLogError(Validable validable, String message, ValidationLevel validationLevel, ReportNode reportNode) {
-        throwExceptionOrLogError(validable, message, validationLevel == ValidationLevel.STEADY_STATE_HYPOTHESIS, reportNode);
+        throwExceptionOrLogError(validable, message, checkValidationActionOnError(validationLevel), reportNode);
     }
path: iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java
user: login: olperr1
body: Thanks!
created_at: 2025-05-16T13:18:24Z
author_association: MEMBER

PR comments:
user: login: sonarqubecloud[bot]
created_at: 2025-02-06T10:16:33Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=com.powsybl%3Apowsybl-core&pullRequest=3273) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [1 New issue](https://sonarcloud.io/project/issues?id=com.powsybl%3Apowsybl-core&pullRequest=3273&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=com.powsybl%3Apowsybl-core&pullRequest=3273&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=com.powsybl%3Apowsybl-core&pullRequest=3273&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [100.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=com.powsybl%3Apowsybl-core&pullRequest=3273&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=com.powsybl%3Apowsybl-core&pullRequest=3273&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=com.powsybl%3Apowsybl-core&pullRequest=3273)


author_association: NONE
user: login: sonarqubecloud[bot]
created_at: 2025-05-19T08:44:05Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=com.powsybl%3Apowsybl-core&pullRequest=3273) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [1 New issue](https://sonarcloud.io/project/issues?id=com.powsybl%3Apowsybl-core&pullRequest=3273&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=com.powsybl%3Apowsybl-core&pullRequest=3273&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=com.powsybl%3Apowsybl-core&pullRequest=3273&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [90.3% Coverage on New Code](https://sonarcloud.io/component_measures?id=com.powsybl%3Apowsybl-core&pullRequest=3273&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=com.powsybl%3Apowsybl-core&pullRequest=3273&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=com.powsybl%3Apowsybl-core&pullRequest=3273)


author_association: NONE