Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Enhance `TimeSeriesSource` with method to retrieve all time keys after a given key [#543](https://github.com/ie3-institute/PowerSystemDataModel/issues/543)
- Enhance `WeatherSource` with method to retrieve all time keys after a given key [#572](https://github.com/ie3-institute/PowerSystemDataModel/issues/572)
- Adding timeseries for voltage values [#1128](https://github.com/ie3-institute/PowerSystemDataModel/issues/1128)

### Fixed

Expand Down
3 changes: 3 additions & 0 deletions docs/readthedocs/io/csvfiles.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ The following keys are supported until now:
* - pqh
- Active, reactive and heat power
Permissible head line: ``time,p,q,h``
* - v
- Voltage mangnitude in pu and angle in °
Permissible head line: ``time,vMag,vAng``
* - weather
- Weather information
Permissible head line: ``time,coordinate,direct_irradiation,diffuse_irradiation,temperature,wind_velocity,wind_direction``
Expand Down
11 changes: 8 additions & 3 deletions docs/readthedocs/models/input/additionaldata/timeseries.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ The following different values are available:
- Electrical active and reactive power

* - `HeatAndPValue`
- Combination of thermal power (e.g. in kW) <br> and electrical active power (e.g. in kW)
- | Combination of thermal power (e.g. in kW)
| and electrical active power (e.g. in kW)

* - `HeatAndSValue`
- Combination of thermal power (e.g. in kW) <br> and electrical active and reactive power (e.g. in kW and kVAr)
- | Combination of thermal power (e.g. in kW)
| and electrical active and reactive power (e.g. in kW and kVAr)

* - `EnergyPriceValue`
- Wholesale market price (e.g. in € / MWh)
Expand All @@ -48,7 +50,10 @@ The following different values are available:

* - `WindValue`
- Combination of wind direction and wind velocity


* - `VoltageValue`
- Combination of voltage magnitude in pu and angle in °

* - `WeatherValue`
- Combination of irradiance, temperature and wind information

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
package edu.ie3.datamodel.exceptions;

/**
* Is thrown, when an something went wrong during entity field mapping creation in a {@link
* Is thrown, when something went wrong during entity field mapping creation in a {@link
* edu.ie3.datamodel.io.processor.EntityProcessor}
*/
public class EntityProcessorException extends Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
import edu.ie3.datamodel.models.value.*;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;

public class TimeBasedSimpleValueFactory<V extends Value>
extends TimeBasedValueFactory<SimpleTimeBasedValueData<V>, V> {
Expand All @@ -25,6 +28,10 @@ public class TimeBasedSimpleValueFactory<V extends Value>
private static final String REACTIVE_POWER = "q";
private static final String HEAT_DEMAND = "heatDemand";

/* voltage */
private static final String VMAG = "vMag";
private static final String VANG = "VAng";

public TimeBasedSimpleValueFactory(Class<? extends V> valueClasses) {
super(valueClasses);
}
Expand Down Expand Up @@ -64,6 +71,17 @@ protected TimeBasedValue<V> buildModel(SimpleTimeBasedValueData<V> data) {
data.getQuantity(REACTIVE_POWER, REACTIVE_POWER_IN));
} else if (PValue.class.isAssignableFrom(data.getTargetClass())) {
value = (V) new PValue(data.getQuantity(ACTIVE_POWER, ACTIVE_POWER_IN));
} else if (VoltageValue.class.isAssignableFrom(data.getTargetClass())) {

try {
value =
(V)
new VoltageValue(
data.getQuantity(VMAG, VOLTAGE_MAGNITUDE),
data.getQuantity(VANG, VOLTAGE_ANGLE));
} catch (FactoryException e) {
value = (V) new VoltageValue(data.getQuantity(VMAG, VOLTAGE_MAGNITUDE));
}
} else {
throw new FactoryException(
"The given factory cannot handle target class '" + data.getTargetClass() + "'.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,13 @@
import edu.ie3.datamodel.io.factory.EntityData;
import edu.ie3.datamodel.io.factory.EntityFactory;
import edu.ie3.datamodel.io.source.TimeSeriesMappingSource;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class TimeSeriesMappingFactory
extends EntityFactory<TimeSeriesMappingSource.MappingEntry, EntityData> {
private static final String PARTICIPANT = "participant";
private static final String ENTITY = "entity";
private static final String TIME_SERIES = "timeSeries";

public TimeSeriesMappingFactory() {
Expand All @@ -26,14 +23,13 @@ public TimeSeriesMappingFactory() {

@Override
protected List<Set<String>> getFields(Class<?> entityClass) {
return Collections.singletonList(
Stream.of(PARTICIPANT, TIME_SERIES).collect(Collectors.toSet()));
return List.of(newSet(ENTITY, TIME_SERIES));
}

@Override
protected TimeSeriesMappingSource.MappingEntry buildModel(EntityData data) {
UUID participant = data.getUUID(PARTICIPANT);
UUID entity = data.getUUID(ENTITY);
UUID timeSeries = data.getUUID(TIME_SERIES);
return new TimeSeriesMappingSource.MappingEntry(participant, timeSeries);
return new TimeSeriesMappingSource.MappingEntry(entity, timeSeries);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ public enum ColumnScheme {
HEAT_DEMAND("h", HeatDemandValue.class),
ACTIVE_POWER_AND_HEAT_DEMAND("ph", HeatAndPValue.class),
APPARENT_POWER_AND_HEAT_DEMAND("pqh", HeatAndSValue.class),
WEATHER("weather", WeatherValue.class);
WEATHER("weather", WeatherValue.class),
VOLTAGE("v", VoltageValue.class);

private final String scheme;
private final Class<? extends Value> valueClass;
Expand Down Expand Up @@ -57,6 +58,7 @@ public static <V extends Value> Optional<ColumnScheme> parse(Class<V> valueClass
if (PValue.class.isAssignableFrom(valueClass)) return Optional.of(ACTIVE_POWER);
if (HeatDemandValue.class.isAssignableFrom(valueClass)) return Optional.of(HEAT_DEMAND);
if (WeatherValue.class.isAssignableFrom(valueClass)) return Optional.of(WEATHER);
if (VoltageValue.class.isAssignableFrom(valueClass)) return Optional.of(VOLTAGE);
return Optional.empty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ protected EntityProcessor(Class<? extends T> registeredClass) throws EntityProce
* during processing
*/
public LinkedHashMap<String, String> handleEntity(T entity) throws EntityProcessorException {
if (!registeredClass.equals(entity.getClass()))
if (!registeredClass.isAssignableFrom(entity.getClass()))
throw new EntityProcessorException(
"Cannot process "
+ entity.getClass().getSimpleName()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import edu.ie3.datamodel.io.factory.EntityData;
import edu.ie3.datamodel.io.factory.timeseries.TimeSeriesMappingFactory;
import edu.ie3.datamodel.models.input.InputEntity;
import edu.ie3.datamodel.models.input.system.SystemParticipantInput;
import edu.ie3.datamodel.models.timeseries.TimeSeries;
import edu.ie3.datamodel.utils.Try;
import edu.ie3.datamodel.utils.Try.*;
Expand Down Expand Up @@ -47,7 +46,7 @@ public Map<UUID, UUID> getMapping() throws SourceException {
.filter(Try::isSuccess)
.map(t -> (Success<MappingEntry, FactoryException>) t)
.map(Success::get)
.collect(Collectors.toMap(MappingEntry::participant, MappingEntry::timeSeries));
.collect(Collectors.toMap(MappingEntry::getEntity, MappingEntry::getTimeSeries));
}

/**
Expand Down Expand Up @@ -80,12 +79,19 @@ private Try<MappingEntry, FactoryException> createMappingEntry(

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

/** Class to represent one entry within the participant to time series mapping */
public record MappingEntry(UUID participant, UUID timeSeries) implements InputEntity {
/** Class to represent one entry within the entity to time series mapping */
public static class MappingEntry implements InputEntity {
private final UUID entity;
private final UUID timeSeries;

/** Returns the {@link UUID} of the {@link SystemParticipantInput}. */
public UUID getParticipant() {
return participant;
public MappingEntry(UUID entity, UUID timeSeries) {
this.entity = entity;
this.timeSeries = timeSeries;
}

/** Returns the {@link UUID} of the {@link edu.ie3.datamodel.models.UniqueEntity}. */
public UUID getEntity() {
return entity;
}

/** Returns the {@link UUID} of the {@link TimeSeries}. */
Expand All @@ -97,17 +103,17 @@ public UUID getTimeSeries() {
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof MappingEntry that)) return false;
return participant.equals(that.participant) && timeSeries.equals(that.timeSeries);
return entity.equals(that.entity) && timeSeries.equals(that.timeSeries);
}

@Override
public int hashCode() {
return Objects.hash(participant, timeSeries);
return Objects.hash(entity, timeSeries);
}

@Override
public String toString() {
return "MappingEntry{" + "participant=" + participant + ", timeSeries=" + timeSeries + '}';
return "MappingEntry{" + "entity=" + entity + ", timeSeries=" + timeSeries + '}';
}
}
}
89 changes: 89 additions & 0 deletions src/main/java/edu/ie3/datamodel/models/value/VoltageValue.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* © 2024. TU Dortmund University,
* Institute of Energy Systems, Energy Efficiency and Energy Economics,
* Research group Distribution grid planning and operation
*/
package edu.ie3.datamodel.models.value;

import static edu.ie3.datamodel.models.StandardUnits.VOLTAGE_ANGLE;
import static edu.ie3.util.quantities.PowerSystemUnits.DEGREE_GEOM;
import static edu.ie3.util.quantities.PowerSystemUnits.PU;
import static java.lang.Math.*;

import java.util.Objects;
import java.util.Optional;
import javax.measure.quantity.Angle;
import javax.measure.quantity.Dimensionless;
import tech.units.indriya.ComparableQuantity;
import tech.units.indriya.quantity.Quantities;

/** Describes a voltage value as a pair of magnitude and angle */
public class VoltageValue implements Value {

/** Magnitude of the voltage in p.u. */
private final ComparableQuantity<Dimensionless> magnitude;
/** Angle of the voltage in degree */
private final ComparableQuantity<Angle> angle;

/**
* @param magnitude of the voltage in p.u.
* @param angle of the voltage in degree
*/
public VoltageValue(
ComparableQuantity<Dimensionless> magnitude, ComparableQuantity<Angle> angle) {
this.magnitude = magnitude;
this.angle = angle;
}

/**
* This constructor will set the angle to 0°
*
* @param magnitude of the voltage in p.u.
*/
public VoltageValue(ComparableQuantity<Dimensionless> magnitude) {
this.magnitude = magnitude;
this.angle = Quantities.getQuantity(0.0, VOLTAGE_ANGLE);
}

public Optional<ComparableQuantity<Dimensionless>> getMagnitude() {
return Optional.ofNullable(magnitude);
}

public Optional<ComparableQuantity<Angle>> getAngle() {
return Optional.ofNullable(angle);
}

public Optional<ComparableQuantity<Dimensionless>> getRealPart() {
double mag = magnitude.to(PU).getValue().doubleValue();
double ang = angle.to(DEGREE_GEOM).getValue().doubleValue();

double eInPu = mag * cos(toRadians(ang));
return Optional.of(Quantities.getQuantity(eInPu, PU));
}

public Optional<ComparableQuantity<Dimensionless>> getImagPart() {
double mag = magnitude.to(PU).getValue().doubleValue();
double ang = angle.to(DEGREE_GEOM).getValue().doubleValue();

double eInPu = mag * sin(toRadians(ang));
return Optional.of(Quantities.getQuantity(eInPu, PU));
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
VoltageValue that = (VoltageValue) o;
return Objects.equals(magnitude, that.magnitude) && Objects.equals(angle, that.angle);
}

@Override
public int hashCode() {
return Objects.hash(magnitude, angle);
}

@Override
public String toString() {
return "VoltageValue{" + "magnitude=" + magnitude + ", angle=" + angle + '}';
}
}
3 changes: 2 additions & 1 deletion src/main/java/edu/ie3/datamodel/utils/TimeSeriesUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ public class TimeSeriesUtils {
ENERGY_PRICE,
APPARENT_POWER_AND_HEAT_DEMAND,
ACTIVE_POWER_AND_HEAT_DEMAND,
HEAT_DEMAND);
HEAT_DEMAND,
VOLTAGE);

/** Private Constructor as this class is not meant to be instantiated */
private TimeSeriesUtils() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class UniquenessValidationUtils extends ValidationUtils {
protected static final FieldSetSupplier<CongestionResult> congestionResultFieldSupplier =
entity -> Set.of(entity.getTime(), entity.getSubgrid());
protected static final FieldSetSupplier<MappingEntry> mappingFieldSupplier =
entity -> Set.of(entity.participant());
entity -> Set.of(entity.getEntity());
protected static final FieldSetSupplier<IdCoordinateInput> idCoordinateSupplier =
entity -> Set.of(entity.id(), entity.point());
protected static final FieldSetSupplier<TimeBasedValue<WeatherValue>> weatherValueFieldSupplier =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,7 @@ class InputEntityProcessorTest extends Specification {
def validResult = new TimeSeriesMappingSource.MappingEntry(UUID.fromString("7eb7b296-f4c4-4020-acf3-e865453b5dbd"), UUID.fromString("bc581c6c-3044-48a1-aea1-5b2cb1370356"))

Map expectedResults = [
"participant": "7eb7b296-f4c4-4020-acf3-e865453b5dbd",
"entity": "7eb7b296-f4c4-4020-acf3-e865453b5dbd",
"timeSeries": "bc581c6c-3044-48a1-aea1-5b2cb1370356"
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ class CsvTimeSeriesMappingSourceIT extends Specification implements CsvTestDataM
def expectedMapping = [
(UUID.fromString("b86e95b0-e579-4a80-a534-37c7a470a409")) : UUID.fromString("9185b8c1-86ba-4a16-8dea-5ac898e8caa5"),
(UUID.fromString("c7ebcc6c-55fc-479b-aa6b-6fa82ccac6b8")) : UUID.fromString("3fbfaa97-cff4-46d4-95ba-a95665e87c26"),
(UUID.fromString("90a96daa-012b-4fea-82dc-24ba7a7ab81c")) : UUID.fromString("3fbfaa97-cff4-46d4-95ba-a95665e87c26")
(UUID.fromString("90a96daa-012b-4fea-82dc-24ba7a7ab81c")) : UUID.fromString("3fbfaa97-cff4-46d4-95ba-a95665e87c26"),
(UUID.fromString("7bed7760-c220-4fe6-88b3-47b246f6ef3f")) : UUID.fromString("eeccbe3c-a47e-448e-8eca-1f369d3c24e6")
]

when:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,15 @@ class CsvTimeSeriesMetaInformationSourceIT extends Specification implements CsvT
new CsvIndividualTimeSeriesMetaInformation(UUID.fromString("9185b8c1-86ba-4a16-8dea-5ac898e8caa5"), ColumnScheme.ACTIVE_POWER, Path.of('its_p_9185b8c1-86ba-4a16-8dea-5ac898e8caa5')),
new CsvIndividualTimeSeriesMetaInformation(UUID.fromString("3fbfaa97-cff4-46d4-95ba-a95665e87c26"), ColumnScheme.APPARENT_POWER, Path.of('its_pq_3fbfaa97-cff4-46d4-95ba-a95665e87c26')),
new CsvIndividualTimeSeriesMetaInformation(UUID.fromString("46be1e57-e4ed-4ef7-95f1-b2b321cb2047"), ColumnScheme.APPARENT_POWER_AND_HEAT_DEMAND, Path.of('its_pqh_46be1e57-e4ed-4ef7-95f1-b2b321cb2047')),
new CsvIndividualTimeSeriesMetaInformation(UUID.fromString("1061af70-1c03-46e1-b960-940b956c429f"), ColumnScheme.APPARENT_POWER, Path.of('its_pq_1061af70-1c03-46e1-b960-940b956c429f'))
new CsvIndividualTimeSeriesMetaInformation(UUID.fromString("1061af70-1c03-46e1-b960-940b956c429f"), ColumnScheme.APPARENT_POWER, Path.of('its_pq_1061af70-1c03-46e1-b960-940b956c429f')),
new CsvIndividualTimeSeriesMetaInformation(UUID.fromString("eeccbe3c-a47e-448e-8eca-1f369d3c24e6"), ColumnScheme.VOLTAGE, Path.of("its_v_eeccbe3c-a47e-448e-8eca-1f369d3c24e6"))
)

when:
def actual = source.timeSeriesMetaInformation

then:
actual.size() == 7
actual.size() == 8
actual.every {
it.key == it.value.uuid &&
expectedTimeSeries.contains(it.value)
Expand All @@ -62,6 +63,7 @@ class CsvTimeSeriesMetaInformationSourceIT extends Specification implements CsvT
"3fbfaa97-cff4-46d4-95ba-a95665e87c26" || "pq"
"46be1e57-e4ed-4ef7-95f1-b2b321cb2047" || "pqh"
"1061af70-1c03-46e1-b960-940b956c429f" || "pq"
"eeccbe3c-a47e-448e-8eca-1f369d3c24e6" || "v"
}

def "The CSV time series meta information source returns an empty optional for an unknown time series UUID"() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,6 @@ class CsvTimeSeriesSourceTest extends Specification implements CsvTestDataMeta {
UUID.fromString("76c9d846-797c-4f07-b7ec-2245f679f5c7") | ColumnScheme.ACTIVE_POWER_AND_HEAT_DEMAND | Path.of("its_ph_76c9d846-797c-4f07-b7ec-2245f679f5c7") || 2 | HeatAndPValue
UUID.fromString("3fbfaa97-cff4-46d4-95ba-a95665e87c26") | ColumnScheme.APPARENT_POWER | Path.of("its_pq_3fbfaa97-cff4-46d4-95ba-a95665e87c26") || 2 | SValue
UUID.fromString("46be1e57-e4ed-4ef7-95f1-b2b321cb2047") | ColumnScheme.APPARENT_POWER_AND_HEAT_DEMAND | Path.of("its_pqh_46be1e57-e4ed-4ef7-95f1-b2b321cb2047") || 2 | HeatAndSValue
UUID.fromString("eeccbe3c-a47e-448e-8eca-1f369d3c24e6") | ColumnScheme.VOLTAGE | Path.of("its_v_eeccbe3c-a47e-448e-8eca-1f369d3c24e6") || 2 | VoltageValue
}
}
Loading