AbstractNetwork.java

/**
 * Copyright (c) 2023, 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.iidm.network.impl;

import com.google.common.collect.FluentIterable;
import com.powsybl.commons.extensions.Extension;
import com.powsybl.iidm.network.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.ZoneOffset;
import java.time.ZonedDateTime;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
import java.util.stream.Stream;

/**
 * @author Miora Vedelago {@literal <miora.ralambotiana at rte-france.com>}
 */
abstract class AbstractNetwork extends AbstractIdentifiable<Network> implements NetworkExt {

    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractNetwork.class);

    private ZonedDateTime caseDate = ZonedDateTime.now(ZoneOffset.UTC); // default is the time at which the network has been created

    private int forecastDistance = 0;

    protected String sourceFormat;

    AbstractNetwork(String id, String name, String sourceFormat) {
        super(id, name);
        this.sourceFormat = Objects.requireNonNull(sourceFormat, "source format is null");
    }

    @Override
    public ContainerType getContainerType() {
        return ContainerType.NETWORK;
    }

    @Override
    public ZonedDateTime getCaseDate() {
        return caseDate;
    }

    @Override
    public Network setCaseDate(ZonedDateTime caseDate) {
        ValidationUtil.checkCaseDate(this, caseDate);
        this.caseDate = caseDate;
        return this;
    }

    @Override
    public int getForecastDistance() {
        return forecastDistance;
    }

    @Override
    public Network setForecastDistance(int forecastDistance) {
        ValidationUtil.checkForecastDistance(this, forecastDistance);
        this.forecastDistance = forecastDistance;
        return this;
    }

    @Override
    public String getSourceFormat() {
        return sourceFormat;
    }

    @Override
    protected String getTypeDescription() {
        return "Network";
    }

    /**
     * Transfer the extensions of a network to another one.
     * @param from the network whose extensions must be transferred
     * @param to the destination network
     */
    protected static void transferExtensions(Network from, Network to) {
        transferExtensions(from, to, false);
    }

    /**
     * Transfer the extensions of a network to another one.
     * @param from the network whose extensions must be transferred
     * @param to the destination network
     * @param ignoreAlreadyPresent should an extension to transfer be ignored if already present in {@code to}?
     */
    protected static void transferExtensions(Network from, Network to, boolean ignoreAlreadyPresent) {
        // Only well-defined extensions (with an interface) are transferred
        new ArrayList<Extension<?>>(from.getExtensions()).forEach(e ->
            Arrays.stream(e.getClass().getInterfaces())
                    .filter(c -> Objects.nonNull(from.getExtension(c)))
                    .forEach(clazz -> {
                        if (ignoreAlreadyPresent && to.getExtension(clazz) != null) {
                            LOGGER.warn("Extension of class \"{}\" was not transferred from \"{}\" to \"{}\": " +
                                            "an extension of this same class already exists in the destination network.",
                                    clazz.getName(), from.getId(), to.getId());
                        } else {
                            ((AbstractIdentifiable<Network>) from).removeExtension((Class<? extends Extension<Network>>) clazz, false);
                            to.addExtension((Class<? super Extension<Network>>) clazz, (Extension<Network>) e);
                        }
                    })
        );
    }

    /**
     * Transfer the properties of a network to another one.
     * @param fromNetwork the network whose properties must be transferred
     * @param toNetwork the destination network
     */
    protected static void transferProperties(AbstractNetwork fromNetwork, AbstractNetwork toNetwork) {
        transferProperties(fromNetwork, toNetwork, false);
    }

    /**
     * Transfer the properties of a network to another one.
     * @param fromNetwork the network whose properties must be transferred
     * @param toNetwork the destination network
     * @param ignoreAlreadyPresent should a property to transfer be ignored if already present in {@code toNetwork}?
     */
    protected static void transferProperties(AbstractNetwork fromNetwork, AbstractNetwork toNetwork, boolean ignoreAlreadyPresent) {
        fromNetwork.getProperties().forEach((key, value) -> {
            if (ignoreAlreadyPresent && toNetwork.hasProperty(key.toString())) {
                LOGGER.warn("Property \"{}\" was not transferred from \"{}\" to \"{}\": it already exists in the destination network.",
                        key, fromNetwork.getId(), toNetwork.getId());
            } else {
                toNetwork.setProperty(key.toString(), value.toString());
                fromNetwork.removeProperty(key.toString());
            }
        });
    }

    abstract class AbstractBusBreakerViewImpl implements BusBreakerView {
        @Override
        public Iterable<Bus> getBuses() {
            return FluentIterable.from(getVoltageLevels())
                    .transformAndConcat(vl -> vl.getBusBreakerView().getBuses());
        }

        @Override
        public Stream<Bus> getBusStream() {
            return getVoltageLevelStream().flatMap(vl -> vl.getBusBreakerView().getBusStream());
        }

        @Override
        public int getBusCount() {
            return getVoltageLevelStream().mapToInt(vl -> vl.getBusBreakerView().getBusCount()).sum();
        }

        @Override
        public Iterable<Switch> getSwitches() {
            return FluentIterable.from(getVoltageLevels())
                    .transformAndConcat(vl -> vl.getBusBreakerView().getSwitches());
        }

        @Override
        public Stream<Switch> getSwitchStream() {
            return getVoltageLevelStream().flatMap(vl -> vl.getBusBreakerView().getSwitchStream());
        }

        @Override
        public int getSwitchCount() {
            return getVoltageLevelStream().mapToInt(vl -> vl.getBusBreakerView().getSwitchCount()).sum();
        }
    }

    abstract class AbstractBusViewImpl implements BusView {
        @Override
        public Iterable<Bus> getBuses() {
            return FluentIterable.from(getVoltageLevels())
                    .transformAndConcat(vl -> vl.getBusView().getBuses());
        }

        @Override
        public Stream<Bus> getBusStream() {
            return getVoltageLevelStream().flatMap(vl -> vl.getBusView().getBusStream());
        }
    }
}