Repository: powsybl/powsybl-open-loadflow
Pull request #1224: Extrapolate reactive capability curve limits when outside active power limits
Author: login: SylvestreSakti
Created: 2025-04-11T16:46:30Z | Merged: 2025-05-20T07:34:13Z

**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)
- [x] Docs have been added / updated (for bug fixes / features)
- [ ] A PR or issue has been opened in all impacted repositories (if any)


**Does this PR already have an issue describing the problem?**
Fixes #1143 



**What kind of change does this PR introduce?**
Adding support of reactive limits extrapolation (following this commit in powsybl-core : https://github.com/powsybl/powsybl-core/pull/3250)


**What is the current behavior?**
No support



**What is the new behavior (if this is a feature change)?**
Support is added with the parameter `extrapolateReactiveLimits`


**Does this PR introduce a breaking change or deprecate an API?**
- [ ] Yes
- [x] No

Linked issue #1143: Extrapolate maxQ and minQ reactive limits when outside active power limits in ReactiveCapabilityCurve
### Describe the current behavior

Currently, when computing reactive limits of a generator (or a battery) that is defined by its ReactiveCapabiltyCurve, when P is out of the bounds of this curve (e.g. when a generator is in starting phase, its P is inferior to Pmin), the computed reactive limits are the same as the reactive limits of Pmin.

### Describe the expected behavior

We would like to extrapolate the reactive limits to have a more accurate modelling of the generator when it is outside its active power limits. 

![image](https://github.com/user-attachments/assets/b27eda66-f905-46ea-bd62-f4e56c545d32)


### Describe the motivation

Differences in modeled behavior are detected when generators are in starting phase, and having extrapolation of the reactive limits enables a more accurate modelling.

### Extra Information

This modification may need a modification of getMinQ() and getMaxQ() of the class ReactiveCapabilityCurve in powsybl.iidm.

Diff:
diff --git a/docs/loadflow/parameters.md b/docs/loadflow/parameters.md
index f9709dce0f..0908c93bd6 100644
--- a/docs/loadflow/parameters.md
+++ b/docs/loadflow/parameters.md
@@ -192,6 +192,15 @@ When `useReactiveLimits` is set to `true`, this parameter is used to prioritize
 outside these limits. If set to `true`, if any generator has a target Q which is outside its reactive power limits (for its given target P), then its target Q 
 is overriden by the value of the exceeded limit (minQ or maxQ). The default value is `false`.
 
+**extrapolateReactiveLimits**  
+When `useReactiveLimits` is set to `true`, this parameter is used for equipment with reactive limits defined by reactive capability curves. If the target P value is outside the reactive
+capability curve limits (if it is below lowest P value or above highest P value), the behavior depends on the parameter `extrapolateReactiveLimit` :
+- If set to `false`: reactive limits at the given target P are defined by the min Q and max Q at the limit of active power of the capability curve.
+- If set to `true`: reactive limits at the given target P are defined by the extrapolation of the reactive limits slope of the reactive capability curve 
+(e.g. if the reactive limits decrease when active power decreases, the reactive limits will continue decreasing at the same rate below the minimal active power of the reactive capability curve)
+
+The default value is `false`.
+
 **phaseShifterControlMode**  
 - `CONTINUOUS_WITH_DISCRETISATION`: phase shifter control is solved by the Newton-Raphson inner-loop.
 - `INCREMENTAL`: phase shifter control is solved in the outer-loop
diff --git a/src/main/java/com/powsybl/openloadflow/OpenLoadFlowParameters.java b/src/main/java/com/powsybl/openloadflow/OpenLoadFlowParameters.java
index ef7b80f247..8d6c7f451c 100644
--- a/src/main/java/com/powsybl/openloadflow/OpenLoadFlowParameters.java
+++ b/src/main/java/com/powsybl/openloadflow/OpenLoadFlowParameters.java
@@ -81,7 +81,7 @@ public enum SlackDistributionFailureBehavior {
 
     public static final boolean GENERATOR_REACTIVE_POWER_REMOTE_CONTROL_DEFAULT_VALUE = false;
 
-    public static final boolean TRANSFORMER_REACTIVE_POWER_REMOTE_CONTROL_DEFAULT_VALUE = false;
+    public static final boolean TRANSFORMER_REACTIVE_POWER_CONTROL_DEFAULT_VALUE = false;
 
     public static final boolean LOAD_POWER_FACTOR_CONSTANT_DEFAULT_VALUE = false;
 
@@ -296,6 +296,8 @@ public enum FictitiousGeneratorVoltageControlCheckMode {
 
     public static final String DISABLE_INCONSISTENT_VOLTAGE_CONTROLS_PARAM_NAME = "disableInconsistentVoltageControls";
 
+    public static final String EXTRAPOLATE_REACTIVE_LIMITS_PARAM_NAME = "extrapolateReactiveLimits";
+
     public static <E extends Enum<E>> List<Object> getEnumPossibleValues(Class<E> enumClass) {
         return EnumSet.allOf(enumClass).stream().map(Enum::name).collect(Collectors.toList());
     }
@@ -377,7 +379,7 @@ public static List<Object> getAcSolverTypePossibleValues() {
         new Parameter(SLACK_BUS_P_MAX_MISMATCH_PARAM_NAME, ParameterType.DOUBLE, "Slack bus max active power mismatch", SLACK_BUS_P_MAX_MISMATCH_DEFAULT_VALUE, ParameterScope.FUNCTIONAL, SLACK_DISTRIBUTION_CATEGORY_KEY),
         new Parameter(VOLTAGE_PER_REACTIVE_POWER_CONTROL_PARAM_NAME, ParameterType.BOOLEAN, "Voltage per reactive power slope", VOLTAGE_PER_REACTIVE_POWER_CONTROL_DEFAULT_VALUE, ParameterScope.FUNCTIONAL, GENERATOR_VOLTAGE_CONTROL_CATEGORY_KEY),
         new Parameter(GENERATOR_REACTIVE_POWER_REMOTE_CONTROL_PARAM_NAME, ParameterType.BOOLEAN, "Generator remote reactive power control", GENERATOR_REACTIVE_POWER_REMOTE_CONTROL_DEFAULT_VALUE, ParameterScope.FUNCTIONAL, REACTIVE_POWER_CONTROL_CATEGORY_KEY),
-        new Parameter(TRANSFORMER_REACTIVE_POWER_CONTROL_PARAM_NAME, ParameterType.BOOLEAN, "Transformer reactive power control", TRANSFORMER_REACTIVE_POWER_REMOTE_CONTROL_DEFAULT_VALUE, ParameterScope.FUNCTIONAL, REACTIVE_POWER_CONTROL_CATEGORY_KEY),
+        new Parameter(TRANSFORMER_REACTIVE_POWER_CONTROL_PARAM_NAME, ParameterType.BOOLEAN, "Transformer reactive power control", TRANSFORMER_REACTIVE_POWER_CONTROL_DEFAULT_VALUE, ParameterScope.FUNCTIONAL, REACTIVE_POWER_CONTROL_CATEGORY_KEY),
         new Parameter(MAX_NEWTON_RAPHSON_ITERATIONS_PARAM_NAME, ParameterType.INTEGER, "Max iterations per Newton-Raphson", NewtonRaphsonParameters.DEFAULT_MAX_ITERATIONS, ParameterScope.FUNCTIONAL, NEWTON_RAPHSON_CATEGORY_KEY),
         new Parameter(MAX_OUTER_LOOP_ITERATIONS_PARAM_NAME, ParameterType.INTEGER, "Max outer loop iterations", AbstractLoadFlowParameters.DEFAULT_MAX_OUTER_LOOP_ITERATIONS, ParameterScope.FUNCTIONAL, OUTER_LOOPS_CATEGORY_KEY),
         new Parameter(NEWTON_RAPHSON_CONV_EPS_PER_EQ_PARAM_NAME, ParameterType.DOUBLE, "Newton-Raphson convergence epsilon per equation", NewtonRaphsonStoppingCriteria.DEFAULT_CONV_EPS_PER_EQ, ParameterScope.FUNCTIONAL, NEWTON_RAPHSON_CATEGORY_KEY),
@@ -439,8 +441,9 @@ public static List<Object> getAcSolverTypePossibleValues() {
         new Parameter(AREA_INTERCHANGE_CONTROL_AREA_TYPE_PARAM_NAME, ParameterType.STRING, "Area type for area interchange control", LfNetworkParameters.AREA_INTERCHANGE_CONTROL_AREA_TYPE_DEFAULT_VALUE, ParameterScope.FUNCTIONAL, SLACK_DISTRIBUTION_CATEGORY_KEY),
         new Parameter(AREA_INTERCHANGE_P_MAX_MISMATCH_PARAM_NAME, ParameterType.DOUBLE, "Area interchange max active power mismatch", AREA_INTERCHANGE_P_MAX_MISMATCH_DEFAULT_VALUE, ParameterScope.FUNCTIONAL, SLACK_DISTRIBUTION_CATEGORY_KEY),
         new Parameter(VOLTAGE_REMOTE_CONTROL_ROBUST_MODE_PARAM_NAME, ParameterType.BOOLEAN, "Generator voltage remote control robust mode", VOLTAGE_REMOTE_CONTROL_ROBUST_MODE_DEFAULT_VALUE, ParameterScope.FUNCTIONAL, GENERATOR_VOLTAGE_CONTROL_CATEGORY_KEY),
-        new Parameter(FORCE_TARGET_Q_IN_REACTIVE_LIMITS_PARAM_NAME, ParameterType.BOOLEAN, "Force targetQ in the reactive limit diagram", FORCE_TARGET_Q_IN_REACTIVE_LIMITS_DEFAULT_VALUE, ParameterScope.FUNCTIONAL, REACTIVE_POWER_CONTROL_CATEGORY_KEY),
-        new Parameter(DISABLE_INCONSISTENT_VOLTAGE_CONTROLS_PARAM_NAME, ParameterType.BOOLEAN, "Disable inconsistent voltage controls", LfNetworkParameters.DISABLE_INCONSISTENT_VOLTAGE_CONTROLS_DEFAULT_VALUE, ParameterScope.FUNCTIONAL, GENERATOR_VOLTAGE_CONTROL_CATEGORY_KEY)
+        new Parameter(FORCE_TARGET_Q_IN_REACTIVE_LIMITS_PARAM_NAME, ParameterType.BOOLEAN, "Force targetQ in the reactive limit diagram", FORCE_TARGET_Q_IN_REACTIVE_LIMITS_DEFAULT_VALUE, ParameterScope.FUNCTIONAL, GENERATOR_VOLTAGE_CONTROL_CATEGORY_KEY),
+        new Parameter(DISABLE_INCONSISTENT_VOLTAGE_CONTROLS_PARAM_NAME, ParameterType.BOOLEAN, "Disable inconsistent voltage controls", LfNetworkParameters.DISABLE_INCONSISTENT_VOLTAGE_CONTROLS_DEFAULT_VALUE, ParameterScope.FUNCTIONAL, GENERATOR_VOLTAGE_CONTROL_CATEGORY_KEY),
+        new Parameter(EXTRAPOLATE_REACTIVE_LIMITS_PARAM_NAME, ParameterType.BOOLEAN, "Extrapolate reactive limits diagram when outside active power limits", LfNetworkParameters.EXTRAPOLATE_REACTIVE_LIMITS_DEFAULT_VALUE, ParameterScope.FUNCTIONAL, GENERATOR_VOLTAGE_CONTROL_CATEGORY_KEY)
     );
 
     public enum VoltageInitModeOverride {
@@ -512,7 +515,7 @@ public enum LowImpedanceBranchMode {
 
     private boolean generatorReactivePowerRemoteControl = GENERATOR_REACTIVE_POWER_REMOTE_CONTROL_DEFAULT_VALUE;
 
-    private boolean transformerReactivePowerControl = TRANSFORMER_REACTIVE_POWER_REMOTE_CONTROL_DEFAULT_VALUE;
+    private boolean transformerReactivePowerControl = TRANSFORMER_REACTIVE_POWER_CONTROL_DEFAULT_VALUE;
 
     private int maxNewtonRaphsonIterations = NewtonRaphsonParameters.DEFAULT_MAX_ITERATIONS;
 
@@ -630,6 +633,8 @@ public enum ReactiveRangeCheckMode {
 
     private boolean disableInconsistentVoltageControls = LfNetworkParameters.DISABLE_INCONSISTENT_VOLTAGE_CONTROLS_DEFAULT_VALUE;
 
+    private boolean extrapolateReactiveLimits = LfNetworkParameters.EXTRAPOLATE_REACTIVE_LIMITS_DEFAULT_VALUE;
+
     public static double checkParameterValue(double parameterValue, boolean condition, String parameterName) {
         if (!condition) {
             throw new IllegalArgumentException("Invalid value for parameter " + parameterName + ": " + parameterValue);
@@ -1382,6 +1387,15 @@ public OpenLoadFlowParameters setDisableInconsistentVoltageControls(boolean disa
         return this;
     }
 
+    public boolean isExtrapolateReactiveLimits() {
+        return extrapolateReactiveLimits;
+    }
+
+    public OpenLoadFlowParameters setExtrapolateReactiveLimits(boolean extrapolateReactiveLimits) {
+        this.extrapolateReactiveLimits = extrapolateReactiveLimits;
+        return this;
+    }
+
     public static OpenLoadFlowParameters load() {
         return load(PlatformConfig.defaultConfig());
     }
@@ -1504,6 +1518,7 @@ public OpenLoadFlowParameters update(PlatformConfig platformConfig) {
                     config.getOptionalEnumProperty(FICTITIOUS_GENERATOR_VOLTAGE_CONTROL_CHECK_MODE, FictitiousGeneratorVoltageControlCheckMode.class)
                             .ifPresent(this::setFictitiousGeneratorVoltageControlCheckMode);
                     config.getOptionalBooleanProperty(VOLTAGE_REMOTE_CONTROL_ROBUST_MODE_PARAM_NAME).ifPresent(this::setVoltageRemoteControlRobustMode);
+                    config.getOptionalBooleanProperty(EXTRAPOLATE_REACTIVE_LIMITS_PARAM_NAME).ifPresent(this::setExtrapolateReactiveLimits);
                 });
         return this;
     }
@@ -1660,6 +1675,8 @@ public OpenLoadFlowParameters update(Map<String, String> properties) {
                 .ifPresent(prop -> this.setForceTargetQInReactiveLimits(Boolean.parseBoolean(prop)));
         Optional.ofNullable(properties.get(DISABLE_INCONSISTENT_VOLTAGE_CONTROLS_PARAM_NAME))
                 .ifPresent(prop -> this.setDisableInconsistentVoltageControls(Boolean.parseBoolean(prop)));
+        Optional.ofNullable(properties.get(EXTRAPOLATE_REACTIVE_LIMITS_PARAM_NAME))
+                .ifPresent(prop -> this.setExtrapolateReactiveLimits(Boolean.parseBoolean(prop)));
         return this;
     }
 
@@ -1739,6 +1756,7 @@ public Map<String, Object> toMap() {
         map.put(VOLTAGE_REMOTE_CONTROL_ROBUST_MODE_PARAM_NAME, voltageRemoteControlRobustMode);
         map.put(FORCE_TARGET_Q_IN_REACTIVE_LIMITS_PARAM_NAME, forceTargetQInReactiveLimits);
         map.put(DISABLE_INCONSISTENT_VOLTAGE_CONTROLS_PARAM_NAME, disableInconsistentVoltageControls);
+        map.put(EXTRAPOLATE_REACTIVE_LIMITS_PARAM_NAME, extrapolateReactiveLimits);
         return map;
     }
 
@@ -1897,7 +1915,8 @@ static LfNetworkParameters getNetworkParameters(LoadFlowParameters parameters, O
                 .setAreaInterchangeControl(parametersExt.isAreaInterchangeControl())
                 .setAreaInterchangeControlAreaType(parametersExt.getAreaInterchangeControlAreaType())
                 .setForceTargetQInReactiveLimits(parametersExt.isForceTargetQInReactiveLimits())
-                .setDisableInconsistentVoltageControls(parametersExt.isDisableInconsistentVoltageControls());
+                .setDisableInconsistentVoltageControls(parametersExt.isDisableInconsistentVoltageControls())
+                .setExtrapolateReactiveLimits(parametersExt.isExtrapolateReactiveLimits());
     }
 
     public static AcLoadFlowParameters createAcParameters(Network network, LoadFlowParameters parameters, OpenLoadFlowParameters parametersExt,
@@ -2138,7 +2157,8 @@ public static boolean equals(LoadFlowParameters parameters1, LoadFlowParameters
                 extension1.getAreaInterchangePMaxMismatch() == extension2.getAreaInterchangePMaxMismatch() &&
                 extension1.isVoltageRemoteControlRobustMode() == extension2.isVoltageRemoteControlRobustMode() &&
                 extension1.isForceTargetQInReactiveLimits() == extension2.isForceTargetQInReactiveLimits() &&
-                extension1.isDisableInconsistentVoltageControls() == extension2.isDisableInconsistentVoltageControls();
+                extension1.isDisableInconsistentVoltageControls() == extension2.isDisableInconsistentVoltageControls() &&
+                extension1.isExtrapolateReactiveLimits() == extension2.isExtrapolateReactiveLimits();
     }
 
     public static LoadFlowParameters clone(LoadFlowParameters parameters) {
@@ -2237,7 +2257,8 @@ public static LoadFlowParameters clone(LoadFlowParameters parameters) {
                     .setAreaInterchangePMaxMismatch(extension.getAreaInterchangePMaxMismatch())
                     .setVoltageRemoteControlRobustMode(extension.isVoltageRemoteControlRobustMode())
                     .setForceTargetQInReactiveLimits(extension.isForceTargetQInReactiveLimits())
-                    .setDisableInconsistentVoltageControls(extension.isDisableInconsistentVoltageControls());
+                    .setDisableInconsistentVoltageControls(extension.isDisableInconsistentVoltageControls())
+                    .setExtrapolateReactiveLimits(extension.isExtrapolateReactiveLimits());
 
             if (extension2 != null) {
                 parameters2.addExtension(OpenLoadFlowParameters.class, extension2);
diff --git a/src/main/java/com/powsybl/openloadflow/network/LfNetworkParameters.java b/src/main/java/com/powsybl/openloadflow/network/LfNetworkParameters.java
index e0b090cb33..58504f769c 100644
--- a/src/main/java/com/powsybl/openloadflow/network/LfNetworkParameters.java
+++ b/src/main/java/com/powsybl/openloadflow/network/LfNetworkParameters.java
@@ -70,6 +70,8 @@ public class LfNetworkParameters {
 
     public static final boolean DISABLE_INCONSISTENT_VOLTAGE_CONTROLS_DEFAULT_VALUE = false;
 
+    public static final boolean EXTRAPOLATE_REACTIVE_LIMITS_DEFAULT_VALUE = false;
+
     private boolean generatorVoltageRemoteControl = true;
 
     private boolean minImpedance = false;
@@ -154,6 +156,8 @@ public class LfNetworkParameters {
 
     private boolean disableInconsistentVoltageControls = DISABLE_INCONSISTENT_VOLTAGE_CONTROLS_DEFAULT_VALUE;
 
+    private boolean extrapolateReactiveLimits = EXTRAPOLATE_REACTIVE_LIMITS_DEFAULT_VALUE;
+
     public LfNetworkParameters() {
     }
 
@@ -622,6 +626,15 @@ public LfNetworkParameters setDisableInconsistentVoltageControls(boolean disable
         return this;
     }
 
+    public boolean isExtrapolateReactiveLimits() {
+        return extrapolateReactiveLimits;
+    }
+
+    public LfNetworkParameters setExtrapolateReactiveLimits(boolean extrapolateReactiveLimits) {
+        this.extrapolateReactiveLimits = extrapolateReactiveLimits;
+        return this;
+    }
+
     @Override
     public String toString() {
         return "LfNetworkParameters(" +
@@ -665,6 +678,7 @@ public String toString() {
                 ", areaInterchangeControlAreaType=" + areaInterchangeControlAreaType +
                 ", forceTargetQInReactiveLimits=" + forceTargetQInReactiveLimits +
                 ", disableInconsistentVoltageControls=" + disableInconsistentVoltageControls +
+                ", extrapolateReactiveLimits=" + extrapolateReactiveLimits +
                 ')';
     }
 }
diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfGenerator.java b/src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfGenerator.java
index b7f6bfeb8e..a0390a3e34 100644
--- a/src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfGenerator.java
+++ b/src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfGenerator.java
@@ -57,9 +57,12 @@ public abstract class AbstractLfGenerator extends AbstractLfInjection implements
 
     protected boolean reference;
 
-    protected AbstractLfGenerator(LfNetwork network, double targetP) {
+    private final boolean extrapolateReactiveLimits;
+
+    protected AbstractLfGenerator(LfNetwork network, double targetP, LfNetworkParameters parameters) {
         super(targetP, targetP);
         this.network = Objects.requireNonNull(network);
+        this.extrapolateReactiveLimits = parameters.isExtrapolateReactiveLimits();
     }
 
     protected record ActivePowerControlHelper(boolean participating, double participationFactor, double droop, double minTargetP, double maxTargetP) {
@@ -152,16 +155,28 @@ public OptionalDouble getRemoteControlReactiveKey() {
 
     @Override
     public double getMinQ() {
-        return getReactiveLimits()
-                .map(limits -> limits.getMinQ(targetP * PerUnit.SB) / PerUnit.SB)
-                .orElse(-Double.MAX_VALUE);
+        if (getReactiveLimits().isEmpty()) {
+            return -Double.MAX_VALUE;
+        }
+        ReactiveLimits reactiveLimits = getReactiveLimits().orElseThrow();
+        if (reactiveLimits.getKind() == ReactiveLimitsKind.CURVE) {
+            return ((ReactiveCapabilityCurve) reactiveLimits).getMinQ(targetP * PerUnit.SB, extrapolateReactiveLimits) / PerUnit.SB;
+        } else {
+            return reactiveLimits.getMinQ(targetP * PerUnit.SB) / PerUnit.SB;
+        }
     }
 
     @Override
     public double getMaxQ() {
-        return getReactiveLimits()
-                .map(limits -> limits.getMaxQ(targetP * PerUnit.SB) / PerUnit.SB)
-                .orElse(Double.MAX_VALUE);
+        if (getReactiveLimits().isEmpty()) {
+            return Double.MAX_VALUE;
+        }
+        ReactiveLimits reactiveLimits = getReactiveLimits().orElseThrow();
+        if (reactiveLimits.getKind() == ReactiveLimitsKind.CURVE) {
+            return ((ReactiveCapabilityCurve) reactiveLimits).getMaxQ(targetP * PerUnit.SB, extrapolateReactiveLimits) / PerUnit.SB;
+        } else {
+            return reactiveLimits.getMaxQ(targetP * PerUnit.SB) / PerUnit.SB;
+        }
     }
 
     @Override
diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfBatteryImpl.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfBatteryImpl.java
index d9a1a6caef..a06dc17b89 100644
--- a/src/main/java/com/powsybl/openloadflow/network/impl/LfBatteryImpl.java
+++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfBatteryImpl.java
@@ -38,7 +38,7 @@ public final class LfBatteryImpl extends AbstractLfGenerator {
     private final double minTargetP;
 
     private LfBatteryImpl(Battery battery, LfNetwork network, LfNetworkParameters parameters, LfNetworkLoadingReport report) {
-        super(network, battery.getTargetP() / PerUnit.SB);
+        super(network, battery.getTargetP() / PerUnit.SB, parameters);
         this.batteryRef = Ref.create(battery, parameters.isCacheEnabled());
         var apcHelper = ActivePowerControlHelper.create(battery, battery.getMinP(), battery.getMaxP());
         initialParticipating = apcHelper.participating();
diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfDanglingLineGenerator.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfDanglingLineGenerator.java
index 8d4b714ee3..864fc9ed7b 100644
--- a/src/main/java/com/powsybl/openloadflow/network/impl/LfDanglingLineGenerator.java
+++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfDanglingLineGenerator.java
@@ -27,7 +27,7 @@ public final class LfDanglingLineGenerator extends AbstractLfGenerator {
 
     private LfDanglingLineGenerator(DanglingLine danglingLine, LfNetwork network, String controlledLfBusId, LfNetworkParameters parameters,
                                     LfNetworkLoadingReport report) {
-        super(network, danglingLine.getGeneration().getTargetP() / PerUnit.SB);
+        super(network, danglingLine.getGeneration().getTargetP() / PerUnit.SB, parameters);
         this.danglingLineRef = Ref.create(danglingLine, parameters.isCacheEnabled());
 
         // local control only
diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfGeneratorImpl.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfGeneratorImpl.java
index f71392d8ab..52b17f2678 100644
--- a/src/main/java/com/powsybl/openloadflow/network/impl/LfGeneratorImpl.java
+++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfGeneratorImpl.java
@@ -52,7 +52,7 @@ public final class LfGeneratorImpl extends AbstractLfGenerator {
     private final boolean forceTargetQInReactiveLimits;
 
     private LfGeneratorImpl(Generator generator, LfNetwork network, LfNetworkParameters parameters, LfNetworkLoadingReport report) {
-        super(network, generator.getTargetP() / PerUnit.SB);
+        super(network, generator.getTargetP() / PerUnit.SB, parameters);
         this.generatorRef = Ref.create(generator, parameters.isCacheEnabled());
         // we force voltage control of generators tagged as condensers or tagged as fictitious if the dedicated mode is activated.
         forceVoltageControl = generator.isCondenser() || generator.isFictitious() && parameters.getFictitiousGeneratorVoltageControlCheckMode() == OpenLoadFlowParameters.FictitiousGeneratorVoltageControlCheckMode.FORCED;
diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfStaticVarCompensatorImpl.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfStaticVarCompensatorImpl.java
index 79aa50d4b2..ecc2955b26 100644
--- a/src/main/java/com/powsybl/openloadflow/network/impl/LfStaticVarCompensatorImpl.java
+++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfStaticVarCompensatorImpl.java
@@ -46,7 +46,7 @@ public final class LfStaticVarCompensatorImpl extends AbstractLfGenerator implem
 
     private LfStaticVarCompensatorImpl(StaticVarCompensator svc, LfNetwork network, AbstractLfBus bus, LfNetworkParameters parameters,
                                        LfNetworkLoadingReport report) {
-        super(network, 0);
+        super(network, 0, parameters);
         this.svcRef = Ref.create(svc, parameters.isCacheEnabled());
         this.nominalV = svc.getTerminal().getVoltageLevel().getNominalV();
         this.reactiveLimits = new MinMaxReactiveLimits() {
diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfVscConverterStationImpl.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfVscConverterStationImpl.java
index b6029fed48..1e8af2c901 100644
--- a/src/main/java/com/powsybl/openloadflow/network/impl/LfVscConverterStationImpl.java
+++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfVscConverterStationImpl.java
@@ -36,7 +36,7 @@ public class LfVscConverterStationImpl extends AbstractLfGenerator implements Lf
     private final boolean hvdcDandlingInIidm;
 
     public LfVscConverterStationImpl(VscConverterStation station, LfNetwork network, LfNetworkParameters parameters, LfNetworkLoadingReport report) {
-        super(network, HvdcUtils.getConverterStationTargetP(station) / PerUnit.SB);
+        super(network, HvdcUtils.getConverterStationTargetP(station) / PerUnit.SB, parameters);
         this.hvdcDandlingInIidm = HvdcConverterStations.isHvdcDanglingInIidm(station);
         this.stationRef = Ref.create(station, parameters.isCacheEnabled());
         this.lossFactor = station.getLossFactor();
diff --git a/src/test/java/com/powsybl/openloadflow/OpenLoadFlowParametersTest.java b/src/test/java/com/powsybl/openloadflow/OpenLoadFlowParametersTest.java
index 08e91155ac..b74854c083 100644
--- a/src/test/java/com/powsybl/openloadflow/OpenLoadFlowParametersTest.java
+++ b/src/test/java/com/powsybl/openloadflow/OpenLoadFlowParametersTest.java
@@ -448,7 +448,7 @@ void testCloneParameters() {
     @Test
     void testToString() {
         OpenLoadFlowParameters parameters = new OpenLoadFlowParameters();
-        assertEquals("OpenLoadFlowParameters(slackBusSelectionMode=MOST_MESHED, slackBusesIds=[], slackDistributionFailureBehavior=LEAVE_ON_SLACK_BUS, voltageRemoteControl=true, lowImpedanceBranchMode=REPLACE_BY_ZERO_IMPEDANCE_LINE, loadPowerFactorConstant=false, plausibleActivePowerLimit=5000.0, newtonRaphsonStoppingCriteriaType=UNIFORM_CRITERIA, slackBusPMaxMismatch=1.0, maxActivePowerMismatch=0.01, maxReactivePowerMismatch=0.01, maxVoltageMismatch=1.0E-4, maxAngleMismatch=1.0E-5, maxRatioMismatch=1.0E-5, maxSusceptanceMismatch=1.0E-4, voltagePerReactivePowerControl=false, generatorReactivePowerRemoteControl=false, transformerReactivePowerControl=false, maxNewtonRaphsonIterations=15, maxOuterLoopIterations=20, newtonRaphsonConvEpsPerEq=1.0E-4, voltageInitModeOverride=NONE, transformerVoltageControlMode=WITH_GENERATOR_VOLTAGE_CONTROL, shuntVoltageControlMode=WITH_GENERATOR_VOLTAGE_CONTROL, minPlausibleTargetVoltage=0.8, maxPlausibleTargetVoltage=1.2, minRealisticVoltage=0.5, maxRealisticVoltage=2.0, reactiveRangeCheckMode=MAX, lowImpedanceThreshold=1.0E-8, networkCacheEnabled=false, svcVoltageMonitoring=true, stateVectorScalingMode=NONE, maxSlackBusCount=1, debugDir=null, incrementalTransformerRatioTapControlOuterLoopMaxTapShift=3, secondaryVoltageControl=false, reactiveLimitsMaxPqPvSwitch=3, phaseShifterControlMode=CONTINUOUS_WITH_DISCRETISATION, alwaysUpdateNetwork=false, mostMeshedSlackBusSelectorMaxNominalVoltagePercentile=95.0, reportedFeatures=[], slackBusCountryFilter=[], actionableSwitchesIds=[], actionableTransformersIds=[], asymmetrical=false, minNominalVoltageTargetVoltageCheck=20.0, reactivePowerDispatchMode=Q_EQUAL_PROPORTION, outerLoopNames=null, useActiveLimits=true, disableVoltageControlOfGeneratorsOutsideActivePowerLimits=false, lineSearchStateVectorScalingMaxIteration=10, lineSearchStateVectorScalingStepFold=1.3333333333333333, maxVoltageChangeStateVectorScalingMaxDv=0.1, maxVoltageChangeStateVectorScalingMaxDphi=0.17453292519943295, linePerUnitMode=IMPEDANCE, useLoadModel=false, dcApproximationType=IGNORE_R, simulateAutomationSystems=false, acSolverType=NEWTON_RAPHSON, maxNewtonKrylovIterations=100, newtonKrylovLineSearch=false, referenceBusSelectionMode=FIRST_SLACK, writeReferenceTerminals=true, voltageTargetPriorities=[GENERATOR, TRANSFORMER, SHUNT], transformerVoltageControlUseInitialTapPosition=false, generatorVoltageControlMinNominalVoltage=-1.0, fictitiousGeneratorVoltageControlCheckMode=FORCED, areaInterchangeControl=false, areaInterchangeControlAreaType=ControlArea, areaInterchangePMaxMismatch=2.0, voltageRemoteControlRobustMode=true, forceTargetQInReactiveLimits=false, disableInconsistentVoltageControls=false)",
+        assertEquals("OpenLoadFlowParameters(slackBusSelectionMode=MOST_MESHED, slackBusesIds=[], slackDistributionFailureBehavior=LEAVE_ON_SLACK_BUS, voltageRemoteControl=true, lowImpedanceBranchMode=REPLACE_BY_ZERO_IMPEDANCE_LINE, loadPowerFactorConstant=false, plausibleActivePowerLimit=5000.0, newtonRaphsonStoppingCriteriaType=UNIFORM_CRITERIA, slackBusPMaxMismatch=1.0, maxActivePowerMismatch=0.01, maxReactivePowerMismatch=0.01, maxVoltageMismatch=1.0E-4, maxAngleMismatch=1.0E-5, maxRatioMismatch=1.0E-5, maxSusceptanceMismatch=1.0E-4, voltagePerReactivePowerControl=false, generatorReactivePowerRemoteControl=false, transformerReactivePowerControl=false, maxNewtonRaphsonIterations=15, maxOuterLoopIterations=20, newtonRaphsonConvEpsPerEq=1.0E-4, voltageInitModeOverride=NONE, transformerVoltageControlMode=WITH_GENERATOR_VOLTAGE_CONTROL, shuntVoltageControlMode=WITH_GENERATOR_VOLTAGE_CONTROL, minPlausibleTargetVoltage=0.8, maxPlausibleTargetVoltage=1.2, minRealisticVoltage=0.5, maxRealisticVoltage=2.0, reactiveRangeCheckMode=MAX, lowImpedanceThreshold=1.0E-8, networkCacheEnabled=false, svcVoltageMonitoring=true, stateVectorScalingMode=NONE, maxSlackBusCount=1, debugDir=null, incrementalTransformerRatioTapControlOuterLoopMaxTapShift=3, secondaryVoltageControl=false, reactiveLimitsMaxPqPvSwitch=3, phaseShifterControlMode=CONTINUOUS_WITH_DISCRETISATION, alwaysUpdateNetwork=false, mostMeshedSlackBusSelectorMaxNominalVoltagePercentile=95.0, reportedFeatures=[], slackBusCountryFilter=[], actionableSwitchesIds=[], actionableTransformersIds=[], asymmetrical=false, minNominalVoltageTargetVoltageCheck=20.0, reactivePowerDispatchMode=Q_EQUAL_PROPORTION, outerLoopNames=null, useActiveLimits=true, disableVoltageControlOfGeneratorsOutsideActivePowerLimits=false, lineSearchStateVectorScalingMaxIteration=10, lineSearchStateVectorScalingStepFold=1.3333333333333333, maxVoltageChangeStateVectorScalingMaxDv=0.1, maxVoltageChangeStateVectorScalingMaxDphi=0.17453292519943295, linePerUnitMode=IMPEDANCE, useLoadModel=false, dcApproximationType=IGNORE_R, simulateAutomationSystems=false, acSolverType=NEWTON_RAPHSON, maxNewtonKrylovIterations=100, newtonKrylovLineSearch=false, referenceBusSelectionMode=FIRST_SLACK, writeReferenceTerminals=true, voltageTargetPriorities=[GENERATOR, TRANSFORMER, SHUNT], transformerVoltageControlUseInitialTapPosition=false, generatorVoltageControlMinNominalVoltage=-1.0, fictitiousGeneratorVoltageControlCheckMode=FORCED, areaInterchangeControl=false, areaInterchangeControlAreaType=ControlArea, areaInterchangePMaxMismatch=2.0, voltageRemoteControlRobustMode=true, forceTargetQInReactiveLimits=false, disableInconsistentVoltageControls=false, extrapolateReactiveLimits=false)",
                 parameters.toString());
     }
 
diff --git a/src/test/java/com/powsybl/openloadflow/OpenLoadFlowProviderTest.java b/src/test/java/com/powsybl/openloadflow/OpenLoadFlowProviderTest.java
index 9d94e20ae2..cc0fc7ef1f 100644
--- a/src/test/java/com/powsybl/openloadflow/OpenLoadFlowProviderTest.java
+++ b/src/test/java/com/powsybl/openloadflow/OpenLoadFlowProviderTest.java
@@ -51,7 +51,7 @@ void test() {
     void testDcParameters() {
         Network network = Mockito.mock(Network.class);
         DcLoadFlowParameters dcParameters = OpenLoadFlowParameters.createDcParameters(network, new LoadFlowParameters().setReadSlackBus(true), new OpenLoadFlowParameters(), new DenseMatrixFactory(), new EvenShiloachGraphDecrementalConnectivityFactory<>(), true);
-        assertEquals("DcLoadFlowParameters(networkParameters=LfNetworkParameters(slackBusSelector=NetworkSlackBusSelector, connectivityFactory=EvenShiloachGraphDecrementalConnectivityFactory, generatorVoltageRemoteControl=false, minImpedance=false, twtSplitShuntAdmittance=false, breakers=false, plausibleActivePowerLimit=5000.0, computeMainConnectedComponentOnly=true, countriesToBalance=[], distributedOnConformLoad=false, phaseControl=false, transformerVoltageControl=false, voltagePerReactivePowerControl=false, generatorReactivePowerRemoteControl=false, transformerReactivePowerControl=false, loadFlowModel=DC, reactiveLimits=false, hvdcAcEmulation=true, minPlausibleTargetVoltage=0.8, maxPlausibleTargetVoltage=1.2, loaderPostProcessorSelection=[], reactiveRangeCheckMode=MAX, lowImpedanceThreshold=1.0E-8, svcVoltageMonitoring=false, maxSlackBusCount=1, debugDir=null, secondaryVoltageControl=false, cacheEnabled=false, asymmetrical=false, minNominalVoltageTargetVoltageCheck=20.0, linePerUnitMode=IMPEDANCE, useLoadModel=false, simulateAutomationSystems=false, referenceBusSelector=ReferenceBusFirstSlackSelector, voltageTargetPriorities=[GENERATOR, TRANSFORMER, SHUNT], fictitiousGeneratorVoltageControlCheckMode=FORCED, areaInterchangeControl=false, areaInterchangeControlAreaType=ControlArea, forceTargetQInReactiveLimits=false, disableInconsistentVoltageControls=false), equationSystemCreationParameters=DcEquationSystemCreationParameters(updateFlows=true, forcePhaseControlOffAndAddAngle1Var=true, useTransformerRatio=true, dcApproximationType=IGNORE_R), matrixFactory=DenseMatrixFactory, distributedSlack=true, balanceType=PROPORTIONAL_TO_GENERATION_P_MAX, setVToNan=true, maxOuterLoopIterations=20)",
+        assertEquals("DcLoadFlowParameters(networkParameters=LfNetworkParameters(slackBusSelector=NetworkSlackBusSelector, connectivityFactory=EvenShiloachGraphDecrementalConnectivityFactory, generatorVoltageRemoteControl=false, minImpedance=false, twtSplitShuntAdmittance=false, breakers=false, plausibleActivePowerLimit=5000.0, computeMainConnectedComponentOnly=true, countriesToBalance=[], distributedOnConformLoad=false, phaseControl=false, transformerVoltageControl=false, voltagePerReactivePowerControl=false, generatorReactivePowerRemoteControl=false, transformerReactivePowerControl=false, loadFlowModel=DC, reactiveLimits=false, hvdcAcEmulation=true, minPlausibleTargetVoltage=0.8, maxPlausibleTargetVoltage=1.2, loaderPostProcessorSelection=[], reactiveRangeCheckMode=MAX, lowImpedanceThreshold=1.0E-8, svcVoltageMonitoring=false, maxSlackBusCount=1, debugDir=null, secondaryVoltageControl=false, cacheEnabled=false, asymmetrical=false, minNominalVoltageTargetVoltageCheck=20.0, linePerUnitMode=IMPEDANCE, useLoadModel=false, simulateAutomationSystems=false, referenceBusSelector=ReferenceBusFirstSlackSelector, voltageTargetPriorities=[GENERATOR, TRANSFORMER, SHUNT], fictitiousGeneratorVoltageControlCheckMode=FORCED, areaInterchangeControl=false, areaInterchangeControlAreaType=ControlArea, forceTargetQInReactiveLimits=false, disableInconsistentVoltageControls=false, extrapolateReactiveLimits=false), equationSystemCreationParameters=DcEquationSystemCreationParameters(updateFlows=true, forcePhaseControlOffAndAddAngle1Var=true, useTransformerRatio=true, dcApproximationType=IGNORE_R), matrixFactory=DenseMatrixFactory, distributedSlack=true, balanceType=PROPORTIONAL_TO_GENERATION_P_MAX, setVToNan=true, maxOuterLoopIterations=20)",
                 dcParameters.toString());
     }
 
@@ -59,7 +59,7 @@ void testDcParameters() {
     void testAcParameters() {
         Network network = Mockito.mock(Network.class);
         AcLoadFlowParameters acParameters = OpenLoadFlowParameters.createAcParameters(network, new LoadFlowParameters().setReadSlackBus(true), new OpenLoadFlowParameters(), new DenseMatrixFactory(), new EvenShiloachGraphDecrementalConnectivityFactory<>(), false, false);
-        assertEquals("AcLoadFlowParameters(networkParameters=LfNetworkParameters(slackBusSelector=NetworkSlackBusSelector, connectivityFactory=EvenShiloachGraphDecrementalConnectivityFactory, generatorVoltageRemoteControl=true, minImpedance=false, twtSplitShuntAdmittance=false, breakers=false, plausibleActivePowerLimit=5000.0, computeMainConnectedComponentOnly=true, countriesToBalance=[], distributedOnConformLoad=false, phaseControl=false, transformerVoltageControl=false, voltagePerReactivePowerControl=false, generatorReactivePowerRemoteControl=false, transformerReactivePowerControl=false, loadFlowModel=AC, reactiveLimits=true, hvdcAcEmulation=true, minPlausibleTargetVoltage=0.8, maxPlausibleTargetVoltage=1.2, loaderPostProcessorSelection=[], reactiveRangeCheckMode=MAX, lowImpedanceThreshold=1.0E-8, svcVoltageMonitoring=true, maxSlackBusCount=1, debugDir=null, secondaryVoltageControl=false, cacheEnabled=false, asymmetrical=false, minNominalVoltageTargetVoltageCheck=20.0, linePerUnitMode=IMPEDANCE, useLoadModel=false, simulateAutomationSystems=false, referenceBusSelector=ReferenceBusFirstSlackSelector, voltageTargetPriorities=[GENERATOR, TRANSFORMER, SHUNT], fictitiousGeneratorVoltageControlCheckMode=FORCED, areaInterchangeControl=false, areaInterchangeControlAreaType=ControlArea, forceTargetQInReactiveLimits=false, disableInconsistentVoltageControls=false), equationSystemCreationParameters=AcEquationSystemCreationParameters(forceA1Var=false), acSolverParameters=NewtonRaphsonParameters(maxIterations=15, stoppingCriteria=DefaultNewtonRaphsonStoppingCriteria, stateVectorScalingMode=NONE, alwaysUpdateNetwork=false, lineSearchStateVectorScalingMaxIteration=10, lineSearchStateVectorScalingStepFold=1.3333333333333333, maxVoltageChangeStateVectorScalingMaxDv=0.1, maxVoltageChangeStateVectorScalingMaxDphi=0.17453292519943295), outerLoops=[DistributedSlackOuterLoop, MonitoringVoltageOuterLoop, ReactiveLimitsOuterLoop], maxOuterLoopIterations=20, matrixFactory=DenseMatrixFactory, voltageInitializer=UniformValueVoltageInitializer, asymmetrical=false, slackDistributionFailureBehavior=LEAVE_ON_SLACK_BUS, solverFactory=NewtonRaphsonFactory, detailedReport=false, voltageRemoteControlRobustMode=true, minRealisticVoltage=0.5, maxRealisticVoltage=2.0)",
+        assertEquals("AcLoadFlowParameters(networkParameters=LfNetworkParameters(slackBusSelector=NetworkSlackBusSelector, connectivityFactory=EvenShiloachGraphDecrementalConnectivityFactory, generatorVoltageRemoteControl=true, minImpedance=false, twtSplitShuntAdmittance=false, breakers=false, plausibleActivePowerLimit=5000.0, computeMainConnectedComponentOnly=true, countriesToBalance=[], distributedOnConformLoad=false, phaseControl=false, transformerVoltageControl=false, voltagePerReactivePowerControl=false, generatorReactivePowerRemoteControl=false, transformerReactivePowerControl=false, loadFlowModel=AC, reactiveLimits=true, hvdcAcEmulation=true, minPlausibleTargetVoltage=0.8, maxPlausibleTargetVoltage=1.2, loaderPostProcessorSelection=[], reactiveRangeCheckMode=MAX, lowImpedanceThreshold=1.0E-8, svcVoltageMonitoring=true, maxSlackBusCount=1, debugDir=null, secondaryVoltageControl=false, cacheEnabled=false, asymmetrical=false, minNominalVoltageTargetVoltageCheck=20.0, linePerUnitMode=IMPEDANCE, useLoadModel=false, simulateAutomationSystems=false, referenceBusSelector=ReferenceBusFirstSlackSelector, voltageTargetPriorities=[GENERATOR, TRANSFORMER, SHUNT], fictitiousGeneratorVoltageControlCheckMode=FORCED, areaInterchangeControl=false, areaInterchangeControlAreaType=ControlArea, forceTargetQInReactiveLimits=false, disableInconsistentVoltageControls=false, extrapolateReactiveLimits=false), equationSystemCreationParameters=AcEquationSystemCreationParameters(forceA1Var=false), acSolverParameters=NewtonRaphsonParameters(maxIterations=15, stoppingCriteria=DefaultNewtonRaphsonStoppingCriteria, stateVectorScalingMode=NONE, alwaysUpdateNetwork=false, lineSearchStateVectorScalingMaxIteration=10, lineSearchStateVectorScalingStepFold=1.3333333333333333, maxVoltageChangeStateVectorScalingMaxDv=0.1, maxVoltageChangeStateVectorScalingMaxDphi=0.17453292519943295), outerLoops=[DistributedSlackOuterLoop, MonitoringVoltageOuterLoop, ReactiveLimitsOuterLoop], maxOuterLoopIterations=20, matrixFactory=DenseMatrixFactory, voltageInitializer=UniformValueVoltageInitializer, asymmetrical=false, slackDistributionFailureBehavior=LEAVE_ON_SLACK_BUS, solverFactory=NewtonRaphsonFactory, detailedReport=false, voltageRemoteControlRobustMode=true, minRealisticVoltage=0.5, maxRealisticVoltage=2.0)",
                      acParameters.toString());
     }
 
@@ -87,7 +87,7 @@ void testGetExtendedVoltageInitializer() {
     @Test
     void specificParametersTest() {
         OpenLoadFlowProvider provider = new OpenLoadFlowProvider();
-        assertEquals(74, provider.getSpecificParameters().size());
+        assertEquals(75, provider.getSpecificParameters().size());
         LoadFlowParameters parameters = new LoadFlowParameters();
 
         provider.loadSpecificParameters(Collections.emptyMap())
@@ -110,7 +110,7 @@ void testCreateMapFromSpecificParameters() {
         OpenLoadFlowParameters parametersExt = new OpenLoadFlowParameters();
         OpenLoadFlowProvider provider = new OpenLoadFlowProvider();
         Map<String, String> map = provider.createMapFromSpecificParameters(parametersExt);
-        assertEquals(74, map.size());
+        assertEquals(75, map.size());
         assertEquals(provider.getSpecificParameters().size(), map.size());
     }
 
diff --git a/src/test/java/com/powsybl/openloadflow/ac/AcloadFlowReactiveLimitsTest.java b/src/test/java/com/powsybl/openloadflow/ac/AcloadFlowReactiveLimitsTest.java
index dd4a20b9a4..7b29b699e0 100644
--- a/src/test/java/com/powsybl/openloadflow/ac/AcloadFlowReactiveLimitsTest.java
+++ b/src/test/java/com/powsybl/openloadflow/ac/AcloadFlowReactiveLimitsTest.java
@@ -13,14 +13,18 @@
 import com.powsybl.loadflow.LoadFlowParameters;
 import com.powsybl.loadflow.LoadFlowResult;
 import com.powsybl.math.matrix.DenseMatrixFactory;
+import com.powsybl.openloadflow.OpenLoadFlowParameters;
 import com.powsybl.openloadflow.OpenLoadFlowProvider;
 import com.powsybl.openloadflow.network.*;
 import com.powsybl.openloadflow.network.impl.Networks;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
 
 import java.util.List;
 
+import static com.powsybl.openloadflow.util.LoadFlowAssert.assertActivePowerEquals;
 import static com.powsybl.openloadflow.util.LoadFlowAssert.assertReactivePowerEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -41,6 +45,7 @@ class AcloadFlowReactiveLimitsTest {
 
     private LoadFlow.Runner loadFlowRunner;
     private LoadFlowParameters parameters;
+    private OpenLoadFlowParameters parametersExt;
 
     private void createNetwork() {
         network = EurostagFactory.fix(EurostagTutorialExample1Factory.create());
@@ -104,6 +109,7 @@ void setUp() {
         loadFlowRunner = new LoadFlow.Runner(new OpenLoadFlowProvider(new DenseMatrixFactory()));
         parameters = new LoadFlowParameters().setUseReactiveLimits(true)
                 .setDistributedSlack(false);
+        parametersExt = OpenLoadFlowParameters.create(parameters);
     }
 
     @Test
@@ -173,4 +179,28 @@ void testZeroReactiveRangeAndReactiveLimitsDisabled() {
         assertReactivePowerEquals(-152.265, gen2.getTerminal());
         assertReactivePowerEquals(-199.998, nhv2Nload.getTerminal2());
     }
+
+    @ParameterizedTest
+    @ValueSource(booleans = {false, true})
+    void testReactiveLimitsExtrapolation(boolean extrapolate) {
+        parametersExt.setExtrapolateReactiveLimits(extrapolate);
+
+        gen2.newReactiveCapabilityCurve() // gen2.targetP = 100 => when extrapolated, minQ = -200 and maxQ = 100
+                .beginPoint().setP(200).setMinQ(-300).setMaxQ(150).endPoint()
+                .beginPoint().setP(300).setMinQ(-400).setMaxQ(200).endPoint()
+                .add();
+        LoadFlowResult result = loadFlowRunner.run(network, parameters);
+        assertTrue(result.isFullyConverged());
+        assertActivePowerEquals(-100.0, gen2.getTerminal());
+        assertReactivePowerEquals(extrapolate ? -100.0 : -150, gen2.getTerminal()); // reaching gen2 maxQ
+
+        gen2.newReactiveCapabilityCurve() // gen2.targetP = 100 => when extrapolated, minQ = 200 and maxQ = 600
+                .beginPoint().setP(200).setMinQ(300).setMaxQ(700).endPoint()
+                .beginPoint().setP(300).setMinQ(400).setMaxQ(800).endPoint()
+                .add();
+        result = loadFlowRunner.run(network, parameters);
+        assertTrue(result.isFullyConverged());
+        assertActivePowerEquals(-100.0, gen2.getTerminal());
+        assertReactivePowerEquals(extrapolate ? -200.0 : -300, gen2.getTerminal()); // reaching gen2 minQ
+    }
 }
diff --git a/src/test/resources/debug-parameters.json b/src/test/resources/debug-parameters.json
index 8a1a265124..ec45316a37 100644
--- a/src/test/resources/debug-parameters.json
+++ b/src/test/resources/debug-parameters.json
@@ -93,7 +93,8 @@
         "areaInterchangeControlAreaType" : "ControlArea",
         "areaInterchangePMaxMismatch" : 2.0,
         "forceTargetQInReactiveLimits" : false,
-        "disableInconsistentVoltageControls" : false
+        "disableInconsistentVoltageControls" : false,
+        "extrapolateReactiveLimits" : false
       }
     }
   },


Reviews:
user: login: vidaldid-rte
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-02T12:32:54Z
user: login: vidaldid-rte
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-02T12:33:15Z
user: login: vidaldid-rte
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-05-15T13:32:20Z
user: login: jeandemanged
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T07:44:41Z
user: login: jeandemanged
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-19T08:59:24Z
user: login: vidaldid-rte
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-19T12:02:53Z

Review comments:
diff_hunk: @@ -152,16 +155,26 @@ public OptionalDouble getRemoteControlReactiveKey() {
 
     @Override
     public double getMinQ() {
-        return getReactiveLimits()
-                .map(limits -> limits.getMinQ(targetP * PerUnit.SB) / PerUnit.SB)
-                .orElse(-Double.MAX_VALUE);
+        if (getReactiveLimits().isEmpty()) {
+            return -Double.MAX_VALUE;
+        }
+        ReactiveLimits reactiveLimits = getReactiveLimits().orElseThrow();
+        if (reactiveLimits.getKind() == ReactiveLimitsKind.CURVE) {
+            return ((ReactiveCapabilityCurve) reactiveLimits).getMinQ(targetP * PerUnit.SB, extrapolateReactiveLimits) / PerUnit.SB;
+        }
path: src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfGenerator.java
user: login: vidaldid-rte
body: A else is better here.
created_at: 2025-05-02T12:32:54Z
author_association: COLLABORATOR
diff_hunk: @@ -152,16 +155,26 @@ public OptionalDouble getRemoteControlReactiveKey() {
 
     @Override
     public double getMinQ() {
-        return getReactiveLimits()
-                .map(limits -> limits.getMinQ(targetP * PerUnit.SB) / PerUnit.SB)
-                .orElse(-Double.MAX_VALUE);
+        if (getReactiveLimits().isEmpty()) {
+            return -Double.MAX_VALUE;
+        }
+        ReactiveLimits reactiveLimits = getReactiveLimits().orElseThrow();
+        if (reactiveLimits.getKind() == ReactiveLimitsKind.CURVE) {
+            return ((ReactiveCapabilityCurve) reactiveLimits).getMinQ(targetP * PerUnit.SB, extrapolateReactiveLimits) / PerUnit.SB;
+        }
+        return reactiveLimits.getMinQ(targetP * PerUnit.SB) / PerUnit.SB;
     }
 
     @Override
     public double getMaxQ() {
-        return getReactiveLimits()
-                .map(limits -> limits.getMaxQ(targetP * PerUnit.SB) / PerUnit.SB)
-                .orElse(Double.MAX_VALUE);
+        if (getReactiveLimits().isEmpty()) {
+            return Double.MAX_VALUE;
+        }
+        ReactiveLimits reactiveLimits = getReactiveLimits().orElseThrow();
+        if (reactiveLimits.getKind() == ReactiveLimitsKind.CURVE) {
+            return ((ReactiveCapabilityCurve) reactiveLimits).getMaxQ(targetP * PerUnit.SB, extrapolateReactiveLimits) / PerUnit.SB;
+        }
path: src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfGenerator.java
user: login: vidaldid-rte
body: else
created_at: 2025-05-02T12:33:15Z
author_association: COLLABORATOR
diff_hunk: @@ -192,6 +192,15 @@ When `useReactiveLimits` is set to `true`, this parameter is used to prioritize
 outside these limits. If set to `true`, if any generator has a target Q which is outside its reactive power limits (for its given target P), then its target Q 
 is overriden by the value of the exceeded limit (minQ or maxQ). The default value is `false`.
 
+**extrapolateReactiveLimits**  
+When `useReactiveLimits` is set to `true`, this parameter is used for equipments that have reactive limits defined by reactive capability curves. If the target P value is outside the reactive
path: docs/loadflow/parameters.md
user: login: jeandemanged
body: ```suggestion
When `useReactiveLimits` is set to `true`, this parameter is used for equipment with reactive limits defined by reactive capability curves. If the target P value is outside the reactive

```
created_at: 2025-05-19T07:44:41Z
author_association: MEMBER

PR comments:
user: login: sonarqubecloud[bot]
created_at: 2025-05-20T07:33:07Z
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-open-loadflow&pullRequest=1224) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=com.powsybl%3Apowsybl-open-loadflow&pullRequest=1224&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-open-loadflow&pullRequest=1224&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-open-loadflow&pullRequest=1224&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [92.5% Coverage on New Code](https://sonarcloud.io/component_measures?id=com.powsybl%3Apowsybl-open-loadflow&pullRequest=1224&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-open-loadflow&pullRequest=1224&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=com.powsybl%3Apowsybl-open-loadflow&pullRequest=1224)


author_association: NONE