All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.powsybl.openloadflow.lf.outerloop.AbstractIncrementalPhaseControlOuterLoop Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (c) 2023, 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.lf.outerloop;

import com.powsybl.iidm.network.TwoSides;
import com.powsybl.math.matrix.DenseMatrix;
import com.powsybl.openloadflow.equations.EquationSystem;
import com.powsybl.openloadflow.equations.EquationTerm;
import com.powsybl.openloadflow.equations.JacobianMatrix;
import com.powsybl.openloadflow.equations.Quantity;
import com.powsybl.openloadflow.lf.AbstractLoadFlowParameters;
import com.powsybl.openloadflow.lf.LoadFlowContext;
import com.powsybl.openloadflow.network.*;
import com.powsybl.openloadflow.util.PerUnit;
import org.apache.commons.lang3.Range;
import org.apache.commons.lang3.mutable.MutableInt;
import org.slf4j.Logger;

import java.util.List;
import java.util.Objects;

/**
 * @author Geoffroy Jamgotchian {@literal }
 */
public abstract class AbstractIncrementalPhaseControlOuterLoop & Quantity,
                                                               E extends Enum & Quantity,
                                                               P extends AbstractLoadFlowParameters,
                                                               C extends LoadFlowContext,
                                                               O extends OuterLoopContext>
        extends AbstractPhaseControlOuterLoop {

    public static final int MAX_DIRECTION_CHANGE = 3;
    public static final int MAX_TAP_SHIFT = Integer.MAX_VALUE;
    public static final double MIN_TARGET_DEADBAND = 1 / PerUnit.SB; // 1 MW
    public static final double SENSI_EPS = 1e-6;
    public static final double PHASE_SHIFT_CROSS_IMPACT_MARGIN = 0.75;

    protected final Logger logger;

    protected AbstractIncrementalPhaseControlOuterLoop(Logger logger) {
        this.logger = Objects.requireNonNull(logger);
    }

    @Override
    public void initialize(O context) {
        var contextData = new IncrementalContextData();
        context.setData(contextData);

        List controllerBranches = getControllerBranches(context.getNetwork());
        for (LfBranch controllerBranch : controllerBranches) {
            contextData.getControllersContexts().put(controllerBranch.getId(), new IncrementalContextData.ControllerContext(MAX_DIRECTION_CHANGE));
        }

        fixPhaseShifterNecessaryForConnectivity(context.getNetwork(), controllerBranches);
    }

    public static double getHalfTargetDeadband(TransformerPhaseControl phaseControl) {
        return Math.max(phaseControl.getTargetDeadband(), MIN_TARGET_DEADBAND) / 2;
    }

    public abstract static class AbstractSensitivityContext & Quantity, E extends Enum & Quantity> {

        private final List controllerBranches;
        private final EquationSystem equationSystem;
        private final JacobianMatrix jacobianMatrix;
        private final int[] controllerBranchIndex;
        private DenseMatrix sensitivities;

        protected AbstractSensitivityContext(LfNetwork network, List controllerBranches,
                                  EquationSystem equationSystem,
                                  JacobianMatrix jacobianMatrix) {
            this.controllerBranches = Objects.requireNonNull(controllerBranches);
            this.equationSystem = Objects.requireNonNull(equationSystem);
            this.jacobianMatrix = Objects.requireNonNull(jacobianMatrix);
            controllerBranchIndex = LfBranch.createIndex(network, controllerBranches);
        }

        private DenseMatrix getSensitivities() {
            if (sensitivities == null) {
                sensitivities = calculateSensitivityValues(controllerBranches, controllerBranchIndex, equationSystem, jacobianMatrix);
            }
            return sensitivities;
        }

        public abstract DenseMatrix calculateSensitivityValues(List controllerBranches, int[] controllerBranchIndex,
                                                               EquationSystem equationSystem,
                                                               JacobianMatrix jacobianMatrix);

        private EquationTerm getP1(LfBranch controlledBranch) {
            return (EquationTerm) controlledBranch.getP1();
        }

        private EquationTerm getP2(LfBranch controlledBranch) {
            return (EquationTerm) controlledBranch.getP2();
        }

        protected double calculateSensitivityFromA2S(LfBranch controllerBranch, EquationTerm s) {
            return s.calculateSensi(getSensitivities(), controllerBranchIndex[controllerBranch.getNum()]);
        }

        public double calculateSensitivityFromA2P(LfBranch controllerBranch, LfBranch controlledBranch, TwoSides controlledSide) {
            var p = controlledSide == TwoSides.ONE ? getP1(controlledBranch) : getP2(controlledBranch);
            return calculateSensitivityFromA2S(controllerBranch, p);
        }
    }

    protected int checkActivePowerControlPhaseControls(AbstractSensitivityContext sensitivityContext, IncrementalContextData contextData,
                                                           List activePowerControlPhaseControls) {
        MutableInt numOfActivePowerControlPstsThatChangedTap = new MutableInt(0);

        for (TransformerPhaseControl phaseControl : activePowerControlPhaseControls) {
            LfBranch controllerBranch = phaseControl.getControllerBranch();
            LfBranch controlledBranch = phaseControl.getControlledBranch();
            var p = phaseControl.getControlledSide() == TwoSides.ONE
                    ? controlledBranch.getP1() : controlledBranch.getP2();
            double pValue = p.eval();
            double halfTargetDeadband = getHalfTargetDeadband(phaseControl);
            if (Math.abs(pValue - phaseControl.getTargetValue()) > halfTargetDeadband) {
                var controllerContext = contextData.getControllersContexts().get(controllerBranch.getId());
                double dp = phaseControl.getTargetValue() - pValue;
                double a2p = sensitivityContext.calculateSensitivityFromA2P(controllerBranch, controlledBranch, phaseControl.getControlledSide());
                if (Math.abs(a2p) > SENSI_EPS) {
                    double da = Math.toRadians(dp / a2p);
                    logger.trace("Controlled branch '{}' active power is {} MW and out of target value {} MW (half deadband={} MW), a phase shift of {}° is required",
                            controlledBranch.getId(), pValue * PerUnit.SB, phaseControl.getTargetValue() * PerUnit.SB, halfTargetDeadband * PerUnit.SB, Math.toDegrees(da));
                    PiModel piModel = controllerBranch.getPiModel();

                    int oldTapPosition = piModel.getTapPosition();
                    Range tapPositionRange = piModel.getTapPositionRange();
                    piModel.updateTapPositionToReachNewA1(da, MAX_TAP_SHIFT, controllerContext.getAllowedDirection()).ifPresent(direction -> {
                        controllerContext.updateAllowedDirection(direction);
                        numOfActivePowerControlPstsThatChangedTap.add(1);
                    });

                    if (piModel.getTapPosition() != oldTapPosition) {
                        logger.debug("Controller branch '{}' change tap from {} to {} to reach active power target (full range: {})", controllerBranch.getId(),
                                oldTapPosition, piModel.getTapPosition(), tapPositionRange);
                    }
                }
            }
        }
        return numOfActivePowerControlPstsThatChangedTap.getValue();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy