SensitivityFactor.java

/**
 * Copyright (c) 2018, 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.sensitivity;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.powsybl.commons.PowsyblException;
import com.powsybl.contingency.ContingencyContext;
import com.powsybl.contingency.ContingencyContextType;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * Sensitivity factor to be computed in the sensitivity analysis.
 * It regroups in a single object a description of the variable to modify, a description of the function to monitor
 * and a contingency context. A factor corresponds to the definition of a partial derivative to be extracted from the
 * network in a given contingency context. Usually we compute the impact of an injection increase on a branch flow or current,
 * the impact of a shift of a phase tap changer on a branch flow or current or the impact of a voltage target increase on a bus voltage.
 *
 * @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
 */
public class SensitivityFactor {

    private final SensitivityFunctionType functionType;

    private final String functionId;

    private final SensitivityVariableType variableType;

    private final String variableId;

    private final boolean variableSet;

    private final ContingencyContext contingencyContext;

    /**
     * Constructor.
     *
     * @param functionType see {@link com.powsybl.sensitivity.SensitivityFunctionType}
     * @param functionId the id of the equipment to monitor (in general the id of a branch). For BUS_VOLTAGE type, see
     * {@link com.powsybl.iidm.network.IdBasedBusRef}
     * @param variableType see {@link com.powsybl.sensitivity.SensitivityVariableType}
     * @param variableId id of the equipment affected by the injection increase, the angle sift, the voltage target
     *                   increase or the active power set point increase.
     * @param variableSet boolean to says if the variable is a variable set or not
     * @param contingencyContext see {@link com.powsybl.contingency.ContingencyContext}
     */
    public SensitivityFactor(SensitivityFunctionType functionType, String functionId, SensitivityVariableType variableType,
                              String variableId, boolean variableSet, ContingencyContext contingencyContext) {
        this.functionType = Objects.requireNonNull(functionType);
        this.functionId = Objects.requireNonNull(functionId);
        this.variableType = Objects.requireNonNull(variableType);
        this.variableId = Objects.requireNonNull(variableId);
        this.variableSet = variableSet;
        this.contingencyContext = Objects.requireNonNull(contingencyContext);
    }

    public SensitivityFunctionType getFunctionType() {
        return functionType;
    }

    public String getFunctionId() {
        return functionId;
    }

    public SensitivityVariableType getVariableType() {
        return variableType;
    }

    public String getVariableId() {
        return variableId;
    }

    public boolean isVariableSet() {
        return variableSet;
    }

    public ContingencyContext getContingencyContext() {
        return contingencyContext;
    }

    @Override
    public String toString() {
        return "SensitivityFactor(" +
                "functionType=" + functionType +
                ", functionId='" + functionId + '\'' +
                ", variableType=" + variableType +
                ", variableId='" + variableId + '\'' +
                ", variableSet=" + variableSet +
                ", contingencyContext=" + contingencyContext +
                ')';
    }

    public static void writeJson(JsonGenerator jsonGenerator, SensitivityFactor factor) {
        writeJson(jsonGenerator, factor.getFunctionType(), factor.getFunctionId(), factor.getVariableType(),
                factor.getVariableId(), factor.isVariableSet(), factor.getContingencyContext());
    }

    public static void writeJson(JsonGenerator jsonGenerator, SensitivityFunctionType functionType, String functionId, SensitivityVariableType variableType,
                          String variableId, boolean variableSet, ContingencyContext contingencyContext) {
        try {
            jsonGenerator.writeStartObject();
            jsonGenerator.writeStringField("functionType", functionType.name());
            jsonGenerator.writeStringField("functionId", functionId);
            jsonGenerator.writeStringField("variableType", variableType.name());
            jsonGenerator.writeStringField("variableId", variableId);
            jsonGenerator.writeBooleanField("variableSet", variableSet);
            jsonGenerator.writeStringField("contingencyContextType", contingencyContext.getContextType().name());
            if (contingencyContext.getContingencyId() != null) {
                jsonGenerator.writeStringField("contingencyId", contingencyContext.getContingencyId());
            }

            jsonGenerator.writeEndObject();
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    static final class ParsingContext {
        SensitivityFunctionType functionType;
        String functionId;
        SensitivityVariableType variableType;
        String variableId;
        Boolean variableSet;
        ContingencyContextType contingencyContextType;
        String contingencyId;

        void reset() {
            functionType = null;
            functionId = null;
            variableType = null;
            variableId = null;
            variableSet = null;
            contingencyContextType = null;
            contingencyId = null;
        }
    }

    public static SensitivityFactor parseJson(JsonParser parser) {
        Objects.requireNonNull(parser);

        var context = new ParsingContext();
        try {
            JsonToken token;
            while ((token = parser.nextToken()) != null) {
                if (token == JsonToken.FIELD_NAME) {
                    parseJson(parser, context);
                } else if (token == JsonToken.END_OBJECT) {
                    boolean variableSet = Objects.requireNonNull(context.variableSet, "Parameter variableSet is missing");
                    return new SensitivityFactor(context.functionType, context.functionId, context.variableType, context.variableId, variableSet,
                            new ContingencyContext(context.contingencyId, context.contingencyContextType));
                }
            }
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
        throw new PowsyblException("Parsing error");
    }

    static void parseJson(JsonParser parser, ParsingContext context) throws IOException {
        String fieldName = parser.currentName();
        switch (fieldName) {
            case "functionType":
                context.functionType = SensitivityFunctionType.valueOf(parser.nextTextValue());
                break;
            case "functionId":
                context.functionId = parser.nextTextValue();
                break;
            case "variableType":
                context.variableType = SensitivityVariableType.valueOf(parser.nextTextValue());
                break;
            case "variableId":
                context.variableId = parser.nextTextValue();
                break;
            case "variableSet":
                context.variableSet = parser.nextBooleanValue();
                break;
            case "contingencyContextType":
                context.contingencyContextType = ContingencyContextType.valueOf(parser.nextTextValue());
                break;
            case "contingencyId":
                context.contingencyId = parser.nextTextValue();
                break;
            default:
                throw new PowsyblException("Unexpected field: " + fieldName);
        }
    }

    public static List<SensitivityFactor> createMatrix(SensitivityFunctionType functionType, Collection<String> functionIds,
                                                       SensitivityVariableType variableType, Collection<String> variableIds,
                                                       boolean variableSet, ContingencyContext contingencyContext) {
        return functionIds.stream().flatMap(functionId -> variableIds.stream().map(variableId -> new SensitivityFactor(functionType, functionId, variableType, variableId, variableSet, contingencyContext)))
                .collect(Collectors.toList());
    }
}