@@ -44,19 +44,20 @@ import edu.ie3.simona.service.weather.WeatherSource as SimonaWeatherSource
44
44
import edu .ie3 .simona .util .TickUtil .{RichZonedDateTime , TickLong }
45
45
import edu .ie3 .util .DoubleUtils .!~=
46
46
import edu .ie3 .util .interval .ClosedInterval
47
+ import org .locationtech .jts .geom .Point
47
48
import squants .thermal .Kelvin
48
49
import tech .units .indriya .ComparableQuantity
49
50
50
51
import java .nio .file .Paths
51
52
import java .time .ZonedDateTime
52
53
import java .time .format .DateTimeFormatter
53
54
import javax .measure .quantity .Length
55
+ import scala .collection .SortedMap
54
56
import scala .jdk .CollectionConverters .{
55
57
CollectionHasAsScala ,
56
58
IterableHasAsJava ,
57
59
MapHasAsScala ,
58
60
}
59
- import scala .jdk .OptionConverters .RichOptional
60
61
import scala .util .{Failure , Success , Try }
61
62
62
63
/** This class provides an implementation of the SIMONA trait
@@ -83,58 +84,58 @@ private[weather] final case class WeatherSourceWrapper private (
83
84
) extends SimonaWeatherSource
84
85
with LazyLogging {
85
86
86
- /** Get the weather data for the given tick as a weighted average taking into
87
- * account the given weighting of weather coordinates.
88
- *
89
- * @param tick
90
- * Simulation date in question
91
- * @param weightedCoordinates
92
- * The coordinate in question
93
- * @return
94
- * Matching weather data
95
- */
96
87
override def getWeather (
97
- tick : Long ,
88
+ startTick : Long ,
89
+ endTick : Long ,
98
90
weightedCoordinates : WeatherSource .WeightedCoordinates ,
99
- ): WeatherData = {
100
- val dateTime = tick.toDateTime
101
- val interval = new ClosedInterval (dateTime, dateTime)
91
+ ): SortedMap [ZonedDateTime , WeatherData ] = {
92
+ val interval = new ClosedInterval (startTick.toDateTime, endTick.toDateTime)
102
93
val coordinates = weightedCoordinates.weighting.keys.toList.asJavaCollection
103
- val results = source
94
+
95
+ source
104
96
.getWeather(
105
97
interval,
106
98
coordinates,
107
99
)
108
100
.asScala
109
- .toMap
110
- val weatherDataMap = results.flatMap { case (point, timeSeries) =>
111
- // change temperature scale for the upcoming calculations
112
- timeSeries
113
- .getValue(dateTime)
114
- .toScala
115
- .map(weatherValue => point -> toWeatherData(weatherValue))
116
- }
117
-
118
- weatherDataMap.foldLeft((EMPTY_WEATHER_DATA , WeightSum .EMPTY_WEIGHT_SUM )) {
119
- case ((averagedWeather, currentWeightSum), (point, currentWeather)) =>
120
- /** Calculate the contribution of a single coordinate value to the
121
- * averaged weather information. If we got an empty quantity (which can
122
- * be the case, as this particular value might be missing in the
123
- * weather data), we do let it out and also return the "effective"
124
- * weight of 0d.
125
- */
126
-
127
- /* Get pre-calculated weight for this coordinate */
101
+ .map { case (coordinate, timeSeries) =>
102
+ timeSeries.getEntries.asScala.map { weatherValue =>
103
+ (weatherValue.getTime, coordinate) -> weatherValue.getValue
104
+ }.toMap
105
+ }
106
+ .flatten
107
+ .groupMap { case ((time, _), _) =>
108
+ time
109
+ } { case ((_, location), weatherValue) =>
128
110
val weight = weightedCoordinates.weighting.getOrElse(
129
- point , {
130
- logger.warn(s " Received an unexpected point: $point " )
111
+ location , {
112
+ logger.warn(s " Received an unexpected point: $location " )
131
113
0d
132
114
},
133
115
)
134
- /* Sum up weight and contributions */
116
+ val weatherData = toWeatherData(weatherValue)
117
+ (location, weatherData, weight)
118
+ }
119
+ .map { case (time, weather) =>
120
+ time -> spatialDataInterpolation(weather)
121
+ }
122
+ .to(SortedMap )
123
+ }
124
+
125
+ private def spatialDataInterpolation (
126
+ weatherData : Iterable [(Point , WeatherData , Double )]
127
+ ): WeatherData =
128
+ weatherData.foldLeft((EMPTY_WEATHER_DATA , WeightSum .EMPTY_WEIGHT_SUM )) {
129
+ case ((averagedWeather, weightSum), (point, weather, weight)) =>
130
+ /* Calculate the contribution of a single coordinate value to the
131
+ * averaged weather information. If we got an empty quantity (which can
132
+ * be the case, as this particular value might be missing in the
133
+ * weather data), we do let it out and also return the "effective"
134
+ * weight of 0d.
135
+ */
135
136
136
137
/* Determine actual weights and contributions */
137
- val (diffIrradiance, diffIrrWeight) = currentWeather .diffIrr match {
138
+ val (diffIrradiance, diffIrrWeight) = weather .diffIrr match {
138
139
case EMPTY_WEATHER_DATA .diffIrr =>
139
140
// Some data sets do not provide diffuse irradiance, so we do not
140
141
// warn here
@@ -144,15 +145,15 @@ private[weather] final case class WeatherSourceWrapper private (
144
145
(averagedWeather.diffIrr + nonEmptyDiffIrr * weight, weight)
145
146
}
146
147
147
- val (dirIrradience , dirIrrWeight) = currentWeather .dirIrr match {
148
+ val (dirIrradiance , dirIrrWeight) = weather .dirIrr match {
148
149
case EMPTY_WEATHER_DATA .`dirIrr` =>
149
150
logger.warn(s " Direct solar irradiance not available at $point. " )
150
151
(averagedWeather.dirIrr, 0d )
151
152
case nonEmptyDirIrr =>
152
153
(averagedWeather.dirIrr + nonEmptyDirIrr * weight, weight)
153
154
}
154
155
155
- val (temperature, tempWeight) = currentWeather .temp match {
156
+ val (temperature, tempWeight) = weather .temp match {
156
157
case EMPTY_WEATHER_DATA .temp =>
157
158
logger.warn(s " Temperature not available at $point. " )
158
159
(averagedWeather.temp, 0d )
@@ -162,7 +163,7 @@ private[weather] final case class WeatherSourceWrapper private (
162
163
(averagedWeather.temp + nonEmptyTemp.in(Kelvin ) * weight, weight)
163
164
}
164
165
165
- val (windVelocity, windVelWeight) = currentWeather .windVel match {
166
+ val (windVelocity, windVelWeight) = weather .windVel match {
166
167
case EMPTY_WEATHER_DATA .windVel =>
167
168
logger.warn(s " Wind velocity not available at $point. " )
168
169
(averagedWeather.windVel, 0d )
@@ -171,8 +172,8 @@ private[weather] final case class WeatherSourceWrapper private (
171
172
}
172
173
173
174
(
174
- WeatherData (diffIrradiance, dirIrradience , temperature, windVelocity),
175
- currentWeightSum .add(
175
+ WeatherData (diffIrradiance, dirIrradiance , temperature, windVelocity),
176
+ weightSum .add(
176
177
diffIrrWeight,
177
178
dirIrrWeight,
178
179
tempWeight,
@@ -183,18 +184,7 @@ private[weather] final case class WeatherSourceWrapper private (
183
184
case (weatherData : WeatherData , weightSum : WeightSum ) =>
184
185
weightSum.scale(weatherData)
185
186
}
186
- }
187
187
188
- /** Determine an Array with all ticks between the request frame's start and
189
- * end on which new data is available
190
- *
191
- * @param requestFrameStart
192
- * Beginning of the announced request frame
193
- * @param requestFrameEnd
194
- * End of the announced request frame
195
- * @return
196
- * Array with data ticks
197
- */
198
188
override def getDataTicks (
199
189
requestFrameStart : Long ,
200
190
requestFrameEnd : Long ,
0 commit comments