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 1 commit
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased/Snapshot]

### Added
- 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)
- -Fixed em fields in input models [#1331](https://github.com/ie3-institute/PowerSystemDataModel/issues/1331)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
/*
* © 2021. TU Dortmund University,
* © 2020. 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.timeseries;

import edu.ie3.datamodel.exceptions.FactoryException;
import edu.ie3.datamodel.models.StandardUnits;
import edu.ie3.datamodel.models.timeseries.individual.TimeBasedValue;
import edu.ie3.datamodel.models.value.SolarIrradianceValue;
import edu.ie3.datamodel.models.value.TemperatureValue;
import edu.ie3.datamodel.models.value.WeatherValue;
import edu.ie3.datamodel.models.value.WindValue;
import edu.ie3.util.TimeUtil;
import edu.ie3.util.quantities.PowerSystemUnits;
import edu.ie3.util.quantities.interfaces.Irradiance;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import javax.measure.quantity.Angle;
import javax.measure.quantity.Length;
Expand All @@ -24,85 +22,75 @@
import org.locationtech.jts.geom.Point;
import tech.units.indriya.ComparableQuantity;
import tech.units.indriya.quantity.Quantities;
import tech.units.indriya.unit.Units;

/**
* 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 String TIME = "time";
private static final String COORDINATE_ID = "coordinateId";
private static final String DIFFUSE_IRRADIANCE = "diffuseIrradiance";
private static final String DIRECT_IRRADIANCE = "directIrradiance";
private static final String TEMPERATURE = "temperature";
private static final String WIND_DIRECTION = "windDirection";
private static final String WIND_VELOCITY = "windVelocity";

private static final String GROUND_TEMPERATURE_SURFACE = "groundTemperatureSurface";
private static final String GROUND_TEMPERATURE_1M = "groundTemperature1m";

public CosmoTimeBasedWeatherValueFactory(TimeUtil timeUtil) {
super(timeUtil);
}

public CosmoTimeBasedWeatherValueFactory(DateTimeFormatter dateTimeFormatter) {
super(dateTimeFormatter);
}

public CosmoTimeBasedWeatherValueFactory() {
super();
}

@Override
protected List<Set<String>> getFields(Class<?> entityClass) {
Set<String> minConstructorParams =
newSet(
COORDINATE_ID,
DIFFUSE_IRRADIANCE,
DIRECT_IRRADIANCE,
TEMPERATURE,
WIND_DIRECTION,
WIND_VELOCITY);
new HashSet<>(
Set.of(
COORDINATE_ID,
TIME,
DIFFUSE_IRRADIANCE,
DIRECT_IRRADIANCE,
TEMPERATURE,
WIND_DIRECTION,
WIND_VELOCITY));

Set<String> allParameters =
expandSet(minConstructorParams, GROUND_TEMPERATURE_SURFACE, GROUND_TEMPERATURE_1M);
minConstructorParams.remove(COORDINATE_ID);
allParameters.remove(COORDINATE_ID);
return Collections.singletonList(minConstructorParams);
Set<String> withGroundTemps = new HashSet<>(minConstructorParams);
withGroundTemps.add(GROUND_TEMPERATURE_SURFACE);
withGroundTemps.add(GROUND_TEMPERATURE_1M);

return List.of(minConstructorParams, withGroundTemps);
}

@Override
protected TimeBasedValue<WeatherValue> buildModel(TimeBasedWeatherValueData data) {
Point coordinate = data.getCoordinate();
ZonedDateTime time = timeUtil.toZonedDateTime(data.getField(TIME));
SolarIrradianceValue solarIrradiance = new SolarIrradianceValue(
data.getQuantity(DIRECT_IRRADIANCE, PowerSystemUnits.WATT_PER_SQUAREMETRE),
data.getQuantity(DIFFUSE_IRRADIANCE, PowerSystemUnits.WATT_PER_SQUAREMETRE)
);
TemperatureValue temperature = new TemperatureValue(
data.getQuantity(TEMPERATURE, StandardUnits.TEMPERATURE)
);
WindValue wind = new WindValue(
data.getQuantity(WIND_DIRECTION, StandardUnits.WIND_DIRECTION),
data.getQuantity(WIND_VELOCITY, StandardUnits.WIND_VELOCITY)
);

Map<ComparableQuantity<Length>, TemperatureValue> groundTemperatures = new HashMap<>();
ComparableQuantity<Irradiance> directIrradiance =
data.getQuantity(DIRECT_IRRADIANCE, PowerSystemUnits.WATT_PER_SQUAREMETRE);
ComparableQuantity<Irradiance> diffuseIrradiance =
data.getQuantity(DIFFUSE_IRRADIANCE, PowerSystemUnits.WATT_PER_SQUAREMETRE);
ComparableQuantity<Temperature> temperature =
data.getQuantity(TEMPERATURE, StandardUnits.TEMPERATURE);
ComparableQuantity<Angle> windDirection =
data.getQuantity(WIND_DIRECTION, StandardUnits.WIND_DIRECTION);
ComparableQuantity<Speed> windVelocity =
data.getQuantity(WIND_VELOCITY, StandardUnits.WIND_VELOCITY);

Map<ComparableQuantity<Length>, TemperatureValue> groundTemperatures = new HashMap<>();

data.getField(GROUND_TEMPERATURE_SURFACE).ifPresent(value -> {
ComparableQuantity<Length> depth = Quantities.getQuantity(0, StandardUnits.DEPTH);
TemperatureValue tempValue = new TemperatureValue(
data.getQuantity(GROUND_TEMPERATURE_SURFACE, StandardUnits.TEMPERATURE)
);
groundTemperatures.put(depth, tempValue);
});
try {
TemperatureValue tempValue =
new TemperatureValue(
data.getQuantity(GROUND_TEMPERATURE_SURFACE, StandardUnits.TEMPERATURE));
groundTemperatures.put(Quantities.getQuantity(0, Units.METRE), tempValue);
} catch (FactoryException ignored) {
}

data.getField(GROUND_TEMPERATURE_1M).ifPresent(value -> {
ComparableQuantity<Length> depth = Quantities.getQuantity(1, StandardUnits.DEPTH);
TemperatureValue tempValue = new TemperatureValue(
data.getQuantity(GROUND_TEMPERATURE_1M, StandardUnits.TEMPERATURE)
);
groundTemperatures.put(depth, tempValue);
});
try {
TemperatureValue tempValue =
new TemperatureValue(data.getQuantity(GROUND_TEMPERATURE_1M, StandardUnits.TEMPERATURE));
groundTemperatures.put(Quantities.getQuantity(1, Units.METRE), tempValue);
} catch (FactoryException ignored) {
}

WeatherValue weatherValue =
new WeatherValue(
Expand All @@ -112,7 +100,8 @@ protected TimeBasedValue<WeatherValue> buildModel(TimeBasedWeatherValueData data
temperature,
windDirection,
windVelocity,
groundTemperatures);
groundTemperatures);

return new TimeBasedValue<>(time, weatherValue);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,32 @@
*/
package edu.ie3.datamodel.io.factory.timeseries;

import edu.ie3.datamodel.exceptions.FactoryException;
import edu.ie3.datamodel.models.StandardUnits;
import edu.ie3.datamodel.models.timeseries.individual.TimeBasedValue;
import edu.ie3.datamodel.models.value.TemperatureValue;
import edu.ie3.datamodel.models.value.WeatherValue;
import edu.ie3.util.quantities.PowerSystemUnits;
import edu.ie3.util.quantities.interfaces.Irradiance;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import javax.measure.quantity.Angle;
import javax.measure.quantity.Length;
import javax.measure.quantity.Speed;
import javax.measure.quantity.Temperature;
import org.locationtech.jts.geom.Point;
import tech.units.indriya.ComparableQuantity;
import tech.units.indriya.quantity.Quantities;
import tech.units.indriya.unit.Units;

/**
* Factory implementation of {@link TimeBasedWeatherValueFactory}, that is able to handle field to
* value mapping in the column scheme, ie<sup>3</sup> uses to store its data from German Federal
* Weather Service's ICON-EU model
*/
public class IconTimeBasedWeatherValueFactory extends TimeBasedWeatherValueFactory {
/* 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 WIND_VELOCITY_U = "u131m";
private static final String WIND_VELOCITY_V = "v131m";

/** Ground-/Skin-Temperature at surface (0m) */
private static final String GROUND_TEMP_SURFACE = "tG";
/** Soil-Temperature at 100cm depth (example parameter name) */
private static final String SOIL_TEMP_100CM = "tso100cm";

public IconTimeBasedWeatherValueFactory() {
Expand All @@ -52,13 +46,19 @@ protected List<Set<String>> getFields(Class<?> entityClass) {
Set<String> minParameters =
newSet(
DIFFUSE_IRRADIANCE, DIRECT_IRRADIANCE, TEMPERATURE, WIND_VELOCITY_U, WIND_VELOCITY_V);

Set<String> minParametersWithGroundTemp = new HashSet<>(minParameters);
minParametersWithGroundTemp.add(GROUND_TEMP_SURFACE);
minParametersWithGroundTemp.add(SOIL_TEMP_100CM);

Set<String> allParameters =
expandSet(
minParameters,
"albrad",
"asobs",
"aswdifuS",
"tG",
GROUND_TEMP_SURFACE,
SOIL_TEMP_100CM,
"u10m",
"u20m",
"u216m",
Expand All @@ -78,7 +78,7 @@ protected List<Set<String>> getFields(Class<?> entityClass) {
"sobsrad",
"t131m");

return Arrays.asList(minParameters, allParameters);
return Arrays.asList(minParameters, minParametersWithGroundTemp, allParameters);
}

@Override
Expand All @@ -93,56 +93,48 @@ protected TimeBasedValue<WeatherValue> buildModel(TimeBasedWeatherValueData data
data.getQuantity(TEMPERATURE, Units.KELVIN).to(StandardUnits.TEMPERATURE);
ComparableQuantity<Angle> windDirection = getWindDirection(data);
ComparableQuantity<Speed> windVelocity = getWindVelocity(data);

Map<ComparableQuantity<Length>, TemperatureValue> groundTemperatures = new HashMap<>();

try {
TemperatureValue tempValue =
new TemperatureValue(
data.getQuantity(GROUND_TEMP_SURFACE, Units.KELVIN).to(StandardUnits.TEMPERATURE));
groundTemperatures.put(Quantities.getQuantity(0, Units.METRE), tempValue);
} catch (FactoryException ignored) {
}

try {
TemperatureValue tempValue =
new TemperatureValue(
data.getQuantity(SOIL_TEMP_100CM, Units.KELVIN).to(StandardUnits.TEMPERATURE));
groundTemperatures.put(Quantities.getQuantity(1, Units.METRE), tempValue);
} catch (FactoryException ignored) {
}

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

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 =
data.getDouble(WIND_VELOCITY_U); // Wind component from west to east (parallel to latitudes)
double v =
data.getDouble(
WIND_VELOCITY_V); // Wind component from south to north (parallel to longitudes)
double u = data.getDouble(WIND_VELOCITY_U);
double v = data.getDouble(WIND_VELOCITY_V);

double angle = Math.toDegrees(Math.atan2(-u, -v));
return Quantities.getQuantity(angle < 0 ? angle + 360d : angle, PowerSystemUnits.DEGREE_GEOM)
.to(StandardUnits.WIND_DIRECTION);
}

/**
* Determines the wind velocity. In ICON the wind velocity is given in three dimensional Cartesian
* coordinates. Here, the upward component is neglected. 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<Speed> getWindVelocity(TimeBasedWeatherValueData data) {
/* Get the three dimensional parts of the wind velocity vector in cartesian coordinates */
double u = data.getDouble(WIND_VELOCITY_U);
double v = data.getDouble(WIND_VELOCITY_V);

Expand Down
Loading
Loading