DecomposedFlowRescalerMaxCurrentOverload.java

/**
 * 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.flow_decomposition.rescaler;

import com.powsybl.flow_decomposition.DecomposedFlow;
import com.powsybl.flow_decomposition.FlowPartition;
import com.powsybl.iidm.network.Branch;
import com.powsybl.iidm.network.CurrentLimits;
import com.powsybl.iidm.network.Network;

import static com.powsybl.flow_decomposition.FlowDecompositionParameters.DEFAULT_PROPORTIONAL_RESCALER_MIN_FLOW_TOLERANCE;

/**
 * @author Caio Luke {@literal <caio.luke at artelys.com>}
 */
public class DecomposedFlowRescalerMaxCurrentOverload extends AbstractDecomposedFlowRescaler {

    private final double minFlowTolerance; // min flow in MW to rescale

    public DecomposedFlowRescalerMaxCurrentOverload(double minFlowTolerance) {
        this.minFlowTolerance = minFlowTolerance;
    }

    public DecomposedFlowRescalerMaxCurrentOverload() {
        this(DEFAULT_PROPORTIONAL_RESCALER_MIN_FLOW_TOLERANCE);
    }

    @Override
    protected boolean shouldRescaleFlows(DecomposedFlow decomposedFlow) {
        return hasFiniteAcFlowsOnEachTerminal(decomposedFlow) && hasAbsDcFlowGreaterThanTolerance(decomposedFlow, minFlowTolerance);
    }

    @Override
    protected FlowPartition computeRescaledFlowsPartition(DecomposedFlow decomposedFlow, Network network) {
        FlowPartition initialFlowPartition = decomposedFlow.getFlowPartition();
        double acTerminal1Current = decomposedFlow.getAcTerminal1Current();
        double acTerminal2Current = decomposedFlow.getAcTerminal2Current();

        Branch<?> branch = network.getBranch(decomposedFlow.getBranchId());
        double pActivePowerOnly = getPActivePowerOnly(branch, acTerminal1Current, acTerminal2Current);
        return DecomposedFlowRescalerProportional.getFlowPartition(decomposedFlow, initialFlowPartition, pActivePowerOnly);
    }

    private static double getPActivePowerOnly(Branch<?> branch, double acTerminal1Current, double acTerminal2Current) {
        double nominalTerminal1Voltage = branch.getTerminal1().getVoltageLevel().getNominalV();
        double nominalTerminal2Voltage = branch.getTerminal2().getVoltageLevel().getNominalV();
        CurrentLimits currentLimitsTerminal1 = branch.getNullableCurrentLimits1();
        CurrentLimits currentLimitsTerminal2 = branch.getNullableCurrentLimits2();

        // Calculate active power P = sqrt(3) * I * (V/1000) * cos(phi)
        // with cos(phi) = 1, therefore considering active power only
        double pTerminal1ActivePowerOnly = acTerminal1Current * (nominalTerminal1Voltage / 1000) * Math.sqrt(3);
        double pTerminal2ActivePowerOnly = acTerminal2Current * (nominalTerminal2Voltage / 1000) * Math.sqrt(3);

        // if the branch has limits, compare current overloads
        // if it doesn't, compare currents
        double pActivePowerOnly;
        if (currentLimitsTerminal1 == null || currentLimitsTerminal2 == null) {
            pActivePowerOnly = acTerminal1Current >= acTerminal2Current ? pTerminal1ActivePowerOnly : pTerminal2ActivePowerOnly;
        } else {
            double currentOverloadTerminal1 = acTerminal1Current / currentLimitsTerminal1.getPermanentLimit();
            double currentOverloadTerminal2 = acTerminal2Current / currentLimitsTerminal2.getPermanentLimit();
            pActivePowerOnly = currentOverloadTerminal1 >= currentOverloadTerminal2 ? pTerminal1ActivePowerOnly : pTerminal2ActivePowerOnly;
        }
        return pActivePowerOnly;
    }
}