Skip to content

add ground temperature 1m as option to weather data #1351

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

Draft
wants to merge 15 commits into
base: dev
Choose a base branch
from
Draft
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 @@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
- Extend Validation to EnergyManagement Systems. [#1356](https://github.com/ie3-institute/PowerSystemDataModel/issues/1356)
- Add ground temperature (1m) as option to weather data. [#1343](https://github.com/ie3-institute/PowerSystemDataModel/issues/1343)

### Fixed
- Fixed handling of `CongestionResult.InputModelType` in `EntityProcessor` [#1325](https://github.com/ie3-institute/PowerSystemDataModel/issues/1325)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,29 @@
import edu.ie3.util.quantities.interfaces.Irradiance;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import javax.measure.quantity.Angle;
import javax.measure.quantity.Speed;
import javax.measure.quantity.Temperature;
import org.locationtech.jts.geom.Point;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tech.units.indriya.ComparableQuantity;

/**
* Factory implementation of {@link TimeBasedWeatherValueFactory}, that is able to handle field to
* value mapping in the typical PowerSystemDataModel (PSDM) column scheme
*/
public class CosmoTimeBasedWeatherValueFactory extends TimeBasedWeatherValueFactory {

private static final Logger logger =
LoggerFactory.getLogger(CosmoTimeBasedWeatherValueFactory.class);
Comment on lines +32 to +33
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does logger log in this class?


private static final String DIFFUSE_IRRADIANCE = "diffuseIrradiance";
private static final String DIRECT_IRRADIANCE = "directIrradiance";
private static final String TEMPERATURE = "temperature";
private static final String GROUND_TEMPERATURE = "groundTemperature";
private static final String WIND_DIRECTION = "windDirection";
private static final String WIND_VELOCITY = "windVelocity";

Expand All @@ -55,7 +61,10 @@ protected List<Set<String>> getFields(Class<?> entityClass) {
TEMPERATURE,
WIND_DIRECTION,
WIND_VELOCITY);
return Collections.singletonList(minConstructorParams);

Set<String> withGroundTemp = expandSet(minConstructorParams, GROUND_TEMPERATURE);

return List.of(minConstructorParams, withGroundTemp);
Comment on lines +65 to +67
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What will happen if there are no ground temperatures and one calls this method?

}

@Override
Expand All @@ -72,14 +81,24 @@ protected TimeBasedValue<WeatherValue> buildModel(TimeBasedWeatherValueData data
data.getQuantity(WIND_DIRECTION, StandardUnits.WIND_DIRECTION);
ComparableQuantity<Speed> windVelocity =
data.getQuantity(WIND_VELOCITY, StandardUnits.WIND_VELOCITY);

ComparableQuantity<Temperature> groundTemperature = null;
try {
groundTemperature = data.getQuantity(GROUND_TEMPERATURE, StandardUnits.TEMPERATURE);
} catch (IllegalArgumentException ignored) {

}
Comment on lines +85 to +90
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion this is a bit counterintuitive. The advantage of try is that one get's a result of the try. But we ignore this.

But first, please change to Optional<ComparableQuantity<Tempearture>> . And then it should be possible to try to get the quantity from data, as for the others, and wrap it into some Optional.ofNullable, or similar.


WeatherValue weatherValue =
new WeatherValue(
coordinate,
directIrradiance,
diffuseIrradiance,
temperature,
windDirection,
windVelocity);
windVelocity,
groundTemperature);

return new TimeBasedValue<>(time, weatherValue);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import javax.measure.quantity.Speed;
import javax.measure.quantity.Temperature;
import org.locationtech.jts.geom.Point;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tech.units.indriya.ComparableQuantity;
import tech.units.indriya.quantity.Quantities;
import tech.units.indriya.unit.Units;
Expand All @@ -27,10 +29,15 @@
* Weather Service's ICON-EU model
*/
public class IconTimeBasedWeatherValueFactory extends TimeBasedWeatherValueFactory {

private static final Logger logger =
LoggerFactory.getLogger(IconTimeBasedWeatherValueFactory.class);
Comment on lines +33 to +34
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for logger here


/* Redefine the column names to meet the icon specifications */
private static final String DIFFUSE_IRRADIANCE = "aswdifdS";
private static final String DIRECT_IRRADIANCE = "aswdirS";
private static final String TEMPERATURE = "t2m";
private static final String GROUND_TEMPERATURE = "tG";
private static final String WIND_VELOCITY_U = "u131m";
private static final String WIND_VELOCITY_V = "v131m";

Expand All @@ -47,6 +54,7 @@ protected List<Set<String>> getFields(Class<?> entityClass) {
Set<String> minParameters =
newSet(
DIFFUSE_IRRADIANCE, DIRECT_IRRADIANCE, TEMPERATURE, WIND_VELOCITY_U, WIND_VELOCITY_V);

Set<String> allParameters =
expandSet(
minParameters,
Expand Down Expand Up @@ -88,30 +96,26 @@ protected TimeBasedValue<WeatherValue> buildModel(TimeBasedWeatherValueData data
data.getQuantity(TEMPERATURE, Units.KELVIN).to(StandardUnits.TEMPERATURE);
ComparableQuantity<Angle> windDirection = getWindDirection(data);
ComparableQuantity<Speed> windVelocity = getWindVelocity(data);

ComparableQuantity<Temperature> groundTemperature = null;
try {
groundTemperature =
data.getQuantity(GROUND_TEMPERATURE, Units.KELVIN).to(StandardUnits.TEMPERATURE);
} catch (IllegalArgumentException ignored) {
}

Comment on lines +100 to +106
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as for cosmo

WeatherValue weatherValue =
new WeatherValue(
coordinate,
directIrradiance,
diffuseIrradiance,
temperature,
windDirection,
windVelocity);
windVelocity,
groundTemperature);
return new TimeBasedValue<>(time, weatherValue);
}

/**
* Determines the wind direction. In ICON the wind velocity is given in three dimensional
* Cartesian coordinates. Here, the upward component is neglected. 0° or 0 rad are defined to
* point northwards. The angle increases clockwise. Please note, that the wind direction is the
* direction, the wind <b>comes</b> from and not goes to. We choose to use the wind velocity
* calculations at 131 m above ground, as this is a height that pretty good matches the common hub
* height of today's onshore wind generators, that are commonly connected to the voltage levels of
* interest.
*
* @param data Collective information to convert
* @return A {@link ComparableQuantity} of type {@link Speed}, that is converted to {@link
* StandardUnits#WIND_VELOCITY}
*/
private static ComparableQuantity<Angle> getWindDirection(TimeBasedWeatherValueData data) {
/* Get the three dimensional parts of the wind velocity vector in cartesian coordinates */
double u =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* © 2025. 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 edu.ie3.datamodel.models.StandardUnits;
import java.util.Objects;
import java.util.Optional;
import javax.measure.quantity.Temperature;
import tech.units.indriya.ComparableQuantity;

/** Describes a ground temperature value. */
public class GroundTemperatureValue implements Value {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can reuse many parts from TemperatureValue if you make GroundTemperatureValue extending from it.


/** Ground temperature (typically in K) */
private final ComparableQuantity<Temperature> temperature;

/**
* @param temperature Ground temperature (typically in K)
*/
public GroundTemperatureValue(ComparableQuantity<Temperature> temperature) {
this.temperature = temperature == null ? null : temperature.to(StandardUnits.TEMPERATURE);
}

public Optional<ComparableQuantity<Temperature>> getTemperature() {
return Optional.ofNullable(temperature);
}

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

@Override
public int hashCode() {
return Objects.hash(temperature);
}

@Override
public String toString() {
return "GroundTemperatureValue{" + "temperature=" + temperature + '}';
}
}
38 changes: 29 additions & 9 deletions src/main/java/edu/ie3/datamodel/models/value/WeatherValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@

import edu.ie3.util.quantities.interfaces.Irradiance;
import java.util.Objects;
import java.util.Optional;
import javax.measure.quantity.Angle;
import javax.measure.quantity.Speed;
import javax.measure.quantity.Temperature;
import org.locationtech.jts.geom.Point;
import tech.units.indriya.ComparableQuantity;

/** Describes weather as a combination of solar irradiance, temperature and wind values */
/**
* Describes weather as a combination of solar irradiance, temperature, wind, and ground temperature
* values.
*/
public class WeatherValue implements Value {
/** The coordinate of this weather value set */
private final Point coordinate;
Expand All @@ -27,21 +31,27 @@ public class WeatherValue implements Value {
/** Wind values for this coordinate */
private final WindValue wind;

/** GroundTemeperature value */
private final GroundTemperatureValue groundTemperature;

/**
* @param coordinate of this weather value set
* @param solarIrradiance values for this coordinate
* @param temperature values for this coordinate
* @param wind values for this coordinate
* @param groundTemperature values for this coordinate (can be null)
*/
public WeatherValue(
Point coordinate,
SolarIrradianceValue solarIrradiance,
TemperatureValue temperature,
WindValue wind) {
WindValue wind,
GroundTemperatureValue groundTemperature) {
this.coordinate = coordinate;
this.solarIrradiance = solarIrradiance;
this.temperature = temperature;
this.wind = wind;
this.groundTemperature = groundTemperature;
}

/**
Expand All @@ -52,19 +62,22 @@ public WeatherValue(
* @param direction Direction, the wind comes from as an angle from north increasing clockwise
* (typically in rad)
* @param velocity Wind velocity for this coordinate (typically in m/s)
* @param groundTemperature Ground temperature (typically in K)
*/
public WeatherValue(
Point coordinate,
ComparableQuantity<Irradiance> directSolarIrradiance,
ComparableQuantity<Irradiance> diffuseSolarIrradiance,
ComparableQuantity<Temperature> temperature,
ComparableQuantity<Angle> direction,
ComparableQuantity<Speed> velocity) {
ComparableQuantity<Speed> velocity,
ComparableQuantity<Temperature> groundTemperature) {
this(
coordinate,
new SolarIrradianceValue(directSolarIrradiance, diffuseSolarIrradiance),
new TemperatureValue(temperature),
new WindValue(direction, velocity));
new WindValue(direction, velocity),
new GroundTemperatureValue(groundTemperature));
}

public Point getCoordinate() {
Expand All @@ -83,20 +96,25 @@ public WindValue getWind() {
return wind;
}

public Optional<GroundTemperatureValue> getGroundTemperature() {
return Optional.ofNullable(groundTemperature);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
WeatherValue that = (WeatherValue) o;
return coordinate.equals(that.coordinate)
&& solarIrradiance.equals(that.solarIrradiance)
&& temperature.equals(that.temperature)
&& wind.equals(that.wind);
return Objects.equals(coordinate, that.coordinate)
&& Objects.equals(solarIrradiance, that.solarIrradiance)
&& Objects.equals(temperature, that.temperature)
&& Objects.equals(wind, that.wind)
&& Objects.equals(groundTemperature, that.groundTemperature);
}

@Override
public int hashCode() {
return Objects.hash(coordinate, solarIrradiance, temperature, wind);
return Objects.hash(coordinate, solarIrradiance, temperature, wind, groundTemperature);
}

@Override
Expand All @@ -110,6 +128,8 @@ public String toString() {
+ temperature
+ ", wind="
+ wind
+ ", groundTemperature="
+ groundTemperature
+ '}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@ import edu.ie3.util.quantities.QuantityUtil

trait WeatherSourceTestHelper {

static boolean equalsIgnoreUUID(IndividualTimeSeries<WeatherValue> ts1,
IndividualTimeSeries<WeatherValue> ts2) {
static boolean equalsIgnoreUUID(IndividualTimeSeries<WeatherValue> ts1, IndividualTimeSeries<WeatherValue> ts2) {
return equalsIgnoreUUID(ts1.entries, ts2.entries)
}

static boolean equalsIgnoreUUID(Collection<TimeBasedValue<WeatherValue>> c1,
Collection<TimeBasedValue<WeatherValue>> c2) {
static boolean equalsIgnoreUUID(Collection<TimeBasedValue<WeatherValue>> c1, Collection<TimeBasedValue<WeatherValue>> c2) {
if (c1 == null || c2 == null) return (c1 == null && c2 == null)
if (c1.size() != c2.size()) return false
for (TimeBasedValue<WeatherValue> value1 : c1) {
Expand All @@ -39,4 +37,4 @@ trait WeatherSourceTestHelper {
weatherValue1.wind.velocity.present == weatherValue2.wind.velocity.present && QuantityUtil.isEquivalentAbs(weatherValue1.wind.velocity.get(), weatherValue2.wind.velocity.get(), 1E-10) &&
weatherValue1.wind.direction.present == weatherValue2.wind.direction.present && QuantityUtil.isEquivalentAbs(weatherValue1.wind.direction.get(), weatherValue2.wind.direction.get(), 1E-10)
}
}
}
Loading