From cdb4e23b58c9391399e6bceba3bf772a5058a9e6 Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Wed, 17 Sep 2025 15:04:08 +0200 Subject: [PATCH 1/4] Added abstraction for power value sources. --- CHANGELOG.md | 1 + .../io/csv/CsvLoadProfileMetaInformation.java | 8 +- .../timeseries/BdewLoadProfileFactory.java | 8 +- .../timeseries/LoadProfileFactory.java | 6 +- .../timeseries/RandomLoadProfileFactory.java | 8 +- .../io/naming/DatabaseNamingStrategy.java | 2 +- .../EntityPersistenceNamingStrategy.java | 2 +- .../LoadProfileMetaInformation.java | 16 +--- .../edu/ie3/datamodel/io/sink/SqlSink.java | 4 +- .../io/source/LoadProfileSource.java | 54 +++++-------- .../datamodel/io/source/PowerValueSource.java | 80 +++++++++++++++++++ .../io/source/csv/CsvLoadProfileSource.java | 32 +++----- .../io/source/sql/SqlLoadProfileSource.java | 41 +++------- .../datamodel/models/profile/LoadProfile.java | 7 +- .../models/profile/PowerProfile.java | 17 ++++ .../models/timeseries/TimeSeries.java | 10 +-- .../individual/IndividualTimeSeries.java | 9 ++- .../repetitive/BdewLoadProfileTimeSeries.java | 12 +-- .../repetitive/LoadProfileTimeSeries.java | 20 ++--- .../RandomLoadProfileTimeSeries.java | 11 ++- .../repetitive/RepetitiveTimeSeries.java | 6 -- .../BdewLoadProfileFactoryTest.groovy | 11 +-- .../RandomLoadProfileFactoryTest.groovy | 9 +-- .../source/sql/SqlLoadProfileSourceIT.groovy | 19 ++--- .../ie3/test/common/TimeSeriesTestData.groovy | 1 - 25 files changed, 193 insertions(+), 201 deletions(-) create mode 100644 src/main/java/edu/ie3/datamodel/io/source/PowerValueSource.java create mode 100644 src/main/java/edu/ie3/datamodel/models/profile/PowerProfile.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 13be3698e..b0331d262 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added weathersource documentation [#1390](https://github.com/ie3-institute/PowerSystemDataModel/issues/1390) - Added standard asset parameter for `3wTransformer` in `ReadTheDocs` [#1417](https://github.com/ie3-institute/PowerSystemDataModel/issues/1417) +- Added abstraction for power value sources [#1438](https://github.com/ie3-institute/PowerSystemDataModel/issues/1438) ### Fixed - Fixed small issues in tests [#1400](https://github.com/ie3-institute/PowerSystemDataModel/issues/1400) diff --git a/src/main/java/edu/ie3/datamodel/io/csv/CsvLoadProfileMetaInformation.java b/src/main/java/edu/ie3/datamodel/io/csv/CsvLoadProfileMetaInformation.java index 905f1ee3f..8f5aeae13 100644 --- a/src/main/java/edu/ie3/datamodel/io/csv/CsvLoadProfileMetaInformation.java +++ b/src/main/java/edu/ie3/datamodel/io/csv/CsvLoadProfileMetaInformation.java @@ -42,13 +42,9 @@ public int hashCode() { @Override public String toString() { return "CsvLoadProfileMetaInformation{" - + "uuid='" - + getUuid() - + '\'' - + ", profile='" + + "profile='" + getProfile() - + '\'' - + "fullFilePath=" + + ", fullFilePath=" + fullFilePath + '}'; } diff --git a/src/main/java/edu/ie3/datamodel/io/factory/timeseries/BdewLoadProfileFactory.java b/src/main/java/edu/ie3/datamodel/io/factory/timeseries/BdewLoadProfileFactory.java index 32f3e4780..67451014e 100644 --- a/src/main/java/edu/ie3/datamodel/io/factory/timeseries/BdewLoadProfileFactory.java +++ b/src/main/java/edu/ie3/datamodel/io/factory/timeseries/BdewLoadProfileFactory.java @@ -9,7 +9,6 @@ import edu.ie3.datamodel.exceptions.FactoryException; import edu.ie3.datamodel.exceptions.ParsingException; -import edu.ie3.datamodel.io.naming.timeseries.LoadProfileMetaInformation; import edu.ie3.datamodel.models.profile.BdewStandardLoadProfile; import edu.ie3.datamodel.models.timeseries.TimeSeriesEntry; import edu.ie3.datamodel.models.timeseries.repetitive.BdewLoadProfileTimeSeries; @@ -70,14 +69,11 @@ protected List> getFields(Class entityClass) { @Override public BdewLoadProfileTimeSeries build( - LoadProfileMetaInformation metaInformation, Set> entries) { - - BdewStandardLoadProfile profile = parseProfile(metaInformation.getProfile()); + BdewStandardLoadProfile profile, Set> entries) { ComparableQuantity maxPower = calculateMaxPower(profile, entries); ComparableQuantity profileEnergyScaling = getLoadProfileEnergyScaling(profile); - return new BdewLoadProfileTimeSeries( - metaInformation.getUuid(), profile, entries, maxPower, profileEnergyScaling); + return new BdewLoadProfileTimeSeries(profile, entries, maxPower, profileEnergyScaling); } @Override diff --git a/src/main/java/edu/ie3/datamodel/io/factory/timeseries/LoadProfileFactory.java b/src/main/java/edu/ie3/datamodel/io/factory/timeseries/LoadProfileFactory.java index 6a07a3f40..bb0f4cd6e 100644 --- a/src/main/java/edu/ie3/datamodel/io/factory/timeseries/LoadProfileFactory.java +++ b/src/main/java/edu/ie3/datamodel/io/factory/timeseries/LoadProfileFactory.java @@ -6,7 +6,6 @@ package edu.ie3.datamodel.io.factory.timeseries; import edu.ie3.datamodel.io.factory.Factory; -import edu.ie3.datamodel.io.naming.timeseries.LoadProfileMetaInformation; import edu.ie3.datamodel.models.profile.LoadProfile; import edu.ie3.datamodel.models.timeseries.repetitive.LoadProfileEntry; import edu.ie3.datamodel.models.timeseries.repetitive.LoadProfileTimeSeries; @@ -24,7 +23,7 @@ * @param

type of load profile * @param type of load values */ -public abstract class LoadProfileFactory

+public abstract class LoadProfileFactory

> extends Factory, LoadProfileEntry> { protected static final String QUARTER_HOUR = "quarterHour"; @@ -37,8 +36,7 @@ protected LoadProfileFactory(Class... valueClass) { super(valueClass); } - public abstract LoadProfileTimeSeries build( - LoadProfileMetaInformation metaInformation, Set> entries); + public abstract LoadProfileTimeSeries build(P profile, Set> entries); public abstract P parseProfile(String profile); diff --git a/src/main/java/edu/ie3/datamodel/io/factory/timeseries/RandomLoadProfileFactory.java b/src/main/java/edu/ie3/datamodel/io/factory/timeseries/RandomLoadProfileFactory.java index 440639388..2addfa46a 100644 --- a/src/main/java/edu/ie3/datamodel/io/factory/timeseries/RandomLoadProfileFactory.java +++ b/src/main/java/edu/ie3/datamodel/io/factory/timeseries/RandomLoadProfileFactory.java @@ -8,7 +8,6 @@ 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; @@ -73,14 +72,11 @@ protected List> getFields(Class entityClass) { @Override public RandomLoadProfileTimeSeries build( - LoadProfileMetaInformation metaInformation, Set> entries) { - RandomLoadProfile profile = RANDOM_LOAD_PROFILE; - + RandomLoadProfile profile, Set> entries) { ComparableQuantity maxPower = calculateMaxPower(profile, entries); ComparableQuantity profileEnergyScaling = getLoadProfileEnergyScaling(profile); - return new RandomLoadProfileTimeSeries( - metaInformation.getUuid(), profile, entries, maxPower, profileEnergyScaling); + return new RandomLoadProfileTimeSeries(profile, entries, maxPower, profileEnergyScaling); } @Override diff --git a/src/main/java/edu/ie3/datamodel/io/naming/DatabaseNamingStrategy.java b/src/main/java/edu/ie3/datamodel/io/naming/DatabaseNamingStrategy.java index 7e720e2d2..3d4f63ff6 100644 --- a/src/main/java/edu/ie3/datamodel/io/naming/DatabaseNamingStrategy.java +++ b/src/main/java/edu/ie3/datamodel/io/naming/DatabaseNamingStrategy.java @@ -97,7 +97,7 @@ Optional getEntityName(T timeSeries) { logger.error("Unable to determine content of time series {}", timeSeries); return Optional.empty(); } - } else if (timeSeries instanceof LoadProfileTimeSeries) { + } else if (timeSeries instanceof LoadProfileTimeSeries) { return Optional.of(getLoadProfileEntityName()); } else { logger.error("There is no naming strategy defined for {}", timeSeries); diff --git a/src/main/java/edu/ie3/datamodel/io/naming/EntityPersistenceNamingStrategy.java b/src/main/java/edu/ie3/datamodel/io/naming/EntityPersistenceNamingStrategy.java index 0c6f5ca2a..7a73c6fee 100644 --- a/src/main/java/edu/ie3/datamodel/io/naming/EntityPersistenceNamingStrategy.java +++ b/src/main/java/edu/ie3/datamodel/io/naming/EntityPersistenceNamingStrategy.java @@ -371,7 +371,7 @@ Optional getEntityName(T timeSeries) { logger.error("Unable to determine content of time series {}", timeSeries); return Optional.empty(); } - } else if (timeSeries instanceof LoadProfileTimeSeries loadProfileTimeSeries) { + } else if (timeSeries instanceof LoadProfileTimeSeries loadProfileTimeSeries) { return Optional.of( prefix .concat("lpts") diff --git a/src/main/java/edu/ie3/datamodel/io/naming/timeseries/LoadProfileMetaInformation.java b/src/main/java/edu/ie3/datamodel/io/naming/timeseries/LoadProfileMetaInformation.java index 875c881e6..9190be187 100644 --- a/src/main/java/edu/ie3/datamodel/io/naming/timeseries/LoadProfileMetaInformation.java +++ b/src/main/java/edu/ie3/datamodel/io/naming/timeseries/LoadProfileMetaInformation.java @@ -18,11 +18,6 @@ public LoadProfileMetaInformation(String profile) { this.profile = profile; } - public LoadProfileMetaInformation(UUID uuid, String profile) { - super(uuid); - this.profile = profile; - } - public String getProfile() { return profile; } @@ -37,18 +32,11 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(super.hashCode(), profile); + return Objects.hash(profile); } @Override public String toString() { - return "LoadProfileTimeSeriesMetaInformation{" - + "uuid='" - + getUuid() - + '\'' - + ", profile='" - + profile - + '\'' - + '}'; + return "LoadProfileTimeSeriesMetaInformation{profile='" + profile + '}'; } } diff --git a/src/main/java/edu/ie3/datamodel/io/sink/SqlSink.java b/src/main/java/edu/ie3/datamodel/io/sink/SqlSink.java index 438f88fd7..eb007b99c 100644 --- a/src/main/java/edu/ie3/datamodel/io/sink/SqlSink.java +++ b/src/main/java/edu/ie3/datamodel/io/sink/SqlSink.java @@ -190,7 +190,7 @@ private void persistMixedList(List entities, DbGridMetadat void persistList(List entities, Class cls, DbGridMetadata identifier) throws SQLException { // Check if there are only elements of the same class - Class firstClass = entities.get(0).getClass(); + Class firstClass = entities.getFirst().getClass(); boolean allSameClass = entities.stream().allMatch(e -> e.getClass() == firstClass); if (allSameClass) { @@ -251,7 +251,7 @@ private , V extends Value, R extends Value> void pe TriFunction queryBuilder; String timeSeriesIdentifier; - if (timeSeries instanceof LoadProfileTimeSeries lpts) { + if (timeSeries instanceof LoadProfileTimeSeries lpts) { timeSeriesIdentifier = lpts.getLoadProfile().getKey(); queryBuilder = this::basicInsertQueryValuesLPTS; } else { diff --git a/src/main/java/edu/ie3/datamodel/io/source/LoadProfileSource.java b/src/main/java/edu/ie3/datamodel/io/source/LoadProfileSource.java index fa49931ad..57dbc1613 100644 --- a/src/main/java/edu/ie3/datamodel/io/source/LoadProfileSource.java +++ b/src/main/java/edu/ie3/datamodel/io/source/LoadProfileSource.java @@ -14,35 +14,36 @@ import edu.ie3.datamodel.io.factory.timeseries.LoadProfileData; import edu.ie3.datamodel.io.factory.timeseries.LoadProfileFactory; import edu.ie3.datamodel.io.factory.timeseries.RandomLoadProfileFactory; +import edu.ie3.datamodel.io.naming.timeseries.LoadProfileMetaInformation; import edu.ie3.datamodel.io.source.csv.CsvDataSource; import edu.ie3.datamodel.io.source.csv.CsvLoadProfileSource; import edu.ie3.datamodel.models.profile.BdewStandardLoadProfile; import edu.ie3.datamodel.models.profile.LoadProfile; import edu.ie3.datamodel.models.timeseries.repetitive.LoadProfileEntry; -import edu.ie3.datamodel.models.timeseries.repetitive.LoadProfileTimeSeries; import edu.ie3.datamodel.models.timeseries.repetitive.RandomLoadProfileTimeSeries; -import edu.ie3.datamodel.models.value.PValue; import edu.ie3.datamodel.models.value.Value; import edu.ie3.datamodel.models.value.load.BdewLoadValues; import edu.ie3.datamodel.models.value.load.LoadValues; import edu.ie3.datamodel.models.value.load.RandomLoadValues; import edu.ie3.datamodel.utils.Try; import java.time.ZonedDateTime; -import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; -import javax.measure.quantity.Energy; -import javax.measure.quantity.Power; -import tech.units.indriya.ComparableQuantity; public abstract class LoadProfileSource

> - extends EntitySource { + extends EntitySource implements PowerValueSource.TimeSeriesBased { + protected final P profile; protected final Class entryClass; protected final LoadProfileFactory entryFactory; - protected LoadProfileSource(Class entryClass, LoadProfileFactory entryFactory) { + protected LoadProfileSource( + LoadProfileMetaInformation metaInformation, + Class entryClass, + LoadProfileFactory entryFactory) { + this.profile = entryFactory.parseProfile(metaInformation.getProfile()); this.entryClass = entryClass; this.entryFactory = entryFactory; } @@ -60,33 +61,18 @@ protected Try, FactoryException> createEntries( return entryFactory.get(factoryData); } - public abstract LoadProfileTimeSeries getTimeSeries(); + /** Returns the load profile entries as a set. */ + public abstract Set> getEntries(); - /** - * Method to return all time keys after a given timestamp. - * - * @param time given time - * @return a list of time keys - */ - public abstract List getTimeKeysAfter(ZonedDateTime time); - - /** - * Method to get the value for a given time. - * - * @param time for which a value is needed - * @return an optional - * @throws SourceException if an exception occurred - */ - public abstract Optional getValue(ZonedDateTime time) throws SourceException; - - /** Returns the load profile of this source. */ - public abstract P getLoadProfile(); - - /** Returns the maximal power value of the time series */ - public abstract Optional> getMaxPower(); + @Override + public P getProfile() { + return profile; + } - /** Returns the load profile energy scaling for this load profile time series. */ - public abstract Optional> getLoadProfileEnergyScaling(); + @Override + public Optional getNextTimeKey(ZonedDateTime time) { + return Optional.of(time.plusSeconds(getResolution(getProfile()))); + } /** * Returns the resolution for the given {@link LoadProfile}. @@ -125,7 +111,7 @@ public static long getResolution(LoadProfile loadProfile) { metaInformation -> new CsvLoadProfileSource<>( buildInSource, metaInformation, BdewLoadValues.class, factory)) - .collect(Collectors.toMap(CsvLoadProfileSource::getLoadProfile, Function.identity())); + .collect(Collectors.toMap(CsvLoadProfileSource::getProfile, Function.identity())); } /** diff --git a/src/main/java/edu/ie3/datamodel/io/source/PowerValueSource.java b/src/main/java/edu/ie3/datamodel/io/source/PowerValueSource.java new file mode 100644 index 000000000..632980c7e --- /dev/null +++ b/src/main/java/edu/ie3/datamodel/io/source/PowerValueSource.java @@ -0,0 +1,80 @@ +/* + * © 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.source; + +import edu.ie3.datamodel.models.profile.LoadProfile; +import edu.ie3.datamodel.models.profile.PowerProfile; +import edu.ie3.datamodel.models.value.PValue; +import java.time.ZonedDateTime; +import java.util.Optional; +import javax.measure.quantity.Energy; +import javax.measure.quantity.Power; +import tech.units.indriya.ComparableQuantity; + +/** Interface defining base functionality for power value sources. */ +public sealed interface PowerValueSource< + P extends PowerProfile, ID extends PowerValueSource.InputData> + permits PowerValueSource.MarkovBased, PowerValueSource.TimeSeriesBased { + + /** Returns the profile of this source. */ + P getProfile(); + + /** + * Method to get the next power value based on the provided input data. + * + * @param data input data that is used to calculate the next power value. + * @return an option for the power value. + */ + Optional getPowerValue(ID data); + + /** + * Method to determine the next timestamp for which data is present. + * + * @param time current time + * @return an option for the next timestamp or {@link Optional#empty()} if no timestamp was found. + */ + Optional getNextTimeKey(ZonedDateTime time); + + /** Returns the maximal power that can be returned by this source. */ + Optional> getMaxPower(); + + /** Returns the energy scaling of this power source. */ + Optional> getProfileEnergyScaling(); + + // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + // non-sealed implementations + + /** Interface for time-series-based power value sources. */ + non-sealed interface TimeSeriesBased + extends PowerValueSource {} + + /** Interface for markov-chain-based power value sources. */ + non-sealed interface MarkovBased extends PowerValueSource {} + + // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + // input data + + /** + * Interface for the input data of {@link #getPowerValue(InputData)}. The data is used to + * determine the next power. + */ + sealed interface InputData permits PowerValueSource.TimeSeriesInputValue { + /** Returns the timestamp for which a power value is needed. */ + ZonedDateTime getTimes(); + } + + /** + * Input data for time-series-based power value sources. + * + * @param time + */ + record TimeSeriesInputValue(ZonedDateTime time) implements InputData { + @Override + public ZonedDateTime getTimes() { + return time; + } + } +} diff --git a/src/main/java/edu/ie3/datamodel/io/source/csv/CsvLoadProfileSource.java b/src/main/java/edu/ie3/datamodel/io/source/csv/CsvLoadProfileSource.java index 429580fac..12fc9cfe1 100644 --- a/src/main/java/edu/ie3/datamodel/io/source/csv/CsvLoadProfileSource.java +++ b/src/main/java/edu/ie3/datamodel/io/source/csv/CsvLoadProfileSource.java @@ -18,7 +18,6 @@ import edu.ie3.datamodel.models.value.load.LoadValues; import edu.ie3.datamodel.utils.Try; import java.nio.file.Path; -import java.time.ZonedDateTime; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @@ -31,7 +30,7 @@ */ public class CsvLoadProfileSource

> extends LoadProfileSource { - private final LoadProfileTimeSeries loadProfileTimeSeries; + private final LoadProfileTimeSeries loadProfileTimeSeries; private final CsvDataSource dataSource; private final Path filePath; @@ -40,13 +39,13 @@ public CsvLoadProfileSource( CsvLoadProfileMetaInformation metaInformation, Class entryClass, LoadProfileFactory entryFactory) { - super(entryClass, entryFactory); + super(metaInformation, entryClass, entryFactory); this.dataSource = source; this.filePath = metaInformation.getFullFilePath(); /* Read in the full time series */ try { - this.loadProfileTimeSeries = buildLoadProfileTimeSeries(metaInformation, this::createEntries); + this.loadProfileTimeSeries = buildLoadProfileTimeSeries(this::createEntries); } catch (SourceException e) { throw new IllegalArgumentException( "Unable to obtain load profile time series with profile '" @@ -61,25 +60,23 @@ public void validate() throws ValidationException { validate(entryClass, () -> dataSource.getSourceFields(filePath), entryFactory); } - @Override - public LoadProfileTimeSeries getTimeSeries() { + public LoadProfileTimeSeries getTimeSeries() { return loadProfileTimeSeries; } @Override - public List getTimeKeysAfter(ZonedDateTime time) { - return loadProfileTimeSeries.getTimeKeysAfter(time); + public Set> getEntries() { + return loadProfileTimeSeries.getEntries(); } @Override - public Optional getValue(ZonedDateTime time) throws SourceException { - return loadProfileTimeSeries.getValue(time); + public Optional getPowerValue(TimeSeriesInputValue data) { + return loadProfileTimeSeries.getValue(data.time()); } @Override - @SuppressWarnings("unchecked") - public P getLoadProfile() { - return (P) getTimeSeries().getLoadProfile(); + public P getProfile() { + return loadProfileTimeSeries.getLoadProfile(); } @Override @@ -88,7 +85,7 @@ public Optional> getMaxPower() { } @Override - public Optional> getLoadProfileEnergyScaling() { + public Optional> getProfileEnergyScaling() { return loadProfileTimeSeries.loadProfileScaling(); } @@ -99,15 +96,12 @@ public Optional> getLoadProfileEnergyScaling() { * entries are obtained entries with the help of {@code fieldToValueFunction}. If the file does * not exist, an empty Stream is returned. * - * @param metaInformation containing an unique identifier of the time series, a path to the file - * to read as well as the profile * @param fieldToValueFunction function, that is able to transfer a mapping (from field to value) * onto a specific instance of the targeted entry class * @throws SourceException If the file cannot be read properly * @return an individual time series */ - protected LoadProfileTimeSeries buildLoadProfileTimeSeries( - CsvLoadProfileMetaInformation metaInformation, + protected LoadProfileTimeSeries buildLoadProfileTimeSeries( Function, Try, FactoryException>> fieldToValueFunction) throws SourceException { @@ -121,6 +115,6 @@ protected LoadProfileTimeSeries buildLoadProfileTimeSeries( .getOrThrow() .collect(Collectors.toSet()); - return entryFactory.build(metaInformation, entries); + return entryFactory.build(profile, entries); } } diff --git a/src/main/java/edu/ie3/datamodel/io/source/sql/SqlLoadProfileSource.java b/src/main/java/edu/ie3/datamodel/io/source/sql/SqlLoadProfileSource.java index 3b54684f8..69f72d452 100644 --- a/src/main/java/edu/ie3/datamodel/io/source/sql/SqlLoadProfileSource.java +++ b/src/main/java/edu/ie3/datamodel/io/source/sql/SqlLoadProfileSource.java @@ -7,7 +7,6 @@ import static edu.ie3.datamodel.io.source.sql.SqlDataSource.createBaseQueryString; -import edu.ie3.datamodel.exceptions.SourceException; import edu.ie3.datamodel.exceptions.ValidationException; import edu.ie3.datamodel.io.connectors.SqlConnector; import edu.ie3.datamodel.io.factory.timeseries.LoadProfileFactory; @@ -22,7 +21,6 @@ import edu.ie3.datamodel.models.value.load.LoadValues; import edu.ie3.datamodel.utils.TimeSeriesUtils; import java.time.ZonedDateTime; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -41,13 +39,10 @@ */ public class SqlLoadProfileSource

> extends LoadProfileSource { - protected static final Logger log = LoggerFactory.getLogger(SqlTimeSeriesSource.class); + protected static final Logger log = LoggerFactory.getLogger(SqlLoadProfileSource.class); private final SqlDataSource dataSource; private final String tableName; - private final LoadProfileMetaInformation metaInformation; - private final P loadProfile; - // General fields private static final String WHERE = " WHERE "; private static final String LOAD_PROFILE = "load_profile"; @@ -65,12 +60,10 @@ public SqlLoadProfileSource( LoadProfileMetaInformation metaInformation, Class entryClass, LoadProfileFactory entryFactory) { - super(entryClass, entryFactory); + super(metaInformation, entryClass, entryFactory); this.dataSource = dataSource; this.tableName = "load_profiles"; - this.metaInformation = metaInformation; - this.loadProfile = entryFactory.parseProfile(metaInformation.getProfile()); String dbTimeColumnName = dataSource.getDbColumnName(entryFactory.getTimeFieldString(), tableName); @@ -101,39 +94,31 @@ public void validate() throws ValidationException { // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- @Override - public LoadProfileTimeSeries getTimeSeries() { - Set> entries = getEntries(queryFull, ps -> {}); - return entryFactory.build(metaInformation, entries); + public Set> getEntries() { + return getEntries(queryFull, ps -> {}); } @Override - public List getTimeKeysAfter(ZonedDateTime time) { - return List.of(time.plusMinutes(15)); - } + public Optional getPowerValue(TimeSeriesInputValue data) { + ZonedDateTime time = data.time(); - @Override - public Optional getValue(ZonedDateTime time) throws SourceException { Set> entries = getEntries(queryTime, ps -> ps.setInt(1, TimeSeriesUtils.calculateQuarterHourOfDay(time))); if (entries.isEmpty()) return Optional.empty(); if (entries.size() > 1) log.warn("Retrieved more than one result value, using the first"); - return Optional.of(entries.stream().toList().get(0).getValue().getValue(time, loadProfile)); - } - - @Override - public P getLoadProfile() { - return loadProfile; + return Optional.of(entries.stream().toList().getFirst().getValue().getValue(time, profile)); } @Override public Optional> getMaxPower() { + // TODO: Improve this calculation return Optional.ofNullable( - entryFactory.calculateMaxPower(loadProfile, getEntries(queryFull, ps -> {}))); + entryFactory.calculateMaxPower(profile, getEntries(queryFull, ps -> {}))); } @Override - public Optional> getLoadProfileEnergyScaling() { - return Optional.ofNullable(entryFactory.getLoadProfileEnergyScaling(loadProfile)); + public Optional> getProfileEnergyScaling() { + return Optional.ofNullable(entryFactory.getLoadProfileEnergyScaling(profile)); } // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- @@ -178,7 +163,7 @@ private String createQueryFull(String schemaName, String tableName) { + WHERE + LOAD_PROFILE + " = '" - + loadProfile.getKey() + + profile.getKey() + "'"; } @@ -197,7 +182,7 @@ private String createQueryForTime(String schemaName, String tableName, String ti + WHERE + LOAD_PROFILE + " = '" - + loadProfile.getKey() + + profile.getKey() + "' AND " + timeColumnName + "=?;"; diff --git a/src/main/java/edu/ie3/datamodel/models/profile/LoadProfile.java b/src/main/java/edu/ie3/datamodel/models/profile/LoadProfile.java index 116d3389e..4a2847305 100644 --- a/src/main/java/edu/ie3/datamodel/models/profile/LoadProfile.java +++ b/src/main/java/edu/ie3/datamodel/models/profile/LoadProfile.java @@ -6,16 +6,11 @@ package edu.ie3.datamodel.models.profile; import edu.ie3.datamodel.exceptions.ParsingException; -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 - */ - String getKey(); +public interface LoadProfile extends PowerProfile { /** * Parses the given key to {@link StandardLoadProfile}. diff --git a/src/main/java/edu/ie3/datamodel/models/profile/PowerProfile.java b/src/main/java/edu/ie3/datamodel/models/profile/PowerProfile.java new file mode 100644 index 000000000..a39a3ae0e --- /dev/null +++ b/src/main/java/edu/ie3/datamodel/models/profile/PowerProfile.java @@ -0,0 +1,17 @@ +/* + * © 2025. TU Dortmund University, + * Institute of Energy Systems, Energy Efficiency and Energy Economics, + * Research group Distribution grid planning and operation +*/ +package edu.ie3.datamodel.models.profile; + +import java.io.Serializable; + +/** Interface defining a power profile. */ +public interface PowerProfile extends Serializable { + + /** + * @return The identifying String + */ + String getKey(); +} diff --git a/src/main/java/edu/ie3/datamodel/models/timeseries/TimeSeries.java b/src/main/java/edu/ie3/datamodel/models/timeseries/TimeSeries.java index f9fa97891..3387e92b3 100644 --- a/src/main/java/edu/ie3/datamodel/models/timeseries/TimeSeries.java +++ b/src/main/java/edu/ie3/datamodel/models/timeseries/TimeSeries.java @@ -70,15 +70,7 @@ public Optional> getTimeBasedValue(ZonedDateTime time) { * @param time Reference in time * @return The next later known time instant */ - protected abstract Optional getNextDateTime(ZonedDateTime time); - - /** - * Get all {@link ZonedDateTime}s after the given time. - * - * @param time given time - * @return a list of all time keys - */ - public abstract List getTimeKeysAfter(ZonedDateTime time); + public abstract Optional getNextDateTime(ZonedDateTime time); /** * Get the most recent available value before or at the given time step as a TimeBasedValue diff --git a/src/main/java/edu/ie3/datamodel/models/timeseries/individual/IndividualTimeSeries.java b/src/main/java/edu/ie3/datamodel/models/timeseries/individual/IndividualTimeSeries.java index d3e196a28..f0b52f0ea 100644 --- a/src/main/java/edu/ie3/datamodel/models/timeseries/individual/IndividualTimeSeries.java +++ b/src/main/java/edu/ie3/datamodel/models/timeseries/individual/IndividualTimeSeries.java @@ -60,13 +60,18 @@ public Optional getPreviousDateTime(ZonedDateTime time) { } @Override - protected Optional getNextDateTime(ZonedDateTime time) { + public Optional getNextDateTime(ZonedDateTime time) { return timeToValue.keySet().stream() .filter(valueTime -> valueTime.compareTo(time) > 0) .min(Comparator.naturalOrder()); } - @Override + /** + * Get all {@link ZonedDateTime}s after the given time. + * + * @param time given time + * @return a list of all time keys + */ public List getTimeKeysAfter(ZonedDateTime time) { return timeToValue.keySet().stream().filter(timeKey -> timeKey.isAfter(time)).sorted().toList(); } diff --git a/src/main/java/edu/ie3/datamodel/models/timeseries/repetitive/BdewLoadProfileTimeSeries.java b/src/main/java/edu/ie3/datamodel/models/timeseries/repetitive/BdewLoadProfileTimeSeries.java index 524697166..5bdeb7b5c 100644 --- a/src/main/java/edu/ie3/datamodel/models/timeseries/repetitive/BdewLoadProfileTimeSeries.java +++ b/src/main/java/edu/ie3/datamodel/models/timeseries/repetitive/BdewLoadProfileTimeSeries.java @@ -9,7 +9,6 @@ import edu.ie3.datamodel.models.value.load.BdewLoadValues; import java.util.Objects; import java.util.Set; -import java.util.UUID; import javax.measure.quantity.Energy; import javax.measure.quantity.Power; import tech.units.indriya.ComparableQuantity; @@ -18,20 +17,15 @@ * Describes a bdew load profile time series with repetitive values that can be calculated from a * pattern. Each value of this timeseries is given in W. */ -public class BdewLoadProfileTimeSeries extends LoadProfileTimeSeries { +public class BdewLoadProfileTimeSeries + extends LoadProfileTimeSeries { public BdewLoadProfileTimeSeries( - UUID uuid, BdewStandardLoadProfile loadProfile, Set> values, ComparableQuantity maxPower, ComparableQuantity profileEnergyScaling) { - super(uuid, loadProfile, values, maxPower, profileEnergyScaling); - } - - @Override - public BdewStandardLoadProfile getLoadProfile() { - return (BdewStandardLoadProfile) super.getLoadProfile(); + super(loadProfile, values, maxPower, profileEnergyScaling); } @Override diff --git a/src/main/java/edu/ie3/datamodel/models/timeseries/repetitive/LoadProfileTimeSeries.java b/src/main/java/edu/ie3/datamodel/models/timeseries/repetitive/LoadProfileTimeSeries.java index 7c896fcbe..0ac730377 100644 --- a/src/main/java/edu/ie3/datamodel/models/timeseries/repetitive/LoadProfileTimeSeries.java +++ b/src/main/java/edu/ie3/datamodel/models/timeseries/repetitive/LoadProfileTimeSeries.java @@ -19,9 +19,9 @@ /** * Describes a load profile time series with repetitive values that can be calculated from a pattern */ -public class LoadProfileTimeSeries +public class LoadProfileTimeSeries

> extends RepetitiveTimeSeries, V, PValue> { - private final LoadProfile loadProfile; + private final P loadProfile; private final Map valueMapping; /** @@ -34,12 +34,11 @@ public class LoadProfileTimeSeries private final ComparableQuantity profileEnergyScaling; public LoadProfileTimeSeries( - UUID uuid, - LoadProfile loadProfile, + P loadProfile, Set> entries, ComparableQuantity maxPower, ComparableQuantity profileEnergyScaling) { - super(uuid, entries); + super(UUID.randomUUID(), entries); this.loadProfile = loadProfile; this.valueMapping = entries.stream() @@ -64,7 +63,7 @@ public Optional> loadProfileScaling() { } /** Returns the {@link LoadProfile}. */ - public LoadProfile getLoadProfile() { + public P getLoadProfile() { return loadProfile; } @@ -83,15 +82,10 @@ public Optional getPreviousDateTime(ZonedDateTime time) { } @Override - protected Optional getNextDateTime(ZonedDateTime time) { + public Optional getNextDateTime(ZonedDateTime time) { return Optional.of(time.plusMinutes(15)); } - @Override - public List getTimeKeysAfter(ZonedDateTime time) { - return List.of(time.plusMinutes(15)); // dummy value that will return next quarter-hour value - } - /** Returns the value mapping. */ protected Map getValueMapping() { return valueMapping; @@ -108,7 +102,7 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; - LoadProfileTimeSeries that = (LoadProfileTimeSeries) o; + LoadProfileTimeSeries that = (LoadProfileTimeSeries) o; return loadProfile.equals(that.loadProfile) && valueMapping.equals(that.valueMapping); } diff --git a/src/main/java/edu/ie3/datamodel/models/timeseries/repetitive/RandomLoadProfileTimeSeries.java b/src/main/java/edu/ie3/datamodel/models/timeseries/repetitive/RandomLoadProfileTimeSeries.java index de9142fd9..5de6b32e4 100644 --- a/src/main/java/edu/ie3/datamodel/models/timeseries/repetitive/RandomLoadProfileTimeSeries.java +++ b/src/main/java/edu/ie3/datamodel/models/timeseries/repetitive/RandomLoadProfileTimeSeries.java @@ -9,7 +9,6 @@ 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; @@ -18,20 +17,20 @@ * 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 { +public class RandomLoadProfileTimeSeries + extends LoadProfileTimeSeries { public RandomLoadProfileTimeSeries( - UUID uuid, - LoadProfile loadProfile, + LoadProfile.RandomLoadProfile loadProfile, Set> entries, ComparableQuantity maxPower, ComparableQuantity profileEnergyScaling) { - super(uuid, loadProfile, entries, maxPower, profileEnergyScaling); + super(loadProfile, entries, maxPower, profileEnergyScaling); } @Override public LoadProfile.RandomLoadProfile getLoadProfile() { - return (LoadProfile.RandomLoadProfile) super.getLoadProfile(); + return super.getLoadProfile(); } @Override diff --git a/src/main/java/edu/ie3/datamodel/models/timeseries/repetitive/RepetitiveTimeSeries.java b/src/main/java/edu/ie3/datamodel/models/timeseries/repetitive/RepetitiveTimeSeries.java index 9338688bb..4237845ed 100644 --- a/src/main/java/edu/ie3/datamodel/models/timeseries/repetitive/RepetitiveTimeSeries.java +++ b/src/main/java/edu/ie3/datamodel/models/timeseries/repetitive/RepetitiveTimeSeries.java @@ -32,10 +32,4 @@ protected RepetitiveTimeSeries(UUID uuid, Set entries) { public Optional getValue(ZonedDateTime time) { return Optional.ofNullable(calc(time)); } - - @Override - public List getTimeKeysAfter(ZonedDateTime time) { - // dummy value - return getNextDateTime(time).map(List::of).orElseGet(Collections::emptyList); - } } diff --git a/src/test/groovy/edu/ie3/datamodel/io/factory/timeseries/BdewLoadProfileFactoryTest.groovy b/src/test/groovy/edu/ie3/datamodel/io/factory/timeseries/BdewLoadProfileFactoryTest.groovy index c82957c8a..e42bd120c 100644 --- a/src/test/groovy/edu/ie3/datamodel/io/factory/timeseries/BdewLoadProfileFactoryTest.groovy +++ b/src/test/groovy/edu/ie3/datamodel/io/factory/timeseries/BdewLoadProfileFactoryTest.groovy @@ -5,7 +5,7 @@ */ package edu.ie3.datamodel.io.factory.timeseries -import edu.ie3.datamodel.io.naming.timeseries.LoadProfileMetaInformation + import edu.ie3.datamodel.models.profile.BdewStandardLoadProfile import edu.ie3.datamodel.models.timeseries.repetitive.LoadProfileEntry import edu.ie3.datamodel.models.value.load.BdewLoadValues @@ -14,8 +14,6 @@ import spock.lang.Shared import spock.lang.Specification import tech.units.indriya.quantity.Quantities -import java.time.Month - class BdewLoadProfileFactoryTest extends Specification { @Shared BdewLoadProfileFactory factory @@ -126,13 +124,8 @@ class BdewLoadProfileFactoryTest extends Specification { } def "A BDEWLoadProfileFactory builds time series from entries"() { - given: - UUID uuid = UUID.fromString("fa3894c1-25af-479c-8a40-1323bb9150a9") - LoadProfileMetaInformation metaInformation = new LoadProfileMetaInformation(uuid, "g0") - - when: - def lpts = factory.build(metaInformation, allEntries) + def lpts = factory.build(BdewStandardLoadProfile.G0, allEntries) then: lpts.loadProfile == BdewStandardLoadProfile.G0 diff --git a/src/test/groovy/edu/ie3/datamodel/io/factory/timeseries/RandomLoadProfileFactoryTest.groovy b/src/test/groovy/edu/ie3/datamodel/io/factory/timeseries/RandomLoadProfileFactoryTest.groovy index 901a3a3b1..b3d2b54b4 100644 --- a/src/test/groovy/edu/ie3/datamodel/io/factory/timeseries/RandomLoadProfileFactoryTest.groovy +++ b/src/test/groovy/edu/ie3/datamodel/io/factory/timeseries/RandomLoadProfileFactoryTest.groovy @@ -5,7 +5,7 @@ */ package edu.ie3.datamodel.io.factory.timeseries -import edu.ie3.datamodel.io.naming.timeseries.LoadProfileMetaInformation + import edu.ie3.datamodel.models.profile.LoadProfile import edu.ie3.datamodel.models.timeseries.repetitive.LoadProfileEntry import edu.ie3.datamodel.models.value.load.RandomLoadValues @@ -130,13 +130,8 @@ class RandomLoadProfileFactoryTest extends Specification { } def "A RandomLoadProfileFactory builds time series from entries"() { - given: - UUID uuid = UUID.fromString("fa3894c1-25af-479c-8a40-1323bb9150a9") - LoadProfileMetaInformation metaInformation = new LoadProfileMetaInformation(uuid, "random") - - when: - def lpts = factory.build(metaInformation, allEntries) + def lpts = factory.build(LoadProfile.RandomLoadProfile.RANDOM_LOAD_PROFILE, allEntries) then: lpts.loadProfile == LoadProfile.RandomLoadProfile.RANDOM_LOAD_PROFILE diff --git a/src/test/groovy/edu/ie3/datamodel/io/source/sql/SqlLoadProfileSourceIT.groovy b/src/test/groovy/edu/ie3/datamodel/io/source/sql/SqlLoadProfileSourceIT.groovy index c29dcca76..be6026ecc 100644 --- a/src/test/groovy/edu/ie3/datamodel/io/source/sql/SqlLoadProfileSourceIT.groovy +++ b/src/test/groovy/edu/ie3/datamodel/io/source/sql/SqlLoadProfileSourceIT.groovy @@ -5,7 +5,6 @@ */ package edu.ie3.datamodel.io.source.sql - import static edu.ie3.test.common.TimeSeriesSourceTestData.G3_VALUE_00MIN import static edu.ie3.test.common.TimeSeriesSourceTestData.TIME_00MIN @@ -13,6 +12,7 @@ import edu.ie3.datamodel.io.connectors.SqlConnector import edu.ie3.datamodel.io.factory.timeseries.BdewLoadProfileFactory import edu.ie3.datamodel.io.naming.DatabaseNamingStrategy import edu.ie3.datamodel.io.naming.timeseries.LoadProfileMetaInformation +import edu.ie3.datamodel.io.source.PowerValueSource import edu.ie3.datamodel.models.profile.BdewStandardLoadProfile import edu.ie3.datamodel.models.value.load.BdewLoadValues import edu.ie3.test.helper.TestContainerHelper @@ -41,8 +41,6 @@ class SqlLoadProfileSourceIT extends Specification implements TestContainerHelpe static String schemaName = "public" - static UUID timeSeriesUuid = UUID.fromString("9b880468-309c-43c1-a3f4-26dd26266216") - def setupSpec() { // Copy sql import scripts into docker MountableFile sqlImportFile = getMountableFile("_timeseries/") @@ -56,7 +54,7 @@ class SqlLoadProfileSourceIT extends Specification implements TestContainerHelpe } connector = new SqlConnector(postgreSQLContainer.jdbcUrl, postgreSQLContainer.username, postgreSQLContainer.password) - def metaInformation = new LoadProfileMetaInformation(timeSeriesUuid, "g3") + def metaInformation = new LoadProfileMetaInformation("g3") namingStrategy = new DatabaseNamingStrategy() @@ -65,7 +63,7 @@ class SqlLoadProfileSourceIT extends Specification implements TestContainerHelpe def "A SqlTimeSeriesSource can read and correctly parse a single value for a specific date"() { when: - def value = loadSource.getValue(TIME_00MIN) + def value = loadSource.getPowerValue(new PowerValueSource.TimeSeriesInputValue(TIME_00MIN)) then: value.present @@ -74,11 +72,10 @@ class SqlLoadProfileSourceIT extends Specification implements TestContainerHelpe def "A SqlTimeSeriesSource can read all value data"() { when: - def timeSeries = loadSource.timeSeries + def entries = loadSource.entries then: - timeSeries.uuid == timeSeriesUuid - timeSeries.entries.size() == 3 + entries.size() == 3 } def "The SqlTimeSeriesSource returns the time keys after a given key correctly"() { @@ -86,11 +83,9 @@ class SqlLoadProfileSourceIT extends Specification implements TestContainerHelpe def time = TimeUtil.withDefaults.toZonedDateTime("2020-01-01T00:00:00Z") when: - def actual = loadSource.getTimeKeysAfter(time) + def actual = loadSource.getNextTimeKey(time) then: - actual == [ - TimeUtil.withDefaults.toZonedDateTime("2020-01-01T00:15:00Z") - ] + actual == Optional.of(TimeUtil.withDefaults.toZonedDateTime("2020-01-01T00:15:00Z")) } } diff --git a/src/test/groovy/edu/ie3/test/common/TimeSeriesTestData.groovy b/src/test/groovy/edu/ie3/test/common/TimeSeriesTestData.groovy index f332d0669..eb25d08fa 100644 --- a/src/test/groovy/edu/ie3/test/common/TimeSeriesTestData.groovy +++ b/src/test/groovy/edu/ie3/test/common/TimeSeriesTestData.groovy @@ -401,7 +401,6 @@ trait TimeSeriesTestData { ] as Set BdewLoadProfileTimeSeries loadProfileTimeSeries = new BdewLoadProfileTimeSeries( - UUID.fromString("b0ad5ba2-0d5e-4c9b-b818-4079cebf59cc"), BdewStandardLoadProfile.G2, [ new LoadProfileEntry<>( From 651fa76a10f4f546be0aa029037f1d71cff09c39 Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Mon, 22 Sep 2025 15:42:02 +0200 Subject: [PATCH 2/4] Renaming some methods in `PowerValueSource`. --- .../edu/ie3/datamodel/io/source/PowerValueSource.java | 10 +++++----- .../datamodel/io/source/csv/CsvLoadProfileSource.java | 2 +- .../datamodel/io/source/sql/SqlLoadProfileSource.java | 2 +- .../io/source/sql/SqlLoadProfileSourceIT.groovy | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/edu/ie3/datamodel/io/source/PowerValueSource.java b/src/main/java/edu/ie3/datamodel/io/source/PowerValueSource.java index 632980c7e..e4fe75709 100644 --- a/src/main/java/edu/ie3/datamodel/io/source/PowerValueSource.java +++ b/src/main/java/edu/ie3/datamodel/io/source/PowerValueSource.java @@ -28,7 +28,7 @@ public sealed interface PowerValueSource< * @param data input data that is used to calculate the next power value. * @return an option for the power value. */ - Optional getPowerValue(ID data); + Optional getValue(ID data); /** * Method to determine the next timestamp for which data is present. @@ -58,12 +58,12 @@ non-sealed interface MarkovBased extends PowerValueSource> getEntries() { } @Override - public Optional getPowerValue(TimeSeriesInputValue data) { + public Optional getValue(TimeSeriesInputValue data) { return loadProfileTimeSeries.getValue(data.time()); } diff --git a/src/main/java/edu/ie3/datamodel/io/source/sql/SqlLoadProfileSource.java b/src/main/java/edu/ie3/datamodel/io/source/sql/SqlLoadProfileSource.java index 69f72d452..a57d599ff 100644 --- a/src/main/java/edu/ie3/datamodel/io/source/sql/SqlLoadProfileSource.java +++ b/src/main/java/edu/ie3/datamodel/io/source/sql/SqlLoadProfileSource.java @@ -99,7 +99,7 @@ public Set> getEntries() { } @Override - public Optional getPowerValue(TimeSeriesInputValue data) { + public Optional getValue(TimeSeriesInputValue data) { ZonedDateTime time = data.time(); Set> entries = diff --git a/src/test/groovy/edu/ie3/datamodel/io/source/sql/SqlLoadProfileSourceIT.groovy b/src/test/groovy/edu/ie3/datamodel/io/source/sql/SqlLoadProfileSourceIT.groovy index be6026ecc..5e2fade92 100644 --- a/src/test/groovy/edu/ie3/datamodel/io/source/sql/SqlLoadProfileSourceIT.groovy +++ b/src/test/groovy/edu/ie3/datamodel/io/source/sql/SqlLoadProfileSourceIT.groovy @@ -63,7 +63,7 @@ class SqlLoadProfileSourceIT extends Specification implements TestContainerHelpe def "A SqlTimeSeriesSource can read and correctly parse a single value for a specific date"() { when: - def value = loadSource.getPowerValue(new PowerValueSource.TimeSeriesInputValue(TIME_00MIN)) + def value = loadSource.getValue(new PowerValueSource.TimeSeriesInputValue(TIME_00MIN)) then: value.present From 2abbd59c3037223b963df967d81dd8481a7a81e1 Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Tue, 23 Sep 2025 15:18:00 +0200 Subject: [PATCH 3/4] Fix after merge. --- .../edu/ie3/datamodel/io/source/sql/SqlLoadProfileSource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/edu/ie3/datamodel/io/source/sql/SqlLoadProfileSource.java b/src/main/java/edu/ie3/datamodel/io/source/sql/SqlLoadProfileSource.java index bad5073bb..6a8567dfb 100644 --- a/src/main/java/edu/ie3/datamodel/io/source/sql/SqlLoadProfileSource.java +++ b/src/main/java/edu/ie3/datamodel/io/source/sql/SqlLoadProfileSource.java @@ -106,7 +106,7 @@ public Optional getValue(TimeSeriesInputValue data) { getEntries(queryTime, ps -> ps.setInt(1, TimeSeriesUtils.calculateQuarterHourOfDay(time))); if (entries.isEmpty()) return Optional.empty(); if (entries.size() > 1) log.warn("Retrieved more than one result value, using the first"); - return entries.stream().findFirst().map(entry -> entry.getValue().getValue(time, loadProfile)); + return entries.stream().findFirst().map(entry -> entry.getValue().getValue(time, profile)); } @Override From 1155374481e4c95b07c021d768a9b4bb4eba3a6d Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Wed, 24 Sep 2025 14:11:40 +0200 Subject: [PATCH 4/4] Adding value supplier method. --- .../datamodel/io/source/PowerValueSource.java | 11 +++++++ .../io/source/csv/CsvLoadProfileSource.java | 6 ++++ .../io/source/sql/SqlLoadProfileSource.java | 28 ++++++++++++++--- .../repetitive/LoadProfileTimeSeries.java | 19 ++++++++++-- .../RandomLoadProfileTimeSeriesTest.groovy | 31 +++++++++++++++++++ 5 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 src/test/groovy/edu/ie3/datamodel/models/timeseries/RandomLoadProfileTimeSeriesTest.groovy diff --git a/src/main/java/edu/ie3/datamodel/io/source/PowerValueSource.java b/src/main/java/edu/ie3/datamodel/io/source/PowerValueSource.java index e4fe75709..88674ee50 100644 --- a/src/main/java/edu/ie3/datamodel/io/source/PowerValueSource.java +++ b/src/main/java/edu/ie3/datamodel/io/source/PowerValueSource.java @@ -10,6 +10,7 @@ import edu.ie3.datamodel.models.value.PValue; import java.time.ZonedDateTime; import java.util.Optional; +import java.util.function.Supplier; import javax.measure.quantity.Energy; import javax.measure.quantity.Power; import tech.units.indriya.ComparableQuantity; @@ -30,6 +31,16 @@ public sealed interface PowerValueSource< */ Optional getValue(ID data); + /** + * Method to get a supplier for the next power value based on the provided input data. Depending + * on the implementation the supplier will either always return the same value or each time a + * random value. To return one constant value please use {@link #getValue(InputData)}. + * + * @param data input data that is used to calculate the next power value. + * @return A supplier for an option on the value at the given time step. + */ + Supplier> getValueSupplier(ID data); + /** * Method to determine the next timestamp for which data is present. * diff --git a/src/main/java/edu/ie3/datamodel/io/source/csv/CsvLoadProfileSource.java b/src/main/java/edu/ie3/datamodel/io/source/csv/CsvLoadProfileSource.java index 2747e5bf5..87e67339f 100644 --- a/src/main/java/edu/ie3/datamodel/io/source/csv/CsvLoadProfileSource.java +++ b/src/main/java/edu/ie3/datamodel/io/source/csv/CsvLoadProfileSource.java @@ -20,6 +20,7 @@ import java.nio.file.Path; import java.util.*; import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Collectors; import javax.measure.quantity.Energy; import javax.measure.quantity.Power; @@ -74,6 +75,11 @@ public Optional getValue(TimeSeriesInputValue data) { return loadProfileTimeSeries.getValue(data.time()); } + @Override + public Supplier> getValueSupplier(TimeSeriesInputValue data) { + return loadProfileTimeSeries.supplyValue(data.time()); + } + @Override public P getProfile() { return loadProfileTimeSeries.getLoadProfile(); diff --git a/src/main/java/edu/ie3/datamodel/io/source/sql/SqlLoadProfileSource.java b/src/main/java/edu/ie3/datamodel/io/source/sql/SqlLoadProfileSource.java index 6a8567dfb..51ae171d4 100644 --- a/src/main/java/edu/ie3/datamodel/io/source/sql/SqlLoadProfileSource.java +++ b/src/main/java/edu/ie3/datamodel/io/source/sql/SqlLoadProfileSource.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.Supplier; import java.util.stream.Collectors; import javax.measure.quantity.Energy; import javax.measure.quantity.Power; @@ -101,12 +102,20 @@ public Set> getEntries() { @Override public Optional getValue(TimeSeriesInputValue data) { ZonedDateTime time = data.time(); + return queryForValue(time).map(loadValue -> loadValue.getValue(time, profile)); + } - Set> entries = - getEntries(queryTime, ps -> ps.setInt(1, TimeSeriesUtils.calculateQuarterHourOfDay(time))); - if (entries.isEmpty()) return Optional.empty(); - if (entries.size() > 1) log.warn("Retrieved more than one result value, using the first"); - return entries.stream().findFirst().map(entry -> entry.getValue().getValue(time, profile)); + @Override + public Supplier> getValueSupplier(TimeSeriesInputValue data) { + ZonedDateTime time = data.time(); + Optional> loadValueOption = queryForValue(time); + + if (loadValueOption.isPresent()) { + LoadValues

loadValue = loadValueOption.get(); + return () -> Optional.of(loadValue.getValue(time, profile)); + } else { + return Optional::empty; + } } @Override @@ -138,6 +147,15 @@ private Set> getEntries(String query, SqlDataSource.AddParam .collect(Collectors.toSet()); } + private Optional> queryForValue(ZonedDateTime time) { + Set> entries = + getEntries(queryTime, ps -> ps.setInt(1, TimeSeriesUtils.calculateQuarterHourOfDay(time))); + if (entries.isEmpty()) return Optional.empty(); + if (entries.size() > 1) log.warn("Retrieved more than one result value, using the first"); + + return entries.stream().findFirst().map(LoadProfileEntry::getValue); + } + /** * Build a {@link LoadProfileEntry} of type {@code V}, whereas the underlying {@link Value} does * not need any additional information. diff --git a/src/main/java/edu/ie3/datamodel/models/timeseries/repetitive/LoadProfileTimeSeries.java b/src/main/java/edu/ie3/datamodel/models/timeseries/repetitive/LoadProfileTimeSeries.java index 4a4905666..d3c72ea15 100644 --- a/src/main/java/edu/ie3/datamodel/models/timeseries/repetitive/LoadProfileTimeSeries.java +++ b/src/main/java/edu/ie3/datamodel/models/timeseries/repetitive/LoadProfileTimeSeries.java @@ -11,6 +11,7 @@ import edu.ie3.datamodel.utils.TimeSeriesUtils; import java.time.ZonedDateTime; import java.util.*; +import java.util.function.Supplier; import java.util.stream.Collectors; import javax.measure.quantity.Energy; import javax.measure.quantity.Power; @@ -21,8 +22,8 @@ */ public class LoadProfileTimeSeries

> extends RepetitiveTimeSeries, V, PValue> { - private final P loadProfile; - private final Map valueMapping; + protected final P loadProfile; + protected final Map valueMapping; /** * The maximum average power consumption per quarter-hour calculated over all seasons and weekday @@ -76,6 +77,20 @@ public Set> getEntries() { return set; } + /** + * Method to get a supplier for the next power value based on the provided input time. Depending + * on the implementation the supplier will either always return the same value or each time a + * random value. To return one constant value please use {@link #getValue(ZonedDateTime)}. + * + * @param time Queried time. + * @return A supplier for an option on the value at the given time step. + */ + public Supplier> supplyValue(ZonedDateTime time) { + int quarterHour = TimeSeriesUtils.calculateQuarterHourOfDay(time); + LoadValues

loadValue = valueMapping.get(quarterHour); + return () -> Optional.ofNullable(loadValue.getValue(time, loadProfile)); + } + @Override public Optional getPreviousDateTime(ZonedDateTime time) { return Optional.of(time.minusMinutes(15)); diff --git a/src/test/groovy/edu/ie3/datamodel/models/timeseries/RandomLoadProfileTimeSeriesTest.groovy b/src/test/groovy/edu/ie3/datamodel/models/timeseries/RandomLoadProfileTimeSeriesTest.groovy new file mode 100644 index 000000000..8f79bb010 --- /dev/null +++ b/src/test/groovy/edu/ie3/datamodel/models/timeseries/RandomLoadProfileTimeSeriesTest.groovy @@ -0,0 +1,31 @@ +/* + * © 2025. 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 + +import edu.ie3.datamodel.io.source.LoadProfileSource +import edu.ie3.util.TimeUtil +import spock.lang.Specification + +class RandomLoadProfileTimeSeriesTest extends Specification { + + + def "A RandomLoadProfileTimeSeries should supply a random value correctly"() { + given: + def lpts = LoadProfileSource.randomLoadProfile.getTimeSeries() + def time = TimeUtil.withDefaults.toZonedDateTime("2020-01-01T00:00:00Z") + + def supplier = lpts.supplyValue(time) + + when: + def values = [ + supplier.get(), + supplier.get(), + supplier.get() + ].collect { it.get()} + then: + values.size() > 1 + } +}