@@ -15,17 +15,30 @@ import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils
15
15
import edu .ie3 .simona .config .SimonaConfig .LoadRuntimeConfig
16
16
import edu .ie3 .simona .test .common .UnitSpec
17
17
import edu .ie3 .simona .test .matchers .DoubleMatchers
18
+ import edu .ie3 .util .TimeUtil
18
19
import edu .ie3 .util .quantities .PowerSystemUnits
19
- import edu .ie3 .util .scala .quantities .{ApparentPower , Voltamperes }
20
+ import edu .ie3 .util .scala .quantities .{
21
+ ApparentPower ,
22
+ Kilovoltamperes ,
23
+ Voltamperes ,
24
+ }
25
+ import squants .Percent
26
+ import squants .energy .{KilowattHours , Power , Watts }
20
27
import tech .units .indriya .quantity .Quantities
21
28
22
29
import java .util .UUID
23
30
24
- class ProfileLoadModelSpec extends UnitSpec with DoubleMatchers {
31
+ class ProfileLoadModelSpec
32
+ extends UnitSpec
33
+ with DoubleMatchers
34
+ with LoadModelTestHelper {
25
35
26
36
private implicit val powerTolerance : ApparentPower = Voltamperes (1e-2 )
27
37
private implicit val doubleTolerance : Double = 1e-6
28
38
39
+ private val simulationStartDate =
40
+ TimeUtil .withDefaults.toZonedDateTime(" 2022-01-01T00:00:00Z" )
41
+
29
42
" A profile load model" should {
30
43
31
44
val loadInput = new LoadInput (
@@ -126,5 +139,79 @@ class ProfileLoadModelSpec extends UnitSpec with DoubleMatchers {
126
139
127
140
}
128
141
142
+ " reach the targeted annual energy consumption" in {
143
+ forAll(
144
+ Table (" profile" , H0 , L0 , G0 )
145
+ ) { profile =>
146
+ val input = loadInput.copy().loadprofile(profile).build()
147
+
148
+ val config = LoadRuntimeConfig (
149
+ calculateMissingReactivePowerWithModel = false ,
150
+ scaling = 1.0 ,
151
+ uuids = List .empty,
152
+ modelBehaviour = " profile" ,
153
+ reference = " energy" ,
154
+ )
155
+
156
+ val targetEnergyConsumption = KilowattHours (
157
+ loadInput
158
+ .geteConsAnnual()
159
+ .to(PowerSystemUnits .KILOWATTHOUR )
160
+ .getValue
161
+ .doubleValue
162
+ )
163
+
164
+ val model = ProfileLoadModel (input, config)
165
+
166
+ /* Test against a permissible deviation of 2 %. As per official documentation of the bdew load profiles
167
+ * [https://www.bdew.de/media/documents/2000131_Anwendung-repraesentativen_Lastprofile-Step-by-step.pdf], 1.5 %
168
+ * are officially permissible. But, as we currently do not take (bank) holidays into account, we cannot reach
169
+ * this accuracy. */
170
+
171
+ calculateEnergyDiffForYear(
172
+ model,
173
+ simulationStartDate,
174
+ targetEnergyConsumption,
175
+ ) should be < Percent (2 )
176
+ }
177
+ }
178
+
179
+ " approximately reach the maximum power in a simulated year" in {
180
+ forAll(
181
+ Table (" profile" , H0 , L0 , G0 )
182
+ ) { profile =>
183
+ val input = loadInput.copy().loadprofile(profile).build()
184
+
185
+ val config = LoadRuntimeConfig (
186
+ calculateMissingReactivePowerWithModel = false ,
187
+ scaling = 1.0 ,
188
+ uuids = List .empty,
189
+ modelBehaviour = " profile" ,
190
+ reference = " power" ,
191
+ )
192
+
193
+ val model = ProfileLoadModel (input, config)
194
+
195
+ val targetMaximumPower = Kilovoltamperes (
196
+ input
197
+ .getsRated()
198
+ .to(PowerSystemUnits .KILOVOLTAMPERE )
199
+ .getValue
200
+ .doubleValue
201
+ ).toActivePower(input.getCosPhiRated)
202
+
203
+ val maximumPower = calculatePowerForYear(
204
+ model,
205
+ simulationStartDate,
206
+ ).maxOption.value
207
+
208
+ // the maximum value depends on the year of the simulation,
209
+ // since the maximum value for h0 will be reached on Saturdays in the winter
210
+ // and since the dynamization function reaches its maximum on day 366 (leap year)
211
+ implicit val tolerance : Power = Watts (1 )
212
+ maximumPower should approximate(targetMaximumPower)
213
+ }
214
+ }
215
+
129
216
}
130
217
}
0 commit comments