Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
9fb1eaa
placeholder in rose-meta
jwarner8 Jul 13, 2026
abdde1d
add meta to rose edit
jwarner8 Jul 13, 2026
8f110e8
add template variables
jwarner8 Jul 13, 2026
4893929
initial code lodge for tidying
jwarner8 Jul 14, 2026
b3aa6fe
add notes to pickup
jwarner8 Jul 15, 2026
b9f652e
latest changes
jwarner8 Jul 16, 2026
ca8033c
lodge latest code
jwarner8 Jul 16, 2026
ae02f93
clear out old code
jwarner8 Jul 16, 2026
6c3ed5e
return if no analysis
jwarner8 Jul 16, 2026
8b938f0
Merge branch 'main' into 2016_support_reanalysis
jwarner8 Jul 29, 2026
c04e46f
back out rose meta changes
jwarner8 Jul 29, 2026
d16ae12
remove template variables for reanalysis
jwarner8 Jul 29, 2026
12868ad
restore changes in collapse backing out code
jwarner8 Jul 29, 2026
89abe06
intial code move to utils
jwarner8 Jul 29, 2026
f227f7e
add conf bits back in
jwarner8 Jul 29, 2026
cca95dd
working code for UM and ERA5
jwarner8 Jul 29, 2026
cb0717b
Merge branch 'main' into 2016_support_reanalysis
jwarner8 Jul 29, 2026
8b3e66c
add docstring to main
jwarner8 Jul 29, 2026
b9afb02
tidy up function docstring, comments
jwarner8 Jul 29, 2026
9a97879
comments for _create_forecasts and docstring
jwarner8 Jul 29, 2026
b0d82d6
do pre-commit fixes
jwarner8 Jul 29, 2026
b7b0752
add tests placeholder
jwarner8 Jul 29, 2026
f6df532
add imports
jwarner8 Aug 3, 2026
c486359
remove double import
jwarner8 Aug 3, 2026
c91ced8
add tests
jwarner8 Aug 3, 2026
710097c
lodge about section to readme
jwarner8 Aug 3, 2026
1348533
add req args
jwarner8 Aug 3, 2026
5d48bac
add req args
jwarner8 Aug 3, 2026
de3553f
additional readme args
jwarner8 Aug 3, 2026
0330380
completed readme
jwarner8 Aug 3, 2026
11a9ffa
indent arg list
jwarner8 Aug 3, 2026
bf3ab58
minor text clarifications and tidy
jwarner8 Aug 3, 2026
04c6a5a
refactor func location
jwarner8 Aug 3, 2026
70f3e9f
Merge branch 'main' into 2016_support_reanalysis
jwarner8 Aug 3, 2026
725c1b8
Add utils folder to ruff linting and formatting
jfrost-mo Aug 4, 2026
20615a5
Add utils to testpaths
jfrost-mo Aug 4, 2026
ef7dba2
Merge branch 'main' into 2016_support_reanalysis
jwarner8 Aug 7, 2026
8c51870
switch to using isoformat
jwarner8 Aug 7, 2026
5420747
switch to passing datetime rather than str
jwarner8 Aug 7, 2026
42580a9
add datetime parsing as command line arg
jwarner8 Aug 7, 2026
d5c82de
specify cycle freq units
jwarner8 Aug 7, 2026
604388d
directly convert str of cyclefreq to timedelta
jwarner8 Aug 7, 2026
fadf4d5
tidyup from code review
jwarner8 Aug 7, 2026
361d410
apply changes from code review
jwarner8 Aug 7, 2026
450467f
update tests given changes to function names
jwarner8 Aug 7, 2026
9cd6c6f
update refs to using new ISO datetime standard
jwarner8 Aug 7, 2026
6f9b667
update identify_number_of_cycles_required to use timedelta
jwarner8 Aug 7, 2026
7d2fc04
update tests to use new datetime format
jwarner8 Aug 7, 2026
8aa6245
update import for github actions
jwarner8 Aug 7, 2026
24c4433
Merge branch 'main' into 2016_support_reanalysis
jwarner8 Aug 10, 2026
ee52f70
Merge branch 'main' into 2016_support_reanalysis
jwarner8 Aug 19, 2026
4f7b2e7
Merge branch 'main' into 2016_support_reanalysis
jwarner8 Aug 19, 2026
d4b5f1b
add new entry and generalise function name, tidy up docstring
jwarner8 Aug 19, 2026
1018383
Add shebang utils/proc_reanalysis/process_reanalysis.py
jwarner8 Aug 19, 2026
ef09c9b
make executable
jwarner8 Aug 19, 2026
e2e0a9b
Merge branch '2016_support_reanalysis' of github.com:MetOffice/CSET i…
jwarner8 Aug 19, 2026
80860bf
Update utils/proc_reanalysis/process_reanalysis.py
jwarner8 Aug 19, 2026
fd18441
use timedelta for forecast length
jwarner8 Aug 19, 2026
b9bebe1
update argument names
jwarner8 Aug 19, 2026
53270d5
update argument names to script
jwarner8 Aug 19, 2026
56cd5bb
Merge branch '2016_support_reanalysis' of github.com:MetOffice/CSET i…
jwarner8 Aug 19, 2026
218289a
use standard cycle time iso time format
jwarner8 Aug 19, 2026
404ca39
Merge branch 'main' into 2016_support_reanalysis
jwarner8 Aug 19, 2026
d6344b2
update test
jwarner8 Aug 19, 2026
0f1bf8f
fix object types in tests
jwarner8 Aug 19, 2026
2985aa8
update type hints
jwarner8 Aug 19, 2026
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
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ markers = [
]
minversion = "7"
pythonpath = ["src"]
testpaths = ["tests"]
testpaths = ["tests", "utils"]

[tool.coverage.run]
branch = true
Expand All @@ -83,7 +83,7 @@ skip = "build,*.css,*.ipynb,*.js,*.html,*.svg,*.xml,.git"

[tool.ruff]
line-length = 88
src = ["src", "test"]
src = ["src", "test", "utils"]

[tool.ruff.lint]
extend-select = ["B", "D"]
Expand Down
8 changes: 5 additions & 3 deletions src/CSET/operators/read.py
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ def _loading_callback(cube: iris.cube.Cube, field, filename: str) -> iris.cube.C
_lfric_time_callback(cube)
_lfric_forecast_period_callback(cube)
cube = _fix_no_time_coords_callback(cube)
_normalise_ML_varname(cube)
_normalise_longname(cube)
return cube


Expand Down Expand Up @@ -1098,8 +1098,8 @@ def _fix_no_time_coords_callback(cube: iris.cube.Cube):
return cube


def _normalise_ML_varname(cube: iris.cube.Cube):
"""Fix plev variable names to standard names."""
def _normalise_longname(cube: iris.cube.Cube):
"""Normalise long_name to the LFRic standard list."""
if cube.coords("pressure"):
if cube.name() == "x_wind":
cube.long_name = "zonal_wind_at_pressure_levels"
Expand All @@ -1116,6 +1116,8 @@ def _normalise_ML_varname(cube: iris.cube.Cube):
cube.long_name = "eastward_wind_at_10m"
if cube.name() == "y_wind" and cube.var_name == "v_wind_at_10m":
cube.long_name = "northward_wind_at_10m"
if cube.name() == "air_pressure_at_sea_level":
cube.long_name = "air_pressure_at_mean_sea_level"


def _check_combine_point_observations(cubes: iris.cube.CubeList):
Expand Down
4 changes: 2 additions & 2 deletions tests/operators/test_read.py
Original file line number Diff line number Diff line change
Expand Up @@ -1166,11 +1166,11 @@ def test_fix_no_time_coords_callback(cube):
assert cube.coord("time").units == "hours since 0001-01-01 00:00:00"


def test_normalise_ML_varname(transect_source_cube):
def test_normalise_longname(transect_source_cube):
"""Check that pressure varname is changed."""
cube = transect_source_cube.copy()
cube.rename = "air_temperature"
read._normalise_ML_varname(cube)
read._normalise_longname(cube)
assert cube.long_name == "temperature_at_pressure_levels"


Expand Down
100 changes: 100 additions & 0 deletions utils/proc_reanalysis/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# process_reanalysis

## About

The script `process_reanalysis.py` is a utility for converting atmospheric reanalysis datasets into a forecast-style format that can be directly compared with numerical weather prediction (NWP) model forecasts.

The primary motivation for this tool is to be able to analyse reanalysis alongside model forecasts in CSET, by choosing reanalysis to be the base model for verification and evaluation. CSET expects forecast data to contain forecast metadata such as:

- `forecast_reference_time`
- `forecast_period`
- `time`

Reanalysis datasets typically only contain valid time `time`, and is a series of files where the `forecast_reference_time` changes every 6 hours, and the `forecast_period` is zero.

This script resolves this issue by transforming reanalysis data into an effective forecast representation. Rather than treating reanalysis as a special data source in CSET, the transformed output can be ingested directly into the CSET workflow. This allows reanalysis to be treated as another "model" within CSET.

The script currently supports datasets that can be loaded by Iris and has primarily been developed and tested using:

- ERA5 reanalysis
- Unified Model (UM) analysis data

Other model analyses may work, as only the time dimensions are manipulated, but this has not been tested.
No scientific changes are made to the meteorological fields themselves.

For each requested forecast cycle, for each variable found in the reanalysis the script:
1. Extracts the required period of time from reanalysis data.
2. Treats the start of that extraction as a forecast initialisation.
3. Generates a forecast period coordinate.
4. Generates a forecast reference time coordinate.
5. Preserves the original valid time information.
6. Saves the result as a forecast-style NetCDF file.

> [!TIP]
> This script does not download reanalysis data - a user must fetch this first from archive/api.

## Usage

The python script requires the Iris package to be installed and available to python.

Run it with:

```
python process_reanalysis.py \
--files "<input_files>" \
--cyclestart YYYYMMDDTHHMMZ" \
--cycleend YYYYMMDDTHHMMZ" \
--cyclefreq <hours> \
--forecastlength <hours> \
--outpath "<output_directory>"
```

Required Arguments:

- `--files`: Path to the input reanalysis data. This can be a single file or wildcard expression understood by Iris. If a wildcard is used, then quote the input to prevent the shell expanding the filelist as arguments to python.
- `--cyclestart`: First forecast initialisation time that you want the reanalysis to simulate, in format <year><month><day>T<hour><minute>Z.
- `--cycleend`: Final forecast initialisation time, inclusive, that you want the reanalysis to simulate, in format <year><month><day>T<hour><minute>Z.
- `--cyclefreq`: Frequency between forecast cycles, in hours, as an integer.
- `--forecastlength`: Length of the forecast you want the reanalysis to simulate, in hours, as an integer.
- `--outpath`: Path of where to store the output data. The code will write a file per forecast initialisation, in the format of `reanalysis_%Y%m%dT%H%MZ_.nc`.

## Examples

1. A single forecast that goes out to 48h, initialised on the 1st January 2024 at 00Z.

```
python process_reanalysis.py \
--files "/data/era5/*.nc" \
--cyclestart "20240101T0000Z" \
--cycleend "20240101T0000Z" \
--cyclefreq 6 \
--forecastlength 48 \
--outpath /my/output/path/
```
Producing one file `my/output/path/reanalysis_20240101T0000Z.nc`

2. Produce 6-hourly analysis across one day.

```
python process_reanalysis.py \
--files "/data/era5/*.nc" \
--cyclestart "20240101T0000Z" \
--cycleend "20240101T1800Z" \
--cyclefreq 6 \
--forecastlength 48 \
--outpath /my/output/path/
```
Produces

```
/my/output/path/reanalysis_20240101T0000Z.nc
/my/output/path/reanalysis_20240101T0600Z.nc
/my/output/path/reanalysis_20240101T1200Z.nc
/my/output/path/reanalysis_20240101T1800Z.nc
```

## Owners

The following people should be contacted for queries or issues with this utility:

* [@jwarner8](https://github.com/jwarner8)
Loading