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

### Fixed
- Removing opened `SwitchInput` during connectivity check [#1221](https://github.com/ie3-institute/PowerSystemDataModel/issues/1221)
Expand Down
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ plugins {
id 'jacoco' // java code coverage plugin
id "org.sonarqube" version "6.0.1.5171" // sonarqube
id 'net.thauvin.erik.gradle.semver' version '1.0.4' // semantic versioning
id "com.github.johnrengelman.shadow" version "8.1.1" // fat jar
}

ext {
Expand Down Expand Up @@ -67,6 +68,9 @@ dependencies {
// Graphs
implementation 'org.jgrapht:jgrapht-core:1.5.2'

// Statistics (for random load model)
implementation 'de.lmu.ifi.dbs.elki:elki:0.7.5'

// testing
testImplementation "org.apache.groovy:groovy:$groovyBinaryVersion"

Expand Down
34 changes: 29 additions & 5 deletions docs/uml/main/TimeSeriesDatamodelConcept.puml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ package models {
}

DefaultLoadProfiles --|> LoadProfile
RandomLoadProfile --|> LoadProfile

interface StandardLoadProfile {
+ {static} parse(String): StandardLoadProfile
Expand Down Expand Up @@ -130,12 +131,22 @@ package models {
}
RepetitiveTimeSeries --|> TimeSeries

class LoadProfileInput {
- type: StandardLoadProfile
- dayOfWeekToHourlyValues: Map<DayOfWeek, Map<Integer, PValue>>
abstract class LoadProfileTimeSeries<E extends LoadProfileEntry> {
- loadProfile: StandardLoadProfile
- valueMapping: Map<Key, Map<Integer, E>>
+ getLoadProfile(): LoadProfile
# fromTime(ZonedDateTime): Key
}
LoadProfileInput --|> RepetitiveTimeSeries
LoadProfileInput *-- StandardLoadProfile
LoadProfileTimeSeries --|> RepetitiveTimeSeries
LoadProfileTimeSeries *-- LoadProfile

class BDEWLoadProfileTimeSeries {}
BDEWLoadProfileTimeSeries --|> LoadProfileTimeSeries
BDEWLoadProfileTimeSeries *-- BdewLoadProfileEntry

class RandomLoadProfileTimeSeries {}
RandomLoadProfileTimeSeries --|> LoadProfileTimeSeries
RandomLoadProfileTimeSeries *-- RandomLoadProfileEntry

abstract class TimeSeriesEntry <V extends Value> {
# value: V
Expand All @@ -152,8 +163,21 @@ package models {
class LoadProfileEntry {
- dayOfWeek: DayOfWeek
- quarterHourOfDay: int
+ getDayOfWeek(): DayOfWeek
+ getQuarterHourOfDay(): Integer
}
LoadProfileEntry --|> TimeSeriesEntry: <<bind>>:PValue

class BdewLoadProfileEntry {
- season: Season
+ getSeason(): Season
}
BdewLoadProfileEntry --|> LoadProfileEntry

class RandomLoadProfileEntry {
- gev: GeneralizedExtremeValueDistribution
}
RandomLoadProfileEntry --|> LoadProfileEntry
}
}

Expand Down
14 changes: 0 additions & 14 deletions docs/uml/main/input/InputDatamodelConcept.puml
Original file line number Diff line number Diff line change
Expand Up @@ -142,20 +142,6 @@ package models {
MeasurementUnitInput --|> AssetInput
MeasurementUnitInput ..|> HasNodes

class RandomLoadParameter {
- quarterHour: int
- kWd: Double
- kSa: Double
- kSu: Double
- myWd: Double
- mySa: Double
- mySu: Double
- sigmaWd: Double
- sigmaSa: Double
- sigmaSu: Double
}
RandomLoadParameter --|> InputEntity

abstract class AssetTypeInput {
- id: String
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* © 2024. TU Dortmund University,
* Institute of Energy Systems, Energy Efficiency and Energy Economics,
* Research group Distribution grid planning and operation
*/
package edu.ie3.datamodel.io.factory.timeseries;

import static edu.ie3.datamodel.models.profile.LoadProfile.RandomLoadProfile.RANDOM_LOAD_PROFILE;
import static tech.units.indriya.unit.Units.WATT;

import edu.ie3.datamodel.io.naming.timeseries.LoadProfileMetaInformation;
import edu.ie3.datamodel.models.profile.LoadProfile.RandomLoadProfile;
import edu.ie3.datamodel.models.timeseries.repetitive.LoadProfileEntry;
import edu.ie3.datamodel.models.timeseries.repetitive.RandomLoadProfileTimeSeries;
import edu.ie3.datamodel.models.value.load.RandomLoadValues;
import edu.ie3.util.quantities.PowerSystemUnits;
import java.util.List;
import java.util.Set;
import javax.measure.quantity.Energy;
import javax.measure.quantity.Power;
import tech.units.indriya.ComparableQuantity;
import tech.units.indriya.quantity.Quantities;

public class RandomLoadProfileFactory
extends LoadProfileFactory<RandomLoadProfile, RandomLoadValues> {
public static final String K_WEEKDAY = "kWd";
public static final String K_SATURDAY = "kSa";
public static final String K_SUNDAY = "kSu";
public static final String MY_WEEKDAY = "myWd";
public static final String MY_SATURDAY = "mySa";
public static final String MY_SUNDAY = "mySu";
public static final String SIGMA_WEEKDAY = "sigmaWd";
public static final String SIGMA_SATURDAY = "sigmaSa";
public static final String SIGMA_SUNDAY = "sigmaSu";

public RandomLoadProfileFactory() {
super(RandomLoadValues.class);
}

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

return new LoadProfileEntry<>(
new RandomLoadValues(
data.getDouble(K_SATURDAY),
data.getDouble(K_SUNDAY),
data.getDouble(K_WEEKDAY),
data.getDouble(MY_SATURDAY),
data.getDouble(MY_SUNDAY),
data.getDouble(MY_WEEKDAY),
data.getDouble(SIGMA_SATURDAY),
data.getDouble(SIGMA_SUNDAY),
data.getDouble(SIGMA_WEEKDAY)),
quarterHour);
}

@Override
protected List<Set<String>> getFields(Class<?> entityClass) {
return List.of(
newSet(
QUARTER_HOUR,
K_WEEKDAY,
K_SATURDAY,
K_SUNDAY,
MY_WEEKDAY,
MY_SATURDAY,
MY_SUNDAY,
SIGMA_WEEKDAY,
SIGMA_SATURDAY,
SIGMA_SUNDAY));
}

@Override
public RandomLoadProfileTimeSeries build(
LoadProfileMetaInformation metaInformation, Set<LoadProfileEntry<RandomLoadValues>> entries) {
RandomLoadProfile profile = RANDOM_LOAD_PROFILE;

ComparableQuantity<Power> maxPower = calculateMaxPower(profile, entries);
ComparableQuantity<Energy> profileEnergyScaling = getLoadProfileEnergyScaling(profile);

return new RandomLoadProfileTimeSeries(
metaInformation.getUuid(), profile, entries, maxPower, profileEnergyScaling);
}

@Override
public RandomLoadProfile parseProfile(String profile) {
return RANDOM_LOAD_PROFILE;
}

/**
* This is the 95 % quantile resulting from 10,000 evaluations of the year 2019. It is only
* needed, when the load is meant to be scaled to rated active power.
*
* @return Reference active power to use for later model calculations
*/
@Override
public ComparableQuantity<Power> calculateMaxPower(
RandomLoadProfile loadProfile, Set<LoadProfileEntry<RandomLoadValues>> loadProfileEntries) {
return Quantities.getQuantity(159d, WATT);
}

/**
* Returns the profile energy scaling factor, the random profile is scaled to.
*
* <p>It is said in 'Kays - Agent-based simulation environment for improving the planning of
* distribution grids', that the Generalized Extreme Value distribution's parameters are sampled
* from input data, that is normalized to 1,000 kWh annual energy consumption. However, due to
* inaccuracies in random data reproduction, the sampled values will lead to an average annual
* energy consumption of approx. this value. It has been found by 1,000 evaluations of the year
* 2019.
*/
@Override
public ComparableQuantity<Energy> getLoadProfileEnergyScaling(RandomLoadProfile loadProfile) {
return Quantities.getQuantity(716.5416966513656, PowerSystemUnits.KILOWATTHOUR);
}
}
4 changes: 2 additions & 2 deletions src/main/java/edu/ie3/datamodel/io/processor/Processor.java
Original file line number Diff line number Diff line change
Expand Up @@ -261,8 +261,8 @@ protected String processMethodResult(Object methodReturnObject, Method method, S
processVoltageLevel((VoltageLevel) methodReturnObject, fieldName));
case "Point", "LineString" -> resultStringBuilder.append(
geoJsonWriter.write((Geometry) methodReturnObject));
case "LoadProfile", "BdewStandardLoadProfile" -> resultStringBuilder.append(
((LoadProfile) methodReturnObject).getKey());
case "LoadProfile", "BdewStandardLoadProfile", "RandomLoadProfile" -> resultStringBuilder
.append(((LoadProfile) methodReturnObject).getKey());
case "AssetTypeInput",
"BmTypeInput",
"ChpTypeInput",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import edu.ie3.datamodel.models.timeseries.repetitive.*;
import edu.ie3.datamodel.models.value.*;
import edu.ie3.datamodel.models.value.load.BdewLoadValues;
import edu.ie3.datamodel.models.value.load.RandomLoadValues;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -54,7 +55,9 @@ public class TimeSeriesProcessor<
new TimeSeriesProcessorKey(
IndividualTimeSeries.class, TimeBasedValue.class, HeatAndSValue.class),
new TimeSeriesProcessorKey(
BdewLoadProfileTimeSeries.class, LoadProfileEntry.class, BdewLoadValues.class));
BdewLoadProfileTimeSeries.class, LoadProfileEntry.class, BdewLoadValues.class),
new TimeSeriesProcessorKey(
RandomLoadProfileTimeSeries.class, LoadProfileEntry.class, RandomLoadValues.class));

/**
* Specific combination of time series class, entry class and value class, this processor is
Expand Down
22 changes: 16 additions & 6 deletions src/main/java/edu/ie3/datamodel/models/profile/LoadProfile.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.io.Serializable;
import java.util.Arrays;
import java.util.stream.Collectors;
import java.util.stream.Stream;

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

static LoadProfile[] getAllProfiles() {
final LoadProfile[][] all =
new LoadProfile[][] {
BdewStandardLoadProfile.values(), NbwTemperatureDependantLoadProfile.values()
};

return Arrays.stream(all).flatMap(Arrays::stream).toArray(LoadProfile[]::new);
return Stream.of(
BdewStandardLoadProfile.values(),
NbwTemperatureDependantLoadProfile.values(),
(LoadProfile[]) RandomLoadProfile.values())
.flatMap(Arrays::stream)
.toArray(LoadProfile[]::new);
}

/**
Expand Down Expand Up @@ -70,4 +71,13 @@ public String getKey() {
return "No load profile assigned";
}
}

enum RandomLoadProfile implements LoadProfile {
RANDOM_LOAD_PROFILE;

@Override
public String getKey() {
return "random";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ public class LoadProfileTimeSeries<V extends LoadValues>
private final Map<Integer, V> valueMapping;

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

/** The profile energy scaling in kWh. */
public final ComparableQuantity<Energy> profileEnergyScaling;
private final ComparableQuantity<Energy> profileEnergyScaling;

public LoadProfileTimeSeries(
UUID uuid,
Expand All @@ -50,6 +50,19 @@ public LoadProfileTimeSeries(
this.profileEnergyScaling = profileEnergyScaling;
}

/**
* Returns the maximum average power consumption per quarter-hour calculated over all seasons and
* weekday types of given load profile in Watt.
*/
public Optional<ComparableQuantity<Power>> maxPower() {
return Optional.ofNullable(maxPower);
}

/** Returns the profile energy scaling in kWh. */
public Optional<ComparableQuantity<Energy>> loadProfileScaling() {
return Optional.ofNullable(profileEnergyScaling);
}

/** Returns the {@link LoadProfile}. */
public LoadProfile getLoadProfile() {
return loadProfile;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* © 2024. TU Dortmund University,
* Institute of Energy Systems, Energy Efficiency and Energy Economics,
* Research group Distribution grid planning and operation
*/
package edu.ie3.datamodel.models.timeseries.repetitive;

import de.lmu.ifi.dbs.elki.math.statistics.distribution.GeneralizedExtremeValueDistribution;
import edu.ie3.datamodel.models.profile.LoadProfile;
import edu.ie3.datamodel.models.value.load.RandomLoadValues;
import java.util.Set;
import java.util.UUID;
import javax.measure.quantity.Energy;
import javax.measure.quantity.Power;
import tech.units.indriya.ComparableQuantity;

/**
* Describes a random load profile time series based on a {@link
* GeneralizedExtremeValueDistribution}. Each value of this# timeseries is given in kW.
*/
public class RandomLoadProfileTimeSeries extends LoadProfileTimeSeries<RandomLoadValues> {

public RandomLoadProfileTimeSeries(
UUID uuid,
LoadProfile loadProfile,
Set<LoadProfileEntry<RandomLoadValues>> entries,
ComparableQuantity<Power> maxPower,
ComparableQuantity<Energy> profileEnergyScaling) {
super(uuid, loadProfile, entries, maxPower, profileEnergyScaling);
}

@Override
public LoadProfile.RandomLoadProfile getLoadProfile() {
return (LoadProfile.RandomLoadProfile) super.getLoadProfile();
}

@Override
public String toString() {
return "Random" + super.toString();
}
}
Loading