Skip to content

Add ERA5PrescribedRadiation for regional hindcast forcing #206

@ewquon

Description

@ewquon

Motivation

#200 introduces three *PrescribedRadiation constructors (JRA55PrescribedRadiation, ECCOPrescribedRadiation, OSPapaPrescribedRadiation) covering MIP-protocol global ocean spin-ups, ECCO state-estimate-driven runs, and single-column observational forcing respectively. Missing from the family is the natural radiation analogue for regional hindcast forcing at mesoscale resolution.

The data plumbing is already in place: src/DataWrangling/ERA5/ERA5.jl:87–88, 117–118 already maps :downwelling_shortwave_radiation → "surface_solar_radiation_downwards" (ssrd) and :downwelling_longwave_radiation → "surface_thermal_radiation_downwards" (strd) for both the CDS API and the NetCDF short names. What's missing is the convenience constructor that wires those up into a PrescribedRadiation.

Target use case

Regional / mesoscale hindcast simulations (typically ~O(1) km grids) where the user is replaying a specific past period with a regional ocean model initialized from GLORYS and forced by ERA5 atmosphere. This is a different regime from the existing *PrescribedRadiation constructors:

  • Not OMIP-protocol-bound, so JRA55-do's MIP-comparability isn't a constraint.
  • Not budget-closed multi-decadal, so ECCO is the wrong tool.
  • Not single-column, so OSPapa is the wrong tool.

Why ERA5 specifically for this regime

  • Self-consistency with GLORYS. GLORYS12V1 is a NEMO 1/12° ocean reanalysis forced at the surface by ERA5 (older versions used ERA-Interim). Using ERA5 radiation in a hindcast initialized from GLORYS continues the same forcing the ocean state was assimilated under, minimizing spurious spin-up drift. Pairing ERA5 atmosphere with JRA55-do radiation introduces a forcing mismatch the ocean would adjust away from over weeks of integration.
  • Higher native resolution than JRA55. 0.25° vs. 1.25° — the downscaling factor to 2–3 km is ~10× rather than ~50×, and coastal/orographic gradients are better preserved at the input.
  • Hourly output vs. JRA55's 3-hourly. Matters for diurnal-cycle fidelity in the SW driver — relevant for boundary-layer evolution, sea-breeze onset, and daytime heating of the upper ocean mixed layer.

Proposed API

Mirror the JRA55 template at src/DataWrangling/JRA55/JRA55_prescribed_radiation.jl:

ERA5PrescribedRadiation(architecture = CPU(), FT = Float32;
                        dataset = ERA5Hourly(),
                        bounding_box = nothing,
                        start_date = first_date(dataset, :downwelling_shortwave_radiation),
                        end_date = last_date(dataset, :downwelling_shortwave_radiation),
                        time_indexing = Cyclical(),
                        ocean_surface = SurfaceRadiationProperties(0.05, 0.97),
                        sea_ice_surface = SurfaceRadiationProperties(0.7, 1.0),
                        stefan_boltzmann_constant = default_stefan_boltzmann_constant,
                        kw...)

Returns a PrescribedRadiation backed by ERA5 SW/LW FieldTimeSeries — interchangeable with the other *PrescribedRadiation constructors at the EarthSystemModel API level.

Implementation notes

Three things that go beyond a straight copy of the JRA55 template:

  1. Unit conversion: J/m² → W/m². ERA5 stores ssrd/strd as accumulated energy (J/m²) over the previous hour, not instantaneous flux. Need to divide by 3600 to get W/m². Add a unit-conversion type (e.g., InverseAccumulationInterval(3600)) following the pattern of the existing Kelvin, InverseGravity, etc. in src/DataWrangling/metadata.jl, and a corresponding convert_units dispatch in src/DataWrangling/metadata_field.jl. Wire it via conversion_units(::ERA5RadiationMetadata).

  2. Bounding-box plumbing. Regional hindcast users almost never want global ERA5 radiation — they want a domain-bounded subset matching their model footprint. The BoundingBox(longitude=…, latitude=…) machinery is the right entry point; the constructor should forward bounding_box to both the CDS download request and the FieldTimeSeries allocation.

  3. CDS download dispatch. ext/NumericalEarthCDSAPIExt.jl already handles surface-data requests via reanalysis-era5-single-levels; adding the two radiation variable names to the existing ERA5_dataset_variable_names and verifying the request handler covers them should suffice — they're surface variables, not pressure-level, so no new request structure is needed.

File layout

  • src/DataWrangling/ERA5/ERA5_prescribed_radiation.jl — the constructor, mirroring JRA55_prescribed_radiation.jl
  • Include from src/DataWrangling/ERA5/ERA5.jl
  • Export from the ERA5 submodule and re-export from src/NumericalEarth.jl

Acceptance criteria

  • ERA5PrescribedRadiation(arch; bounding_box, start_date, end_date) returns a working PrescribedRadiation whose time_step! updates the SW/LW fields correctly
  • Unit conversion verified: a known ERA5 grid point in J/m² at hourly accumulation produces the expected W/m² value
  • Pairs cleanly with OceanOnlyModel(ocean; atmosphere, radiation) where atmosphere is an ERA5PrescribedAtmosphere (when that exists) or a JRA55PrescribedAtmosphere (today), exercising the new top-level radiation slot from (0.4.0) Add radiation as a top-level EarthSystemModel component #200
  • Test in test/test_radiations.jl (or a new test/test_era5_radiation.jl) using a small bounding_box to cap CDS download cost in CI
  • Docstring covers the J/m²→W/m² conversion, the GLORYS-pairing rationale, and the regional-hindcast use case so users don't blindly reach for it in a global MIP context

Related

Out of scope

  • CERES-based bias correction of ERA5 radiation (a separate, larger effort)
  • Operational / near-real-time ERA5 access for forecasting (the operational ECMWF IFS would be the right product for that, not ERA5)
  • ERA5-Land radiation variants

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancement ✨ideas and requests for new features

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions