-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Retry LU decomposition if incremental mode fails (#1108)
* Retry LU decomposition if incremental mode fails Signed-off-by: Didier Vidal <didier.vidal_externe@rte-france.com> * not recomputing der. They should not be destroyed by previous failed update Signed-off-by: Didier Vidal <didier.vidal_externe@rte-france.com> * Add unit test checking the replay mechanism Signed-off-by: Didier Vidal <didier.vidal_externe@rte-france.com> * complete coverage Signed-off-by: Didier Vidal <didier.vidal_externe@rte-france.com> * hopefully fill coverage now Signed-off-by: Didier Vidal <didier.vidal_externe@rte-france.com> * sonar picomanagement module does not like public tests - and some people apparently care about sonar stats of so called issues Signed-off-by: Didier Vidal <didier.vidal_externe@rte-france.com> * Add copyright and authorship on new file Signed-off-by: Didier Vidal <didier.vidal_externe@rte-france.com> --------- Signed-off-by: Didier Vidal <didier.vidal_externe@rte-france.com>
- Loading branch information
1 parent
c7da76f
commit d96584a
Showing
2 changed files
with
156 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
143 changes: 143 additions & 0 deletions
143
src/test/java/com/powsybl/openloadflow/equations/LUDecompositionTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
/** | ||
* Copyright (c) 2024, 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.openloadflow.equations; | ||
|
||
import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; | ||
import com.powsybl.math.matrix.DenseMatrix; | ||
import com.powsybl.math.matrix.DenseMatrixFactory; | ||
import com.powsybl.math.matrix.LUDecomposition; | ||
import com.powsybl.math.matrix.MatrixException; | ||
import com.powsybl.openloadflow.ac.equations.AcEquationType; | ||
import com.powsybl.openloadflow.ac.equations.AcVariableType; | ||
import com.powsybl.openloadflow.network.FirstSlackBusSelector; | ||
import com.powsybl.openloadflow.network.LfBus; | ||
import com.powsybl.openloadflow.network.LfNetwork; | ||
import com.powsybl.openloadflow.network.impl.Networks; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import java.util.List; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertFalse; | ||
import static org.junit.jupiter.api.Assertions.assertThrows; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
/** | ||
* @author Didier Vidal {@literal <didier.vidal_externe at rte-france.com>} | ||
*/ | ||
class LUDecompositionTest { | ||
|
||
class LUDecompositionMockIncrementalFailure implements LUDecomposition { | ||
private final LUDecomposition delegate; | ||
private final SpyMatrixFactory spy; | ||
|
||
public LUDecompositionMockIncrementalFailure(LUDecomposition delegate, SpyMatrixFactory spy) { | ||
this.delegate = delegate; | ||
this.spy = spy; | ||
} | ||
|
||
@Override | ||
public void update() { | ||
delegate.update(); | ||
} | ||
|
||
@Override | ||
public void solve(double[] b) { | ||
delegate.solve(b); | ||
} | ||
|
||
@Override | ||
public void solveTransposed(double[] b) { | ||
delegate.solveTransposed(b); | ||
} | ||
|
||
@Override | ||
public void solve(DenseMatrix b) { | ||
delegate.solve(b); | ||
} | ||
|
||
@Override | ||
public void solveTransposed(DenseMatrix b) { | ||
delegate.solveTransposed(b); | ||
} | ||
|
||
@Override | ||
public void close() { | ||
delegate.close(); | ||
} | ||
|
||
@Override | ||
public void update(boolean allowIncrementalUpdate) { | ||
if (allowIncrementalUpdate || spy.alwaysFail) { | ||
spy.exceptionThrown = true; | ||
throw new MatrixException("Sorry incremental update failed"); | ||
} else { | ||
delegate.update(allowIncrementalUpdate); | ||
} | ||
} | ||
} | ||
|
||
class SpyMatrixFactory extends DenseMatrixFactory { | ||
boolean exceptionThrown = false; | ||
boolean alwaysFail = false; | ||
|
||
@Override | ||
public DenseMatrix create(int rowCount, int columnCount, int estimatedValueCount) { | ||
return new DenseMatrix(rowCount, columnCount) { | ||
@Override | ||
public LUDecomposition decomposeLU() { | ||
return new LUDecompositionMockIncrementalFailure(super.decomposeLU(), SpyMatrixFactory.this); | ||
} | ||
}; | ||
} | ||
} | ||
|
||
@Test | ||
void testIncrementalLURobustification() { | ||
|
||
// The condition that cause an incremental LU decomposition to fail is hard to reproduce on small models. | ||
// It has been seen in Security Analysis, on large models, when a contingence moves an AC emulation HVDC link above PMax | ||
// | ||
// This test uses a mocked DenseMatrix to reproduce the condition and check that the solve succeds | ||
|
||
SpyMatrixFactory spyMatrixFactory = new SpyMatrixFactory(); | ||
|
||
List<LfNetwork> lfNetworks = Networks.load(EurostagTutorialExample1Factory.create(), new FirstSlackBusSelector()); | ||
LfNetwork network = lfNetworks.get(0); | ||
|
||
LfBus bus = network.getBus(0); | ||
|
||
EquationSystem<AcVariableType, AcEquationType> equationSystem = new EquationSystem<>(); | ||
equationSystem.createEquation(bus.getNum(), AcEquationType.BUS_TARGET_V).addTerm(equationSystem.getVariable(bus.getNum(), AcVariableType.BUS_V).createTerm()) | ||
.setActive(true); | ||
|
||
double[] values = new double[] {0.1}; | ||
|
||
try (JacobianMatrix j = new JacobianMatrix(equationSystem, spyMatrixFactory)) { | ||
// First update | ||
j.solve(values); | ||
assertFalse(spyMatrixFactory.exceptionThrown); | ||
// second update that triggers incremental update | ||
j.updateStatus(JacobianMatrix.Status.VALUES_INVALID); | ||
j.solve(values); | ||
// An eception should be thrown but the solve should continue | ||
assertTrue(spyMatrixFactory.exceptionThrown); | ||
|
||
// Force always fail | ||
spyMatrixFactory.alwaysFail = true; | ||
j.updateStatus(JacobianMatrix.Status.VALUES_INVALID); | ||
assertThrows(MatrixException.class, () -> j.solve(values)); | ||
|
||
// Force always fail in non incremental case | ||
spyMatrixFactory.alwaysFail = true; | ||
j.updateStatus(JacobianMatrix.Status.VALUES_AND_ZEROS_INVALID); | ||
assertThrows(MatrixException.class, () -> j.solve(values)); | ||
} | ||
|
||
} | ||
} |