Skip to content

Paper figures improvements - #258

Open
clairemerker wants to merge 28 commits into
paper-figuresfrom
paper-figures-improvements
Open

clairemerker wants to merge 28 commits into
paper-figuresfrom
paper-figures-improvements

Conversation

@clairemerker

Copy link
Copy Markdown
Contributor

No description provided.

clairemerker and others added 27 commits July 21, 2026 11:23
A standalone "hero" figure for the paper: a nested zoom from the global
forecast to Switzerland to Val Calanca, for 2 m temperature and 10 m wind
speed. The global panel carries the high-resolution LAM data over the
refined region; the Val Calanca panel renders the true ICON (R19B08) cell
mesh via tripcolor. Includes zoom-callout locator boxes/connectors, thinned
flow arrows, per-column continuous colorbars, and a --dark poster variant
(black background, white text and glow).

Runs standalone or via Snakemake: new publication.teaser config block
(enabled, init_time, leadtime, dark, grid) drives the publication_teaser
rule, added to the publication_all target.

Also drop the stale data_download_eckit_geo_grids input from the meteogram
rule; that rule was removed with the earthkit v1 upgrade and the dangling
reference prevented the workflow from loading.
…s-add-teaser

# Conflicts:
#	config/varda-single_paper_analysis.yaml
… comparison helper

Rework notebooks/publication/meteogram.py around an explicit PANELS list: each
panel places a param (or a station A-B difference, e.g. PMSL ALT-LUG) at a grid
cell, with optional scale/unit/ylabel/zero_line and auto circular wind-direction
markers. One render engine covers both single-station custom grids and the
multi-station comparison (via a comparison_panels helper). Panel labels (a),(b),
… shared legend, page-width aspect. Add the two short single-init inference
configs (2025-03-21, 2025-06-27) whose manifests the figures read.
…ecards, scoremaps)

Add a per-figure catalog under 'Rendering the figures' describing what each
notebook shows, what data it needs, and the files it writes; add scorecards;
correct the stored-filename table to the current names (publication_leadtime*,
publication_meteogram*, publication_scorecard*, publication_scoremaps*).
- Fixed print widths via evalml.publication.style.figure_width(): 5.7in
  (2-column) for leadtime/meteogram, 3.35in (1-column) for scoremaps; height
  kept proportional.
- One shared style for every figure (publication.mplstyle): Helvetica ->
  Liberation Sans fallback, absolute point sizes (8/7/6), thin data lines, thin
  solid grid, thin axes frame + ticks. Removed per-notebook font bumps so text
  is identical across figures.
- Meteogram: panel-engine refinements - reading-order (row-major) (a)(b)(c)
  labels, temperatures in degC (per-param offset, skipped for A-B differences),
  init time in the x-axis label, station-name column subtitles (comparison),
  figure title moved into the saved filename.
Fix the region-mask spec API (list of shapefile specs), switch the Valais
case to hourly deaggregated precip (TOT_PREC1) with a valid-time date axis
and a black line, and label the truth as KENDA-CH1. Pin Liberation Sans and
thin the shared frame/ticks (0.4/0.3). Document the standalone script and its
invocation in the publication-figures guide.
Resolve the scoremaps.py conflict (drop the dead _make_figure builder, keep the
fixed 1-column print width) and bring in the stations-scoremaps config.
…igure

Add the standalone publication_teaser.py hero-figure renderer (global forecast
+ nested regional zooms, light/dark variants) as a by-hand CLI script, matching
the decoupled plotting architecture. Keep Snakemake at manifest-only — the
teaser is run standalone like the other publication figures, so the teaser
Snakemake rules from that branch are intentionally not merged. Carry the
PublicationTeaserConfig schema alongside the existing scorecards config.
Thin the map frame to the shared 0.4 pt black spine and re-assert the
publication style after earthkit's Figure() resets rcParams (which had
forced a grey 1.0 pt frame and non-shared fonts); thin the country borders
(0.5) and coastlines (0.4). Drop the bold weight and the semi-transparent box
on the (a)/(b)/(c) panel labels. Remove PDF export (a vectorised ~1M-point
field ran to hundreds of MB); PNG only, at 250 dpi. Give the score column
labels two lines with their symbols: 'MSE skill (S)' and 'Bias contribution
to MSE skill (S_BIAS)'.
Match the shared style's figure.dpi (250) across the leadtime, meteogram and
region figures' PNG exports (were 200).
Shrink the every-6h emphasis markers on the Varda-single and AIFS curves
(markersize 5 -> 3) and move the rotated left-column row labels further out
(x -0.25 -> -0.33) so they clear the y-tick numbers.
Use the shared publication style (re-asserted after earthkit's Figure() resets
rcParams) and size the figure to the exact 2-column print width (5.7 in) by
saving without earthkit's default tight bbox. Tie the caption fonts to the
shared sizes, and fix the caption to show the initialisation time + lead time
(it previously showed valid time + lead, double-counting the lead). Thin the
map furniture (global coastlines 0.25, regional coastlines 0.4, region borders
0.4, locator box 0.7, zoom callouts 0.5). Drop PDF export (the ~1M-point
forecast fields would balloon a vector PDF); PNG only at 250 dpi.
Pin text/label/tick colours to black and set mathtext.default=regular in the
shared style, so earthkit's Figure() (which resets these to grey #333333 and
leaves inline math in DejaVu) no longer makes the scoremaps/teaser text look
different after the style is re-applied. Style the scoremaps (a)/(b)/(c) panel
labels like the meteogram (bold, top-left, base size) and switch the teaser's
caption colour to black for cross-figure consistency.
Drop bbox_inches="tight" (which trimmed each figure to a different width) and
save at the fixed figsize instead, so every 2-column figure is exactly 5.7 in
and every 1-column figure exactly 3.35 in — consistent font sizes when placed
to scale. Reserve left margin on the leadtime figure so its rotated row labels
stay inside the canvas, and reserve a bottom band on the scoremaps so the
'… better' colorbar annotations aren't clipped.
Add an opt-in group_as_header path to the shared scorecard renderer: a
(variable, None) sentinel row draws the variable name as a bold subtitle
spanning above its metric rows (no dots), freeing the wide left margin the
wrapped names used to need. Default stays off, so the operational workflow
report is unchanged.

Enable it in the publication notebook and retune for print: insert the header
sentinels, stop wrapping variable names, drop the 5 in/panel width floor, and
size col_width/margins/fonts for a compact 2-column-scale figure. Save at the
exact figure size (no tight bbox) and auto-resolve the single candidate.
Set handlelength=3.0 (and handletextpad=0.5) on the shared legend so the dashed
ICON-CH1/CH2-EPS-mean line styles are distinguishable from the solid ones.
The teaser runs standalone (CLI args), so it doesn't need a config block; revert
this file to match paper-figures.
Switch off the Gridliner lines (child.xlines/ylines) in addition to its labels,
so the maps show only the skill field + country/coastline outlines.
Tune the scorecard print layout: header subtitles at 7 pt, metric labels 6 pt,
compact rows, tightened title/legend/region-title gaps, small inter-section
padding, and wider lead columns (col_width 0.11) so the lead-time labels are
legible — landing ~6.5 in wide (spacing all lead columns side-by-side needs a
bit more than the 2-column width). Shorten the variable label to
'Tot. precipitation' (hourly / 6-hourly).
New standalone workflow/scripts/plot_region_map.py: shades the three forecast
regions (Jura / Swiss Plateau / Alps) over a Copernicus-DEM hillshade and
overlays the SwissMetNet stations used in the paper's verification, symbol-coded
by observation completeness (all params / without pressure / precip only) with
the four case-study sites ringed.

The station set is read from the per-parameter station-verification result files
(<PARAM>_<lt>_caa0.nc), i.e. the exact coordinates scored per parameter, rather
than a raw jretrieve group query. The DEM and result store are read-only.

Adds REGION_COLORS (Paul Tol light, colourblind-safe) and region_color() to the
shared publication style, and documents the figure in docs/publication_figures.md.
Union the verified stations by their nat_abbr code (the `values` coordinate of
the caa0 files) instead of by coordinate: a site logs its sensors a few metres
apart, so coordinate matching split one station into two symbols. Union-by-code
gives 294 distinct stations (all-params 59 = every pressure station, 134 without
pressure, 101 precip-only).

Switch the station symbols to open circle / open triangle / cross (black, thin
outlines) for clearer shape distinction on the hillshade.
…llshade

- Tint the region legend swatches to the on-map colour (fill over the mean
  relief brightness) so they no longer look pale next to the shaded map.
- Case-study marker: drop the centre dot and shrink the ring so the map marker
  matches the legend exactly (a plain small red ring).
- Cache the strided Copernicus DEM subset locally (output/.cache), keyed by
  extent, so the 5 GB store tile is read only once; re-renders are fast.
- Rasterize the hillshade image so the vector PDF stays small (~260 KB) and
  writes quickly.
Add a 50 km scale bar (SW corner, drawn in the projection's metre units and
verified against a geodesic), a north arrow (NE corner), and a light dashed
1-degree lat/lon graticule labelled on the bottom/left, drawn under the
borders/stations so it stays subtle.
…ighter layout

- Left-align metric/score labels with the parameter-name subtitles.
- Grey dividers above and below each parameter name; break the (now grey)
  region separators across those header bands.
- Non-uniform row spacing: tighter metric rows, more room around parameter names.
- Trim padding (label-to-first-column gap, inter-card gap, right border) and
  squeeze column width so the figure hits the 5.7 in two-column target.
- Bold parenthesised panel letters (a)/(b) to match the other figures.
- Rename precipitation label to 'Total precipitation'.
Rename shared PARAM_LABELS: 'Wind Speed' -> '10m Wind Speed',
'Wind Direction' -> '10m Wind Direction', and 'Sea Level Pressure' ->
'Mean Sea-Level Pressure'. Applies across the scorecard, meteogram,
scoremap and leadtime figures.
Add a second output, publication_teaser_comparison.png, placing the Varda-Single
forecast next to the corresponding KENDA analysis (same valid time) and their
difference, over the Switzerland cutout, for 2 m temperature, 10 m wind and 2 m
dew point temperature. Forecast/analysis share one colour scale per field; the
difference uses a symmetric diverging scale. Maps sit on a fixed equal-cell grid
with colorbar insets so rows stay evenly spaced and the field colorbar clears the
difference panel.

Loads the regional Varda fields (incl. TD_2M) from the LAM GRIB and the matching
KENDA-CH1 analysis step from the anemoi zarr; both render on the shared ICON mesh.
Exposed via a --kenda option (defaults to the paper KENDA dataset; '' to skip).

@jonasbhend jonasbhend left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @clairemerker. Thanks a lot for the many improvements. I have only minor comments and am happy to merge once these are addressed.

Comment on lines 10 to 12

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- 2025-03-21T00:00

I think this emphasizes that a single initialization is run.

Comment on lines 9 to 11

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- 2025-06-27T18:00

same as above

**`scoremaps.py` — Spatial skill-score maps.** 2-D skill maps (MSE skill and the
bias contribution to it) for each parameter at chosen lead times, candidate vs
baseline; plus a per-season variant.
- *Needs:* a **gridded (zarr) truth** manifest with scoremap NC files (produced

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- *Needs:* truth dataset manifest(s) with scoremap NC files (produced

scoremaps.py also supports adding scores per station on top of map, or standalone map with just stations.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a bit worried that changes to the 'original' scorecard script affect the look of this? Have you checked if the standard scorecard still looks the same / acceptable?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants