TimeSeriesMappingConfigTableLoaderTest.java
package com.powsybl.metrix.mapping;
import com.google.common.collect.Range;
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
import com.powsybl.metrix.mapping.timeseries.FileSystemTimeSeriesStore;
import com.powsybl.timeseries.*;
import com.powsybl.timeseries.ast.TimeSeriesNameNodeCalc;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.threeten.extra.Interval;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static com.powsybl.metrix.mapping.TimeSeriesMappingConfigTableLoader.*;
import static com.powsybl.metrix.mapping.timeseries.FileSystemTimeSeriesStore.ExistingFilePolicy.APPEND;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
/**
* @author Nicolas Rol {@literal <nicolas.rol at rte-france.com>}
*/
class TimeSeriesMappingConfigTableLoaderTest {
private FileSystem fileSystem;
private RegularTimeSeriesIndex index;
private FileSystemTimeSeriesStore tsStore;
private TimeSeriesMappingConfigTableLoader tableLoader;
@BeforeEach
public void setUp() throws IOException {
this.fileSystem = Jimfs.newFileSystem(Configuration.unix());
// TimeSeries index
Instant now = Instant.ofEpochMilli(978303600000L);
index = RegularTimeSeriesIndex.create(now, now.plus(3, ChronoUnit.HOURS), Duration.ofHours(1));
// TimeSeries
StoredDoubleTimeSeries ts1 = TimeSeries.createDouble("ts1", index, 1d, 2d, 3d, 4d);
StoredDoubleTimeSeries ts2 = TimeSeries.createDouble("ts2", index, 1d, 3d, 5d, 7d);
StoredDoubleTimeSeries ts3 = TimeSeries.createDouble("ts3", index, 1d, 3d, 5d, 7d);
StoredDoubleTimeSeries ts4 = TimeSeries.createDouble("distributionKeyTs", index, 1d, 2d, 3d, 4d);
StoredDoubleTimeSeries ts5 = TimeSeries.createDouble("mappedTs", index, 1d, 2d, 3d, 4d);
StoredDoubleTimeSeries ts6 = TimeSeries.createDouble("equipmentTs", index, 1d, 2d, 3d, 4d);
StringTimeSeries ts7 = TimeSeries.createString("disconnected_ids", index, "id1", "id2", "id1,id2", "");
// TimeSeriesStore
Path resDir = Files.createDirectory(fileSystem.getPath("/tmp"));
tsStore = new FileSystemTimeSeriesStore(resDir);
tsStore.importTimeSeries(List.of(ts1, ts2), 1, APPEND);
tsStore.importTimeSeries(List.of(ts1), 2, APPEND);
tsStore.importTimeSeries(List.of(ts3), -1, APPEND);
tsStore.importTimeSeries(List.of(ts4), 1, APPEND);
tsStore.importTimeSeries(List.of(ts5), 1, APPEND);
tsStore.importTimeSeries(List.of(ts6), 1, APPEND);
tsStore.importTimeSeries(List.of(ts7), 1, APPEND);
// Mapping config
TimeSeriesMappingConfig mappingConfig = getTimeSeriesMappingConfig();
// Table loader
tableLoader = new TimeSeriesMappingConfigTableLoader(mappingConfig, tsStore);
}
private static TimeSeriesMappingConfig getTimeSeriesMappingConfig() {
TimeSeriesMappingConfig mappingConfig = new TimeSeriesMappingConfig();
mappingConfig.setMappedTimeSeriesNames(Set.of("mappedTs"));
mappingConfig.setTimeSeriesToEquipment(Map.of("equipmentTs", Set.of(new MappingKey(EquipmentVariable.P0, "id"))));
mappingConfig.setTimeSeriesToPlannedOutagesMapping(Map.of("disconnected_ids", Set.of("id1", "id2")));
mappingConfig.setDistributionKeys(Map.of(new MappingKey(EquipmentVariable.TARGET_P, "id"), new TimeSeriesDistributionKey("distributionKeyTs")));
mappingConfig.setTimeSeriesNodes(Map.of("calculatedTs", new TimeSeriesNameNodeCalc("ts1")));
return mappingConfig;
}
@AfterEach
public void tearDown() throws IOException {
this.fileSystem.close();
}
@Test
void usedTimeSeriesNamesTest() {
Iterable<String> usedTimeSeriesNames = tableLoader.findUsedTimeSeriesNames();
assertThat(usedTimeSeriesNames).hasSize(4).containsExactlyInAnyOrder("mappedTs", "equipmentTs", "distributionKeyTs", "disconnected_ids");
Set<String> timeSeriesNamesToLoad = tableLoader.findTimeSeriesNamesToLoad();
assertThat(timeSeriesNamesToLoad).hasSize(4).containsExactlyInAnyOrder("mappedTs", "equipmentTs", "distributionKeyTs", "disconnected_ids");
Set<String> usedTimeSeriesNamesToLoad = tableLoader.findTimeSeriesNamesToLoad(Set.of("mappedTs", "calculatedTs"));
assertThat(usedTimeSeriesNamesToLoad).hasSize(2).containsExactlyInAnyOrder("mappedTs", "ts1");
}
@Test
void loadTest() {
TimeSeriesTable loadToTable = tableLoader.loadToTable(1, tsStore, Range.closed(0, 1), Set.of("ts2", "calculatedTs"));
assertThat(loadToTable.getTimeSeriesNames()).hasSize(3).containsExactlyInAnyOrder("ts1", "ts2", "calculatedTs");
TimeSeriesTable loadTable = tableLoader.load(1, Set.of("ts2"), Range.closed(0, 1));
assertThat(loadTable.getTimeSeriesNames()).hasSize(4).containsExactlyInAnyOrder("distributionKeyTs", "equipmentTs", "mappedTs", "ts2");
}
@Test
void plannedOutagesTest() {
final String disconnectedIdsTsName = "disconnected_ids";
// Expected results
// step index -> disconnected id list
// 0 : id1
// 1 : id2
// 2 : id1, id2
// 3 : none
Map<String, double[]> expectedResults = Map.of(
disconnectedIdsTsName + "_id1", new double[]{0, 1, 0, 1},
disconnectedIdsTsName + "_id2", new double[]{1, 0, 0, 1});
// Compute disconnected equipment time series
StringTimeSeries plannedOutagesTimeSeries = tsStore.getStringTimeSeries(disconnectedIdsTsName, 1).orElseThrow(() -> new TimeSeriesException("Invalid planned outages time series name"));
List<DoubleTimeSeries> actualDoubleTimeSeries = computeDisconnectedEquipmentTimeSeries(disconnectedIdsTsName, plannedOutagesTimeSeries.toArray(), Set.of("id1", "id2"), index);
assertThat(actualDoubleTimeSeries).hasSize(2);
actualDoubleTimeSeries.forEach(ts -> assertTrue(expectedResults.containsKey(ts.getMetadata().getName())));
actualDoubleTimeSeries.forEach(ts -> assertArrayEquals(expectedResults.get(ts.getMetadata().getName()), ts.toArray()));
// Build time series store containing disconnected time series
Map<String, Set<String>> timeSeriesToPlannedOutagesMapping = Map.of(disconnectedIdsTsName, Set.of("id1", "id2"));
ReadOnlyTimeSeriesStore actualStore = buildPlannedOutagesStore(tsStore, 1, timeSeriesToPlannedOutagesMapping);
assertTrue(actualStore.timeSeriesExists(disconnectedIdsTsName + "_id1"));
assertTrue(actualStore.timeSeriesExists(disconnectedIdsTsName + "_id2"));
// Build store containing initial time series and disconnected time series
ReadOnlyTimeSeriesStore actualStoreWithPlannedOutages = buildStoreWithPlannedOutages(tsStore, 1, timeSeriesToPlannedOutagesMapping);
assertTrue(actualStoreWithPlannedOutages.timeSeriesExists(disconnectedIdsTsName + "_id1"));
assertTrue(actualStoreWithPlannedOutages.timeSeriesExists(disconnectedIdsTsName + "_id2"));
tsStore.getTimeSeriesNames(null).forEach(ts -> assertTrue(actualStoreWithPlannedOutages.timeSeriesExists(ts)));
// Same with initial store containing
ReadOnlyTimeSeriesStore secondActualStoreWithPlannedOutages = buildStoreWithPlannedOutages(actualStoreWithPlannedOutages, 1, timeSeriesToPlannedOutagesMapping);
assertEquals(actualStoreWithPlannedOutages, secondActualStoreWithPlannedOutages);
}
@Test
void testCheckIndexUnicity() {
StoredDoubleTimeSeries ts = TimeSeries.createDouble("ts", index, 1d, 2d, 3d, 4d);
TimeSeriesIndex actualIndex = checkIndexUnicity(new ReadOnlyTimeSeriesStoreCache(List.of(ts)), Set.of("ts"));
assertEquals(actualIndex, index);
TimeSeriesIndex actualTableIndex = tableLoader.checkIndexUnicity();
assertEquals(actualTableIndex, index);
TimeSeriesIndex actualEmptyIndex = checkIndexUnicity(new ReadOnlyTimeSeriesStoreCache(List.of(ts)), Collections.emptySet());
assertEquals(InfiniteTimeSeriesIndex.INSTANCE, actualEmptyIndex);
// It should fail in case of store containing time series with different index
TimeSeriesIndex otherIndex = RegularTimeSeriesIndex.create(Interval.parse("2015-01-01T01:00:00Z/2015-01-01T02:00:00Z"), Duration.ofHours(1));
StoredDoubleTimeSeries otherTs = TimeSeries.createDouble("otherTs", otherIndex, 1d, 2d);
ReadOnlyTimeSeriesStoreCache otherTsStore = new ReadOnlyTimeSeriesStoreCache(List.of(ts, otherTs));
Set<String> tsSet = Set.of("ts", "otherTs");
TimeSeriesMappingException exception = assertThrows(TimeSeriesMappingException.class, () -> checkIndexUnicity(otherTsStore, tsSet));
assertTrue(exception.getMessage().contains("Time series involved in the mapping must have the same index"));
}
@Test
void testCheckValues() {
// Assertion when it works
try {
tableLoader.checkValues(Set.of(1));
// existingVersions.containsAll(versions)
checkValues(tsStore, Set.of(1), Set.of("ts1"));
// isNotVersioned(existingVersions)
checkValues(tsStore, Set.of(-1), Set.of("ts3"));
// !existingVersions.isEmpty()
checkValues(tsStore, Set.of(1), Set.of("ts4"));
} catch (Exception e) {
fail();
}
// It should fail when a version is missing
Set<Integer> versions = Set.of(1, 2);
Set<String> timeSeriesNames = Set.of("ts2");
TimeSeriesMappingException exception = assertThrows(TimeSeriesMappingException.class, () -> checkValues(tsStore, versions, timeSeriesNames));
assertEquals("The time series store does not contain values for ts ts2 and version(s) [2]", exception.getMessage());
}
}