Skip to content
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

add UKV #226

Draft
wants to merge 21 commits into
base: main
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
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[![pypi badge](https://img.shields.io/pypi/v/nwp-consumer?&color=086788)](https://pypi.org/project/nwp-consumer)
[![documentation badge](https://img.shields.io/badge/docs-latest-333333)](https://openclimatefix.github.io/nwp-consumer/)
[![contributors badge](https://img.shields.io/github/contributors/openclimatefix/nwp-consumer?color=FFFFFF)](https://github.com/openclimatefix/nwp-consumer/graphs/contributors)
[![workflows badge](https://img.shields.io/github/actions/workflow/status/openclimatefix/nwp-consumer/branch_ci.yml?branch=main&color=FFD053)](https://github.com/openclimatefix/nwp-consumer/actions/workflows/ci.yml)
[![workflows badge](https://img.shields.io/github/actions/workflow/status/openclimatefix/nwp-consumer/branch_ci.yml?branch=main&color=FFD053)](https://github.com/openclimatefix/nwp-consumer/actions/workflows/branch_ci.yml)
[![ease of contribution: easy](https://img.shields.io/badge/ease%20of%20contribution:%20easy-32bd50)](https://github.com/openclimatefix/ocf-meta-repo?tab=readme-ov-file#overview-of-ocfs-nowcasting-repositories)

Some renewables, such as solar and wind, generate power according to the weather conditions.
Expand Down Expand Up @@ -102,6 +102,8 @@ parameter modifications to the model's expected coordinates in it's metadata for
repository.

## Development

### Linting and static type checking

This project uses [MyPy](https://mypy.readthedocs.io/en/stable/) for static type checking
and [Ruff](https://docs.astral.sh/ruff/) for linting.
Expand Down Expand Up @@ -151,7 +153,7 @@ src and flat layouts.

## Contributing and community

[![issues badge](https://img.shields.io/github/issues/openclimatefix/ocf-template?color=FFAC5F)](https://github.com/openclimatefix/ocf-template/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc)
[![issues badge](https://img.shields.io/github/issues/openclimatefix/nwp-consumer?color=FFAC5F)](https://github.com/openclimatefix/nwp-consumer/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc)

- PR's are welcome! See the [Organisation Profile](https://github.com/openclimatefix) for details on contributing
- Find out about our other projects in the [OCF Meta Repo](https://github.com/openclimatefix/ocf-meta-repo)
Expand Down
23 changes: 23 additions & 0 deletions src/nwp_consumer/internal/entities/coordinates.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ class NWPDimensionCoordinateMap:
longitude: list[float] | None = None
"""The longitude coordinates of the forecast grid in degrees. """

x: list[float] | None = None
"""X coordinates of an OSGB (or other alternative projection) grid."""
y: list[float] | None = None
"""Y coordinates of an OSGB (or other alternative projection) grid."""

def __post_init__(self) -> None:
"""Rigidly set input value ordering and precision."""
self.variable = sorted(self.variable)
Expand Down Expand Up @@ -204,6 +209,20 @@ def from_pandas(
"Longitude coordinates should run from -180 -> 180. "
"Modify the coordinate in the source data to be in ascending order.",
))
if "y" in pd_indexes \
and pd_indexes["y"].values[0] > pd_indexes["y"].values[-1]:
return Failure(ValueError(
"Cannot create NWPDimensionCoordinateMap instance from pandas indexes "
"as the y values are not in ascending order. "
"Modify the coordinate in the source data to be in ascending order.",
))
if "x" in pd_indexes \
and pd_indexes["x"].values[0] > pd_indexes["x"].values[-1]:
return Failure(ValueError(
"Cannot create NWPDimensionCoordinateMap instance from pandas indexes "
"as the x values are not in ascending order. "
"Modify the coordinate in the source data to be in ascending order.",
))

# Convert the pandas Index objects to lists of the appropriate types
return Success(
Expand Down Expand Up @@ -231,6 +250,10 @@ def from_pandas(
if "latitude" in pd_indexes else None,
longitude=pd_indexes["longitude"].to_list() \
if "longitude" in pd_indexes else None,
y=pd_indexes["y"].to_list() \
if "y" in pd_indexes else None,
x=pd_indexes["x"].to_list() \
if "x" in pd_indexes else None,
),
)

Expand Down
61 changes: 61 additions & 0 deletions src/nwp_consumer/internal/entities/modelmetadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
"""

import dataclasses
import datetime as dt
import logging

import numpy as np
import pandas as pd

from .coordinates import NWPDimensionCoordinateMap
from .parameters import Parameter
Expand Down Expand Up @@ -55,6 +57,14 @@ class ModelMetadata:
Which prints grid data from the grib file.
"""

running_hours: list[int]
"""The hours of the day that the model runs.

Raw Repositories that provide data for the model may not have every running time.
In this instance, use `with_running_hours` to specify the running hours specific
to the repository.
"""

chunk_count_overrides: dict[str, int] = dataclasses.field(default_factory=dict)
"""Mapping of dimension names to the desired number of chunks in that dimension.

Expand Down Expand Up @@ -117,6 +127,19 @@ def with_chunk_count_overrides(self, overrides: dict[str, int]) -> "ModelMetadat
)
return dataclasses.replace(self, chunk_count_overrides=overrides)

def with_running_hours(self, hours: list[int]) -> "ModelMetadata":
"""Returns metadata for the given model with the given running hours."""
return dataclasses.replace(self, running_hours=hours)

def month_its(self, year: int, month: int) -> list[dt.datetime]:
"""Generate all init times for a given month."""
days = pd.Period(f"{year}-{month}").days_in_month
its: list[dt.datetime] = []
for day in range(1, days + 1):
for hour in self.running_hours:
its.append(dt.datetime(year, month, day, hour, tzinfo=dt.UTC))
return its

class Models:
"""Namespace containing known models."""

Expand Down Expand Up @@ -149,6 +172,7 @@ class Models:
latitude=[float(f"{lat / 10:.2f}") for lat in range(900, -900 - 1, -1)],
longitude=[float(f"{lon / 10:.2f}") for lon in range(-1800, 1800 + 1, 1)],
),
running_hours=[0, 6, 12, 18],
)
"""ECMWF's High Resolution Integrated Forecast System."""

Expand All @@ -168,6 +192,7 @@ class Models:
latitude=[v/10 for v in range(900, -900, -1)],
longitude=[v/10 for v in range(-1800, 1800, 1)],
),
running_hours=[0, 12],
)
"""Summary statistics from ECMWF's Ensemble Forecast System."""

Expand Down Expand Up @@ -196,6 +221,7 @@ class Models:
latitude=[v/10 for v in range(900, -900, -1)],
longitude=[v/10 for v in range(-1800, 1800, 1)],
),
running_hours=[0, 6, 12, 18],
)
"""Full ensemble data from ECMWF's Ensemble Forecast System."""

Expand Down Expand Up @@ -227,6 +253,7 @@ class Models:
latitude=[float(lat) for lat in range(90, -90 - 1, -1)],
longitude=[float(lon) for lon in range(-180, 180 + 1, 1)],
),
running_hours=[0, 6, 12, 18],
)
"""NCEP's Global Forecast System."""

Expand Down Expand Up @@ -262,6 +289,7 @@ class Models:
],
# TODO: Change to -180 -> 180
),
running_hours=[0, 6, 12, 18],
)
"""MetOffice's Unified Model, in the Global configuration, at a resolution of 17km."""

Expand Down Expand Up @@ -295,6 +323,39 @@ class Models:
for lon in np.arange(-179.929687, 179.929688 + 0.140625, 0.140625)
],
),
running_hours=[0, 6, 12, 18],
)
"""MetOffice's Unified Model, in the Global configuration, at a resolution of 10km."""

MO_UM_UKV_2KM: ModelMetadata = ModelMetadata(
name="um-ukv",
resolution="2km",
expected_coordinates=NWPDimensionCoordinateMap(
init_time=[],
step=list(range(0, 55)),
variable=sorted(
[
Parameter.CLOUD_COVER_TOTAL,
Parameter.CLOUD_COVER_HIGH,
Parameter.CLOUD_COVER_MEDIUM,
Parameter.CLOUD_COVER_LOW,
Parameter.VISIBILITY_SL,
Parameter.RELATIVE_HUMIDITY_SL,
Parameter.SNOW_DEPTH_GL,
Parameter.DOWNWARD_LONGWAVE_RADIATION_FLUX_GL,
Parameter.DOWNWARD_SHORTWAVE_RADIATION_FLUX_GL,
Parameter.TEMPERATURE_SL,
Parameter.WIND_U_COMPONENT_10m,
Parameter.WIND_V_COMPONENT_10m,
Parameter.WIND_DIRECTION_10m,
Parameter.WIND_SPEED_10m,
Parameter.TOTAL_PRECIPITATION_RATE_GL,
],
),
x=list(range(0, 455)),
y=list(range(0, 639)),
),
running_hours=list(range(0, 24)),
)
"""MetOffice's Unified Model in the UKV configuration, at a resolution of 2km"""

Loading