RaoResultJsonConstants.java

/*
 * Copyright (c) 2021, 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.raoresult.io.json;

import com.powsybl.openrao.commons.OpenRaoException;
import com.powsybl.openrao.commons.Unit;
import com.powsybl.openrao.data.crac.api.Crac;
import com.powsybl.openrao.data.crac.api.Instant;
import com.powsybl.openrao.data.crac.api.State;
import com.powsybl.iidm.network.TwoSides;
import com.powsybl.openrao.data.raoresult.api.ComputationStatus;
import org.apache.commons.lang3.StringUtils;

import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;

/**
 * @author Baptiste Seguinot {@literal <baptiste.seguinot at rte-france.com>}
 */
public final class RaoResultJsonConstants {

    private RaoResultJsonConstants() {
    }

    public static final String RAO_RESULT_IO_VERSION = "1.8";
    // v1.6 : voltage cnecs' voltage values are divided into minVoltage and maxVoltage
    // v1.7 : replaced "optimizationStepsExecuted" with "executionDetails"
    // v1.8 : export tap instead of set-point for PST range actions

    // header
    public static final String TYPE = "type";
    public static final String VERSION = "version";
    public static final String INFO = "info";
    public static final String RAO_RESULT_TYPE = "RAO_RESULT";
    public static final String RAO_RESULT_INFO = "Generated by Open RAO https://powsybl.readthedocs.io/projects/openrao";

    public static final String CONTINGENCY_ID = "contingency";

    // costs
    public static final String COST_RESULTS = "costResults";
    public static final String FUNCTIONAL_COST = "functionalCost";
    public static final String VIRTUAL_COSTS = "virtualCost";

    // flowCnecResults and angleCnecResults
    public static final String FLOWCNEC_RESULTS = "flowCnecResults";
    public static final String FLOWCNEC_ID = "flowCnecId";
    public static final String FLOW = "flow";
    public static final String ANGLECNEC_RESULTS = "angleCnecResults";
    public static final String ANGLECNEC_ID = "angleCnecId";
    public static final String ANGLE = "angle";
    public static final String VOLTAGECNEC_RESULTS = "voltageCnecResults";
    public static final String VOLTAGECNEC_ID = "voltageCnecId";
    public static final String VOLTAGE = "voltage";
    public static final String MIN_VOLTAGE = "minVoltage";
    public static final String MAX_VOLTAGE = "maxVoltage";
    public static final String MARGIN = "margin";
    public static final String RELATIVE_MARGIN = "relativeMargin";
    public static final String COMMERCIAL_FLOW = "commercialFlow";
    public static final String LOOP_FLOW = "loopFlow";
    public static final String ZONAL_PTDF_SUM = "zonalPtdfSum";

    // remedial action results
    public static final String STATES_ACTIVATED = "activatedStates";

    // networkActionResults
    public static final String NETWORKACTION_RESULTS = "networkActionResults";
    public static final String NETWORKACTION_ID = "networkActionId";

    // rangeActionResults
    public static final String PSTRANGEACTION_RESULTS = "pstRangeActionResults";
    public static final String PSTRANGEACTION_ID = "pstRangeActionId";
    public static final String STANDARDRANGEACTION_RESULTS = "standardRangeActionResults";
    public static final String RANGEACTION_RESULTS = "rangeActionResults";
    public static final String RANGEACTION_ID = "rangeActionId";
    public static final String INITIAL_TAP = "initialTap";
    public static final String INITIAL_SETPOINT = "initialSetpoint";
    public static final String AFTER_PRA_TAP = "afterPraTap";
    public static final String AFTER_PRA_SETPOINT = "afterPraSetpoint";
    public static final String TAP = "tap";
    public static final String SETPOINT = "setpoint";

    // instants
    public static final String INSTANT = "instant";
    public static final String INITIAL_INSTANT_ID = "initial";
    public static final String PREVENTIVE_INSTANT_ID = "preventive";
    public static final String OUTAGE_INSTANT_ID = "outage";
    public static final String AUTO_INSTANT_ID = "auto";
    public static final String CURATIVE_INSTANT_ID = "curative";

    // units
    public static final String AMPERE_UNIT = "ampere";
    public static final String MEGAWATT_UNIT = "megawatt";
    public static final String DEGREE_UNIT = "degree";
    public static final String KILOVOLT_UNIT = "kilovolt";
    public static final String PERCENT_IMAX_UNIT = "percent_imax";
    public static final String TAP_UNIT = "tap";

    // branch side
    public static final String LEFT_SIDE = "leftSide";
    public static final String RIGHT_SIDE = "rightSide";
    public static final String SIDE_ONE = "side1";
    public static final String SIDE_TWO = "side2";

    // optimization states - for retro-compatibility only
    public static final String INITIAL_OPT_STATE = "initial";
    public static final String AFTER_PRA_OPT_STATE = "afterPRA";
    public static final String AFTER_ARA_OPT_STATE = "afterARA";
    public static final String AFTER_CRA_OPT_STATE = "afterCRA";

    // computation statuses
    public static final String COMPUTATION_STATUS = "computationStatus";
    public static final String DEFAULT_STATUS = "default";
    public static final String PARTIAL_FAILURE_STATUS = "partial-failure";
    public static final String FAILURE_STATUS = "failure";
    public static final String COMPUTATION_STATUS_MAP = "computationStatusMap";

    // optimized steps executed by the RAO
    public static final String OPTIMIZATION_STEPS_EXECUTED = "optimizationStepsExecuted";
    public static final String EXECUTION_DETAILS = "executionDetails";
    public static final String FIRST_PREVENTIVE_ONLY = "The RAO only went through first preventive";
    public static final String FIRST_PREVENTIVE_FELLBACK = "First preventive fellback to initial situation";
    public static final String SECOND_PREVENTIVE_IMPROVED_FIRST = "Second preventive improved first preventive results";
    public static final String SECOND_PREVENTIVE_FELLBACK_TO_FIRST_PREVENTIVE_SITUATION = "Second preventive fellback to first preventive results";
    public static final String SECOND_PREVENTIVE_FELLBACK_TO_INITIAL_SITUATION = "Second preventive fellback to initial situation";

    // manipulate version
    public static int getPrimaryVersionNumber(String fullVersion) {
        return Integer.parseInt(divideVersionNumber(fullVersion)[0]);
    }

    public static int getSubVersionNumber(String fullVersion) {
        return Integer.parseInt(divideVersionNumber(fullVersion)[1]);
    }

    private static String[] divideVersionNumber(String fullVersion) {
        String[] dividedV = fullVersion.split("\\.");
        if (dividedV.length != 2 || !Arrays.stream(dividedV).allMatch(StringUtils::isNumeric)) {
            throw new OpenRaoException("json CRAC version number must be of the form vX.Y");
        }
        return dividedV;
    }

    // serialization of enums
    public static String serializeUnit(Unit unit) {
        switch (unit) {
            case AMPERE:
                return AMPERE_UNIT;
            case DEGREE:
                return DEGREE_UNIT;
            case MEGAWATT:
                return MEGAWATT_UNIT;
            case KILOVOLT:
                return KILOVOLT_UNIT;
            case PERCENT_IMAX:
                return PERCENT_IMAX_UNIT;
            case TAP:
                return TAP_UNIT;
            default:
                throw new OpenRaoException(String.format("Unsupported unit %s", unit));
        }
    }

    public static Unit deserializeUnit(String stringValue) {
        switch (stringValue) {
            case AMPERE_UNIT:
                return Unit.AMPERE;
            case DEGREE_UNIT:
                return Unit.DEGREE;
            case MEGAWATT_UNIT:
                return Unit.MEGAWATT;
            case KILOVOLT_UNIT:
                return Unit.KILOVOLT;
            case PERCENT_IMAX_UNIT:
                return Unit.PERCENT_IMAX;
            case TAP_UNIT:
                return Unit.TAP;
            default:
                throw new OpenRaoException(String.format("Unrecognized unit %s", stringValue));
        }
    }

    // serialization of enums
    public static String serializeSide(TwoSides side) {
        return switch (side) {
            case ONE -> SIDE_ONE;
            case TWO -> SIDE_TWO;
        };
    }

    public static String serializeInstantId(Instant instant) {
        if (instant == null) {
            return INITIAL_INSTANT_ID;
        }
        return instant.getId();
    }

    public static Instant deserializeOptimizedInstant(String stringValue, String jsonFileVersion, Crac crac) {
        String instantId = deserializeOptimizedInstantId(stringValue, jsonFileVersion, crac);
        if (Objects.equals(instantId, INITIAL_INSTANT_ID)) {
            return null;
        }
        return crac.getInstant(instantId);
    }

    public static String deserializeOptimizedInstantId(String stringValue, String jsonFileVersion, Crac crac) {
        int primaryVersionNumber = getPrimaryVersionNumber(jsonFileVersion);
        int subVersionNumber = getSubVersionNumber(jsonFileVersion);
        if (primaryVersionNumber <= 1 && subVersionNumber <= 3) {
            switch (stringValue) {
                case INITIAL_OPT_STATE:
                    return INITIAL_INSTANT_ID;
                case AFTER_PRA_OPT_STATE:
                    return PREVENTIVE_INSTANT_ID;
                case AFTER_ARA_OPT_STATE:
                    return (primaryVersionNumber == 1 && subVersionNumber == 1 && !crac.hasAutoInstant()) ? PREVENTIVE_INSTANT_ID : AUTO_INSTANT_ID;
                case AFTER_CRA_OPT_STATE:
                    return CURATIVE_INSTANT_ID;
                default:
                    throw new OpenRaoException(String.format("Unrecognized optimization state %s", stringValue));
            }
        } else {
            return stringValue;
        }
    }

    public static String serializeStatus(ComputationStatus computationStatus) {
        return switch (computationStatus) {
            case DEFAULT -> DEFAULT_STATUS;
            case PARTIAL_FAILURE -> PARTIAL_FAILURE_STATUS;
            case FAILURE -> FAILURE_STATUS;
            default ->
                throw new OpenRaoException(String.format("Unsupported computation status %s", computationStatus));
        };
    }

    public static ComputationStatus deserializeStatus(String stringValue) {
        return switch (stringValue) {
            case DEFAULT_STATUS -> ComputationStatus.DEFAULT;
            case PARTIAL_FAILURE_STATUS -> ComputationStatus.PARTIAL_FAILURE;
            case FAILURE_STATUS -> ComputationStatus.FAILURE;
            default -> throw new OpenRaoException(String.format("Unrecognized computation status %s", stringValue));
        };
    }

    // state comparator
    public static final Comparator<State> STATE_COMPARATOR = (s1, s2) -> {
        if (s1.getInstant().getOrder() != s2.getInstant().getOrder()) {
            return s1.compareTo(s2);
        } else if (s1.getInstant().isPreventive()) {
            return 0;
        } else {
            // Since instant is not preventive, there is a contingency for sure
            return s1.getContingency().get().getId().compareTo(s2.getContingency().get().getId());
        }
    };

}