LoopFlowThresholdImpl.java

/*
 * Copyright (c) 2020, 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/.
 */
package com.powsybl.openrao.data.crac.loopflowextension;

import com.powsybl.openrao.commons.OpenRaoException;
import com.powsybl.openrao.commons.PhysicalParameter;
import com.powsybl.openrao.commons.Unit;
import com.powsybl.openrao.data.crac.api.cnec.FlowCnec;
import com.powsybl.iidm.network.TwoSides;
import com.powsybl.commons.extensions.AbstractExtension;

/**
 * Cnec extension for loop flow
 *
 * @author Pengbo Wang {@literal <pengbo.wang at rte-international.com>}
 * @author Baptiste Seguinot {@literal <baptiste.seguinot at rte-france.com>}
 */
public class LoopFlowThresholdImpl extends AbstractExtension<FlowCnec> implements LoopFlowThreshold {

    /*
     - if the unit is PERCENT_IMAX, the input flow threshold should be between 0 and 1
     - in the loop-flow threshold, PERCENT_IMAX is considered as a percentage of the Cnec
       threshold (retrieved from the Crac, without considering the frm), and NOT as a percentage
        of the branch current limit (retrieved from the Network)
     */
    private double inputThreshold;
    private Unit inputThresholdUnit;

    LoopFlowThresholdImpl(double value, Unit unit) {
        this.inputThreshold = value;
        this.inputThresholdUnit = unit;
    }

    @Override
    public double getValue() {
        return inputThreshold;
    }

    @Override
    public Unit getUnit() {
        return inputThresholdUnit;
    }

    @Override
    public double getThresholdWithReliabilityMargin(Unit requestedUnit) {
        switch (requestedUnit) {
            case MEGAWATT:
                return getThreshold(requestedUnit) - this.getExtendable().getReliabilityMargin();
            case AMPERE:
                return getThreshold(requestedUnit) - convertMWToA(this.getExtendable().getReliabilityMargin());
            case PERCENT_IMAX:
                return getThreshold(requestedUnit) - convertAToPercentImax(convertMWToA(this.getExtendable().getReliabilityMargin()));
            default:
                throw new OpenRaoException("Loopflow thresholds can only be returned in AMPERE, MEGAWATT or PERCENT_IMAX");
        }
    }

    @Override
    public double getThreshold(Unit requestedUnit) {

        if (requestedUnit.getPhysicalParameter() != PhysicalParameter.FLOW) {
            throw new OpenRaoException("Loopflow thresholds can only be returned in AMPERE, MEGAWATT or PERCENT_IMAX");
        }

        if (requestedUnit == inputThresholdUnit) {
            return inputThreshold;
        }

        if (inputThresholdUnit == Unit.PERCENT_IMAX && requestedUnit == Unit.AMPERE) {
            return convertPercentImaxToA(inputThreshold);
        }

        if (inputThresholdUnit == Unit.PERCENT_IMAX && requestedUnit == Unit.MEGAWATT) {
            return convertAToMW(convertPercentImaxToA(inputThreshold));
        }

        if (inputThresholdUnit == Unit.AMPERE && requestedUnit == Unit.PERCENT_IMAX) {
            return convertAToPercentImax(inputThreshold);
        }

        if (inputThresholdUnit == Unit.AMPERE && requestedUnit == Unit.MEGAWATT) {
            return convertAToMW(inputThreshold);
        }

        if (inputThresholdUnit == Unit.MEGAWATT && requestedUnit == Unit.AMPERE) {
            return convertMWToA(inputThreshold);
        }

        if (inputThresholdUnit == Unit.MEGAWATT && requestedUnit == Unit.PERCENT_IMAX) {
            return convertAToPercentImax(convertMWToA(inputThreshold));
        }

        throw new OpenRaoException(String.format("Cannot convert %s into %s", inputThresholdUnit, requestedUnit));
    }

    private double convertMWToA(double valueInMW) {
        return valueInMW * 1000 / (getExtendable().getNominalVoltage(TwoSides.ONE) * Math.sqrt(3));
    }

    private double convertAToMW(double valueInA) {
        return valueInA * getExtendable().getNominalVoltage(TwoSides.ONE) * Math.sqrt(3) / 1000;
    }

    private double convertAToPercentImax(double valueInA) {
        return valueInA / getCnecFmaxWithoutFrmInA();
    }

    private double convertPercentImaxToA(double valueInPercent) {
        return valueInPercent * getCnecFmaxWithoutFrmInA();
    }

    private double getCnecFmaxWithoutFrmInA() {
        double minUpperBound = Math.min(
            getExtendable().getUpperBound(TwoSides.ONE, Unit.AMPERE).orElse(Double.POSITIVE_INFINITY),
            getExtendable().getUpperBound(TwoSides.TWO, Unit.AMPERE).orElse(Double.POSITIVE_INFINITY)
        );
        double maxLowerBound = Math.max(
            getExtendable().getLowerBound(TwoSides.ONE, Unit.AMPERE).orElse(Double.NEGATIVE_INFINITY),
            getExtendable().getLowerBound(TwoSides.TWO, Unit.AMPERE).orElse(Double.NEGATIVE_INFINITY)
        );
        return Math.min(minUpperBound, -maxLowerBound) + convertMWToA(getExtendable().getReliabilityMargin());
    }
}