Skip to content

Add weather interpolation #1338

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 6 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 @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### 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)
- Add interpolation functionality to estimate missing weather values [#1304](https://github.com/ie3-institute/PowerSystemDataModel/issues/1304)

### Changed
- Updated dependabot workflow and added CODEOWNERS [#1328](https://github.com/ie3-institute/PowerSystemDataModel/issues/1328)
Expand Down
74 changes: 74 additions & 0 deletions src/main/java/edu/ie3/datamodel/io/source/WeatherSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,17 @@
import edu.ie3.datamodel.models.value.WeatherValue;
import edu.ie3.datamodel.utils.Try;
import edu.ie3.util.interval.ClosedInterval;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.measure.Quantity;
import org.apache.commons.lang3.tuple.Pair;
import org.locationtech.jts.geom.Point;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tech.units.indriya.ComparableQuantity;

/** Abstract class for WeatherSource by Csv and Sql Data */
public abstract class WeatherSource extends EntitySource {
Expand Down Expand Up @@ -52,6 +55,32 @@ public void validate() throws ValidationException {
validate(WeatherValue.class, this::getSourceFields, weatherFactory);
}

private WeatherValue interpolateWeatherValue(WeatherValue start, WeatherValue end, double ratio) {
var direct = interpolateOptional(start.getDirectIrradiance(), end.getDirectIrradiance(), ratio);
var diffuse =
interpolateOptional(start.getDiffuseIrradiance(), end.getDiffuseIrradiance(), ratio);

var temp = interpolateOptional(start.getTemperatureValue(), end.getTemperatureValue(), ratio);
var dir = interpolateOptional(start.getWindDirection(), end.getWindDirection(), ratio);
var vel = interpolateOptional(start.getWindVelocity(), end.getWindVelocity(), ratio);

return new WeatherValue(start.getCoordinate(), direct, diffuse, temp, dir, vel);
}

private <Q extends Quantity<Q>> ComparableQuantity<Q> interpolateOptional(
Optional<ComparableQuantity<Q>> startOpt,
Optional<ComparableQuantity<Q>> endOpt,
double ratio) {
return startOpt
.flatMap(startVal -> endOpt.map(endVal -> interpolateQuantity(startVal, endVal, ratio)))
.orElse(null);
}

private <Q extends Quantity<Q>> ComparableQuantity<Q> interpolateQuantity(
ComparableQuantity<Q> a, ComparableQuantity<Q> b, double ratio) {
return a.add(b.subtract(a).multiply(ratio));
}

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

public abstract Map<Point, IndividualTimeSeries<WeatherValue>> getWeather(
Expand All @@ -64,6 +93,51 @@ public abstract Map<Point, IndividualTimeSeries<WeatherValue>> getWeather(
public abstract Optional<TimeBasedValue<WeatherValue>> getWeather(
ZonedDateTime date, Point coordinate) throws SourceException;

public Optional<WeatherValue> getWeatherInterpolated(
ZonedDateTime date, Point coordinate, int plus, int minus) throws SourceException {

ClosedInterval<ZonedDateTime> interpolationInterval =
new ClosedInterval<>(date.minusHours(minus), date.plusHours(plus));
IndividualTimeSeries<WeatherValue> ts =
getWeather(interpolationInterval, List.of(coordinate)).get(coordinate);

if (ts == null) {
log.warn("No time series available for coordinate {}", coordinate);
return Optional.empty();
}

Optional<WeatherValue> value = ts.getValue(date);

if (value.isPresent() && value.get().hasPartialValues()) {
return value;
}

Optional<TimeBasedValue<WeatherValue>> prevValue = ts.getPreviousTimeBasedValue(date);
Optional<TimeBasedValue<WeatherValue>> nextValue = ts.getNextTimeBasedValue(date);

if (prevValue.isEmpty() || nextValue.isEmpty()) {
log.warn(
"Not enough data to interpolate weather value at {} for coordinate {}", date, coordinate);
return Optional.empty();
}

TimeBasedValue<WeatherValue> prev = prevValue.get();
TimeBasedValue<WeatherValue> next = nextValue.get();

Duration totalDuration = Duration.between(prev.getTime(), next.getTime());
Duration partialDuration = Duration.between(prev.getTime(), date);

if (totalDuration.isZero()) {
return Optional.of(prev.getValue());
}

double ratio = (double) partialDuration.toSeconds() / totalDuration.toSeconds();

WeatherValue interpolated = interpolateWeatherValue(prev.getValue(), next.getValue(), ratio);

return Optional.of(interpolated);
}

public abstract Map<Point, List<ZonedDateTime>> getTimeKeysAfter(ZonedDateTime time)
throws SourceException;

Expand Down
30 changes: 30 additions & 0 deletions src/main/java/edu/ie3/datamodel/models/value/WeatherValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

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;
Expand Down Expand Up @@ -81,6 +82,35 @@ public WindValue getWind() {
return wind;
}

public Optional<ComparableQuantity<Irradiance>> getDirectIrradiance() {
return solarIrradiance.getDirectIrradiance();
}

public Optional<ComparableQuantity<Irradiance>> getDiffuseIrradiance() {
return solarIrradiance.getDiffuseIrradiance();
}

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

public Optional<ComparableQuantity<Angle>> getWindDirection() {
return wind.getDirection();
}

public Optional<ComparableQuantity<Speed>> getWindVelocity() {
return wind.getVelocity();
}

/**
* Checks if all mandatory values are present.
*
* @return true if all values are present, false otherwise
*/
public boolean hasPartialValues() {
return solarIrradiance != null || temperature != null || wind != null;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand Down
Loading