-
Notifications
You must be signed in to change notification settings - Fork 5
/
TimeSeriesProcessor.java
279 lines (251 loc) · 12.5 KB
/
TimeSeriesProcessor.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
/*
* © 2021. TU Dortmund University,
* Institute of Energy Systems, Energy Efficiency and Energy Economics,
* Research group Distribution grid planning and operation
*/
package edu.ie3.datamodel.io.processor.timeseries;
import static edu.ie3.datamodel.io.processor.timeseries.FieldSourceToMethod.FieldSource.*;
import edu.ie3.datamodel.exceptions.EntityProcessorException;
import edu.ie3.datamodel.io.processor.EntityProcessor;
import edu.ie3.datamodel.models.timeseries.TimeSeries;
import edu.ie3.datamodel.models.timeseries.TimeSeriesEntry;
import edu.ie3.datamodel.models.timeseries.individual.IndividualTimeSeries;
import edu.ie3.datamodel.models.timeseries.individual.TimeBasedValue;
import edu.ie3.datamodel.models.timeseries.repetitive.LoadProfileEntry;
import edu.ie3.datamodel.models.timeseries.repetitive.LoadProfileInput;
import edu.ie3.datamodel.models.value.*;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class TimeSeriesProcessor<
T extends TimeSeries<E, V>, E extends TimeSeriesEntry<V>, V extends Value>
extends EntityProcessor<TimeSeries> {
/**
* List of all combinations of time series class, entry class and value class, this processor is
* able to handle
*/
public static final List<TimeSeriesProcessorKey> eligibleKeys =
List.of(
new TimeSeriesProcessorKey(
IndividualTimeSeries.class, TimeBasedValue.class, EnergyPriceValue.class),
new TimeSeriesProcessorKey(
IndividualTimeSeries.class, TimeBasedValue.class, TemperatureValue.class),
new TimeSeriesProcessorKey(
IndividualTimeSeries.class, TimeBasedValue.class, WindValue.class),
new TimeSeriesProcessorKey(
IndividualTimeSeries.class, TimeBasedValue.class, SolarIrradianceValue.class),
new TimeSeriesProcessorKey(
IndividualTimeSeries.class, TimeBasedValue.class, WeatherValue.class),
new TimeSeriesProcessorKey(
IndividualTimeSeries.class, TimeBasedValue.class, HeatDemandValue.class),
new TimeSeriesProcessorKey(
IndividualTimeSeries.class, TimeBasedValue.class, PValue.class),
new TimeSeriesProcessorKey(
IndividualTimeSeries.class, TimeBasedValue.class, HeatAndPValue.class),
new TimeSeriesProcessorKey(
IndividualTimeSeries.class, TimeBasedValue.class, SValue.class),
new TimeSeriesProcessorKey(
IndividualTimeSeries.class, TimeBasedValue.class, HeatAndSValue.class),
new TimeSeriesProcessorKey(LoadProfileInput.class, LoadProfileEntry.class, PValue.class));
/**
* Specific combination of time series class, entry class and value class, this processor is
* foreseen to handle.
*/
private final TimeSeriesProcessorKey registeredKey;
/**
* Mapping from field name to the source, where to find the information and which getter method to
* invoke
*/
private final SortedMap<String, FieldSourceToMethod> fieldToSource;
private final String[] flattenedHeaderElements;
public TimeSeriesProcessor(Class<T> timeSeriesClass, Class<E> entryClass, Class<V> valueClass) {
super(timeSeriesClass);
/* Check, if this processor can handle the foreseen combination of time series, entry and value */
TimeSeriesProcessorKey timeSeriesKey =
new TimeSeriesProcessorKey(timeSeriesClass, entryClass, valueClass);
if (!eligibleKeys.contains(timeSeriesKey))
throw new EntityProcessorException(
"Cannot register time series combination '"
+ timeSeriesKey
+ "' with entity processor '"
+ this.getClass().getSimpleName()
+ "'. Eligible combinations: "
+ eligibleKeys.stream()
.map(TimeSeriesProcessorKey::toString)
.collect(Collectors.joining(", ")));
this.registeredKey = timeSeriesKey;
/* Register, where to get which information from */
this.fieldToSource = buildFieldToSource(timeSeriesClass, entryClass, valueClass);
/* Collect all header elements */
this.flattenedHeaderElements = fieldToSource.keySet().toArray(new String[0]);
}
/**
* Collects the mapping, where to find which information and how to get them (in terms of getter
* method).
*
* @param timeSeriesClass Class of the time series
* @param entryClass Class of the entry in the time series for the "outer" fields
* @param valueClass Class of the actual value in the entries for the "inner" fields
* @return A mapping from field name to a tuple of source information and equivalent getter method
*/
private SortedMap<String, FieldSourceToMethod> buildFieldToSource(
Class<T> timeSeriesClass, Class<E> entryClass, Class<V> valueClass) {
/* Get the mapping from field name to getter method ignoring the getter for returning all entries */
Map<String, FieldSourceToMethod> timeSeriesMapping =
mapFieldNameToGetter(timeSeriesClass, Arrays.asList("entries", "uuid", "type"))
.entrySet()
.stream()
.collect(
Collectors.toMap(
Map.Entry::getKey,
entry -> new FieldSourceToMethod(TIMESERIES, entry.getValue())));
/* Get the mapping from field name to getter method for the entry, but ignoring the getter for the value */
Map<String, FieldSourceToMethod> entryMapping =
mapFieldNameToGetter(entryClass, Collections.singletonList("value")).entrySet().stream()
.collect(
Collectors.toMap(
Map.Entry::getKey, entry -> new FieldSourceToMethod(ENTRY, entry.getValue())));
Map<String, FieldSourceToMethod> valueMapping;
if (!valueClass.equals(WeatherValue.class)) {
valueMapping =
mapFieldNameToGetter(valueClass).entrySet().stream()
.collect(
Collectors.toMap(
Map.Entry::getKey,
entry -> new FieldSourceToMethod(VALUE, entry.getValue())));
} else {
/* Treat the nested weather values specially. */
/* Flatten the nested structure of Weather value */
valueMapping =
Stream.concat(
Stream.concat(
Stream.concat(
mapFieldNameToGetter(
valueClass,
Arrays.asList("solarIrradiance", "temperature", "wind"))
.entrySet()
.stream()
.map(
entry ->
new AbstractMap.SimpleEntry<>(
entry.getKey(),
new FieldSourceToMethod(VALUE, entry.getValue()))),
mapFieldNameToGetter(SolarIrradianceValue.class).entrySet().stream()
.map(
entry ->
new AbstractMap.SimpleEntry<>(
entry.getKey(),
new FieldSourceToMethod(
WEATHER_IRRADIANCE, entry.getValue())))),
mapFieldNameToGetter(TemperatureValue.class).entrySet().stream()
.map(
entry ->
new AbstractMap.SimpleEntry<>(
entry.getKey(),
new FieldSourceToMethod(
WEATHER_TEMPERATURE, entry.getValue())))),
mapFieldNameToGetter(WindValue.class).entrySet().stream()
.map(
entry ->
new AbstractMap.SimpleEntry<>(
entry.getKey(),
new FieldSourceToMethod(WEATHER_WIND, entry.getValue()))))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
/* Put everything together */
HashMap<String, FieldSourceToMethod> jointMapping = new HashMap<>();
jointMapping.putAll(timeSeriesMapping);
jointMapping.putAll(entryMapping);
jointMapping.putAll(valueMapping);
/* Let uuid be the first entry */
return putUuidFirst(jointMapping);
}
@Override
public LinkedHashMap<String, String> handleEntity(TimeSeries entity) {
throw new UnsupportedOperationException(
"Don't invoke this simple method, but TimeSeriesProcessor#handleTimeSeries(TimeSeries).");
}
/**
* Handles the time series by processing each entry and collecting the results
*
* @param timeSeries Time series to handle
* @return A set of mappings from field name to value
*/
public Set<LinkedHashMap<String, String>> handleTimeSeries(T timeSeries) {
TimeSeriesProcessorKey key = new TimeSeriesProcessorKey(timeSeries);
if (!registeredKey.equals(key))
throw new EntityProcessorException(
"Cannot handle a time series combination "
+ key
+ " with this EntityProcessor. Please either provide a time series combination of "
+ registeredKey
+ " or create a new processor for "
+ key
+ "!");
Set<LinkedHashMap<String, String>> fieldToValueSet = new LinkedHashSet<>();
for (E entry : timeSeries.getEntries()) {
Map<String, String> entryResult = handleEntry(timeSeries, entry);
/* Prepare the actual result and add them to the set of all results */
fieldToValueSet.add(new LinkedHashMap<>(entryResult));
}
return fieldToValueSet;
}
/**
* Processes a single entry to a mapping from field name to value as String representation. The
* information from the time series are added as well.
*
* @param timeSeries Time series for additional information
* @param entry Actual entry to handle
* @return A sorted map from field name to value as String representation
*/
private Map<String, String> handleEntry(T timeSeries, E entry) {
/* Handle the information in the time series */
Map<String, Method> timeSeriesFieldToMethod = extractFieldToMethod(TIMESERIES);
LinkedHashMap<String, String> timeSeriesResults =
processObject(timeSeries, timeSeriesFieldToMethod);
/* Handle the information in the entry */
Map<String, Method> entryFieldToMethod = extractFieldToMethod(ENTRY);
LinkedHashMap<String, String> entryResults = processObject(entry, entryFieldToMethod);
/* Handle the information in the value */
Map<String, Method> valueFieldToMethod = extractFieldToMethod(VALUE);
LinkedHashMap<String, String> valueResult = processObject(entry.getValue(), valueFieldToMethod);
/* Treat WeatherValues specially, as they are nested ones */
if (entry.getValue() instanceof WeatherValue weatherValue) {
Map<String, Method> irradianceFieldToMethod = extractFieldToMethod(WEATHER_IRRADIANCE);
valueResult.putAll(processObject(weatherValue.getSolarIrradiance(), irradianceFieldToMethod));
Map<String, Method> temperatureFieldToMethod = extractFieldToMethod(WEATHER_TEMPERATURE);
valueResult.putAll(processObject(weatherValue.getTemperature(), temperatureFieldToMethod));
Map<String, Method> windFieldToMethod = extractFieldToMethod(WEATHER_WIND);
valueResult.putAll(processObject(weatherValue.getWind(), windFieldToMethod));
}
/* Join all information and sort them */
Map<String, String> combinedResult = new HashMap<>();
combinedResult.putAll(timeSeriesResults);
combinedResult.putAll(entryResults);
combinedResult.putAll(valueResult);
return putUuidFirst(combinedResult);
}
/**
* Extracts the field name to method map for the specific source
*
* @param source Source to extract field name to methods for
* @return Field name to methods for the desired source
*/
private Map<String, Method> extractFieldToMethod(FieldSourceToMethod.FieldSource source) {
return fieldToSource.entrySet().stream()
.filter(entry -> entry.getValue().source().equals(source))
.collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().method()));
}
@Override
public String[] getHeaderElements() {
return flattenedHeaderElements;
}
@Override
protected List<Class<? extends TimeSeries>> getEligibleEntityClasses() {
return eligibleKeys.stream()
.map(TimeSeriesProcessorKey::getTimeSeriesClass)
.distinct()
.collect(Collectors.toList());
}
}