diff --git a/CHANGELOG.md b/CHANGELOG.md index a7593a97d..1326237be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased/Snapshot] +## Added +- Added support to provide the PSDM with custom `LoadProfile` [#1362](https://github.com/ie3-institute/PowerSystemDataModel/issues/1362) + ### Fixed - Fixed handling of `CongestionResult.InputModelType` in `EntityProcessor` [#1325](https://github.com/ie3-institute/PowerSystemDataModel/issues/1325) - -Fixed em fields in input models [#1331](https://github.com/ie3-institute/PowerSystemDataModel/issues/1331) diff --git a/build.gradle b/build.gradle index dbc1fd406..c0cf72f8b 100644 --- a/build.gradle +++ b/build.gradle @@ -72,7 +72,7 @@ dependencies { implementation 'org.jgrapht:jgrapht-core:1.5.2' // Statistics (for random load model) - implementation 'de.lmu.ifi.dbs.elki:elki:0.7.5' + implementation 'de.lmu.ifi.dbs.elki:elki-core-math:0.7.5' // testing testImplementation "org.apache.groovy:groovy:$groovyBinaryVersion" diff --git a/src/main/java/edu/ie3/datamodel/io/provider/LoadProfileProvider.java b/src/main/java/edu/ie3/datamodel/io/provider/LoadProfileProvider.java new file mode 100644 index 000000000..20346328b --- /dev/null +++ b/src/main/java/edu/ie3/datamodel/io/provider/LoadProfileProvider.java @@ -0,0 +1,75 @@ +/* + * © 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.provider; + +import edu.ie3.datamodel.io.factory.timeseries.BdewLoadProfileFactory; +import edu.ie3.datamodel.io.factory.timeseries.LoadProfileFactory; +import edu.ie3.datamodel.io.factory.timeseries.RandomLoadProfileFactory; +import edu.ie3.datamodel.models.profile.BdewStandardLoadProfile; +import edu.ie3.datamodel.models.profile.LoadProfile; +import edu.ie3.datamodel.models.profile.NbwTemperatureDependantLoadProfile; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** Interface defining a provider for {@link LoadProfile}s and their {@link LoadProfileFactory}. */ +public interface LoadProfileProvider { + + /** A list of all {@link LoadProfile}, that are known. */ + List loadedProfiles = + ServiceLoader.load(LoadProfileProvider.class).stream() + .map(ServiceLoader.Provider::get) + .map(LoadProfileProvider::getProfiles) + .flatMap(Collection::stream) + .toList(); + + /** A map of all known {@link LoadProfile} to their {@link LoadProfileFactory}. */ + Map> profileToFactories = + ServiceLoader.load(LoadProfileProvider.class).stream() + .map(ServiceLoader.Provider::get) + .map(LoadProfileProvider::getFactories) + .flatMap(map -> map.entrySet().stream()) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + + /** Returns a list of {@link LoadProfile}s. */ + List getProfiles(); + + /** Returns a map of {@link LoadProfile} to {@link LoadProfileFactory}. */ + Map> getFactories(); + + /** + * Default load profile provider. This class is used to provide the {@link LoadProfile}s defined + * in the PSDM. + */ + final class DefaultLoadProfileProvider implements LoadProfileProvider { + + @Override + public List getProfiles() { + return Stream.of( + BdewStandardLoadProfile.values(), + NbwTemperatureDependantLoadProfile.values(), + LoadProfile.RandomLoadProfile.values(), + LoadProfile.DefaultLoadProfiles.values()) + .flatMap(Arrays::stream) + .toList(); + } + + @Override + public Map> getFactories() { + BdewLoadProfileFactory bdewLoadProfileFactory = new BdewLoadProfileFactory(); + + Map> factories = new HashMap<>(); + + Arrays.stream(BdewStandardLoadProfile.values()) + .forEach(profile -> factories.put(profile, bdewLoadProfileFactory)); + + factories.put( + LoadProfile.RandomLoadProfile.RANDOM_LOAD_PROFILE, new RandomLoadProfileFactory()); + + return factories; + } + } +} 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 c68ba3435..8f96fbfd7 100644 --- a/src/main/java/edu/ie3/datamodel/models/profile/LoadProfile.java +++ b/src/main/java/edu/ie3/datamodel/models/profile/LoadProfile.java @@ -6,10 +6,10 @@ package edu.ie3.datamodel.models.profile; import edu.ie3.datamodel.exceptions.ParsingException; +import edu.ie3.datamodel.io.provider.LoadProfileProvider; 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 */ @@ -28,13 +28,12 @@ static LoadProfile parse(String key) throws ParsingException { return LoadProfile.getProfile(getAllProfiles(), key); } + /** + * Returns all {@link LoadProfile}s, that are either provided by the PSDM or provided using the + * {@link LoadProfileProvider}. + */ static LoadProfile[] getAllProfiles() { - return Stream.of( - BdewStandardLoadProfile.values(), - NbwTemperatureDependantLoadProfile.values(), - (LoadProfile[]) RandomLoadProfile.values()) - .flatMap(Arrays::stream) - .toArray(LoadProfile[]::new); + return LoadProfileProvider.loadedProfiles.toArray(LoadProfile[]::new); } /** diff --git a/src/main/resources/META-INF/services/edu.ie3.datamodel.io.provider.LoadProfileProvider b/src/main/resources/META-INF/services/edu.ie3.datamodel.io.provider.LoadProfileProvider new file mode 100644 index 000000000..209e64b45 --- /dev/null +++ b/src/main/resources/META-INF/services/edu.ie3.datamodel.io.provider.LoadProfileProvider @@ -0,0 +1 @@ +edu.ie3.datamodel.io.provider.LoadProfileProvider$DefaultLoadProfileProvider \ No newline at end of file diff --git a/src/test/groovy/edu/ie3/datamodel/models/profile/LoadProfileTest.groovy b/src/test/groovy/edu/ie3/datamodel/models/profile/LoadProfileTest.groovy index 0265d24c4..a5f056bcb 100644 --- a/src/test/groovy/edu/ie3/datamodel/models/profile/LoadProfileTest.groovy +++ b/src/test/groovy/edu/ie3/datamodel/models/profile/LoadProfileTest.groovy @@ -187,6 +187,6 @@ class LoadProfileTest extends Specification { then: def e = thrown(ParsingException) - e.message == "No predefined load profile with key 'not_a_key' found. Please provide one of the following keys: h0, l0, l1, l2, g0, g1, g2, g3, g4, g5, g6, ep1, ez2, random" + e.message == "No predefined load profile with key 'not_a_key' found. Please provide one of the following keys: h0, l0, l1, l2, g0, g1, g2, g3, g4, g5, g6, ep1, ez2, random, No load profile assigned" } }