TimeSeriesMapperTest.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/.
 * SPDX-License-Identifier: MPL-2.0
 */
package com.powsybl.metrix.mapping;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Range;
import com.powsybl.iidm.network.Identifiable;
import com.powsybl.iidm.network.Network;
import com.powsybl.timeseries.*;
import com.powsybl.timeseries.ast.FloatNodeCalc;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.threeten.extra.Interval;

import java.io.BufferedWriter;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.TreeSet;

import static com.powsybl.metrix.mapping.AbstractCompareTxt.compareStreamTxt;
import static org.junit.jupiter.api.Assertions.*;

/**
 * @author Paul Bui-Quang {@literal <paul.buiquang at rte-france.com>}
 */
class TimeSeriesMapperTest {

    private Network network;

    private final MappingParameters mappingParameters = MappingParameters.load();

    @BeforeEach
    public void setUp() {
        // create test network
        network = MappingTestNetwork.create();
    }

    @Test
    void testLogEmptyFilter() throws Exception {

        // create time series space mock
        TimeSeriesIndex index = RegularTimeSeriesIndex.create(Interval.parse("2015-01-01T00:00:00Z/2015-07-20T00:00:00Z"), Duration.ofDays(200));
        ReadOnlyTimeSeriesStore store = new ReadOnlyTimeSeriesStoreCache(
            TimeSeries.createDouble("equipment_ts", index, 1d, 2d),
            TimeSeries.createDouble("other_ts", index, 2d, 2d)
        );

        // create mapping config
        TimeSeriesMappingConfig mappingConfig = new TimeSeriesMappingConfig(network);
        TimeSeriesMappingConfigLoader loader = new TimeSeriesMappingConfigLoader(mappingConfig, store.getTimeSeriesNames(new TimeSeriesFilter()));
        loader.addEquipmentMapping(MappableEquipmentType.LOAD, "equipment_ts", null, NumberDistributionKey.ONE, EquipmentVariable.P0);
        loader.addEquipmentTimeSeries("other_ts", OtherVariable.OTHER_VARIABLE, "L1");

        // create mapper
        TimeSeriesMappingLogger logger = new TimeSeriesMappingLogger();
        TimeSeriesMapperParameters parameters = new TimeSeriesMapperParameters(new TreeSet<>(Collections.singleton(1)),
            Range.closed(0, 0), false, false, true, mappingParameters.getToleranceThreshold());
        TimeSeriesMapper mapper = new TimeSeriesMapper(mappingConfig, parameters, network, logger);

        // Observer
        List<TimeSeriesMapperObserver> observersList = ImmutableList.of(new DefaultTimeSeriesMapperObserver());

        // When not ignoring empty filters
        TimeSeriesMappingException exception = assertThrows(TimeSeriesMappingException.class, () -> mapper.mapToNetwork(store, observersList));
        assertEquals("Impossible to scale down 1 of ts equipment_ts to empty equipment list", exception.getMessage());

        // Create mapper
        parameters = new TimeSeriesMapperParameters(new TreeSet<>(Collections.singleton(1)),
            Range.closed(0, 0), false, true, true, mappingParameters.getToleranceThreshold());
        TimeSeriesMapper mapperIgnoring = new TimeSeriesMapper(mappingConfig, parameters, network, logger);

        // When ignoring empty filters
        mapperIgnoring.mapToNetwork(store, observersList);
        StringWriter writer = new StringWriter();
        try (BufferedWriter bufferedWriter = new BufferedWriter(writer)) {
            logger.writeCsv(bufferedWriter, ZoneId.of("UTC"));
            bufferedWriter.flush();
            assertNotNull(compareStreamTxt(writer.toString().getBytes(StandardCharsets.UTF_8), "/expected/", "nonIgnoredEmptyFilterLog.csv"));
        }
    }

    @Test
    void mappingTest() {

        List<String> equipmentTimeSeriesNames = new ArrayList<>();
        List<Identifiable<?>> equipmentIds = new ArrayList<>();
        List<Double> equipmentValues = new ArrayList<>();
        List<String> equipmentVariables = new ArrayList<>();

        List<String> otherTimeSeriesNames = new ArrayList<>();
        List<Identifiable<?>> otherIds = new ArrayList<>();
        List<Double> otherValues = new ArrayList<>();
        List<String> otherVariables = new ArrayList<>();

        // create time series space mock
        TimeSeriesIndex index = RegularTimeSeriesIndex.create(Interval.parse("2015-01-01T00:00:00Z/2015-07-20T00:00:00Z"), Duration.ofDays(200));
        ReadOnlyTimeSeriesStore store = new ReadOnlyTimeSeriesStoreCache(
                TimeSeries.createDouble("equipment_ts", index, 1d, 2d),
                TimeSeries.createDouble("other_ts", index, 2d, 2d)
            );

        // create mapping config
        TimeSeriesMappingConfig mappingConfig = new TimeSeriesMappingConfig(network);
        TimeSeriesMappingConfigLoader loader = new TimeSeriesMappingConfigLoader(mappingConfig, store.getTimeSeriesNames(new TimeSeriesFilter()));
        loader.addEquipmentMapping(MappableEquipmentType.LOAD, "equipment_ts", "LD2", NumberDistributionKey.ONE, EquipmentVariable.P0);
        loader.addEquipmentTimeSeries("other_ts", OtherVariable.OTHER_VARIABLE, "L1");

        // create mapper
        TimeSeriesMappingLogger logger = new TimeSeriesMappingLogger();
        TimeSeriesMapperParameters parameters = new TimeSeriesMapperParameters(new TreeSet<>(Collections.singleton(1)),
                Range.closed(0, 0), false, false, true, mappingParameters.getToleranceThreshold());
        TimeSeriesMapper mapper = new TimeSeriesMapper(mappingConfig, parameters, network, logger);

        // launch TimeSeriesMapper test
        TimeSeriesMapperObserver observer = new DefaultTimeSeriesMapperObserver() {
            @Override
            public void timeSeriesMappedToEquipment(int point, String timeSeriesName, Identifiable<?> identifiable, MappingVariable variable, double equipmentValue) {
                if (variable instanceof EquipmentVariable) {
                    equipmentTimeSeriesNames.add(timeSeriesName);
                    equipmentIds.add(identifiable);
                    equipmentValues.add(equipmentValue);
                    equipmentVariables.add(variable.getVariableName());
                } else if (variable instanceof OtherVariable) {
                    otherTimeSeriesNames.add(timeSeriesName);
                    otherIds.add(identifiable);
                    otherValues.add(equipmentValue);
                    otherVariables.add(variable.getVariableName());
                }
            }
        };
        mapper.mapToNetwork(store, ImmutableList.of(observer));

        assertEquals(1, equipmentTimeSeriesNames.size());
        assertEquals(ImmutableList.of("equipment_ts"), equipmentTimeSeriesNames);
        assertEquals(ImmutableList.of(network.getIdentifiable("LD2")), equipmentIds);
        assertEquals(ImmutableList.of(1.0), equipmentValues);
        assertEquals(ImmutableList.of("p0"), equipmentVariables);

        assertEquals(1, otherTimeSeriesNames.size());
        assertEquals(ImmutableList.of("other_ts"), otherTimeSeriesNames);
        assertEquals(ImmutableList.of(network.getIdentifiable("L1")), otherIds);
        assertEquals(ImmutableList.of(2.0), otherValues);
        assertEquals(ImmutableList.of("otherVariable"), otherVariables);
    }

    @Test
    void goodIndexTest() {
        // create time series space mock
        TimeSeriesIndex index = RegularTimeSeriesIndex.create(Interval.parse("1970-01-01T00:00:00Z/1970-01-02T00:00:00Z"), Duration.ofDays(1));

        ReadOnlyTimeSeriesStore store = new ReadOnlyTimeSeriesStoreCache(
                TimeSeries.createDouble("foo", index, 1d, 1d),
                TimeSeries.createDouble("bar", index, 1d, 1d)
        );

        // create mapping config
        TimeSeriesMappingConfig mappingConfig = new TimeSeriesMappingConfig(network);

        mappingConfig.getTimeSeriesNodes().put("calculated", new FloatNodeCalc(10f));
        TimeSeriesMappingConfigLoader loader = new TimeSeriesMappingConfigLoader(mappingConfig, store.getTimeSeriesNames(new TimeSeriesFilter()));
        loader.addEquipmentMapping(MappableEquipmentType.LOAD, "calculated", "l1", NumberDistributionKey.ONE, EquipmentVariable.P0);
        loader.addEquipmentMapping(MappableEquipmentType.LOAD, "foo", "l2", NumberDistributionKey.ONE, EquipmentVariable.P0);
        loader.addEquipmentMapping(MappableEquipmentType.GENERATOR, "bar", "g1", NumberDistributionKey.ONE, EquipmentVariable.TARGET_P);

        new TimeSeriesMappingConfigTableLoader(mappingConfig, store).checkIndexUnicity();
    }

    @Test
    void wrongIndexTest() {
        // create time series space mock
        TimeSeriesIndex index1 = RegularTimeSeriesIndex.create(Interval.parse("1970-01-01T00:00:00Z/1970-01-02T00:00:00Z"), Duration.ofDays(1));
        TimeSeriesIndex index2 = RegularTimeSeriesIndex.create(Interval.parse("1970-01-01T00:00:00Z/1970-01-03T00:00:00Z"), Duration.ofDays(2));

        ReadOnlyTimeSeriesStore store = new ReadOnlyTimeSeriesStoreCache(
            TimeSeries.createDouble("foo", index1, 1d, 1d),
            TimeSeries.createDouble("bar", index2, 1d, 1d)
        );

        // create mapping config
        TimeSeriesMappingConfig mappingConfig = new TimeSeriesMappingConfig(network);

        mappingConfig.getTimeSeriesNodes().put("calculated", new FloatNodeCalc(10f));
        TimeSeriesMappingConfigLoader loader = new TimeSeriesMappingConfigLoader(mappingConfig, store.getTimeSeriesNames(new TimeSeriesFilter()));
        loader.addEquipmentMapping(MappableEquipmentType.LOAD, "calculated", "l1", NumberDistributionKey.ONE, EquipmentVariable.P0);
        loader.addEquipmentMapping(MappableEquipmentType.LOAD, "foo", "l2", NumberDistributionKey.ONE, EquipmentVariable.P0);
        loader.addEquipmentMapping(MappableEquipmentType.GENERATOR, "bar", "g1", NumberDistributionKey.ONE, EquipmentVariable.TARGET_P);

        TimeSeriesMappingConfigTableLoader timeSeriesMappingConfigTableLoader = new TimeSeriesMappingConfigTableLoader(mappingConfig, store);
        assertThrows(TimeSeriesMappingException.class,
            timeSeriesMappingConfigTableLoader::checkIndexUnicity,
            "Time series involved in the mapping must have the same index");
    }
}