-
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
# Pull Request ## Description add satellite data into site predictions - add satellite class, this is built of the nwp data source class - refactored `NwpDataSource` class to be slightly more generic - added in satellite to recent history model - take a patch of satellite data, not just one value - added forecast horizon as a feature - added test satellite data ![Screenshot 2023-12-19 at 11 06 31](https://github.com/openclimatefix/pv-site-prediction/assets/34686298/db350c63-33c1-4250-853c-1130efc4fa9d) Fixes ##102 ## How Has This Been Tested? - CI tests - added satellite to test configuration + new satellite test data - [ ] Yes ## Checklist: - [ ] My code follows [OCF's coding style guidelines](https://github.com/openclimatefix/.github/blob/main/coding_style.md) - [ ] I have performed a self-review of my own code - [ ] I have made corresponding changes to the documentation - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] I have checked my code and corrected any misspellings
- Loading branch information
1 parent
2635c59
commit d1e4da0
Showing
42 changed files
with
1,494 additions
and
120 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
## Backtest using Satellite | ||
|
||
We've added satellite data into our model. | ||
We use the satellite data from google public dataset's [here](gs://public-datasets-eumetsat-solar-forecasting/satellite/EUMETSAT/SEVIRI_RSS/v4/) | ||
|
||
The backtest was trained on 2018 and 2019 data and tested on 2020 and 2021 data. | ||
|
||
For satellie data we use all the channels, and take an average over 5 by 5 pixels closes to the PV system. | ||
|
||
```bash | ||
poetry run python psp/scripts/train_model.py -n uk_pv -c uk_pv -b 1 | ||
``` | ||
|
||
If we don't include live PV data, and just forecast up to 8 hours ahead, | ||
we get the following results: | ||
The 0 hour improves from 17.0 to 15.6. This is a 9% improvement. | ||
By 8 hours we see very little improvement. | ||
This was run with 20,000 | ||
|
||
|
||
If we include live PV data, and just forecast up to 8 hours ahead, | ||
we get the following results: | ||
The 0 hour improves from 12.9 to 11.6. This is a 10% improvement. | ||
By 8 hours we see very little improvement. | ||
|
||
![results](results.png) | ||
|
||
| config name | NWP | Live PV | Satellite | | ||
|-------------|-----|---------|-----------| | ||
| uk_pv_sat_no_pv | x | | x | | ||
| uk_pv_no_pv | x | | | | ||
| uk_pv_sat | x | x | x | | ||
| uk_pv | x | x | | | ||
|
||
|
||
We don't believe Satellite data adds any value to model after ~2 hours when we include live PV data. | ||
|
||
|
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import pyresample | ||
import xarray as xr | ||
|
||
from psp.data_sources.nwp import NwpDataSource | ||
from psp.data_sources.utils import _TIME, _VALUE, _VARIABLE, _X, _Y | ||
from psp.gis import CoordinateTransformer | ||
|
||
|
||
class SatelliteDataSource(NwpDataSource): | ||
def __init__(self, *args, **kwargs): | ||
super().__init__( | ||
*args, | ||
**kwargs, | ||
filter_on_step=False, | ||
x_dim_name="x_geostationary", | ||
y_dim_name="y_geostationary", | ||
value_name="data", | ||
) | ||
|
||
# Get the coordinate transformer.# get crs | ||
area_definition_yaml = self._data.value.attrs["area"] | ||
geostationary_area_definition = pyresample.area_config.load_area_from_string( | ||
area_definition_yaml | ||
) | ||
geostationary_crs = geostationary_area_definition.crs | ||
|
||
# Get the coordinate transformer, from lat/lon to geostationary. | ||
self._coordinate_transformer = CoordinateTransformer(from_=4326, to=geostationary_crs) | ||
|
||
def prepare_data(self, data: xr.Dataset) -> xr.Dataset: | ||
# Rename the dimensions. | ||
rename_map: dict[str, str] = {} | ||
for old, new in zip( | ||
[ | ||
self._x_dim_name, | ||
self._y_dim_name, | ||
self._time_dim_name, | ||
self._variable_dim_name, | ||
self._value_name, | ||
], | ||
[_X, _Y, _TIME, _VARIABLE, _VALUE], | ||
): | ||
if old != new: | ||
rename_map[old] = new | ||
|
||
data = data.rename(rename_map) | ||
|
||
# Filter data to keep only the variables in self._nwp_variables if it's not None | ||
if self._variables is not None: | ||
data = data.sel(variable=self._variables) | ||
|
||
return data | ||
|
||
def _open(self, paths: list[str]) -> xr.Dataset: | ||
d = xr.open_mfdataset( | ||
paths, | ||
engine="zarr", | ||
concat_dim="time", | ||
combine="nested", | ||
chunks="auto", | ||
join="override", | ||
) | ||
return d |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
from typing import TypeVar | ||
|
||
import xarray as xr | ||
|
||
from psp.gis import CoordinateTransformer | ||
|
||
_X = "x" | ||
_Y = "y" | ||
_TIME = "time" | ||
_STEP = "step" | ||
_VARIABLE = "variable" | ||
_VALUE = "value" | ||
|
||
T = TypeVar("T", bound=xr.Dataset | xr.DataArray) | ||
|
||
|
||
def slice_on_lat_lon( | ||
data: T, | ||
*, | ||
min_lat: float | None = None, | ||
max_lat: float | None = None, | ||
min_lon: float | None = None, | ||
max_lon: float | None = None, | ||
nearest_lat: float | None = None, | ||
nearest_lon: float | None = None, | ||
transformer: CoordinateTransformer, | ||
x_is_ascending: bool, | ||
y_is_ascending: bool, | ||
) -> T: | ||
""" | ||
Slice the data on lat/lon | ||
Args: | ||
---- | ||
data: The data to slice | ||
min_lat: The minimum latitude to slice on | ||
max_lat: The maximum latitude to slice on | ||
min_lon: The minimum longitude to slice on | ||
max_lon: The maximum longitude to slice on | ||
nearest_lat: The latitude to slice on | ||
nearest_lon: The longitude to slice on | ||
transformer: The transformer to use to convert lat/lon to x/y | ||
x_is_ascending: Whether the x values are ascending | ||
y_is_ascending: Whether the y values are ascending | ||
do_average: Take average over the area, of x and y coordinates | ||
""" | ||
# Only allow `None` values for lat/lon if they are all None (in which case we don't filter | ||
# by lat/lon). | ||
num_none = sum([x is None for x in [min_lat, max_lat, min_lon, max_lon]]) | ||
assert num_none in [0, 4] | ||
|
||
if min_lat is not None: | ||
assert min_lat is not None | ||
assert min_lon is not None | ||
assert max_lat is not None | ||
assert max_lon is not None | ||
|
||
assert max_lat >= min_lat | ||
assert max_lon >= min_lon | ||
|
||
# This looks funny because when going from lat/lon to osgb we have to use | ||
# (x, y) = transformer([(lat, lon)]) | ||
# however for lat/lon to geostationary we have to use | ||
# (x_geo, y_geo) = transformer([(lon, lat)]) | ||
|
||
points = [(min_lat, min_lon), (max_lat, max_lon)] | ||
point1, point2 = transformer(points) | ||
min_x, min_y = point1 | ||
max_x, max_y = point2 | ||
|
||
if not x_is_ascending: | ||
min_x, max_x = max_x, min_x | ||
if not y_is_ascending: | ||
min_y, max_y = max_y, min_y | ||
|
||
new_data = data.sel(x=slice(min_x, max_x), y=slice(min_y, max_y)) | ||
|
||
# Type ignore because this is still simpler than adding some `@overload`. | ||
return new_data # type: ignore | ||
|
||
elif nearest_lat is not None and nearest_lon is not None: | ||
((x, y),) = transformer([(nearest_lat, nearest_lon)]) | ||
|
||
return data.sel(x=x, y=y, method="nearest") # type: ignore | ||
|
||
return data |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.