Skip to content

Commit 085cad5

Browse files
Merge remote-tracking branch 'origin/df/#1357-validation-load' into df/#1357-validation-load
2 parents 9095892 + 8019e9f commit 085cad5

36 files changed

+1613
-468
lines changed

CHANGELOG.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
- Enhanced check for invalid field names in sources [#1383](https://github.com/ie3-institute/PowerSystemDataModel/issues/1383)
1111
- Enhancing value retrieval in `TimeSeriesSource` [1280](https://github.com/ie3-institute/PowerSystemDataModel/issues/1280)
1212
- Enhancing the `LoadProfileSource` to return the resolution [1288](https://github.com/ie3-institute/PowerSystemDataModel/issues/1288)
13-
14-
### Fixed
13+
- Enhancing Validation for sRated of `HpTypeInput` [1394](https://github.com/ie3-institute/PowerSystemDataModel/issues/1394)
14+
- Added updated `BdewStandardLoadProfiles` [#1292](https://github.com/ie3-institute/PowerSystemDataModel/issues/1292)
1515

1616
### Changed
17+
- Fixed CFF-Version [#1392](https://github.com/ie3-institute/PowerSystemDataModel/issues/1392)
1718
- Enhanced `ValidationUtils` for `LoadModel` to check for correct profile naming [#1357](https://github.com/ie3-institute/PowerSystemDataModel/issues/1357)
1819

1920
## [8.0.0] - 2025-07-22
@@ -32,7 +33,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3233
- Extend azimuth angle range to [-180°, 180°] for PV inputs [#1330](https://github.com/ie3-institute/PowerSystemDataModel/issues/1330)
3334
- Improved error messages when reading and validating an invalid grid [#1354](https://github.com/ie3-institute/PowerSystemDataModel/issues/1354)
3435
- Changed `SubgridContainer` to represent galvanically seperated grids [#1226](https://github.com/ie3-institute/PowerSystemDataModel/issues/1226)
35-
- Fixed CFF-Version [#1392](https://github.com/ie3-institute/PowerSystemDataModel/issues/1392)
3636

3737
## [7.0.0] - 2025-05-08
3838

docs/readthedocs/io/csvfiles.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,11 @@ The following profiles are supported until now:
169169
- Information
170170
- Supported head line.
171171
* - e.g.: H0
172-
- BDEW standard load profiles ([source](https://www.bdew.de/energie/standardlastprofile-strom/))
172+
- BDEW standard load profiles 1999 ([source](https://www.bdew.de/energie/standardlastprofile-strom/))
173173
- Permissible head line: ``SuSa,SuSu,SuWd,TrSa,TrSu,TrWd,WiSa,WiSu,WiWd,quarterHour``
174+
* - e.g.: h25
175+
- BDEW standard load profiles 2025 ([source](https://www.bdew.de/energie/standardlastprofile-strom/))
176+
- Permissible head line: ``janSa,janSu,janWd,febSa,febSu,febWd,marSa,marSu,marWd,aprSa,aprSu,aprWd,maySa,maySu,mayWd,junSa,junSu,junWd,julSa,julSu,julWd,augSa,augSu,augWd,sepSa,sepSu,sepWd,octSa,octSu,octWd,novSa,novSu,novWd,decSa,decSu,decWd,quarterHour``
174177
* - random
175178
- A random load proile based on: ``Kays - Agent-based simulation environment for improving the planning of distribution grids``
176179
- Permissible head line: ``kSa,kSu,kWd,mySa,mySu,myWd,sigmaSa,sigmaSu,sigmaWd,quarterHour``

src/main/java/edu/ie3/datamodel/io/factory/timeseries/BdewLoadProfileFactory.java

Lines changed: 52 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -15,66 +15,57 @@
1515
import edu.ie3.datamodel.models.timeseries.repetitive.BdewLoadProfileTimeSeries;
1616
import edu.ie3.datamodel.models.timeseries.repetitive.LoadProfileEntry;
1717
import edu.ie3.datamodel.models.value.load.BdewLoadValues;
18-
import java.util.*;
18+
import edu.ie3.datamodel.models.value.load.BdewLoadValues.BdewKey;
19+
import edu.ie3.datamodel.models.value.load.BdewLoadValues.BdewScheme;
20+
import edu.ie3.util.quantities.PowerSystemUnits;
21+
import java.util.Comparator;
22+
import java.util.HashSet;
23+
import java.util.List;
24+
import java.util.Set;
1925
import java.util.function.Function;
20-
import java.util.stream.Stream;
2126
import javax.measure.quantity.Energy;
2227
import javax.measure.quantity.Power;
2328
import tech.units.indriya.ComparableQuantity;
2429
import tech.units.indriya.quantity.Quantities;
2530

2631
public class BdewLoadProfileFactory
2732
extends LoadProfileFactory<BdewStandardLoadProfile, BdewLoadValues> {
28-
public static final String SUMMER_SATURDAY = "SuSa";
29-
public static final String SUMMER_SUNDAY = "SuSu";
30-
public static final String SUMMER_WEEKDAY = "SuWd";
31-
public static final String TRANSITION_SATURDAY = "TrSa";
32-
public static final String TRANSITION_SUNDAY = "TrSu";
33-
public static final String TRANSITION_WEEKDAY = "TrWd";
34-
public static final String WINTER_SATURDAY = "WiSa";
35-
public static final String WINTER_SUNDAY = "WiSu";
36-
public static final String WINTER_WEEKDAY = "WiWd";
33+
// 1999 profile scheme
34+
public static final BdewLoadValues.BdewMap<String> BDEW1999_FIELDS =
35+
BdewKey.toMap(BdewScheme.BDEW1999);
3736

38-
public BdewLoadProfileFactory() {
39-
this(BdewLoadValues.class);
40-
}
37+
// 2025 profile scheme
38+
public static final BdewLoadValues.BdewMap<String> BDEW2025_FIELDS =
39+
BdewKey.toMap(BdewScheme.BDEW2025);
4140

42-
public BdewLoadProfileFactory(Class<BdewLoadValues> valueClass) {
43-
super(valueClass);
41+
public BdewLoadProfileFactory() {
42+
super(BdewLoadValues.class);
4443
}
4544

4645
@Override
4746
protected LoadProfileEntry<BdewLoadValues> buildModel(LoadProfileData<BdewLoadValues> data) {
4847
int quarterHour = data.getInt(QUARTER_HOUR);
4948

50-
return new LoadProfileEntry<>(
51-
new BdewLoadValues(
52-
data.getDouble(SUMMER_SATURDAY),
53-
data.getDouble(SUMMER_SUNDAY),
54-
data.getDouble(SUMMER_WEEKDAY),
55-
data.getDouble(TRANSITION_SATURDAY),
56-
data.getDouble(TRANSITION_SUNDAY),
57-
data.getDouble(TRANSITION_WEEKDAY),
58-
data.getDouble(WINTER_SATURDAY),
59-
data.getDouble(WINTER_SUNDAY),
60-
data.getDouble(WINTER_WEEKDAY)),
61-
quarterHour);
49+
boolean is1999Scheme =
50+
data.containsKey("SuSa") || data.containsKey("su_sa") || data.containsKey("suSa");
51+
52+
BdewLoadValues values;
53+
54+
if (is1999Scheme) {
55+
values = new BdewLoadValues(BdewScheme.BDEW1999, BDEW1999_FIELDS.map(data::getDouble));
56+
57+
} else {
58+
values = new BdewLoadValues(BdewScheme.BDEW2025, BDEW2025_FIELDS.map(data::getDouble));
59+
}
60+
61+
return new LoadProfileEntry<>(values, quarterHour);
6262
}
6363

6464
@Override
6565
protected List<Set<String>> getFields(Class<?> entityClass) {
6666
return List.of(
67-
newSet(
68-
QUARTER_HOUR,
69-
SUMMER_SATURDAY,
70-
SUMMER_SUNDAY,
71-
SUMMER_WEEKDAY,
72-
TRANSITION_SATURDAY,
73-
TRANSITION_SUNDAY,
74-
TRANSITION_WEEKDAY,
75-
WINTER_SATURDAY,
76-
WINTER_SUNDAY,
77-
WINTER_WEEKDAY));
67+
expandSet(new HashSet<>(BDEW1999_FIELDS.values()), QUARTER_HOUR),
68+
expandSet(new HashSet<>(BDEW2025_FIELDS.values()), QUARTER_HOUR));
7869
}
7970

8071
@Override
@@ -101,26 +92,35 @@ public BdewStandardLoadProfile parseProfile(String profile) {
10192
@Override
10293
public ComparableQuantity<Power> calculateMaxPower(
10394
BdewStandardLoadProfile loadProfile, Set<LoadProfileEntry<BdewLoadValues>> entries) {
104-
Function<BdewLoadValues, Stream<Double>> valueExtractor;
105-
106-
if (loadProfile == BdewStandardLoadProfile.H0) {
107-
// maximum dynamization factor is on day 366 (leap year) or day 365 (regular year).
108-
// The difference between day 365 and day 366 is negligible, thus pick 366
109-
valueExtractor =
110-
v ->
111-
Stream.of(v.getWiSa(), v.getWiSu(), v.getWiWd())
112-
.map(p -> BdewLoadValues.dynamization(p, 366));
113-
} else {
114-
valueExtractor = v -> v.values().stream();
115-
}
95+
Function<BdewLoadValues, Double> valueExtractor =
96+
switch (loadProfile) {
97+
case H0, H25, P25, S25 ->
98+
// maximum dynamization factor is on day 366 (leap year) or day 365 (regular year).
99+
// The difference between day 365 and day 366 is negligible, thus pick 366
100+
v -> BdewLoadValues.dynamization(v.getMaxValue(true), 366);
101+
default -> v -> v.getMaxValue(false);
102+
};
116103

117104
double maxPower =
118105
entries.stream()
119106
.map(TimeSeriesEntry::getValue)
120-
.flatMap(valueExtractor)
107+
.map(valueExtractor)
121108
.max(Comparator.naturalOrder())
122109
.orElse(0d);
123110

124111
return Quantities.getQuantity(maxPower, WATT);
125112
}
113+
114+
/** Returns the load profile energy scaling. The default value is 1000 kWh */
115+
@Override
116+
public ComparableQuantity<Energy> getLoadProfileEnergyScaling(
117+
BdewStandardLoadProfile loadProfile) {
118+
119+
// the updated profiled are scaled to 1 million kWh -> 1000 MWh
120+
// old profiles are scaled to 1000 kWh
121+
return switch (loadProfile) {
122+
case H25, G25, L25, P25, S25 -> Quantities.getQuantity(1000d, PowerSystemUnits.MEGAWATTHOUR);
123+
default -> Quantities.getQuantity(1000d, PowerSystemUnits.KILOWATTHOUR);
124+
};
125+
}
126126
}

src/main/java/edu/ie3/datamodel/io/factory/timeseries/LoadProfileFactory.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ public LoadProfileFactory(Class<? extends V> valueClass) {
3232
super(valueClass);
3333
}
3434

35+
@SafeVarargs
36+
protected LoadProfileFactory(Class<? extends V>... valueClass) {
37+
super(valueClass);
38+
}
39+
3540
public abstract LoadProfileTimeSeries<V> build(
3641
LoadProfileMetaInformation metaInformation, Set<LoadProfileEntry<V>> entries);
3742

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
import edu.ie3.datamodel.models.Entity;
1010
import edu.ie3.datamodel.models.StandardUnits;
1111
import edu.ie3.datamodel.utils.Try;
12-
import edu.ie3.datamodel.utils.Try.*;
12+
import edu.ie3.datamodel.utils.Try.Failure;
13+
import edu.ie3.datamodel.utils.Try.Success;
1314
import edu.ie3.util.exceptions.QuantityException;
14-
import java.lang.reflect.Method;
15-
import java.util.*;
15+
import java.util.Collections;
16+
import java.util.LinkedHashMap;
17+
import java.util.SortedMap;
1618
import javax.measure.Quantity;
1719
import javax.measure.quantity.Energy;
1820
import javax.measure.quantity.Power;
@@ -31,7 +33,7 @@ public abstract class EntityProcessor<T extends Entity> extends Processor<T> {
3133

3234
public static final Logger log = LoggerFactory.getLogger(EntityProcessor.class);
3335
protected final String[] headerElements;
34-
private final SortedMap<String, Method> fieldNameToMethod;
36+
private final SortedMap<String, GetterMethod> fieldNameToMethod;
3537

3638
private static final String NODE_INTERNAL = "nodeInternal";
3739

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* © 2025. 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.processor;
7+
8+
import java.lang.reflect.InvocationTargetException;
9+
import java.lang.reflect.Method;
10+
11+
public record GetterMethod(String name, Getter getter, String returnType) {
12+
13+
public GetterMethod(Method method) {
14+
this(method.getName(), method::invoke, method.getReturnType().getSimpleName());
15+
}
16+
17+
public Object invoke(Object object)
18+
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
19+
return getter.get(object);
20+
}
21+
22+
@FunctionalInterface
23+
public interface Getter {
24+
Object get(Object object)
25+
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
26+
}
27+
}

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

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@
1818
import edu.ie3.datamodel.models.result.CongestionResult;
1919
import edu.ie3.datamodel.models.voltagelevels.VoltageLevel;
2020
import edu.ie3.datamodel.utils.Try;
21-
import edu.ie3.datamodel.utils.Try.*;
21+
import edu.ie3.datamodel.utils.Try.Failure;
22+
import edu.ie3.datamodel.utils.Try.Success;
2223
import edu.ie3.util.TimeUtil;
2324
import edu.ie3.util.exceptions.QuantityException;
2425
import java.beans.Introspector;
2526
import java.lang.reflect.InvocationTargetException;
26-
import java.lang.reflect.Method;
2727
import java.time.ZonedDateTime;
2828
import java.util.*;
2929
import java.util.stream.Collectors;
@@ -110,7 +110,7 @@ public int compare(String a, String b) {
110110
* @param cls class to use for mapping
111111
* @return a map of all field values of the class
112112
*/
113-
protected SortedMap<String, Method> mapFieldNameToGetter(Class<?> cls)
113+
protected SortedMap<String, GetterMethod> mapFieldNameToGetter(Class<?> cls)
114114
throws EntityProcessorException {
115115
return mapFieldNameToGetter(cls, Collections.emptyList());
116116
}
@@ -122,10 +122,10 @@ protected SortedMap<String, Method> mapFieldNameToGetter(Class<?> cls)
122122
* @param ignoreFields A collection of all field names to ignore during process
123123
* @return a map of all field values of the class
124124
*/
125-
protected SortedMap<String, Method> mapFieldNameToGetter(
125+
protected SortedMap<String, GetterMethod> mapFieldNameToGetter(
126126
Class<?> cls, Collection<String> ignoreFields) throws EntityProcessorException {
127127
try {
128-
final LinkedHashMap<String, Method> resFieldNameToMethod = new LinkedHashMap<>();
128+
final LinkedHashMap<String, GetterMethod> resFieldNameToMethod = new LinkedHashMap<>();
129129
Arrays.stream(Introspector.getBeanInfo(cls, Object.class).getPropertyDescriptors())
130130
// filter out properties with setters only
131131
.filter(pd -> Objects.nonNull(pd.getReadMethod()))
@@ -139,19 +139,21 @@ protected SortedMap<String, Method> mapFieldNameToGetter(
139139
.forEach(
140140
pd -> {
141141
String fieldName = pd.getName();
142+
GetterMethod method = new GetterMethod(pd.getReadMethod());
143+
142144
// OperationTime needs to be replaced by operatesFrom and operatesUntil
143145
if (fieldName.equalsIgnoreCase(OPERATION_TIME_FIELD_NAME)) {
144146
fieldName = OPERATES_FROM;
145-
resFieldNameToMethod.put(OPERATES_UNTIL, pd.getReadMethod());
147+
resFieldNameToMethod.put(OPERATES_UNTIL, method);
146148
}
147149

148150
// VoltageLevel needs to be replaced by id and nominalVoltage
149151
if (fieldName.equalsIgnoreCase(VOLT_LVL_FIELD_NAME)) {
150152
fieldName = V_RATED;
151-
resFieldNameToMethod.put(VOLT_LVL, pd.getReadMethod());
153+
resFieldNameToMethod.put(VOLT_LVL, method);
152154
}
153155

154-
resFieldNameToMethod.put(fieldName, pd.getReadMethod());
156+
resFieldNameToMethod.put(fieldName, method);
155157
});
156158

157159
return putUuidFirst(resFieldNameToMethod);
@@ -183,12 +185,12 @@ public static <V> SortedMap<String, V> putUuidFirst(Map<String, V> unsorted) {
183185
* @return Mapping from field name to value as String representation
184186
*/
185187
protected LinkedHashMap<String, String> processObject(
186-
Object object, Map<String, Method> fieldNameToGetter) throws EntityProcessorException {
188+
Object object, Map<String, GetterMethod> fieldNameToGetter) throws EntityProcessorException {
187189
try {
188190
LinkedHashMap<String, String> resultMap = new LinkedHashMap<>();
189-
for (Map.Entry<String, Method> entry : fieldNameToGetter.entrySet()) {
191+
for (Map.Entry<String, GetterMethod> entry : fieldNameToGetter.entrySet()) {
190192
String fieldName = entry.getKey();
191-
Method getter = entry.getValue();
193+
GetterMethod getter = entry.getValue();
192194
Optional<Object> methodReturnObjectOpt = Optional.ofNullable(getter.invoke(object));
193195

194196
if (methodReturnObjectOpt.isPresent()) {
@@ -212,12 +214,13 @@ protected LinkedHashMap<String, String> processObject(
212214
* @param fieldName Name of the foreseen field
213215
* @return A String representation of the result
214216
*/
215-
protected String processMethodResult(Object methodReturnObject, Method method, String fieldName)
217+
protected String processMethodResult(
218+
Object methodReturnObject, GetterMethod method, String fieldName)
216219
throws EntityProcessorException {
217220

218221
StringBuilder resultStringBuilder = new StringBuilder();
219222

220-
switch (method.getReturnType().getSimpleName()) {
223+
switch (method.returnType()) {
221224
// primitives (Boolean, Character, Byte, Short, Integer, Long, Float, Double, String,
222225
case "UUID",
223226
"boolean",
@@ -300,9 +303,9 @@ protected String processMethodResult(Object methodReturnObject, Method method, S
300303
"Unable to process value for attribute/field '"
301304
+ fieldName
302305
+ "' and method return type '"
303-
+ method.getReturnType().getSimpleName()
306+
+ method.returnType()
304307
+ "' for method with name '"
305-
+ method.getName()
308+
+ method.name()
306309
+ "' in in entity model "
307310
+ getRegisteredClass().getSimpleName()
308311
+ ".class.");

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,8 @@ public static Collection<EntityProcessor<? extends Entity>> allResultEntityProce
322322
(Class<TimeSeries<TimeSeriesEntry<Value>, Value, Value>>)
323323
key.getTimeSeriesClass(),
324324
(Class<TimeSeriesEntry<Value>>) key.getEntryClass(),
325-
(Class<Value>) key.getValueClass()),
325+
(Class<Value>) key.getValueClass(),
326+
key.getScheme()),
326327
EntityProcessorException.class)),
327328
"time series processors",
328329
EntityProcessorException::new)

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
*/
66
package edu.ie3.datamodel.io.processor.timeseries;
77

8-
import java.lang.reflect.Method;
8+
import edu.ie3.datamodel.io.processor.GetterMethod;
99

1010
/**
11-
* Represent a tuple of {@link FieldSource} to {@link Method} to highlight, where information of a
12-
* time series can be obtained from
11+
* Represent a tuple of {@link FieldSource} to {@link GetterMethod} to highlight, where information
12+
* of a time series can be obtained from
1313
*/
14-
public record FieldSourceToMethod(FieldSource source, Method method) {
14+
public record FieldSourceToMethod(FieldSource source, GetterMethod method) {
1515
@Override
1616
public String toString() {
1717
return "FieldSourceToMethod{" + "source=" + source + ", method=" + method + '}';
@@ -24,6 +24,6 @@ public enum FieldSource {
2424
VALUE,
2525
WEATHER_IRRADIANCE,
2626
WEATHER_TEMPERATURE,
27-
WEATHER_WIND
27+
WEATHER_WIND,
2828
}
2929
}

0 commit comments

Comments
 (0)