Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding a timeseries for voltage values. #1130

Open
wants to merge 6 commits into
base: dev
Choose a base branch
from
Open
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
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