Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3ed7d1e
Adding updated `BdewStandardLoadProfiles`.
staudtMarius Apr 29, 2025
a91c505
fmt
staudtMarius May 6, 2025
cbd353b
Merge branch 'dev' into ms/#1292-add-updated-bdew-standard-load-profiles
staudtMarius May 7, 2025
5a63d66
Merge branch 'dev' into ms/#1292-add-updated-bdew-standard-load-profiles
staudtMarius May 8, 2025
e3376a6
Merge branch 'dev' into ms/#1292-add-updated-bdew-standard-load-profiles
staudtMarius May 9, 2025
505e18a
Adapted `CHANGELOG`.
staudtMarius May 9, 2025
bf69af7
Merge branch 'dev' into ms/#1292-add-updated-bdew-standard-load-profiles
staudtMarius May 23, 2025
a9f37a6
Merge branch 'dev' into ms/#1292-add-updated-bdew-standard-load-profiles
staudtMarius Jun 2, 2025
fe60de2
Enhancing load profile time series documentation.
staudtMarius Jul 11, 2025
2aba803
Merge branch 'dev' into ms/#1292-add-updated-bdew-standard-load-profiles
staudtMarius Jul 14, 2025
ac673db
Merge branch 'dev' into ms/#1292-add-updated-bdew-standard-load-profiles
staudtMarius Jul 14, 2025
bf029c1
Refactoring `BdewLoadValues`.
staudtMarius Jul 14, 2025
22a5c81
Simplify expected scheme in `BdewLoadProfileFactoryTest`
staudtMarius Jul 14, 2025
32d2e10
Refactoring getters in `BdewLoadValues`
staudtMarius Jul 16, 2025
794597d
fmt
staudtMarius Jul 16, 2025
4b20d8b
Fixing sonarqube issues.
staudtMarius Jul 17, 2025
f620aca
Merge branch 'dev' into ms/#1292-add-updated-bdew-standard-load-profiles
staudtMarius Jul 17, 2025
b612f00
Fixing sonarqube issues.
staudtMarius Jul 18, 2025
6b6d98c
Merge branch 'dev' into ms/#1292-add-updated-bdew-standard-load-profiles
staudtMarius Jul 23, 2025
175ba56
Merge branch 'dev' into ms/#1292-add-updated-bdew-standard-load-profiles
staudtMarius Jul 23, 2025
2e91707
Update CHANGELOG.
staudtMarius Jul 23, 2025
c3c38f7
Merge branch 'dev' into ms/#1292-add-updated-bdew-standard-load-profiles
staudtMarius Jul 25, 2025
0ec4ad8
Refactor calculation of max power for `BdewLoadValues`.
staudtMarius Jul 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Enhancing value retrieval in `TimeSeriesSource` [1280](https://github.com/ie3-institute/PowerSystemDataModel/issues/1280)
- Enhancing the `LoadProfileSource` to return the resolution [1288](https://github.com/ie3-institute/PowerSystemDataModel/issues/1288)
- Enhancing Validation for sRated of `HpTypeInput` [1394](https://github.com/ie3-institute/PowerSystemDataModel/issues/1394)

### Fixed
- Added updated `BdewStandardLoadProfiles` [#1292](https://github.com/ie3-institute/PowerSystemDataModel/issues/1292)

### Changed
- Fixed CFF-Version [#1392](https://github.com/ie3-institute/PowerSystemDataModel/issues/1392)

## [8.0.0] - 2025-07-22

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

## [7.0.0] - 2025-05-08

Expand Down
5 changes: 4 additions & 1 deletion docs/readthedocs/io/csvfiles.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,11 @@ The following profiles are supported until now:
- Information
- Supported head line.
* - e.g.: H0
- BDEW standard load profiles ([source](https://www.bdew.de/energie/standardlastprofile-strom/))
- BDEW standard load profiles 1999 ([source](https://www.bdew.de/energie/standardlastprofile-strom/))
- Permissible head line: ``SuSa,SuSu,SuWd,TrSa,TrSu,TrWd,WiSa,WiSu,WiWd,quarterHour``
* - e.g.: h25
- BDEW standard load profiles 2025 ([source](https://www.bdew.de/energie/standardlastprofile-strom/))
- 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``
* - random
- A random load proile based on: ``Kays - Agent-based simulation environment for improving the planning of distribution grids``
- Permissible head line: ``kSa,kSu,kWd,mySa,mySu,myWd,sigmaSa,sigmaSu,sigmaWd,quarterHour``
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,66 +15,57 @@
import edu.ie3.datamodel.models.timeseries.repetitive.BdewLoadProfileTimeSeries;
import edu.ie3.datamodel.models.timeseries.repetitive.LoadProfileEntry;
import edu.ie3.datamodel.models.value.load.BdewLoadValues;
import java.util.*;
import edu.ie3.datamodel.models.value.load.BdewLoadValues.BdewKey;
import edu.ie3.datamodel.models.value.load.BdewLoadValues.BdewScheme;
import edu.ie3.util.quantities.PowerSystemUnits;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Stream;
import javax.measure.quantity.Energy;
import javax.measure.quantity.Power;
import tech.units.indriya.ComparableQuantity;
import tech.units.indriya.quantity.Quantities;

public class BdewLoadProfileFactory
extends LoadProfileFactory<BdewStandardLoadProfile, BdewLoadValues> {
public static final String SUMMER_SATURDAY = "SuSa";
public static final String SUMMER_SUNDAY = "SuSu";
public static final String SUMMER_WEEKDAY = "SuWd";
public static final String TRANSITION_SATURDAY = "TrSa";
public static final String TRANSITION_SUNDAY = "TrSu";
public static final String TRANSITION_WEEKDAY = "TrWd";
public static final String WINTER_SATURDAY = "WiSa";
public static final String WINTER_SUNDAY = "WiSu";
public static final String WINTER_WEEKDAY = "WiWd";
// 1999 profile scheme
public static final BdewLoadValues.BdewMap<String> BDEW1999_FIELDS =
BdewKey.toMap(BdewScheme.BDEW1999);

public BdewLoadProfileFactory() {
this(BdewLoadValues.class);
}
// 2025 profile scheme
public static final BdewLoadValues.BdewMap<String> BDEW2025_FIELDS =
BdewKey.toMap(BdewScheme.BDEW2025);

public BdewLoadProfileFactory(Class<BdewLoadValues> valueClass) {
super(valueClass);
public BdewLoadProfileFactory() {
super(BdewLoadValues.class);
}

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

return new LoadProfileEntry<>(
new BdewLoadValues(
data.getDouble(SUMMER_SATURDAY),
data.getDouble(SUMMER_SUNDAY),
data.getDouble(SUMMER_WEEKDAY),
data.getDouble(TRANSITION_SATURDAY),
data.getDouble(TRANSITION_SUNDAY),
data.getDouble(TRANSITION_WEEKDAY),
data.getDouble(WINTER_SATURDAY),
data.getDouble(WINTER_SUNDAY),
data.getDouble(WINTER_WEEKDAY)),
quarterHour);
boolean is1999Scheme =
data.containsKey("SuSa") || data.containsKey("su_sa") || data.containsKey("suSa");

BdewLoadValues values;

if (is1999Scheme) {
values = new BdewLoadValues(BdewScheme.BDEW1999, BDEW1999_FIELDS.map(data::getDouble));

} else {
values = new BdewLoadValues(BdewScheme.BDEW2025, BDEW2025_FIELDS.map(data::getDouble));
}

return new LoadProfileEntry<>(values, quarterHour);
}

@Override
protected List<Set<String>> getFields(Class<?> entityClass) {
return List.of(
newSet(
QUARTER_HOUR,
SUMMER_SATURDAY,
SUMMER_SUNDAY,
SUMMER_WEEKDAY,
TRANSITION_SATURDAY,
TRANSITION_SUNDAY,
TRANSITION_WEEKDAY,
WINTER_SATURDAY,
WINTER_SUNDAY,
WINTER_WEEKDAY));
expandSet(new HashSet<>(BDEW1999_FIELDS.values()), QUARTER_HOUR),
expandSet(new HashSet<>(BDEW2025_FIELDS.values()), QUARTER_HOUR));
}

@Override
Expand All @@ -101,26 +92,35 @@ public BdewStandardLoadProfile parseProfile(String profile) {
@Override
public ComparableQuantity<Power> calculateMaxPower(
BdewStandardLoadProfile loadProfile, Set<LoadProfileEntry<BdewLoadValues>> entries) {
Function<BdewLoadValues, Stream<Double>> valueExtractor;

if (loadProfile == BdewStandardLoadProfile.H0) {
// maximum dynamization factor is on day 366 (leap year) or day 365 (regular year).
// The difference between day 365 and day 366 is negligible, thus pick 366
valueExtractor =
v ->
Stream.of(v.getWiSa(), v.getWiSu(), v.getWiWd())
.map(p -> BdewLoadValues.dynamization(p, 366));
} else {
valueExtractor = v -> v.values().stream();
}
Function<BdewLoadValues, Double> valueExtractor =
switch (loadProfile) {
case H0, H25, P25, S25 ->
// maximum dynamization factor is on day 366 (leap year) or day 365 (regular year).
// The difference between day 365 and day 366 is negligible, thus pick 366
v -> BdewLoadValues.dynamization(v.getMaxValue(true), 366);
default -> v -> v.getMaxValue(false);
};

double maxPower =
entries.stream()
.map(TimeSeriesEntry::getValue)
.flatMap(valueExtractor)
.map(valueExtractor)
.max(Comparator.naturalOrder())
.orElse(0d);

return Quantities.getQuantity(maxPower, WATT);
}

/** Returns the load profile energy scaling. The default value is 1000 kWh */
@Override
public ComparableQuantity<Energy> getLoadProfileEnergyScaling(
BdewStandardLoadProfile loadProfile) {

// the updated profiled are scaled to 1 million kWh -> 1000 MWh
// old profiles are scaled to 1000 kWh
return switch (loadProfile) {
case H25, G25, L25, P25, S25 -> Quantities.getQuantity(1000d, PowerSystemUnits.MEGAWATTHOUR);
default -> Quantities.getQuantity(1000d, PowerSystemUnits.KILOWATTHOUR);
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ public LoadProfileFactory(Class<? extends V> valueClass) {
super(valueClass);
}

@SafeVarargs
protected LoadProfileFactory(Class<? extends V>... valueClass) {
super(valueClass);
}

public abstract LoadProfileTimeSeries<V> build(
LoadProfileMetaInformation metaInformation, Set<LoadProfileEntry<V>> entries);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
import edu.ie3.datamodel.models.Entity;
import edu.ie3.datamodel.models.StandardUnits;
import edu.ie3.datamodel.utils.Try;
import edu.ie3.datamodel.utils.Try.*;
import edu.ie3.datamodel.utils.Try.Failure;
import edu.ie3.datamodel.utils.Try.Success;
import edu.ie3.util.exceptions.QuantityException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.SortedMap;
import javax.measure.Quantity;
import javax.measure.quantity.Energy;
import javax.measure.quantity.Power;
Expand All @@ -31,7 +33,7 @@ public abstract class EntityProcessor<T extends Entity> extends Processor<T> {

public static final Logger log = LoggerFactory.getLogger(EntityProcessor.class);
protected final String[] headerElements;
private final SortedMap<String, Method> fieldNameToMethod;
private final SortedMap<String, GetterMethod> fieldNameToMethod;

private static final String NODE_INTERNAL = "nodeInternal";

Expand Down
27 changes: 27 additions & 0 deletions src/main/java/edu/ie3/datamodel/io/processor/GetterMethod.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* © 2025. 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;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public record GetterMethod(String name, Getter getter, String returnType) {

public GetterMethod(Method method) {
this(method.getName(), method::invoke, method.getReturnType().getSimpleName());
}

public Object invoke(Object object)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
return getter.get(object);
}

@FunctionalInterface
public interface Getter {
Object get(Object object)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
}
}
33 changes: 18 additions & 15 deletions src/main/java/edu/ie3/datamodel/io/processor/Processor.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
import edu.ie3.datamodel.models.result.CongestionResult;
import edu.ie3.datamodel.models.voltagelevels.VoltageLevel;
import edu.ie3.datamodel.utils.Try;
import edu.ie3.datamodel.utils.Try.*;
import edu.ie3.datamodel.utils.Try.Failure;
import edu.ie3.datamodel.utils.Try.Success;
import edu.ie3.util.TimeUtil;
import edu.ie3.util.exceptions.QuantityException;
import java.beans.Introspector;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -110,7 +110,7 @@ public int compare(String a, String b) {
* @param cls class to use for mapping
* @return a map of all field values of the class
*/
protected SortedMap<String, Method> mapFieldNameToGetter(Class<?> cls)
protected SortedMap<String, GetterMethod> mapFieldNameToGetter(Class<?> cls)
throws EntityProcessorException {
return mapFieldNameToGetter(cls, Collections.emptyList());
}
Expand All @@ -122,10 +122,10 @@ protected SortedMap<String, Method> mapFieldNameToGetter(Class<?> cls)
* @param ignoreFields A collection of all field names to ignore during process
* @return a map of all field values of the class
*/
protected SortedMap<String, Method> mapFieldNameToGetter(
protected SortedMap<String, GetterMethod> mapFieldNameToGetter(
Class<?> cls, Collection<String> ignoreFields) throws EntityProcessorException {
try {
final LinkedHashMap<String, Method> resFieldNameToMethod = new LinkedHashMap<>();
final LinkedHashMap<String, GetterMethod> resFieldNameToMethod = new LinkedHashMap<>();
Arrays.stream(Introspector.getBeanInfo(cls, Object.class).getPropertyDescriptors())
// filter out properties with setters only
.filter(pd -> Objects.nonNull(pd.getReadMethod()))
Expand All @@ -139,19 +139,21 @@ protected SortedMap<String, Method> mapFieldNameToGetter(
.forEach(
pd -> {
String fieldName = pd.getName();
GetterMethod method = new GetterMethod(pd.getReadMethod());

// OperationTime needs to be replaced by operatesFrom and operatesUntil
if (fieldName.equalsIgnoreCase(OPERATION_TIME_FIELD_NAME)) {
fieldName = OPERATES_FROM;
resFieldNameToMethod.put(OPERATES_UNTIL, pd.getReadMethod());
resFieldNameToMethod.put(OPERATES_UNTIL, method);
}

// VoltageLevel needs to be replaced by id and nominalVoltage
if (fieldName.equalsIgnoreCase(VOLT_LVL_FIELD_NAME)) {
fieldName = V_RATED;
resFieldNameToMethod.put(VOLT_LVL, pd.getReadMethod());
resFieldNameToMethod.put(VOLT_LVL, method);
}

resFieldNameToMethod.put(fieldName, pd.getReadMethod());
resFieldNameToMethod.put(fieldName, method);
});

return putUuidFirst(resFieldNameToMethod);
Expand Down Expand Up @@ -183,12 +185,12 @@ public static <V> SortedMap<String, V> putUuidFirst(Map<String, V> unsorted) {
* @return Mapping from field name to value as String representation
*/
protected LinkedHashMap<String, String> processObject(
Object object, Map<String, Method> fieldNameToGetter) throws EntityProcessorException {
Object object, Map<String, GetterMethod> fieldNameToGetter) throws EntityProcessorException {
try {
LinkedHashMap<String, String> resultMap = new LinkedHashMap<>();
for (Map.Entry<String, Method> entry : fieldNameToGetter.entrySet()) {
for (Map.Entry<String, GetterMethod> entry : fieldNameToGetter.entrySet()) {
String fieldName = entry.getKey();
Method getter = entry.getValue();
GetterMethod getter = entry.getValue();
Optional<Object> methodReturnObjectOpt = Optional.ofNullable(getter.invoke(object));

if (methodReturnObjectOpt.isPresent()) {
Expand All @@ -212,12 +214,13 @@ protected LinkedHashMap<String, String> processObject(
* @param fieldName Name of the foreseen field
* @return A String representation of the result
*/
protected String processMethodResult(Object methodReturnObject, Method method, String fieldName)
protected String processMethodResult(
Object methodReturnObject, GetterMethod method, String fieldName)
throws EntityProcessorException {

StringBuilder resultStringBuilder = new StringBuilder();

switch (method.getReturnType().getSimpleName()) {
switch (method.returnType()) {
// primitives (Boolean, Character, Byte, Short, Integer, Long, Float, Double, String,
case "UUID",
"boolean",
Expand Down Expand Up @@ -300,9 +303,9 @@ protected String processMethodResult(Object methodReturnObject, Method method, S
"Unable to process value for attribute/field '"
+ fieldName
+ "' and method return type '"
+ method.getReturnType().getSimpleName()
+ method.returnType()
+ "' for method with name '"
+ method.getName()
+ method.name()
+ "' in in entity model "
+ getRegisteredClass().getSimpleName()
+ ".class.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,8 @@ public static Collection<EntityProcessor<? extends Entity>> allResultEntityProce
(Class<TimeSeries<TimeSeriesEntry<Value>, Value, Value>>)
key.getTimeSeriesClass(),
(Class<TimeSeriesEntry<Value>>) key.getEntryClass(),
(Class<Value>) key.getValueClass()),
(Class<Value>) key.getValueClass(),
key.getScheme()),
EntityProcessorException.class)),
"time series processors",
EntityProcessorException::new)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
*/
package edu.ie3.datamodel.io.processor.timeseries;

import java.lang.reflect.Method;
import edu.ie3.datamodel.io.processor.GetterMethod;

/**
* Represent a tuple of {@link FieldSource} to {@link Method} to highlight, where information of a
* time series can be obtained from
* Represent a tuple of {@link FieldSource} to {@link GetterMethod} to highlight, where information
* of a time series can be obtained from
*/
public record FieldSourceToMethod(FieldSource source, Method method) {
public record FieldSourceToMethod(FieldSource source, GetterMethod method) {
@Override
public String toString() {
return "FieldSourceToMethod{" + "source=" + source + ", method=" + method + '}';
Expand All @@ -24,6 +24,6 @@ public enum FieldSource {
VALUE,
WEATHER_IRRADIANCE,
WEATHER_TEMPERATURE,
WEATHER_WIND
WEATHER_WIND,
}
}
Loading