Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
19d41b9
add cbar entry temp
jwarner8 Mar 23, 2026
4da271d
transect inset
jwarner8 Mar 23, 2026
2677058
add subarea
jwarner8 Mar 23, 2026
c66551b
tmp
jwarner8 Mar 23, 2026
9ae5968
revert mod to colorbar json
jwarner8 Mar 26, 2026
cad8ae0
fix varname
jwarner8 Mar 26, 2026
29d2dea
new aggregation recipe
jwarner8 Mar 26, 2026
b390fbb
adjust padding for transects
jwarner8 Mar 26, 2026
0955f44
add aggregate loader
jwarner8 Mar 26, 2026
3f40aaa
add gui entries
jwarner8 Mar 26, 2026
e1bee53
update conf example with aggregate options
jwarner8 Mar 30, 2026
8ac4040
change ordering of logic to prevent multiple models being loaded
jwarner8 Mar 30, 2026
924392e
extra attribute removal and fix forecast period units
jwarner8 Mar 30, 2026
9dd2e54
switch to using read_cubes, refine seq coord for plotting and title
jwarner8 Mar 30, 2026
8d57dfc
add all aggregation recipe
jwarner8 Mar 30, 2026
c3a864d
add lead time aggregation recipe
jwarner8 Mar 30, 2026
4aec34e
add valid time aggregation recipe
jwarner8 Mar 30, 2026
f4f1882
Merge branch 'main' into ml_transect
jwarner8 Mar 30, 2026
59c79fa
merge main
jwarner8 Apr 2, 2026
c8725fe
increase cbar range
jwarner8 Apr 2, 2026
70a3018
make options disappear
jwarner8 Apr 2, 2026
aadb95d
remove tmp files
jwarner8 Apr 2, 2026
0c434a0
remove debugging
jwarner8 Apr 2, 2026
5190d7b
tidy code
jwarner8 Apr 2, 2026
e1fbd08
Update src/CSET/operators/plot.py
jwarner8 Apr 7, 2026
26a4483
Update src/CSET/operators/plot.py
jwarner8 Apr 7, 2026
2a59872
reorder plotting of line/point
jwarner8 Apr 7, 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
13 changes: 13 additions & 0 deletions src/CSET/cset_workflow/meta/diagnostics/rose-meta.conf
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ help=This functionality extracts data for each variable and extracts a column
type=python_boolean
trigger=template variables=PLEVEL_TRANSECT_STARTCOORDS: True;
template variables=PLEVEL_TRANSECT_FINISHCOORDS: True;
template variables=PLEVEL_TRANSECT_AGGREGATION: True;
compulsory=true
sort-key=1pressure8

Expand All @@ -432,6 +433,18 @@ type=real,real
compulsory=true
sort-key=1pressure8b

[template variables=PLEVEL_TRANSECT_AGGREGATION]
ns=Diagnostics/Pressure
description=Aggregate transects for each time.
Select all options required.
Option1: Aggregate by lead time.
Option2: Aggregate by hour of day.
Option3: Aggregate by validity time.
Option4: All cases aggregated to single profile.
type=python_boolean,python_boolean,python_boolean,python_boolean
compulsory=true
sort-key=1pressure8c

[template variables=SPECTRUM_PLEVEL_FIELD]
ns=Diagnostics/Pressure
description=Create spectrum of specified pressure level fields.
Expand Down
1 change: 1 addition & 0 deletions src/CSET/cset_workflow/rose-suite.conf.example
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ MODERATE_RAIN_PRESENCE_SPATIAL_PLOT=False
!!MULTI_OVERLAY_MASK_VALUE=0.0
!!ONE_TO_ONE=False
PLACEHOLDER_OBS=False
!!PLEVEL_TRANSECT_AGGREGATION=True,True,True,True
!!PLEVEL_TRANSECT_FINISHCOORDS=
!!PLEVEL_TRANSECT_STARTCOORDS=
PLOTTING_PROJECTION=""
Expand Down
25 changes: 25 additions & 0 deletions src/CSET/loaders/transects.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,31 @@ def load(conf: Config):
aggregation=False,
)

# Create a list of case aggregation types.
AGGREGATION_TYPES = ["lead_time", "hour_of_day", "validity_time", "all"]

# Transect aggregation
for model, atype, field in itertools.product(
models, AGGREGATION_TYPES, conf.PRESSURE_LEVEL_FIELDS
):
if conf.PLEVEL_TRANSECT_AGGREGATION[AGGREGATION_TYPES.index(atype)]:
yield RawRecipe(
recipe=f"transect_case_aggregation_{atype}.yaml",
variables={
"VARNAME": field,
"VERTICAL_COORDINATE": "pressure",
"MODEL_NAME": model["name"],
"START_COORDS": conf.PLEVEL_TRANSECT_STARTCOORDS,
"FINISH_COORDS": conf.PLEVEL_TRANSECT_FINISHCOORDS,
"SUBAREA_TYPE": conf.SUBAREA_TYPE if conf.SELECT_SUBAREA else None,
"SUBAREA_EXTENT": conf.SUBAREA_EXTENT
if conf.SELECT_SUBAREA
else None,
},
model_ids=model["id"],
aggregation=True,
)

# Model level fields
if conf.EXTRACT_MLEVEL_TRANSECT:
for model, field in itertools.product(
Expand Down
2 changes: 1 addition & 1 deletion src/CSET/operators/_colorbar_definition.json
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,7 @@
"temperature_at_pressure_levels": {
"cmap": "RdYlBu_r",
"max": 320,
"min": 240,
"min": 200,
"pressure_levels": {
"100": {
"max": 240,
Expand Down
67 changes: 65 additions & 2 deletions src/CSET/operators/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@
import matplotlib.pyplot as plt
import numpy as np
import scipy.fft as fft
from cartopy.mpl.geoaxes import GeoAxes
from iris.cube import Cube
from markdown_it import MarkdownIt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes

from CSET._common import (
combine_dicts,
Expand Down Expand Up @@ -655,15 +657,75 @@ def _plot_and_save_spatial_plot(
fontsize=16,
)

# Inset code
axins = inset_axes(
axes,
width="20%",
height="20%",
loc="upper right",
axes_class=GeoAxes,
axes_kwargs={"map_projection": ccrs.PlateCarree()},
)

axins.coastlines(resolution="50m")
axins.add_feature(cfeature.BORDERS, linewidth=0.3)

SLat, SLon, ELat, ELon = (
float(coord) for coord in cube.attributes["transect_coords"].split("_")
)

# Draw line between them
axins.plot(
[SLon, ELon], [SLat, ELat], color="black", transform=ccrs.PlateCarree()
)
Comment thread
jwarner8 marked this conversation as resolved.

# Plot points (note: lon, lat order for Cartopy)
axins.plot(SLon, SLat, marker="x", color="green", transform=ccrs.PlateCarree())
axins.plot(ELon, ELat, marker="x", color="red", transform=ccrs.PlateCarree())

lon_min, lon_max = sorted([SLon, ELon])
lat_min, lat_max = sorted([SLat, ELat])

# Midpoints
lon_mid = (lon_min + lon_max) / 2
lat_mid = (lat_min + lat_max) / 2

# Maximum half-range
half_range = max(lon_max - lon_min, lat_max - lat_min) / 2
if half_range == 0: # points identical → provide small default
half_range = 1

# Set square extent
axins.set_extent(
[
lon_mid - half_range,
lon_mid + half_range,
lat_mid - half_range,
lat_mid + half_range,
],
crs=ccrs.PlateCarree(),
)

# Ensure square aspect
axins.set_aspect("equal")

else:
# Add title.
axes.set_title(title, fontsize=16)

# Adjust padding if spatial plot or transect
if is_transect(cube):
yinfopad = -0.1
ycbarpad = 0.1
else:
yinfopad = -0.05
ycbarpad = 0.042

# Add watermark with min/max/mean. Currently not user togglable.
# In the bbox dictionary, fc and ec are hex colour codes for grey shade.
axes.annotate(
f"Min: {np.min(cube.data):.3g} Max: {np.max(cube.data):.3g} Mean: {np.mean(cube.data):.3g}",
xy=(1, -0.05),
xy=(1, yinfopad),
xycoords="axes fraction",
xytext=(-5, 5),
textcoords="offset points",
Expand All @@ -689,8 +751,9 @@ def _plot_and_save_spatial_plot(

# Add main colour bar.
cbar = fig.colorbar(
plot, orientation="horizontal", location="bottom", pad=0.042, shrink=0.7
plot, orientation="horizontal", location="bottom", pad=ycbarpad, shrink=0.7
)

cbar.set_label(label=f"{cube.name()} ({cube.units})", size=14)
# add ticks and tick_labels for every levels if less than 20 levels exist
if levels is not None and len(levels) < 20:
Expand Down
42 changes: 42 additions & 0 deletions src/CSET/recipes/level_fields/transect_case_aggregation_all.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
category: Transect
title: $MODEL_NAME Transect of $VARNAME Aggregation over all cases.
description: |
Extracts a $VERTICAL_COORDINATE transect for $VARNAME between two points and
plots it.

Start coordinate: `$START_COORDS`
End coordinate: `$FINISH_COORDS`

steps:
- operator: read.read_cubes
file_paths: $INPUT_PATHS
constraint:
operator: constraints.combine_constraints
cell_method_constraint:
operator: constraints.generate_cell_methods_constraint
cell_methods: []
var_constraint:
operator: constraints.generate_var_constraint
varname: $VARNAME
level_constraint:
operator: constraints.generate_level_constraint
coordinate: $VERTICAL_COORDINATE
levels: '*'
subarea_type: $SUBAREA_TYPE
subarea_extent: $SUBAREA_EXTENT

- operator: aggregate.ensure_aggregatable_across_cases

- operator: collapse.collapse
coordinate: time
method: MEAN

- operator: transect.calc_transect
startcoords: $START_COORDS
endcoords: $FINISH_COORDS

- operator: plot.spatial_contour_plot
sequence_coordinate: time

- operator: write.write_cube_to_nc
overwrite: True
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
category: Transect
title: $MODEL_NAME Transect of $VARNAME Aggregation by hour of day.
description: |
Extracts a $VERTICAL_COORDINATE transect for $VARNAME between two points and
plots it.

Start coordinate: `$START_COORDS`
End coordinate: `$FINISH_COORDS`

steps:
- operator: read.read_cubes
file_paths: $INPUT_PATHS
constraint:
operator: constraints.combine_constraints
cell_method_constraint:
operator: constraints.generate_cell_methods_constraint
cell_methods: []
var_constraint:
operator: constraints.generate_var_constraint
varname: $VARNAME
level_constraint:
operator: constraints.generate_level_constraint
coordinate: $VERTICAL_COORDINATE
levels: '*'
subarea_type: $SUBAREA_TYPE
subarea_extent: $SUBAREA_EXTENT

- operator: aggregate.ensure_aggregatable_across_cases

- operator: collapse.collapse_by_hour_of_day
method: MEAN

- operator: transect.calc_transect
startcoords: $START_COORDS
endcoords: $FINISH_COORDS

- operator: plot.spatial_contour_plot
sequence_coordinate: hour

- operator: write.write_cube_to_nc
overwrite: True
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
category: Transect
title: $MODEL_NAME Transect of $VARNAME Aggregation by lead time.
description: |
Extracts a $VERTICAL_COORDINATE transect for $VARNAME between two points and
plots it.

Start coordinate: `$START_COORDS`
End coordinate: `$FINISH_COORDS`

steps:
- operator: read.read_cubes
file_paths: $INPUT_PATHS
constraint:
operator: constraints.combine_constraints
cell_method_constraint:
operator: constraints.generate_cell_methods_constraint
cell_methods: []
var_constraint:
operator: constraints.generate_var_constraint
varname: $VARNAME
level_constraint:
operator: constraints.generate_level_constraint
coordinate: $VERTICAL_COORDINATE
levels: '*'
subarea_type: $SUBAREA_TYPE
subarea_extent: $SUBAREA_EXTENT

- operator: aggregate.ensure_aggregatable_across_cases

- operator: collapse.collapse
coordinate: "forecast_reference_time"
method: MEAN

- operator: transect.calc_transect
startcoords: $START_COORDS
endcoords: $FINISH_COORDS

- operator: plot.spatial_contour_plot
sequence_coordinate: forecast_period

- operator: write.write_cube_to_nc
overwrite: True
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
category: Transect
title: $MODEL_NAME Transect of $VARNAME Aggregation by validity time.
description: |
Extracts a $VERTICAL_COORDINATE transect for $VARNAME between two points and
plots it.

Start coordinate: `$START_COORDS`
End coordinate: `$FINISH_COORDS`

steps:
- operator: read.read_cubes
file_paths: $INPUT_PATHS
constraint:
operator: constraints.combine_constraints
cell_method_constraint:
operator: constraints.generate_cell_methods_constraint
cell_methods: []
var_constraint:
operator: constraints.generate_var_constraint
varname: $VARNAME
level_constraint:
operator: constraints.generate_level_constraint
coordinate: $VERTICAL_COORDINATE
levels: '*'
subarea_type: $SUBAREA_TYPE
subarea_extent: $SUBAREA_EXTENT

- operator: aggregate.ensure_aggregatable_across_cases

- operator: collapse.collapse_by_validity_time
method: MEAN

- operator: transect.calc_transect
startcoords: $START_COORDS
endcoords: $FINISH_COORDS

- operator: plot.spatial_contour_plot
sequence_coordinate: time

- operator: write.write_cube_to_nc
overwrite: True
Loading