XnecProviderByIds.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.xnec_provider;

import com.powsybl.commons.PowsyblException;
import com.powsybl.contingency.BranchContingency;
import com.powsybl.contingency.Contingency;
import com.powsybl.contingency.ContingencyBuilder;
import com.powsybl.flow_decomposition.XnecProvider;
import com.powsybl.iidm.network.Branch;
import com.powsybl.iidm.network.Network;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author Hugo Schindler {@literal <hugo.schindler at rte-france.com>}
 */
public final class XnecProviderByIds implements XnecProvider {
    private static final Logger LOGGER = LoggerFactory.getLogger(XnecProviderByIds.class);
    private final Map<String, Contingency> contingencyIdToContingencyMap;
    private final Map<Contingency, Set<String>> contingencyToXnecMap;
    private final Set<String> bestCaseBranches;

    private XnecProviderByIds(Map<String, Contingency> contingencyIdToContingencyMap, Map<Contingency, Set<String>> contingencyToXnecMap, Set<String> bestCaseBranches) {
        this.contingencyIdToContingencyMap = contingencyIdToContingencyMap;
        this.contingencyToXnecMap = contingencyToXnecMap;
        this.bestCaseBranches = bestCaseBranches;
    }

    public static Builder builder() {
        return new Builder();
    }

    public static final class Builder {
        private final Map<String, Contingency> contingencyIdToContingencyMap = new HashMap<>();
        private final Map<Contingency, Set<String>> contingencyToXnecMap = new HashMap<>();
        private final Set<String> bestCaseBranches = new HashSet<>();

        private Builder() {
            //private Builder, use XnecProviderByIds.builder()
        }

        public Builder addContingencies(Map<String, Set<String>> contingencies) {
            contingencies.forEach(this::addContingency);
            return this;
        }

        public Builder addContingency(String contingencyId, Set<String> contingencyElementIdSet) {
            Contingency contingency = createContingency(contingencyId, contingencyElementIdSet);
            return addContingency(contingency);
        }

        public Builder addContingency(Contingency contingency) {
            this.contingencyIdToContingencyMap.put(contingency.getId(), contingency);
            this.contingencyToXnecMap.put(contingency, new HashSet<>());
            return this;
        }

        private Contingency createContingency(String contingencyId, Set<String> contingencyElementIdSet) {
            ContingencyBuilder contingencyBuilder = Contingency.builder(contingencyId);
            contingencyElementIdSet.forEach(contingencyBuilder::addBranch);
            return contingencyBuilder.build();
        }

        public Builder addNetworkElementsAfterContingencies(Set<String> branchIds, Set<String> contingencyIds) {
            contingencyIds.forEach(contingencyId -> {
                if (contingencyIdToContingencyMap.containsKey(contingencyId)) {
                    Contingency contingency = contingencyIdToContingencyMap.get(contingencyId);
                    branchIds.forEach(branchId -> {
                        if (!contingency.getElements().contains(new BranchContingency(branchId))) {
                            contingencyToXnecMap.get(contingency).add(branchId);
                        } else {
                            LOGGER.warn("Branch '{}' is used inside contingency '{}'. This pair of branch/contingency is ignored", branchId, contingencyId);
                        }
                    });
                } else {
                    throw new PowsyblException(String.format("Contingency Id '%s' have not been defined. See addContingency and/or addContingencies", contingencyId));
                }
            });
            return this;
        }

        public Builder addNetworkElementsOnBasecase(Set<String> branchIds) {
            bestCaseBranches.addAll(branchIds);
            return this;
        }

        public XnecProviderByIds build() {
            return new XnecProviderByIds(contingencyIdToContingencyMap, contingencyToXnecMap, bestCaseBranches);
        }
    }

    private Set<Branch> mapBranchSetToList(Set<String> branchSet, Network network) {
        return branchSet.stream()
            .map(xnecId -> {
                Branch branch = network.getBranch(xnecId);
                if (branch == null) {
                    LOGGER.warn("Branch {} without contingency was not found in network {}", xnecId, network.getId());
                }
                return branch;
            })
            .filter(Objects::nonNull)
            .collect(Collectors.toSet());
    }

    @Override
    public Set<Branch> getNetworkElements(Network network) {
        return mapBranchSetToList(bestCaseBranches, network);
    }

    @Override
    public Set<Branch> getNetworkElements(String contingencyId, Network network) {
        Objects.requireNonNull(contingencyId, "Contingency Id must be specified");
        if (!contingencyIdToContingencyMap.containsKey(contingencyId)) {
            return Collections.emptySet();
        }
        return mapBranchSetToList(contingencyToXnecMap.get(contingencyIdToContingencyMap.get(contingencyId)), network);
    }

    @Override
    public Map<String, Set<Branch>> getNetworkElementsPerContingency(Network network) {
        Map<String, Set<Branch>> contingencyIdToXnec = new HashMap<>();
        contingencyIdToContingencyMap.forEach((contingencyId, contingency) -> contingencyIdToXnec.put(contingencyId, mapBranchSetToList(contingencyToXnecMap.get(contingency), network)));
        return contingencyIdToXnec;
    }

    @Override
    public List<Contingency> getContingencies(Network network) {
        return contingencyToXnecMap.keySet().stream()
            .filter(Objects::nonNull)
            .collect(Collectors.toList());
    }
}