FlowDecompositionResults.java

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

import com.powsybl.flow_decomposition.rescaler.DecomposedFlowRescaler;
import com.powsybl.iidm.network.*;

import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * This class provides flow decomposition results from a network.
 * Those results are returned by a flowDecompositionComputer when run on a network.
 *
 * @author Sebastien Murgey {@literal <sebastien.murgey at rte-france.com>}
 * @author Hugo Schindler {@literal <hugo.schindler at rte-france.com>}
 * @see FlowDecompositionComputer
 * @see DecomposedFlow
 */
public class FlowDecompositionResults {
    private static final String NO_CONTINGENCY_ID = "";
    private final String networkId;
    private final String id;
    private final Set<Country> zoneSet;
    private final Map<String, DecomposedFlow> decomposedFlowMap = new HashMap<>();

    public class PerStateBuilder {
        private final Map<String, Branch> xnecMap;
        private final String contingencyId;
        private Map<String, Double> acTerminal1ReferenceFlow;
        private Map<String, Double> acTerminal2ReferenceFlow;
        private Map<String, Double> acCurrentTerminal1;
        private Map<String, Double> acCurrentTerminal2;
        private Map<String, Double> dcReferenceFlow;
        private Map<String, FlowPartition> flowPartitions = new HashMap<>();
        private final FlowDecompositionObserverList observers = new FlowDecompositionObserverList();

        PerStateBuilder(String contingencyId, Set<Branch> xnecList) {
            this.xnecMap = xnecList.stream().collect(Collectors.toMap(Identifiable::getId, Function.identity()));
            this.contingencyId = contingencyId;
        }

        void saveAcTerminal1ReferenceFlow(Map<String, Double> acTerminal1ReferenceFlow) {
            this.acTerminal1ReferenceFlow = acTerminal1ReferenceFlow;
        }

        void saveAcTerminal2ReferenceFlow(Map<String, Double> acTerminal2ReferenceFlow) {
            this.acTerminal2ReferenceFlow = acTerminal2ReferenceFlow;
        }

        void saveAcCurrentTerminal1(Map<String, Double> acTerminal1Current) {
            this.acCurrentTerminal1 = acTerminal1Current;
        }

        void saveAcCurrentTerminal2(Map<String, Double> acTerminal2Current) {
            this.acCurrentTerminal2 = acTerminal2Current;
        }

        void saveDcReferenceFlow(Map<String, Double> dcReferenceFlow) {
            this.dcReferenceFlow = dcReferenceFlow;
        }

        public void saveFlowPartitions(Map<String, FlowPartition> flowPartitions) {
            this.flowPartitions = flowPartitions;
        }

        void addObserversList(FlowDecompositionObserverList observers) {
            this.observers.addObserversFrom(observers);
        }

        void build(DecomposedFlowRescaler decomposedFlowRescaler, Network network) {
            flowPartitions
                .forEach((branchId, flowPartition) -> {
                    String xnecId = DecomposedFlow.getXnecId(contingencyId, branchId);
                    decomposedFlowMap.put(xnecId, createDecomposedFlow(branchId, flowPartition, decomposedFlowRescaler, network));
                });
        }

        private DecomposedFlow createDecomposedFlow(String branchId, FlowPartition flowPartition, DecomposedFlowRescaler decomposedFlowRescaler, Network network) {
            Country country1 = NetworkUtil.getTerminalCountry(xnecMap.get(branchId).getTerminal1());
            Country country2 = NetworkUtil.getTerminalCountry(xnecMap.get(branchId).getTerminal2());
            DecomposedFlow decomposedFlow = new DecomposedFlowBuilder()
                    .withBranchId(branchId)
                    .withContingencyId(contingencyId)
                    .withCountry1(country1)
                    .withCountry2(country2)
                    .withAcTerminal1ReferenceFlow(acTerminal1ReferenceFlow.get(branchId))
                    .withAcTerminal2ReferenceFlow(acTerminal2ReferenceFlow.get(branchId))
                    .withDcReferenceFlow(dcReferenceFlow.get(branchId))
                    .withAcCurrentTerminal1(acCurrentTerminal1.get(branchId))
                    .withAcCurrentTerminal2(acCurrentTerminal2.get(branchId))
                    .withFlowPartition(flowPartition)
                    .build();
            observers.computedPreRescalingDecomposedFlows(decomposedFlow);
            return decomposedFlowRescaler.rescale(decomposedFlow, network);
        }
    }

    FlowDecompositionResults(Network network) {
        Date date = Date.from(Instant.now());
        String dateString = new SimpleDateFormat("yyyyMMdd-HHmmss").format(date);
        this.networkId = network.getNameOrId();
        this.id = "Flow_Decomposition_Results_of_" + dateString + "_on_network_" + networkId;
        this.zoneSet = network.getCountries();
    }

    /**
     * @return Network Id
     */
    public String getNetworkId() {
        return networkId;
    }

    /**
     * @return Id composed of a time format and the network id.
     */
    public String getId() {
        return id;
    }

    /**
     * @return the set of available zones in this result
     */
    public Set<Country> getZoneSet() {
        return zoneSet;
    }

    /**
     * @return A rescaled flow decomposition map. The keys are the XNEC and the values are {@code DecomposedFlow} objects.
     */
    public Map<String, DecomposedFlow> getDecomposedFlowMap() {
        return decomposedFlowMap;
    }

    PerStateBuilder getBuilder(String contingencyId, Set<Branch> xnecList) {
        return new PerStateBuilder(contingencyId, xnecList);
    }

    public PerStateBuilder getBuilder(Set<Branch> xnecList) {
        return new PerStateBuilder(NO_CONTINGENCY_ID, xnecList);
    }
}