Skip to content

Added RandomLoadProfileTimeSeries #1233

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Feb 19, 2025
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