Skip to content

Commit ad56b45

Browse files
Merge pull request #591 from ie3-institute/to/#585-ems-data-model
To/#585 ems data model
2 parents 521d9fc + 145d0d4 commit ad56b45

File tree

23 files changed

+809
-213
lines changed

23 files changed

+809
-213
lines changed

src/main/java/edu/ie3/datamodel/io/factory/FactoryData.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,26 @@ field, getField(field)),
137137
}
138138
}
139139

140+
/**
141+
* Parses and returns an array of UUIDs from field value of given field name. Throws {@link
142+
* FactoryException} if field does not exist or parsing fails.
143+
*
144+
* @param field field name
145+
* @return UUID
146+
*/
147+
public UUID[] getUUIDs(String field) {
148+
try {
149+
String[] uuidFields = field.split(" ");
150+
return Arrays.stream(uuidFields).map(UUID::fromString).toArray(UUID[]::new);
151+
} catch (IllegalArgumentException iae) {
152+
throw new FactoryException(
153+
String.format(
154+
"Exception while trying to parse UUIDs of field \"%s\" with value \"%s\"",
155+
field, getField(field)),
156+
iae);
157+
}
158+
}
159+
140160
@Override
141161
public boolean equals(Object o) {
142162
if (this == o) return true;
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* © 2022. TU Dortmund University,
3+
* Institute of Energy Systems, Energy Efficiency and Energy Economics,
4+
* Research group Distribution grid planning and operation
5+
*/
6+
package edu.ie3.datamodel.io.factory.input.participant;
7+
8+
import edu.ie3.datamodel.exceptions.ParsingException;
9+
import edu.ie3.datamodel.io.factory.input.NodeAssetInputEntityData;
10+
import edu.ie3.datamodel.models.ControlStrategy;
11+
import edu.ie3.datamodel.models.OperationTime;
12+
import edu.ie3.datamodel.models.input.NodeInput;
13+
import edu.ie3.datamodel.models.input.OperatorInput;
14+
import edu.ie3.datamodel.models.input.system.EmInput;
15+
import edu.ie3.datamodel.models.input.system.characteristic.ReactivePowerCharacteristic;
16+
import java.util.UUID;
17+
import org.slf4j.Logger;
18+
import org.slf4j.LoggerFactory;
19+
20+
public class EmInputFactory
21+
extends SystemParticipantInputEntityFactory<EmInput, NodeAssetInputEntityData> {
22+
private static final Logger logger = LoggerFactory.getLogger(EmInputFactory.class);
23+
24+
private static final String CONNECTED_ASSETS = "connectedassets";
25+
26+
private static final String CONTROL_STRATEGY = "controlstrategy";
27+
28+
public EmInputFactory() {
29+
super(EmInput.class);
30+
}
31+
32+
@Override
33+
protected String[] getAdditionalFields() {
34+
return new String[] {CONNECTED_ASSETS, CONTROL_STRATEGY};
35+
}
36+
37+
@Override
38+
protected EmInput buildModel(
39+
NodeAssetInputEntityData data,
40+
UUID uuid,
41+
String id,
42+
NodeInput node,
43+
ReactivePowerCharacteristic qCharacteristics,
44+
OperatorInput operator,
45+
OperationTime operationTime) {
46+
ControlStrategy controlStrategy;
47+
try {
48+
controlStrategy = ControlStrategy.parse(data.getField(CONTROL_STRATEGY));
49+
} catch (ParsingException e) {
50+
logger.warn(
51+
"Cannot parse control strategy \"{}\" of energy management system \"{}\". Assign no control strategy instead.",
52+
data.getField(CONTROL_STRATEGY),
53+
id);
54+
controlStrategy = ControlStrategy.DefaultControlStrategies.NO_CONTROL_STRATEGY;
55+
}
56+
final UUID[] connectedAssets = data.getUUIDs(data.getField(CONNECTED_ASSETS));
57+
return new EmInput(
58+
uuid,
59+
id,
60+
operator,
61+
operationTime,
62+
node,
63+
qCharacteristics,
64+
connectedAssets,
65+
controlStrategy);
66+
}
67+
}

src/main/java/edu/ie3/datamodel/io/processor/Processor.java

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import edu.ie3.datamodel.exceptions.EntityProcessorException;
99
import edu.ie3.datamodel.io.factory.input.NodeInputFactory;
1010
import edu.ie3.datamodel.io.processor.result.ResultEntityProcessor;
11+
import edu.ie3.datamodel.models.ControlStrategy;
1112
import edu.ie3.datamodel.models.OperationTime;
1213
import edu.ie3.datamodel.models.StandardUnits;
1314
import edu.ie3.datamodel.models.UniqueEntity;
@@ -269,6 +270,9 @@ protected String processMethodResult(Object methodReturnObject, Method method, S
269270
"ReactivePowerCharacteristic",
270271
"CharacteristicInput" -> resultStringBuilder.append(
271272
((CharacteristicInput<?, ?>) methodReturnObject).deSerialize());
273+
case "UUID[]" -> resultStringBuilder.append(processUUIDArray((UUID[]) methodReturnObject));
274+
case "ControlStrategy" -> resultStringBuilder.append(
275+
((ControlStrategy) methodReturnObject).getKey());
272276
default -> throw new EntityProcessorException(
273277
"Unable to process value for attribute/field '"
274278
+ fieldName
@@ -327,6 +331,28 @@ protected String handleQuantity(Quantity<?> quantity, String fieldName) {
327331
+ ".class."));
328332
}
329333

334+
/**
335+
* This method should handle all quantities that are model processor specific e.g. we need to
336+
* handle active power p different for {@link edu.ie3.datamodel.models.result.ResultEntity}s and
337+
* {@link edu.ie3.datamodel.models.input.system.SystemParticipantInput}s Hence from the
338+
* generalized method {@link #handleQuantity(Quantity, String)}, this allows for the specific
339+
* handling of child implementations. See the implementation @ {@link ResultEntityProcessor} for
340+
* details.
341+
*
342+
* @param quantity the quantity that should be processed
343+
* @param fieldName the field name the quantity is set to
344+
* @return an optional string with the normalized to {@link StandardUnits} value of the quantity
345+
* or empty if an error occurred during processing
346+
*/
347+
protected abstract Optional<String> handleProcessorSpecificQuantity(
348+
Quantity<?> quantity, String fieldName);
349+
350+
protected String processUUIDArray(UUID[] uuids) {
351+
StringBuilder strb = new StringBuilder();
352+
for (UUID uuid : uuids) strb.append(uuid.toString()).append(" ");
353+
return strb.toString().strip();
354+
}
355+
330356
/**
331357
* Handling of elements of type {@link OperationTime}
332358
*
@@ -363,22 +389,6 @@ protected String processZonedDateTime(ZonedDateTime zonedDateTime) {
363389
return zonedDateTime.toString();
364390
}
365391

366-
/**
367-
* This method should handle all quantities that are model processor specific e.g. we need to
368-
* handle active power p different for {@link edu.ie3.datamodel.models.result.ResultEntity}s and
369-
* {@link edu.ie3.datamodel.models.input.system.SystemParticipantInput}s Hence from the
370-
* generalized method {@link #handleQuantity(Quantity, String)}, this allows for the specific
371-
* handling of child implementations. See the implementation @ {@link ResultEntityProcessor} for
372-
* details.
373-
*
374-
* @param quantity the quantity that should be processed
375-
* @param fieldName the field name the quantity is set to
376-
* @return an optional string with the normalized to {@link StandardUnits} value of the quantity
377-
* or empty if an error occurred during processing
378-
*/
379-
protected abstract Optional<String> handleProcessorSpecificQuantity(
380-
Quantity<?> quantity, String fieldName);
381-
382392
/**
383393
* Converts a given quantity to String by extracting the value and applying the toString method to
384394
* it

src/main/java/edu/ie3/datamodel/io/processor/input/InputEntityProcessor.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public class InputEntityProcessor extends EntityProcessor<InputEntity> {
5555
PvInput.class,
5656
StorageInput.class,
5757
WecInput.class,
58+
EmInput.class,
5859
/* -- ThermalUnitInput */
5960
ThermalHouseInput.class,
6061
CylindricalStorageInput.class,

src/main/java/edu/ie3/datamodel/io/source/SystemParticipantSource.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,4 +390,35 @@ Set<HpInput> getHeatPumps(
390390
Set<OperatorInput> operators,
391391
Set<HpTypeInput> types,
392392
Set<ThermalBusInput> thermalBuses);
393+
394+
/**
395+
* Returns a unique set of {@link EmInput} instances.
396+
*
397+
* <p>This set has to be unique in the sense of object uniqueness but also in the sense of {@link
398+
* java.util.UUID} uniqueness of the provided {@link EmInput} which has to be checked manually, as
399+
* {@link EmInput#equals(Object)} is NOT restricted on the uuid of {@link EmInput}.
400+
*
401+
* @return a set of object and uuid unique {@link EmInput} entities
402+
*/
403+
Set<EmInput> getEmSystems();
404+
405+
/**
406+
* This set has to be unique in the sense of object uniqueness but also in the sense of {@link
407+
* java.util.UUID} uniqueness of the provided {@link EmInput} which has to be checked manually, as
408+
* {@link EmInput#equals(Object)} is NOT restricted on the uuid of {@link EmInput}.
409+
*
410+
* <p>In contrast to {@link #getHeatPumps()} this interface provides the ability to pass in an
411+
* already existing set of {@link NodeInput} and {@link OperatorInput} entities, the {@link
412+
* EmInput} instances depend on. Doing so, already loaded nodes can be recycled to improve
413+
* performance and prevent unnecessary loading operations.
414+
*
415+
* <p>If something fails during the creation process it's up to the concrete implementation of an
416+
* empty set or a set with all entities that has been able to be build is returned.
417+
*
418+
* @param operators a set of object and uuid unique {@link OperatorInput} that should be used for
419+
* the returning instances
420+
* @param nodes a set of object and uuid unique {@link NodeInput} entities
421+
* @return a set of object and uuid unique {@link EmInput} entities
422+
*/
423+
Set<EmInput> getEmSystems(Set<NodeInput> nodes, Set<OperatorInput> operators);
393424
}

src/main/java/edu/ie3/datamodel/io/source/csv/CsvSystemParticipantSource.java

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ public class CsvSystemParticipantSource extends CsvDataSource implements SystemP
6363
private final StorageInputFactory storageInputFactory;
6464
private final WecInputFactory wecInputFactory;
6565
private final EvcsInputFactory evcsInputFactory;
66+
private final EmInputFactory emInputFactory;
6667

6768
public CsvSystemParticipantSource(
6869
String csvSep,
@@ -87,6 +88,7 @@ public CsvSystemParticipantSource(
8788
this.storageInputFactory = new StorageInputFactory();
8889
this.wecInputFactory = new WecInputFactory();
8990
this.evcsInputFactory = new EvcsInputFactory();
91+
this.emInputFactory = new EmInputFactory();
9092
}
9193

9294
/** {@inheritDoc} */
@@ -168,6 +170,11 @@ public Optional<SystemParticipants> getSystemParticipants() {
168170
.filter(isPresentCollectIfNot(HpInput.class, nonBuildEntities))
169171
.map(Optional::get)
170172
.collect(Collectors.toSet());
173+
Set<EmInput> emInputs =
174+
nodeAssetEntityStream(EmInput.class, emInputFactory, nodes, operators)
175+
.filter(isPresentCollectIfNot(EmInput.class, nonBuildEntities))
176+
.map(Optional::get)
177+
.collect(Collectors.toSet());
171178

172179
// if we found invalid elements return an empty optional and log the problems
173180
if (!nonBuildEntities.isEmpty()) {
@@ -187,7 +194,8 @@ public Optional<SystemParticipants> getSystemParticipants() {
187194
loads,
188195
pvInputs,
189196
storages,
190-
wecInputs));
197+
wecInputs,
198+
emInputs));
191199
}
192200

193201
/** {@inheritDoc} */
@@ -637,7 +645,7 @@ private Optional<HpInputEntityData> buildHpEntityData(
637645

638646
// if the requested entity is not present we return an empty element and
639647
// log a warning
640-
if (!hpInputEntityDataOpt.isPresent()) {
648+
if (hpInputEntityDataOpt.isEmpty()) {
641649
logSkippingWarning(
642650
typedEntityData.getTargetClass().getSimpleName(),
643651
saveMapGet(fieldsToAttributes, "uuid", FIELDS_TO_VALUES_MAP),
@@ -732,4 +740,29 @@ private Optional<ChpInputEntityData> buildChpEntityData(
732740
thermalBus.get(),
733741
thermalStorage.get()));
734742
}
743+
744+
@Override
745+
public Set<EmInput> getEmSystems() {
746+
Set<OperatorInput> operators = typeSource.getOperators();
747+
return getEmSystems(rawGridSource.getNodes(operators), operators);
748+
}
749+
750+
/**
751+
* {@inheritDoc}
752+
*
753+
* <p>If the set of {@link NodeInput} entities is not exhaustive for all available {@link
754+
* LoadInput} entities (e.g. a {@link NodeInput} entity is missing) or if an error during the
755+
* building process occurs, the entity that misses something will be skipped (which can be seen as
756+
* a filtering functionality), but all entities that are able to be built will be returned anyway
757+
* and the elements that couldn't have been built are logged.
758+
*
759+
* <p>If the set with {@link OperatorInput} is not exhaustive, the corresponding operator is set
760+
* to {@link OperatorInput#NO_OPERATOR_ASSIGNED}
761+
*/
762+
@Override
763+
public Set<EmInput> getEmSystems(Set<NodeInput> nodes, Set<OperatorInput> operators) {
764+
return nodeAssetEntityStream(EmInput.class, emInputFactory, nodes, operators)
765+
.flatMap(Optional::stream)
766+
.collect(Collectors.toSet());
767+
}
735768
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* © 2022. TU Dortmund University,
3+
* Institute of Energy Systems, Energy Efficiency and Energy Economics,
4+
* Research group Distribution grid planning and operation
5+
*/
6+
package edu.ie3.datamodel.models;
7+
8+
import edu.ie3.datamodel.exceptions.ParsingException;
9+
import java.io.Serializable;
10+
import java.util.Arrays;
11+
12+
public interface ControlStrategy extends Serializable {
13+
String getKey();
14+
15+
static ControlStrategy parse(String key) throws ParsingException {
16+
if (key == null || key.isEmpty())
17+
return ControlStrategy.DefaultControlStrategies.NO_CONTROL_STRATEGY;
18+
19+
String filterKey = key.toLowerCase().replace("-", "_");
20+
return Arrays.stream(EmControlStrategy.values())
21+
.filter(profile -> profile.getKey().equals(filterKey))
22+
.findFirst()
23+
.orElseThrow(
24+
() -> new ParsingException("Cannot parse \"" + key + "\" to a valid control strategy"));
25+
}
26+
27+
enum DefaultControlStrategies implements ControlStrategy {
28+
NO_CONTROL_STRATEGY;
29+
30+
@Override
31+
public String getKey() {
32+
return "No control strategy assigned";
33+
}
34+
}
35+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* © 2022. TU Dortmund University,
3+
* Institute of Energy Systems, Energy Efficiency and Energy Economics,
4+
* Research group Distribution grid planning and operation
5+
*/
6+
package edu.ie3.datamodel.models;
7+
8+
import java.util.Arrays;
9+
import java.util.Locale;
10+
import java.util.stream.Collectors;
11+
12+
public enum EmControlStrategy implements ControlStrategy {
13+
SELF_OPTIMIZATION("self_optimization");
14+
15+
private final String key;
16+
17+
EmControlStrategy(String key) {
18+
this.key = key.toLowerCase(Locale.ROOT);
19+
}
20+
21+
public static EmControlStrategy get(String key) {
22+
return Arrays.stream(EmControlStrategy.values())
23+
.filter(controlStrategy -> controlStrategy.key.equalsIgnoreCase(key))
24+
.findFirst()
25+
.orElseThrow(
26+
() ->
27+
new IllegalArgumentException(
28+
"No predefined energy management control strategy '"
29+
+ key
30+
+ "' found. Please provide one of the following keys: "
31+
+ Arrays.stream(EmControlStrategy.values())
32+
.map(EmControlStrategy::getKey)
33+
.collect(Collectors.joining(", "))));
34+
}
35+
36+
@Override
37+
public String getKey() {
38+
return key;
39+
}
40+
41+
@Override
42+
public String toString() {
43+
return "EmControlStrategy{" + "key='" + key + '\'' + '}';
44+
}
45+
}

0 commit comments

Comments
 (0)