diff --git a/CHANGELOG.md b/CHANGELOG.md index ffa9b06..1d7d986 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Adding some utility methods [#368](https://github.com/ie3-institute/simonaAPI/issues/368) +- Extending flex option handling in `ExtInputContainer` [#371](https://github.com/ie3-institute/simonaAPI/issues/371) ### Changed - Changes to sent and received em data [#2366](https://github.com/ie3-institute/simonaAPI/issues/366) diff --git a/src/main/java/edu/ie3/simona/api/data/container/ExtInputContainer.java b/src/main/java/edu/ie3/simona/api/data/container/ExtInputContainer.java index 273468c..81f4e05 100644 --- a/src/main/java/edu/ie3/simona/api/data/container/ExtInputContainer.java +++ b/src/main/java/edu/ie3/simona/api/data/container/ExtInputContainer.java @@ -8,10 +8,7 @@ import edu.ie3.datamodel.models.value.PValue; import edu.ie3.datamodel.models.value.Value; -import edu.ie3.simona.api.data.model.em.EmCommunicationMessage; -import edu.ie3.simona.api.data.model.em.EmSetPoint; -import edu.ie3.simona.api.data.model.em.FlexOptionRequest; -import edu.ie3.simona.api.data.model.em.FlexOptions; +import edu.ie3.simona.api.data.model.em.*; import java.util.*; /** Contains all inputs for SIMONA for a certain tick */ @@ -27,7 +24,7 @@ public final class ExtInputContainer implements ExtDataContainer { /** Map uuid to primary input value for SIMONA. */ private final Map primaryData = new HashMap<>(); - // mapping for em data + // mappings for em data /** Map uuid to flex option requests. */ private final Map flexRequests = new HashMap<>(); @@ -61,7 +58,8 @@ public boolean isEmpty() { return primaryData.isEmpty() && flexRequests.isEmpty() && flexOptions.isEmpty() - && setPoints.isEmpty(); + && setPoints.isEmpty() + && emMessages.isEmpty(); } /** Returns the tick the data is provided for. */ @@ -100,8 +98,34 @@ public void addRequest(UUID receiver) { flexRequests.put(receiver, new FlexOptionRequest(receiver, false)); } - public void addRequest(UUID receiver, FlexOptionRequest request) { - flexRequests.put(receiver, request); + /** + * Method for adding a flex option request. + * + * @param request to be added + */ + public void addRequest(FlexOptionRequest request) { + flexRequests.put(request.receiver(), request); + } + + /** + * Method for adding flex options to a given receiver. + * + * @param multiFlexOptions that will be added to this container + */ + public void addFlexOptions(MultiFlexOptions multiFlexOptions) { + flexOptions + .computeIfAbsent(multiFlexOptions.receiver(), k -> new ArrayList<>()) + .addAll(multiFlexOptions.disaggregated().values()); + } + + /** + * Method for adding flex options to a given receiver. + * + * @param receiver that will receive the flex options + * @param flexOption that will be added + */ + public void addFlexOptions(UUID receiver, FlexOptions flexOption) { + flexOptions.computeIfAbsent(receiver, k -> new ArrayList<>()).add(flexOption); } /** diff --git a/src/main/java/edu/ie3/simona/api/data/model/em/GeneralFlexOptions.java b/src/main/java/edu/ie3/simona/api/data/model/em/EnergyBoundariesFlexOptions.java similarity index 92% rename from src/main/java/edu/ie3/simona/api/data/model/em/GeneralFlexOptions.java rename to src/main/java/edu/ie3/simona/api/data/model/em/EnergyBoundariesFlexOptions.java index 22b5c3c..c8cdb8e 100644 --- a/src/main/java/edu/ie3/simona/api/data/model/em/GeneralFlexOptions.java +++ b/src/main/java/edu/ie3/simona/api/data/model/em/EnergyBoundariesFlexOptions.java @@ -16,7 +16,7 @@ import tech.units.indriya.ComparableQuantity; /** - * General flex options that can represent various flex option types. + * Energy boundaries flex options that can represent various flex option types. * * @param model That is providing this flex options. * @param flexType The type of the flex options. @@ -27,7 +27,7 @@ * @param tickToEnergyLimits A map: tick to energy limits. * @param disaggregated A map: uuid to disaggregated flex options. */ -public record GeneralFlexOptions( +public record EnergyBoundariesFlexOptions( UUID receiver, UUID model, String flexType, @@ -39,7 +39,7 @@ public record GeneralFlexOptions( Map disaggregated) implements FlexOptions { - public GeneralFlexOptions( + public EnergyBoundariesFlexOptions( UUID receiver, UUID model, String flexType, diff --git a/src/main/java/edu/ie3/simona/api/data/model/em/FlexOptions.java b/src/main/java/edu/ie3/simona/api/data/model/em/FlexOptions.java index 77a4675..47b9e15 100644 --- a/src/main/java/edu/ie3/simona/api/data/model/em/FlexOptions.java +++ b/src/main/java/edu/ie3/simona/api/data/model/em/FlexOptions.java @@ -6,6 +6,7 @@ package edu.ie3.simona.api.data.model.em; +import java.util.Map; import java.util.UUID; /** Interface that defines flex options. */ @@ -14,6 +15,9 @@ public interface FlexOptions extends EmData { /** Returns the receiver of this flex options. */ UUID receiver(); + /** Returns the disaggregated flex options. */ + Map disaggregated(); + /** * Enriches the flex option with disaggregated flex options. * diff --git a/src/main/java/edu/ie3/simona/api/data/model/em/MultiFlexOptions.java b/src/main/java/edu/ie3/simona/api/data/model/em/MultiFlexOptions.java new file mode 100644 index 0000000..1a6aaeb --- /dev/null +++ b/src/main/java/edu/ie3/simona/api/data/model/em/MultiFlexOptions.java @@ -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.simona.api.data.model.em; + +import java.util.*; + +/** + * Container class for grouping disaggregated flex options to a single receiver. + * + * @param receiver that should receive the flex options + * @param disaggregated flex options + */ +public record MultiFlexOptions(UUID receiver, Map disaggregated) + implements FlexOptions { + public MultiFlexOptions(UUID receiver) { + this(receiver, new HashMap<>()); + } + + @Override + public void addDisaggregated(UUID model, FlexOptions flexOptions) { + disaggregated.put(model, flexOptions); + } +} diff --git a/src/test/groovy/edu/ie3/simona/api/data/container/ExtInputContainerTest.groovy b/src/test/groovy/edu/ie3/simona/api/data/container/ExtInputContainerTest.groovy index b813e87..a75e6a7 100644 --- a/src/test/groovy/edu/ie3/simona/api/data/container/ExtInputContainerTest.groovy +++ b/src/test/groovy/edu/ie3/simona/api/data/container/ExtInputContainerTest.groovy @@ -3,6 +3,7 @@ package edu.ie3.simona.api.data.container import edu.ie3.datamodel.models.value.PValue import edu.ie3.simona.api.data.model.em.EmSetPoint import edu.ie3.simona.api.data.model.em.FlexOptionRequest +import edu.ie3.simona.api.data.model.em.MultiFlexOptions import edu.ie3.simona.api.data.model.em.PowerLimitFlexOptions import spock.lang.Shared import spock.lang.Specification @@ -12,8 +13,34 @@ import static edu.ie3.util.quantities.PowerSystemUnits.KILOWATT class ExtInputContainerTest extends Specification { - @Shared - private UUID sender = UUID.randomUUID() + def "An ExtInputContainer should return the tick correctly"() { + expect: + container.tick == expectedTick + container.maybeNextTick == expectedMaybeNextTick + + where: + container | expectedTick | expectedMaybeNextTick + new ExtInputContainer(0L) | 0L | Optional.empty() + new ExtInputContainer(0L, 900L) | 0L | Optional.of(900L) + } + + def "An ExtInputContainer should check if it is empty correctly"() { + expect: + def container1 = new ExtInputContainer(0L) + container1.empty + container1.addPrimaryValue(UUID.randomUUID(), null) + !container1.empty + + def container2 = new ExtInputContainer(0L) + container2.empty + container2.addFlexComMessage(null) + !container2.empty + + def container3 = new ExtInputContainer(0L) + container3.empty + container3.addRequest(UUID.randomUUID()) + !container3.empty + } def "An ExtInputContainer should add primary data correctly"() { given: @@ -57,6 +84,21 @@ class ExtInputContainerTest extends Specification { container.flexOptions == [(receiver): [flexOptions]] } + def "An ExtInputContainer should add multi flex option data correctly"() { + given: + UUID receiver = UUID.randomUUID() + UUID sender = UUID.randomUUID() + def flexOptions = new PowerLimitFlexOptions(receiver, sender, Quantities.getQuantity(0d, KILOWATT), Quantities.getQuantity(2d, KILOWATT), Quantities.getQuantity(5d, KILOWATT)) + + def container = new ExtInputContainer(0L) + + when: + container.addFlexOptions(new MultiFlexOptions(receiver, [(sender): flexOptions])) + + then: + container.flexOptions == [(receiver): [flexOptions]] + } + def "An ExtInputContainer should add set point data correctly"() { given: UUID receiver = UUID.randomUUID() diff --git a/src/test/groovy/edu/ie3/simona/api/data/model/em/FlexOptionsTest.groovy b/src/test/groovy/edu/ie3/simona/api/data/model/em/FlexOptionsTest.groovy index bc93fe1..8d54765 100644 --- a/src/test/groovy/edu/ie3/simona/api/data/model/em/FlexOptionsTest.groovy +++ b/src/test/groovy/edu/ie3/simona/api/data/model/em/FlexOptionsTest.groovy @@ -46,7 +46,7 @@ class FlexOptionsTest extends Specification { UUID receiver = UUID.randomUUID() when: - def flexOptions = new GeneralFlexOptions( + def flexOptions = new EnergyBoundariesFlexOptions( receiver, senderUuid, "general flex type",