AcSolverFactoryTest.java

/**
 * 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.ac.solver;

import com.google.auto.service.AutoService;
import com.powsybl.commons.PowsyblException;
import com.powsybl.commons.parameters.Parameter;
import com.powsybl.commons.report.ReportNode;
import com.powsybl.iidm.network.Network;
import com.powsybl.loadflow.LoadFlow;
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.ac.AcLoadFlowParameters;
import com.powsybl.openloadflow.ac.equations.AcEquationType;
import com.powsybl.openloadflow.ac.equations.AcVariableType;
import com.powsybl.openloadflow.equations.EquationSystem;
import com.powsybl.openloadflow.equations.EquationVector;
import com.powsybl.openloadflow.equations.JacobianMatrix;
import com.powsybl.openloadflow.equations.TargetVector;
import com.powsybl.openloadflow.graph.EvenShiloachGraphDecrementalConnectivityFactory;
import com.powsybl.openloadflow.network.FourBusNetworkFactory;
import com.powsybl.openloadflow.network.LfNetwork;
import com.powsybl.openloadflow.network.util.UniformValueVoltageInitializer;
import com.powsybl.openloadflow.network.util.VoltageInitializer;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

/**
 * @author Damien Jeandemange {@literal <damien.jeandemange at artelys.com>}
 */
class AcSolverFactoryTest {

    public record AcSolverMockParameters(int maxIterations) implements AcSolverParameters {

        @Override
        public String toString() {
            return "AcSolverMockParameters(" +
                    "maxIterations=" + maxIterations +
                    ')';
        }
    }

    public static class AcSolverMock implements AcSolver {
        private static final Logger LOGGER = LoggerFactory.getLogger(AcSolverMock.class);
        private final LfNetwork network;
        private final EquationSystem<AcVariableType, AcEquationType> equationSystem;
        private final AcSolverMockParameters parameters;

        public AcSolverMock(LfNetwork network, EquationSystem<AcVariableType, AcEquationType> equationSystem, AcSolverMockParameters parameters) {
            this.network = network;
            this.equationSystem = equationSystem;
            this.parameters = parameters;
        }

        @Override
        public String getName() {
            return "AC Solver Mock";
        }

        @Override
        public AcSolverResult run(VoltageInitializer voltageInitializer, ReportNode reportNode) {
            AcSolverUtil.initStateVector(network, equationSystem, new UniformValueVoltageInitializer());
            LOGGER.info("I am a not so advanced solver only able to return flat 1 p.u. /_ 0.0, in max iterations, leaving 34 MW slack mismatch.");
            return new AcSolverResult(AcSolverStatus.CONVERGED, parameters.maxIterations(), 0.34);
        }
    }

    @AutoService(AcSolverFactory.class)
    public static class AcSolverFactoryMock implements AcSolverFactory {

        public static final String NAME = "AC_SOLVER_MOCK";

        @Override
        public String getName() {
            return NAME;
        }

        @Override
        public AcSolverParameters createParameters(LoadFlowParameters parameters) {
            return new AcSolverMockParameters(12);
        }

        @Override
        public AcSolver create(LfNetwork network, AcLoadFlowParameters parameters, EquationSystem<AcVariableType, AcEquationType> equationSystem, JacobianMatrix<AcVariableType, AcEquationType> j, TargetVector<AcVariableType, AcEquationType> targetVector, EquationVector<AcVariableType, AcEquationType> equationVector) {
            return new AcSolverMock(network, equationSystem, (AcSolverMockParameters) parameters.getAcSolverParameters());
        }
    }

    @Test
    void testAcSolverTypeParam() {
        OpenLoadFlowProvider provider = new OpenLoadFlowProvider();
        Parameter acSolverType = provider.getSpecificParameters().stream().filter(p -> p.getName().equals(OpenLoadFlowParameters.AC_SOLVER_TYPE_PARAM_NAME)).findFirst().orElseThrow();
        assertEquals("NEWTON_RAPHSON", acSolverType.getDefaultValue());
        assertEquals(List.of("AC_SOLVER_MOCK", "NEWTON_KRYLOV", "NEWTON_RAPHSON"), acSolverType.getPossibleValues());
    }

    @Test
    void testMockAcSolverType() {
        Network network = FourBusNetworkFactory.createBaseNetwork();
        LoadFlow.Runner loadFlowRunner = new LoadFlow.Runner(new OpenLoadFlowProvider(new DenseMatrixFactory()));
        LoadFlowParameters parameters = new LoadFlowParameters().setDistributedSlack(false);
        OpenLoadFlowParameters parametersExt = OpenLoadFlowParameters.create(parameters);
        parametersExt.setAcSolverType(AcSolverFactoryMock.NAME);

        AcLoadFlowParameters acLoadFlowParameters = OpenLoadFlowParameters.createAcParameters(
                network,
                parameters,
                parametersExt,
                new DenseMatrixFactory(),
                new EvenShiloachGraphDecrementalConnectivityFactory<>()
        );
        assertEquals("AcSolverMockParameters(maxIterations=12)", acLoadFlowParameters.getAcSolverParameters().toString());

        LoadFlowResult result = loadFlowRunner.run(network, parametersExt.getExtendable());
        assertTrue(result.isFullyConverged());
        assertEquals(12, result.getComponentResults().get(0).getIterationCount());
        assertEquals(34, result.getComponentResults().get(0).getSlackBusResults().get(0).getActivePowerMismatch());
    }

    @Test
    void testNotFoundAcSolver() {
        LoadFlowParameters parameters = new LoadFlowParameters();
        OpenLoadFlowParameters parametersExt = OpenLoadFlowParameters.create(parameters);
        Throwable t = assertThrows(PowsyblException.class, () -> parametersExt.setAcSolverType("notExists"));
        assertEquals("AC Solver 'notExists' not found", t.getMessage());
    }
}