Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
95 commits
Select commit Hold shift + click to select a range
05aa325
feat(solver): add a first version of the Resilient LF Model implement…
mdeboute Apr 8, 2025
9f3bbff
refactor(test): add new non-regression tests for the Knitro based Res…
mdeboute Apr 9, 2025
ae56473
refactor(test): improve the tests by adding check on angles and curre…
mdeboute Apr 17, 2025
8408cc7
refactor(solver): remove some useless logs on the Knitro Resilient So…
mdeboute Apr 17, 2025
693d4c7
refactor(pom): add my name in the contributors
mdeboute Apr 17, 2025
3dfefcd
refactor: remove useless logger statements and comments in the RKN so…
mdeboute Apr 17, 2025
afd5b51
refactor: fix the message of an exception in KnitroLoadFlowParameters
mdeboute Apr 24, 2025
6650f97
refactor: redesign the resilient solver file for better readability &…
mdeboute Apr 24, 2025
55c045a
refactor(test): change the applyHighSusceptancePerturbation method to…
Apr 24, 2025
9305931
Merge remote-tracking branch 'origin/feat/resilient-lf' into feat/res…
Apr 24, 2025
57ed9ee
fix(test): update default tolerance and adjust per-unit current calcu…
mdeboute Apr 29, 2025
bf6e01f
refactor(solver): move penalty weights to class-level constants in Re…
mdeboute Apr 29, 2025
eac3116
refactor(test): change the testResilienceWithLowImpedanceLineOnVariou…
Apr 29, 2025
36c5364
Merge remote-tracking branch 'origin/feat/resilient-lf' into feat/res…
Apr 29, 2025
b6264dd
feat(test): add test for convergence on TYNDP data and refactor path …
mdeboute May 2, 2025
de9f5fb
fix(solver): update warning message for missing Jacobian entry to spe…
mdeboute May 2, 2025
e64a5ea
feat(test): add a test for a three bus configuration with a low imped…
May 2, 2025
1184625
refactor(test): rename test method for consistency in naming convention
mdeboute May 2, 2025
0004575
Merge remote-tracking branch 'origin/feat/resilient-lf' into feat/res…
May 5, 2025
4688bdc
refactor(test): change the three bus configuration test to make it mo…
May 5, 2025
1b5e728
feat(test): add an active power perturbation test on HU networks and …
May 9, 2025
218e6c3
feat(test): add a reactive power perturbation test on HU networks and…
May 13, 2025
290a4e3
feat(solver): enhance KnitroStatus enum with detailed status codes an…
mdeboute May 14, 2025
ecb1383
Merge remote-tracking branch 'origin/feat/resilient-lf' into feat/res…
mdeboute May 14, 2025
9b877cc
fix(solver): add slack contribution to nonlinear constraints: include…
mdeboute May 16, 2025
f5d53c4
refactor(solver): adjust penalty weights and enhance slack logging: r…
mdeboute May 19, 2025
1ca0075
feat(solver): update penalty weights and introduce lambda for absolut…
mdeboute May 19, 2025
377ffe8
refactor(solver): standardize variable naming to respect conventions
mdeboute May 19, 2025
66f293e
feat(test): split unit convergence tests and perturbation tests into …
May 20, 2025
fa38574
refactor(test): delete old ResilientAcLoadFlowTest class
May 20, 2025
bcce458
refactor(test): reformat code
mdeboute May 20, 2025
0d6cad9
fix(solver): add slack coefficient terms for P & Q to the updated Jac…
Jun 3, 2025
a419488
Merge remote-tracking branch 'origin/feat/resilient-lf' into feat/res…
Jun 3, 2025
c8ef8a2
refactor(solver): improve variable initialization and enhance slack v…
mdeboute Jun 3, 2025
fb832be
fix(solver): update the number of slacks variables and constraint ba…
Jun 3, 2025
91340cb
refactor(test): reorganize unit tests and add ES data convergence test
Jun 3, 2025
26f1207
refactor(test): switch to SparseMatrixFactory and set voltage initial…
mdeboute Jun 4, 2025
03e101c
feat(solver): add Hessian computation mode parameter and the sparsity…
mdeboute Jun 4, 2025
afd402c
refactor(test): reorganize unit and perturbation tests and add ES dat…
Jun 4, 2025
d6439f1
fix(test): select a load that can be anywhere in the network for the …
Jun 4, 2025
31b56ab
refactor(solver): add slack variables back on slack buses
Jun 5, 2025
a843f18
refactor(solver): remove the logic of slack variables on slack buses …
Jun 5, 2025
4da88cd
feat(test): add a class that provides networks for test classes and m…
Jun 5, 2025
baa74e8
Merge remote-tracking branch 'origin/feat/resilient-lf' into feat/res…
Jun 5, 2025
c8fb28f
fix(solver): change condition to detect missing values in sparse matr…
Jun 6, 2025
23ae2cf
refactor(solver): change gradient callback function to reduce iterati…
Jun 10, 2025
b81be61
fix(solver): inverse the sign convention for negative and positive P …
Jun 10, 2025
64d1089
refactor(test): add some information like the algorithm used, the tes…
Jun 13, 2025
627da93
fix(test): add Disabled annotation to convergence test on Rte network…
Jun 13, 2025
09110ec
feat(solver): update voltage bounds and iteration limits; adjust pena…
mdeboute Jun 13, 2025
34b8764
Merge remote-tracking branch 'origin/feat/resilient-lf' into feat/res…
mdeboute Jun 13, 2025
75aeb58
refactor(test): clean up unused test method and imports in ResilientA…
mdeboute Jun 13, 2025
4404b35
refactor(test): remove commented-out test method and streamline compa…
mdeboute Jun 13, 2025
2f23c5e
feat(test): add convergence and perturbation tests on RTE data networks
Jun 16, 2025
6a769bd
fix(test): fix voltage perturbation test by checking if considered ge…
Jun 16, 2025
c4e5a3c
feat(solver): enhance slack variable handling and load flow validatio…
mdeboute Jun 17, 2025
4447709
Merge remote-tracking branch 'origin/feat/resilient-lf' into feat/res…
mdeboute Jun 17, 2025
05f0bd5
feat(solver): add exact hessian sparsity pattern to the solver
Jun 26, 2025
0314ca0
feat(solver): add a parameter to activate and deactivate the option t…
Jun 26, 2025
76756a7
fix(solver): remove unnecessary comments from code
Jun 26, 2025
54c3535
feat(solver): add a coefficient mu in front the quadratic terms in th…
Jun 27, 2025
fe9938d
fix(test): set back test parameters to old parameters
Jun 30, 2025
756710d
fix(solver): set back test parameters to old parameters in load flow …
Jun 30, 2025
a048854
refactor(solver): rewrite hessian matrix non zeros computation using …
mdeboute Jul 2, 2025
7e008af
fix(solver): fix value assignment for slack variables in jacobian cal…
Jul 2, 2025
67589ee
refactor(solver): set knitro parameters to obtain convergence on TYNDP
Jul 3, 2025
759c405
refactor(solver): update Knitro solver parameters
mdeboute Jul 24, 2025
432b9a0
fix(test): set active power perturbation to 10% to comply with busine…
Jul 28, 2025
80fc03e
feat(solver): add more knitro parameters and adapt tests
Sep 12, 2025
e67f89a
fix(solver): fix bug gradient callback crashing when using dense grad…
Sep 12, 2025
978de29
fix(test): convert angles to radians when comparing electrical quanti…
Sep 12, 2025
ada3d0b
feat(solver): remove coefficient mu in front of quadratic terms in th…
Sep 12, 2025
9d670db
feat(solver): removed weight wK as it is redundant
Sep 22, 2025
2748d45
Merge branch 'refs/heads/main' into feat/resilient-lf
p-arvy Oct 29, 2025
c7a4c81
WIP Cleaning Code for futur merge with main
YoannAnezin Oct 20, 2025
d0c379b
feat(solver): remove load flow checker method from solver
Nov 14, 2025
b12b4dd
feat(test): add unit tests for new knitro solver parameters
Nov 14, 2025
0ce1189
feat(solver): separate knitro status enum from solver
Nov 14, 2025
f91f412
feat(solver): remove old dependencies and add developer
Nov 14, 2025
c6a0914
refactor(solver): add a utility class for repeated methods
Nov 14, 2025
b7a6719
refactor(solver): remove repeated code from KnitroSolver
Nov 14, 2025
27098b0
refactor(solver): remove repeated code from ResilientKnitroSolver
Nov 14, 2025
2a10f3d
feat(test): keep unit tests on IEEE networks
Nov 21, 2025
23750a3
feat(test): keep perturbation tests on IEEE networks
Nov 21, 2025
e68aa44
feat(test): remove non reproducible ResilientKnitroSolver tests
Nov 25, 2025
db2d685
feat(test): add a voltage perturbation test and utils for resilient k…
Nov 26, 2025
9b30097
feat(test): add voltage and active power perturbation tests
Nov 26, 2025
04cc3b4
feat(readme): add description of the resilient knitro solver
Nov 26, 2025
a0f3e53
wip
p-arvy Nov 27, 2025
f366e69
change resilient for relaxed
p-arvy Nov 27, 2025
91d6cc7
wip
p-arvy Nov 27, 2025
49f2738
wip
p-arvy Nov 27, 2025
43ddfcf
feat(test): remove tests on IEEE300 network
Nov 27, 2025
e8ba13e
refactor common code between knitro problem and solver
p-arvy Nov 27, 2025
568d9ea
Merge remote-tracking branch 'origin/feat/resilient-lf' into feat/res…
p-arvy Nov 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ org.eclipse.jdt.groovy.core.prefs

# Generated readthedocs pages
build-docs/

# Testing
outputs
17 changes: 12 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ By participating, you are expected to uphold this code. Please report unacceptab
PowSyBl Open Load Flow Knitro Solver is an extension to [PowSyBl Open Load Flow](https://github.com/powsybl/powsybl-open-loadflow) allowing to solve
the load flow equations with the **non-linear solver Knitro** instead of the default **Newton-Raphson** method.

The Knitro solver extension models the load flow problem as a **constraint satisfaction problem** (without an objective function).
The Knitro solver extension offers two different ways to model the load flow problem: either as a **constraint satisfaction problem** (without an objective function) or as an **optimisation problem** with relaxed constraints (and an objective function minimizing the violations).

## Getting Started

Expand Down Expand Up @@ -168,29 +168,36 @@ parameters.addExtension(KnitroLoadFlowParameters.class, knitroLoadFlowParameters

### Knitro Parameters

1. **Voltage Bounds**:
1. **Knitro Solver Type**:
- Specifies the way to model the load flow problem :
- **Knitro Solver Types**:
- `STANDARD (default)` : the constraint satisfaction problem formulation and a direct substitute to the Newton-Raphson solver.
- `RELAXED` : the optimisation problem formulation relaxing satisfaction problem.
- Use `setKnitroSolverType` in the `KnitroLoadFlowParameters` extension.

2. **Voltage Bounds**:
- Default range: **0.5 p.u. to 1.5 p.u.** : they define lower and upper bounds of voltages.
- Modify using:
- `setLowerVoltageBound`
- `setUpperVoltageBound`

2. **Jacobian Matrix Usage**:
3. **Jacobian Matrix Usage**:
- The solver utilizes the **Jacobian matrix** for solving successive linear approximations of the problem.
- **Gradient Computation Modes**:
- `1 (exact)`: Gradients computed in PowSyBl are provided to Knitro.
- `2 (forward)`: Knitro computes gradients via forward finite differences.
- `3 (central)`: Knitro computes gradients via central finite differences.
- Use `setGradientComputationMode` in the `KnitroLoadFlowParameters` extension.

3. **Jacobian Sparsity**:
4. **Jacobian Sparsity**:
- Default: **Sparse form** (highly recommended, improves calculation as load flow problems are highly sparse problems).
- To specify dense form:
- Use `setGradientUserRoutineKnitro` in the `KnitroLoadFlowParameters` extension.
- **Options**:
- `1 (dense)`: All constraints are considered as dependent of all variables.
- `2 (sparse)`: Derivatives are computed only for variables involved in the constraints (recommended).

4. **Maximum Iterations**:
5. **Maximum Iterations**:
- Default: **200**
- Modify using `setMaxIterations`.

Expand Down
23 changes: 23 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,24 @@
<organization>Artelys</organization>
<organizationUrl>http://www.artelys.com</organizationUrl>
</developer>
<developer>
<name>Martin Debouté</name>
<email>[email protected]</email>
<organization>Artelys</organization>
<organizationUrl>http://www.artelys.com</organizationUrl>
</developer>
<developer>
<name>Amine Makhen</name>
<email>[email protected]</email>
<organization>Artelys</organization>
<organizationUrl>http://www.artelys.com</organizationUrl>
</developer>
<developer>
<name>Pierre Arvy</name>
<email>[email protected]</email>
<organization>Artelys</organization>
<organizationUrl>http://www.artelys.com</organizationUrl>
</developer>
</developers>

<properties>
Expand Down Expand Up @@ -85,6 +103,11 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.13</version>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
/**
* Copyright (c) 2024, Artelys (http://www.artelys.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.openloadflow.knitro.solver;

import com.artelys.knitro.api.*;
import com.powsybl.commons.PowsyblException;
import com.powsybl.commons.report.ReportNode;
import com.powsybl.openloadflow.ac.equations.AcEquationType;
import com.powsybl.openloadflow.ac.equations.AcVariableType;
import com.powsybl.openloadflow.ac.solver.AbstractAcSolver;
import com.powsybl.openloadflow.ac.solver.AcSolverResult;
import com.powsybl.openloadflow.ac.solver.AcSolverStatus;
import com.powsybl.openloadflow.ac.solver.AcSolverUtil;
import com.powsybl.openloadflow.equations.*;
import com.powsybl.openloadflow.network.LfBus;
import com.powsybl.openloadflow.network.LfNetwork;
import com.powsybl.openloadflow.network.util.VoltageInitializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

import static com.google.common.primitives.Doubles.toArray;

/**
* Abstract base class for Knitro solvers, providing common functionality.
*
* @author Pierre Arvy {@literal <pierre.arvy at artelys.com>}
* @author Jeanne Archambault {@literal <jeanne.archambault at artelys.com>}
* @author Martin Debouté {@literal <martin.deboute at artelys.com>}
* @author Amine Makhen {@literal <amine.makhen at artelys.com>}
*/
public abstract class AbstractKnitroSolver extends AbstractAcSolver {

protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractKnitroSolver.class);

protected KnitroSolverParameters knitroParameters;

public AbstractKnitroSolver(LfNetwork network, KnitroSolverParameters knitroParameters,

Check warning on line 44 in src/main/java/com/powsybl/openloadflow/knitro/solver/AbstractKnitroSolver.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Change the visibility of this constructor to "protected".

See more on https://sonarcloud.io/project/issues?id=com.powsybl%3Apowsybl-open-loadflow-knitro-solver&issues=AZrGbCQxbpU4U_eJO6SF&open=AZrGbCQxbpU4U_eJO6SF&pullRequest=16
EquationSystem<AcVariableType, AcEquationType> equationSystem,
JacobianMatrix<AcVariableType, AcEquationType> j, TargetVector<AcVariableType, AcEquationType> targetVector,
EquationVector<AcVariableType, AcEquationType> equationVector,
boolean detailedReport) {

super(network, equationSystem, j, targetVector, equationVector, detailedReport);
this.knitroParameters = knitroParameters;
}

/**
* Sets Knitro solver parameters based on the provided KnitroSolverParameters object.
* Uses the improved parameter set from RelaxedKnitroSolver.
*
* @param solver The Knitro solver instance to configure.
* @throws KNException if Knitro fails to accept a parameter.
*/
protected void setSolverParameters(KNSolver solver) throws KNException {
LOGGER.info("Configuring Knitro solver parameters...");

solver.setParam(KNConstants.KN_PARAM_GRADOPT, knitroParameters.getGradientComputationMode());
solver.setParam(KNConstants.KN_PARAM_FEASTOL, knitroParameters.getRelConvEps());
solver.setParam(KNConstants.KN_PARAM_FEASTOLABS, knitroParameters.getAbsConvEps());
solver.setParam(KNConstants.KN_PARAM_OPTTOL, knitroParameters.getRelOptEps());
solver.setParam(KNConstants.KN_PARAM_OPTTOLABS, knitroParameters.getAbsOptEps());
solver.setParam(KNConstants.KN_PARAM_MAXIT, knitroParameters.getMaxIterations());
solver.setParam(KNConstants.KN_PARAM_HESSOPT, knitroParameters.getHessianComputationMode());
solver.setParam(KNConstants.KN_PARAM_SOLTYPE, KNConstants.KN_SOLTYPE_BESTFEAS);
solver.setParam(KNConstants.KN_PARAM_OUTLEV, 3);

LOGGER.info("Knitro parameters set: GRADOPT={}, HESSOPT={}, FEASTOL={}, OPTTOL={}, MAXIT={}",
knitroParameters.getGradientComputationMode(),
knitroParameters.getHessianComputationMode(),
knitroParameters.getRelConvEps(),
knitroParameters.getRelOptEps(),
knitroParameters.getMaxIterations());
}

/**
* Creates the Knitro problem instance. Must be implemented by subclasses.
*
* @param voltageInitializer The voltage initializer to use.
* @return The created Knitro problem instance.
* @throws KNException if an error occurs while creating the problem.
*/
protected abstract KNProblem createKnitroProblem(VoltageInitializer voltageInitializer);

/**
* Processes the solution after solving. Can be overridden by subclasses for additional processing.
*
* @param solver The Knitro solver instance.
* @param solution The solution obtained from Knitro.
* @param problemInstance The problem instance.
*/
protected void processSolution(KNSolver solver, KNSolution solution, KNProblem problemInstance) {
// Default implementation: log solution details
LOGGER.info("==== Solution Summary ====");
LOGGER.info("Objective value = {}", solution.getObjValue());
try {
LOGGER.info("Feasibility violation = {}", solver.getAbsFeasError());
LOGGER.info("Optimality violation = {}", solver.getAbsOptError());

LOGGER.debug("Optimal x");
for (int i = 0; i < solution.getX().size(); i++) {
LOGGER.debug(" x[{}] = {}", i, solution.getX().get(i));
}
LOGGER.debug("Optimal constraint values (with corresponding multiplier)");
List<Double> constraintValues = solver.getConstraintValues();
for (int i = 0; i < problemInstance.getNumCons(); i++) {
LOGGER.debug(" c[{}] = {} (lambda = {} )", i, constraintValues.get(i), solution.getLambda().get(i));
}
LOGGER.debug("Constraint violation");
for (int i = 0; i < problemInstance.getNumCons(); i++) {
LOGGER.debug(" violation[{}] = {} ", i, solver.getConViol(i));
}
} catch (KNException e) {
LOGGER.warn("Failed to get some solution details", e);
}
}

/**
* Gets the solution from the solver. Can be overridden by subclasses.
*
* @param solver The Knitro solver instance.
* @return The solution.
*/
protected KNSolution getSolution(KNSolver solver) {
return solver.getSolution();
}

/**
* Updates the network with the solution if required.
*
* @param solution The solution to apply.
* @param solverStatus The solver status.
*/
protected void updateNetworkIfRequired(KNSolution solution, AcSolverStatus solverStatus) {
if (solverStatus == AcSolverStatus.CONVERGED || knitroParameters.isAlwaysUpdateNetwork()) {
equationSystem.getStateVector().set(toArray(solution.getX()));
for (Equation<AcVariableType, AcEquationType> equation : equationSystem.getEquations()) {
for (EquationTerm<AcVariableType, AcEquationType> term : equation.getTerms()) {
term.setStateVector(equationSystem.getStateVector());
}
}
AcSolverUtil.updateNetwork(network, equationSystem);
}
}

@Override
public AcSolverResult run(VoltageInitializer voltageInitializer, ReportNode reportNode) {
int nbIter;
AcSolverStatus acStatus;
KNProblem instance;

try {
instance = createKnitroProblem(voltageInitializer);
} catch (Exception e) {
throw new PowsyblException("Exception while trying to build Knitro Problem", e);
}

try (KNSolver solver = new KNSolver(instance)) {
solver.initProblem();
setSolverParameters(solver);
solver.solve();

KNSolution solution = getSolution(solver);
acStatus = KnitroStatus.fromStatusCode(solution.getStatus()).toAcSolverStatus();
NonLinearExternalSolverUtils.logKnitroStatus(KnitroStatus.fromStatusCode(solution.getStatus()));
nbIter = solver.getNumberIters();

processSolution(solver, solution, instance);
updateNetworkIfRequired(solution, acStatus);

} catch (KNException e) {
throw new PowsyblException("Exception while trying to solve with Knitro", e);
}

double slackBusActivePowerMismatch = network.getSlackBuses().stream()
.mapToDouble(LfBus::getMismatchP)
.sum();
return new AcSolverResult(acStatus, nbIter, slackBusActivePowerMismatch);
}
}

Loading