Skip to content

Commit db68a2c

Browse files
Merge pull request #1233 from ie3-institute/ms/#1232-add-RandomLoadProfileTimeSeries
Added `RandomLoadProfileTimeSeries`
2 parents 4a4e4d1 + 306a43a commit db68a2c

File tree

15 files changed

+556
-155
lines changed

15 files changed

+556
-155
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414
- Extend ValidationUtils for validating ThermalGrids [#1216](https://github.com/ie3-institute/PowerSystemDataModel/issues/1216)
1515
- Enhance `TimeSeriesSource` with method to retrieve the previous value before a given key [#1182](https://github.com/ie3-institute/PowerSystemDataModel/issues/1182)
1616
- Added `BdewLoadProfileTimeSeries` [#1230](https://github.com/ie3-institute/PowerSystemDataModel/issues/1230)
17+
- Added `RandomLoadProfileTimeSeries` [#1232](https://github.com/ie3-institute/PowerSystemDataModel/issues/1232)
1718

1819
### Fixed
1920
- Removing opened `SwitchInput` during connectivity check [#1221](https://github.com/ie3-institute/PowerSystemDataModel/issues/1221)

build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ plugins {
1111
id 'jacoco' // java code coverage plugin
1212
id "org.sonarqube" version "6.0.1.5171" // sonarqube
1313
id 'net.thauvin.erik.gradle.semver' version '1.0.4' // semantic versioning
14+
id "com.github.johnrengelman.shadow" version "8.1.1" // fat jar
1415
}
1516

1617
ext {
@@ -67,6 +68,9 @@ dependencies {
6768
// Graphs
6869
implementation 'org.jgrapht:jgrapht-core:1.5.2'
6970

71+
// Statistics (for random load model)
72+
implementation 'de.lmu.ifi.dbs.elki:elki:0.7.5'
73+
7074
// testing
7175
testImplementation "org.apache.groovy:groovy:$groovyBinaryVersion"
7276

docs/uml/main/TimeSeriesDatamodelConcept.puml

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ package models {
3232
}
3333

3434
DefaultLoadProfiles --|> LoadProfile
35+
RandomLoadProfile --|> LoadProfile
3536

3637
interface StandardLoadProfile {
3738
+ {static} parse(String): StandardLoadProfile
@@ -130,12 +131,22 @@ package models {
130131
}
131132
RepetitiveTimeSeries --|> TimeSeries
132133

133-
class LoadProfileInput {
134-
- type: StandardLoadProfile
135-
- dayOfWeekToHourlyValues: Map<DayOfWeek, Map<Integer, PValue>>
134+
abstract class LoadProfileTimeSeries<E extends LoadProfileEntry> {
135+
- loadProfile: StandardLoadProfile
136+
- valueMapping: Map<Key, Map<Integer, E>>
137+
+ getLoadProfile(): LoadProfile
138+
# fromTime(ZonedDateTime): Key
136139
}
137-
LoadProfileInput --|> RepetitiveTimeSeries
138-
LoadProfileInput *-- StandardLoadProfile
140+
LoadProfileTimeSeries --|> RepetitiveTimeSeries
141+
LoadProfileTimeSeries *-- LoadProfile
142+
143+
class BDEWLoadProfileTimeSeries {}
144+
BDEWLoadProfileTimeSeries --|> LoadProfileTimeSeries
145+
BDEWLoadProfileTimeSeries *-- BdewLoadProfileEntry
146+
147+
class RandomLoadProfileTimeSeries {}
148+
RandomLoadProfileTimeSeries --|> LoadProfileTimeSeries
149+
RandomLoadProfileTimeSeries *-- RandomLoadProfileEntry
139150

140151
abstract class TimeSeriesEntry <V extends Value> {
141152
# value: V
@@ -152,8 +163,21 @@ package models {
152163
class LoadProfileEntry {
153164
- dayOfWeek: DayOfWeek
154165
- quarterHourOfDay: int
166+
+ getDayOfWeek(): DayOfWeek
167+
+ getQuarterHourOfDay(): Integer
155168
}
156169
LoadProfileEntry --|> TimeSeriesEntry: <<bind>>:PValue
170+
171+
class BdewLoadProfileEntry {
172+
- season: Season
173+
+ getSeason(): Season
174+
}
175+
BdewLoadProfileEntry --|> LoadProfileEntry
176+
177+
class RandomLoadProfileEntry {
178+
- gev: GeneralizedExtremeValueDistribution
179+
}
180+
RandomLoadProfileEntry --|> LoadProfileEntry
157181
}
158182
}
159183

docs/uml/main/input/InputDatamodelConcept.puml

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -142,20 +142,6 @@ package models {
142142
MeasurementUnitInput --|> AssetInput
143143
MeasurementUnitInput ..|> HasNodes
144144

145-
class RandomLoadParameter {
146-
- quarterHour: int
147-
- kWd: Double
148-
- kSa: Double
149-
- kSu: Double
150-
- myWd: Double
151-
- mySa: Double
152-
- mySu: Double
153-
- sigmaWd: Double
154-
- sigmaSa: Double
155-
- sigmaSu: Double
156-
}
157-
RandomLoadParameter --|> InputEntity
158-
159145
abstract class AssetTypeInput {
160146
- id: String
161147
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* © 2024. TU Dortmund University,
3+
* Institute of Energy Systems, Energy Efficiency and Energy Economics,
4+
* Research group Distribution grid planning and operation
5+
*/
6+
package edu.ie3.datamodel.io.factory.timeseries;
7+
8+
import static edu.ie3.datamodel.models.profile.LoadProfile.RandomLoadProfile.RANDOM_LOAD_PROFILE;
9+
import static tech.units.indriya.unit.Units.WATT;
10+
11+
import edu.ie3.datamodel.io.naming.timeseries.LoadProfileMetaInformation;
12+
import edu.ie3.datamodel.models.profile.LoadProfile.RandomLoadProfile;
13+
import edu.ie3.datamodel.models.timeseries.repetitive.LoadProfileEntry;
14+
import edu.ie3.datamodel.models.timeseries.repetitive.RandomLoadProfileTimeSeries;
15+
import edu.ie3.datamodel.models.value.load.RandomLoadValues;
16+
import edu.ie3.util.quantities.PowerSystemUnits;
17+
import java.util.List;
18+
import java.util.Set;
19+
import javax.measure.quantity.Energy;
20+
import javax.measure.quantity.Power;
21+
import tech.units.indriya.ComparableQuantity;
22+
import tech.units.indriya.quantity.Quantities;
23+
24+
public class RandomLoadProfileFactory
25+
extends LoadProfileFactory<RandomLoadProfile, RandomLoadValues> {
26+
public static final String K_WEEKDAY = "kWd";
27+
public static final String K_SATURDAY = "kSa";
28+
public static final String K_SUNDAY = "kSu";
29+
public static final String MY_WEEKDAY = "myWd";
30+
public static final String MY_SATURDAY = "mySa";
31+
public static final String MY_SUNDAY = "mySu";
32+
public static final String SIGMA_WEEKDAY = "sigmaWd";
33+
public static final String SIGMA_SATURDAY = "sigmaSa";
34+
public static final String SIGMA_SUNDAY = "sigmaSu";
35+
36+
public RandomLoadProfileFactory() {
37+
super(RandomLoadValues.class);
38+
}
39+
40+
@Override
41+
protected LoadProfileEntry<RandomLoadValues> buildModel(LoadProfileData<RandomLoadValues> data) {
42+
int quarterHour = data.getInt(QUARTER_HOUR);
43+
44+
return new LoadProfileEntry<>(
45+
new RandomLoadValues(
46+
data.getDouble(K_SATURDAY),
47+
data.getDouble(K_SUNDAY),
48+
data.getDouble(K_WEEKDAY),
49+
data.getDouble(MY_SATURDAY),
50+
data.getDouble(MY_SUNDAY),
51+
data.getDouble(MY_WEEKDAY),
52+
data.getDouble(SIGMA_SATURDAY),
53+
data.getDouble(SIGMA_SUNDAY),
54+
data.getDouble(SIGMA_WEEKDAY)),
55+
quarterHour);
56+
}
57+
58+
@Override
59+
protected List<Set<String>> getFields(Class<?> entityClass) {
60+
return List.of(
61+
newSet(
62+
QUARTER_HOUR,
63+
K_WEEKDAY,
64+
K_SATURDAY,
65+
K_SUNDAY,
66+
MY_WEEKDAY,
67+
MY_SATURDAY,
68+
MY_SUNDAY,
69+
SIGMA_WEEKDAY,
70+
SIGMA_SATURDAY,
71+
SIGMA_SUNDAY));
72+
}
73+
74+
@Override
75+
public RandomLoadProfileTimeSeries build(
76+
LoadProfileMetaInformation metaInformation, Set<LoadProfileEntry<RandomLoadValues>> entries) {
77+
RandomLoadProfile profile = RANDOM_LOAD_PROFILE;
78+
79+
ComparableQuantity<Power> maxPower = calculateMaxPower(profile, entries);
80+
ComparableQuantity<Energy> profileEnergyScaling = getLoadProfileEnergyScaling(profile);
81+
82+
return new RandomLoadProfileTimeSeries(
83+
metaInformation.getUuid(), profile, entries, maxPower, profileEnergyScaling);
84+
}
85+
86+
@Override
87+
public RandomLoadProfile parseProfile(String profile) {
88+
return RANDOM_LOAD_PROFILE;
89+
}
90+
91+
/**
92+
* This is the 95 % quantile resulting from 10,000 evaluations of the year 2019. It is only
93+
* needed, when the load is meant to be scaled to rated active power.
94+
*
95+
* @return Reference active power to use for later model calculations
96+
*/
97+
@Override
98+
public ComparableQuantity<Power> calculateMaxPower(
99+
RandomLoadProfile loadProfile, Set<LoadProfileEntry<RandomLoadValues>> loadProfileEntries) {
100+
return Quantities.getQuantity(159d, WATT);
101+
}
102+
103+
/**
104+
* Returns the profile energy scaling factor, the random profile is scaled to.
105+
*
106+
* <p>It is said in 'Kays - Agent-based simulation environment for improving the planning of
107+
* distribution grids', that the Generalized Extreme Value distribution's parameters are sampled
108+
* from input data, that is normalized to 1,000 kWh annual energy consumption. However, due to
109+
* inaccuracies in random data reproduction, the sampled values will lead to an average annual
110+
* energy consumption of approx. this value. It has been found by 1,000 evaluations of the year
111+
* 2019.
112+
*/
113+
@Override
114+
public ComparableQuantity<Energy> getLoadProfileEnergyScaling(RandomLoadProfile loadProfile) {
115+
return Quantities.getQuantity(716.5416966513656, PowerSystemUnits.KILOWATTHOUR);
116+
}
117+
}

src/main/java/edu/ie3/datamodel/io/processor/Processor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,8 +261,8 @@ protected String processMethodResult(Object methodReturnObject, Method method, S
261261
processVoltageLevel((VoltageLevel) methodReturnObject, fieldName));
262262
case "Point", "LineString" -> resultStringBuilder.append(
263263
geoJsonWriter.write((Geometry) methodReturnObject));
264-
case "LoadProfile", "BdewStandardLoadProfile" -> resultStringBuilder.append(
265-
((LoadProfile) methodReturnObject).getKey());
264+
case "LoadProfile", "BdewStandardLoadProfile", "RandomLoadProfile" -> resultStringBuilder
265+
.append(((LoadProfile) methodReturnObject).getKey());
266266
case "AssetTypeInput",
267267
"BmTypeInput",
268268
"ChpTypeInput",

src/main/java/edu/ie3/datamodel/io/processor/timeseries/TimeSeriesProcessor.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import edu.ie3.datamodel.models.timeseries.repetitive.*;
1717
import edu.ie3.datamodel.models.value.*;
1818
import edu.ie3.datamodel.models.value.load.BdewLoadValues;
19+
import edu.ie3.datamodel.models.value.load.RandomLoadValues;
1920
import java.lang.reflect.Method;
2021
import java.util.*;
2122
import java.util.stream.Collectors;
@@ -54,7 +55,9 @@ public class TimeSeriesProcessor<
5455
new TimeSeriesProcessorKey(
5556
IndividualTimeSeries.class, TimeBasedValue.class, HeatAndSValue.class),
5657
new TimeSeriesProcessorKey(
57-
BdewLoadProfileTimeSeries.class, LoadProfileEntry.class, BdewLoadValues.class));
58+
BdewLoadProfileTimeSeries.class, LoadProfileEntry.class, BdewLoadValues.class),
59+
new TimeSeriesProcessorKey(
60+
RandomLoadProfileTimeSeries.class, LoadProfileEntry.class, RandomLoadValues.class));
5861

5962
/**
6063
* Specific combination of time series class, entry class and value class, this processor is

src/main/java/edu/ie3/datamodel/models/profile/LoadProfile.java

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.io.Serializable;
1010
import java.util.Arrays;
1111
import java.util.stream.Collectors;
12+
import java.util.stream.Stream;
1213

1314
public interface LoadProfile extends Serializable {
1415
/** @return The identifying String */
@@ -28,12 +29,12 @@ static LoadProfile parse(String key) throws ParsingException {
2829
}
2930

3031
static LoadProfile[] getAllProfiles() {
31-
final LoadProfile[][] all =
32-
new LoadProfile[][] {
33-
BdewStandardLoadProfile.values(), NbwTemperatureDependantLoadProfile.values()
34-
};
35-
36-
return Arrays.stream(all).flatMap(Arrays::stream).toArray(LoadProfile[]::new);
32+
return Stream.of(
33+
BdewStandardLoadProfile.values(),
34+
NbwTemperatureDependantLoadProfile.values(),
35+
(LoadProfile[]) RandomLoadProfile.values())
36+
.flatMap(Arrays::stream)
37+
.toArray(LoadProfile[]::new);
3738
}
3839

3940
/**
@@ -70,4 +71,13 @@ public String getKey() {
7071
return "No load profile assigned";
7172
}
7273
}
74+
75+
enum RandomLoadProfile implements LoadProfile {
76+
RANDOM_LOAD_PROFILE;
77+
78+
@Override
79+
public String getKey() {
80+
return "random";
81+
}
82+
}
7383
}

src/main/java/edu/ie3/datamodel/models/timeseries/repetitive/LoadProfileTimeSeries.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ public class LoadProfileTimeSeries<V extends LoadValues>
2525
private final Map<Integer, V> valueMapping;
2626

2727
/**
28-
* The maximum average power consumption per quarter-hour for a given calculated over all seasons
29-
* and weekday types of given load profile.
28+
* The maximum average power consumption per quarter-hour calculated over all seasons and weekday
29+
* types of given load profile.
3030
*/
31-
public final ComparableQuantity<Power> maxPower;
31+
private final ComparableQuantity<Power> maxPower;
3232

3333
/** The profile energy scaling in kWh. */
34-
public final ComparableQuantity<Energy> profileEnergyScaling;
34+
private final ComparableQuantity<Energy> profileEnergyScaling;
3535

3636
public LoadProfileTimeSeries(
3737
UUID uuid,
@@ -50,6 +50,19 @@ public LoadProfileTimeSeries(
5050
this.profileEnergyScaling = profileEnergyScaling;
5151
}
5252

53+
/**
54+
* Returns the maximum average power consumption per quarter-hour calculated over all seasons and
55+
* weekday types of given load profile in Watt.
56+
*/
57+
public Optional<ComparableQuantity<Power>> maxPower() {
58+
return Optional.ofNullable(maxPower);
59+
}
60+
61+
/** Returns the profile energy scaling in kWh. */
62+
public Optional<ComparableQuantity<Energy>> loadProfileScaling() {
63+
return Optional.ofNullable(profileEnergyScaling);
64+
}
65+
5366
/** Returns the {@link LoadProfile}. */
5467
public LoadProfile getLoadProfile() {
5568
return loadProfile;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* © 2024. TU Dortmund University,
3+
* Institute of Energy Systems, Energy Efficiency and Energy Economics,
4+
* Research group Distribution grid planning and operation
5+
*/
6+
package edu.ie3.datamodel.models.timeseries.repetitive;
7+
8+
import de.lmu.ifi.dbs.elki.math.statistics.distribution.GeneralizedExtremeValueDistribution;
9+
import edu.ie3.datamodel.models.profile.LoadProfile;
10+
import edu.ie3.datamodel.models.value.load.RandomLoadValues;
11+
import java.util.Set;
12+
import java.util.UUID;
13+
import javax.measure.quantity.Energy;
14+
import javax.measure.quantity.Power;
15+
import tech.units.indriya.ComparableQuantity;
16+
17+
/**
18+
* Describes a random load profile time series based on a {@link
19+
* GeneralizedExtremeValueDistribution}. Each value of this# timeseries is given in kW.
20+
*/
21+
public class RandomLoadProfileTimeSeries extends LoadProfileTimeSeries<RandomLoadValues> {
22+
23+
public RandomLoadProfileTimeSeries(
24+
UUID uuid,
25+
LoadProfile loadProfile,
26+
Set<LoadProfileEntry<RandomLoadValues>> entries,
27+
ComparableQuantity<Power> maxPower,
28+
ComparableQuantity<Energy> profileEnergyScaling) {
29+
super(uuid, loadProfile, entries, maxPower, profileEnergyScaling);
30+
}
31+
32+
@Override
33+
public LoadProfile.RandomLoadProfile getLoadProfile() {
34+
return (LoadProfile.RandomLoadProfile) super.getLoadProfile();
35+
}
36+
37+
@Override
38+
public String toString() {
39+
return "Random" + super.toString();
40+
}
41+
}

0 commit comments

Comments
 (0)