Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/main/java/edu/ie3/datamodel/io/factory/FactoryData.java
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,26 @@ field, getField(field)),
}
}

/**
* Parses and returns an array of UUIDs from field value of given field name. Throws {@link
* FactoryException} if field does not exist or parsing fails.
*
* @param field field name
* @return UUID
*/
public UUID[] getUUIDs(String field) {
try {
String[] uuidFields = field.split(" ");
return Arrays.stream(uuidFields).map(UUID::fromString).toArray(UUID[]::new);
} catch (IllegalArgumentException iae) {
throw new FactoryException(
String.format(
"Exception while trying to parse UUIDs of field \"%s\" with value \"%s\"",
field, getField(field)),
iae);
}
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* © 2022. 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.input.participant;

import edu.ie3.datamodel.exceptions.ParsingException;
import edu.ie3.datamodel.io.factory.input.NodeAssetInputEntityData;
import edu.ie3.datamodel.models.ControlStrategy;
import edu.ie3.datamodel.models.OperationTime;
import edu.ie3.datamodel.models.input.NodeInput;
import edu.ie3.datamodel.models.input.OperatorInput;
import edu.ie3.datamodel.models.input.system.EmInput;
import edu.ie3.datamodel.models.input.system.characteristic.ReactivePowerCharacteristic;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class EmInputFactory
extends SystemParticipantInputEntityFactory<EmInput, NodeAssetInputEntityData> {
private static final Logger logger = LoggerFactory.getLogger(EmInputFactory.class);

private static final String CONNECTED_ASSETS = "connectedassets";

private static final String CONTROL_STRATEGY = "controlstrategy";

public EmInputFactory() {
super(EmInput.class);
}

@Override
protected String[] getAdditionalFields() {
return new String[] {CONNECTED_ASSETS, CONTROL_STRATEGY};
}

@Override
protected EmInput buildModel(
NodeAssetInputEntityData data,
UUID uuid,
String id,
NodeInput node,
ReactivePowerCharacteristic qCharacteristics,
OperatorInput operator,
OperationTime operationTime) {
ControlStrategy controlStrategy;
try {
controlStrategy = ControlStrategy.parse(data.getField(CONTROL_STRATEGY));
} catch (ParsingException e) {
logger.warn(
"Cannot parse control strategy \"{}\" of energy management system \"{}\". Assign no control strategy instead.",
data.getField(CONTROL_STRATEGY),
id);
controlStrategy = ControlStrategy.DefaultControlStrategies.NO_CONTROL_STRATEGY;
}
final UUID[] connectedAssets = data.getUUIDs(data.getField(CONNECTED_ASSETS));
return new EmInput(
uuid,
id,
operator,
operationTime,
node,
qCharacteristics,
connectedAssets,
controlStrategy);
}
}
42 changes: 26 additions & 16 deletions src/main/java/edu/ie3/datamodel/io/processor/Processor.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import edu.ie3.datamodel.exceptions.EntityProcessorException;
import edu.ie3.datamodel.io.factory.input.NodeInputFactory;
import edu.ie3.datamodel.io.processor.result.ResultEntityProcessor;
import edu.ie3.datamodel.models.ControlStrategy;
import edu.ie3.datamodel.models.OperationTime;
import edu.ie3.datamodel.models.StandardUnits;
import edu.ie3.datamodel.models.UniqueEntity;
Expand Down Expand Up @@ -269,6 +270,9 @@ protected String processMethodResult(Object methodReturnObject, Method method, S
"ReactivePowerCharacteristic",
"CharacteristicInput" -> resultStringBuilder.append(
((CharacteristicInput<?, ?>) methodReturnObject).deSerialize());
case "UUID[]" -> resultStringBuilder.append(processUUIDArray((UUID[]) methodReturnObject));
case "ControlStrategy" -> resultStringBuilder.append(
((ControlStrategy) methodReturnObject).getKey());
default -> throw new EntityProcessorException(
"Unable to process value for attribute/field '"
+ fieldName
Expand Down Expand Up @@ -327,6 +331,28 @@ protected String handleQuantity(Quantity<?> quantity, String fieldName) {
+ ".class."));
}

/**
* This method should handle all quantities that are model processor specific e.g. we need to
* handle active power p different for {@link edu.ie3.datamodel.models.result.ResultEntity}s and
* {@link edu.ie3.datamodel.models.input.system.SystemParticipantInput}s Hence from the
* generalized method {@link #handleQuantity(Quantity, String)}, this allows for the specific
* handling of child implementations. See the implementation @ {@link ResultEntityProcessor} for
* details.
*
* @param quantity the quantity that should be processed
* @param fieldName the field name the quantity is set to
* @return an optional string with the normalized to {@link StandardUnits} value of the quantity
* or empty if an error occurred during processing
*/
protected abstract Optional<String> handleProcessorSpecificQuantity(
Quantity<?> quantity, String fieldName);

protected String processUUIDArray(UUID[] uuids) {
StringBuilder strb = new StringBuilder();
for (UUID uuid : uuids) strb.append(uuid.toString()).append(" ");
return strb.toString().strip();
}

/**
* Handling of elements of type {@link OperationTime}
*
Expand Down Expand Up @@ -363,22 +389,6 @@ protected String processZonedDateTime(ZonedDateTime zonedDateTime) {
return zonedDateTime.toString();
}

/**
* This method should handle all quantities that are model processor specific e.g. we need to
* handle active power p different for {@link edu.ie3.datamodel.models.result.ResultEntity}s and
* {@link edu.ie3.datamodel.models.input.system.SystemParticipantInput}s Hence from the
* generalized method {@link #handleQuantity(Quantity, String)}, this allows for the specific
* handling of child implementations. See the implementation @ {@link ResultEntityProcessor} for
* details.
*
* @param quantity the quantity that should be processed
* @param fieldName the field name the quantity is set to
* @return an optional string with the normalized to {@link StandardUnits} value of the quantity
* or empty if an error occurred during processing
*/
protected abstract Optional<String> handleProcessorSpecificQuantity(
Quantity<?> quantity, String fieldName);

/**
* Converts a given quantity to String by extracting the value and applying the toString method to
* it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public class InputEntityProcessor extends EntityProcessor<InputEntity> {
PvInput.class,
StorageInput.class,
WecInput.class,
EmInput.class,
/* -- ThermalUnitInput */
ThermalHouseInput.class,
CylindricalStorageInput.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -390,4 +390,35 @@ Set<HpInput> getHeatPumps(
Set<OperatorInput> operators,
Set<HpTypeInput> types,
Set<ThermalBusInput> thermalBuses);

/**
* Returns a unique set of {@link EmInput} instances.
*
* <p>This set has to be unique in the sense of object uniqueness but also in the sense of {@link
* java.util.UUID} uniqueness of the provided {@link EmInput} which has to be checked manually, as
* {@link EmInput#equals(Object)} is NOT restricted on the uuid of {@link EmInput}.
*
* @return a set of object and uuid unique {@link EmInput} entities
*/
Set<EmInput> getEmSystems();

/**
* This set has to be unique in the sense of object uniqueness but also in the sense of {@link
* java.util.UUID} uniqueness of the provided {@link EmInput} which has to be checked manually, as
* {@link EmInput#equals(Object)} is NOT restricted on the uuid of {@link EmInput}.
*
* <p>In contrast to {@link #getHeatPumps()} this interface provides the ability to pass in an
* already existing set of {@link NodeInput} and {@link OperatorInput} entities, the {@link
* EmInput} instances depend on. Doing so, already loaded nodes can be recycled to improve
* performance and prevent unnecessary loading operations.
*
* <p>If something fails during the creation process it's up to the concrete implementation of an
* empty set or a set with all entities that has been able to be build is returned.
*
* @param operators a set of object and uuid unique {@link OperatorInput} that should be used for
* the returning instances
* @param nodes a set of object and uuid unique {@link NodeInput} entities
* @return a set of object and uuid unique {@link EmInput} entities
*/
Set<EmInput> getEmSystems(Set<NodeInput> nodes, Set<OperatorInput> operators);
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public class CsvSystemParticipantSource extends CsvDataSource implements SystemP
private final StorageInputFactory storageInputFactory;
private final WecInputFactory wecInputFactory;
private final EvcsInputFactory evcsInputFactory;
private final EmInputFactory emInputFactory;

public CsvSystemParticipantSource(
String csvSep,
Expand All @@ -87,6 +88,7 @@ public CsvSystemParticipantSource(
this.storageInputFactory = new StorageInputFactory();
this.wecInputFactory = new WecInputFactory();
this.evcsInputFactory = new EvcsInputFactory();
this.emInputFactory = new EmInputFactory();
}

/** {@inheritDoc} */
Expand Down Expand Up @@ -168,6 +170,11 @@ public Optional<SystemParticipants> getSystemParticipants() {
.filter(isPresentCollectIfNot(HpInput.class, nonBuildEntities))
.map(Optional::get)
.collect(Collectors.toSet());
Set<EmInput> emInputs =
nodeAssetEntityStream(EmInput.class, emInputFactory, nodes, operators)
.filter(isPresentCollectIfNot(EmInput.class, nonBuildEntities))
.map(Optional::get)
.collect(Collectors.toSet());

// if we found invalid elements return an empty optional and log the problems
if (!nonBuildEntities.isEmpty()) {
Expand All @@ -187,7 +194,8 @@ public Optional<SystemParticipants> getSystemParticipants() {
loads,
pvInputs,
storages,
wecInputs));
wecInputs,
emInputs));
}

/** {@inheritDoc} */
Expand Down Expand Up @@ -637,7 +645,7 @@ private Optional<HpInputEntityData> buildHpEntityData(

// if the requested entity is not present we return an empty element and
// log a warning
if (!hpInputEntityDataOpt.isPresent()) {
if (hpInputEntityDataOpt.isEmpty()) {
logSkippingWarning(
typedEntityData.getTargetClass().getSimpleName(),
saveMapGet(fieldsToAttributes, "uuid", FIELDS_TO_VALUES_MAP),
Expand Down Expand Up @@ -732,4 +740,29 @@ private Optional<ChpInputEntityData> buildChpEntityData(
thermalBus.get(),
thermalStorage.get()));
}

@Override
public Set<EmInput> getEmSystems() {
Set<OperatorInput> operators = typeSource.getOperators();
return getEmSystems(rawGridSource.getNodes(operators), operators);
}

/**
* {@inheritDoc}
*
* <p>If the set of {@link NodeInput} entities is not exhaustive for all available {@link
* LoadInput} entities (e.g. a {@link NodeInput} entity is missing) or if an error during the
* building process occurs, the entity that misses something will be skipped (which can be seen as
* a filtering functionality), but all entities that are able to be built will be returned anyway
* and the elements that couldn't have been built are logged.
*
* <p>If the set with {@link OperatorInput} is not exhaustive, the corresponding operator is set
* to {@link OperatorInput#NO_OPERATOR_ASSIGNED}
*/
@Override
public Set<EmInput> getEmSystems(Set<NodeInput> nodes, Set<OperatorInput> operators) {
return nodeAssetEntityStream(EmInput.class, emInputFactory, nodes, operators)
.flatMap(Optional::stream)
.collect(Collectors.toSet());
}
}
35 changes: 35 additions & 0 deletions src/main/java/edu/ie3/datamodel/models/ControlStrategy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* © 2022. TU Dortmund University,
* Institute of Energy Systems, Energy Efficiency and Energy Economics,
* Research group Distribution grid planning and operation
*/
package edu.ie3.datamodel.models;

import edu.ie3.datamodel.exceptions.ParsingException;
import java.io.Serializable;
import java.util.Arrays;

public interface ControlStrategy extends Serializable {
String getKey();

static ControlStrategy parse(String key) throws ParsingException {
if (key == null || key.isEmpty())
return ControlStrategy.DefaultControlStrategies.NO_CONTROL_STRATEGY;

String filterKey = key.toLowerCase().replace("-", "_");
return Arrays.stream(EmControlStrategy.values())
.filter(profile -> profile.getKey().equals(filterKey))
.findFirst()
.orElseThrow(
() -> new ParsingException("Cannot parse \"" + key + "\" to a valid control strategy"));
}

enum DefaultControlStrategies implements ControlStrategy {
NO_CONTROL_STRATEGY;

@Override
public String getKey() {
return "No control strategy assigned";
}
}
}
45 changes: 45 additions & 0 deletions src/main/java/edu/ie3/datamodel/models/EmControlStrategy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* © 2022. TU Dortmund University,
* Institute of Energy Systems, Energy Efficiency and Energy Economics,
* Research group Distribution grid planning and operation
*/
package edu.ie3.datamodel.models;

import java.util.Arrays;
import java.util.Locale;
import java.util.stream.Collectors;

public enum EmControlStrategy implements ControlStrategy {
SELF_OPTIMIZATION("self_optimization");

private final String key;

EmControlStrategy(String key) {
this.key = key.toLowerCase(Locale.ROOT);
}

public static EmControlStrategy get(String key) {
return Arrays.stream(EmControlStrategy.values())
.filter(controlStrategy -> controlStrategy.key.equalsIgnoreCase(key))
.findFirst()
.orElseThrow(
() ->
new IllegalArgumentException(
"No predefined energy management control strategy '"
+ key
+ "' found. Please provide one of the following keys: "
+ Arrays.stream(EmControlStrategy.values())
.map(EmControlStrategy::getKey)
.collect(Collectors.joining(", "))));
}

@Override
public String getKey() {
return key;
}

@Override
public String toString() {
return "EmControlStrategy{" + "key='" + key + '\'' + '}';
}
}
Loading