Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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 @@ -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
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 @@ -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
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* © 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.util.quantities.PowerSystemUnits.*

import spock.lang.Specification
import tech.units.indriya.quantity.Quantities


class VoltageValueTest extends Specification {

def "A VoltageValue should return the real part correctly"() {
when:
def actual = value.realPart

then:
actual.isPresent()
actual.get() =~ expected

where:
value | expected
new VoltageValue(Quantities.getQuantity(1, PU), Quantities.getQuantity(0, DEGREE_GEOM)) | Quantities.getQuantity(1, PU)
new VoltageValue(Quantities.getQuantity(1, PU), Quantities.getQuantity(45, DEGREE_GEOM)) | Quantities.getQuantity(0.7071067811865476, PU)
new VoltageValue(Quantities.getQuantity(1, PU), Quantities.getQuantity(90, DEGREE_GEOM)) | Quantities.getQuantity(6.123233995736766E-17, PU) // ~0pu
}


def "A VoltageValue should return the imaginary part correctly"() {
when:
def actual = value.imagPart

then:
actual.isPresent()
actual.get() =~ expected

where:
value | expected
new VoltageValue(Quantities.getQuantity(1, PU), Quantities.getQuantity(0, DEGREE_GEOM)) | Quantities.getQuantity(0, PU)
new VoltageValue(Quantities.getQuantity(1, PU), Quantities.getQuantity(45, DEGREE_GEOM)) | Quantities.getQuantity(0.7071067811865475, PU)
new VoltageValue(Quantities.getQuantity(1, PU), Quantities.getQuantity(90, DEGREE_GEOM)) | Quantities.getQuantity(1, PU)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"time";"vMag";"vAng"
2020-01-01T00:00:00Z;1.0;45.0
2020-01-01T00:15:00Z;0.9;