Package changes for the SpeedyWeather coupling (extension lives in SpeedyWeather) - #16
Conversation
Version of docs/plans/src_changes_for_speedyweather.md restricted to the changes on this branch; mg/ecckd-speedy carries the extended one. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Conflicts resolved by keeping this branch's SpeedyWeather 0.23 port of the extension and its test (with main's identifier names and main's constants helper, which passes the host's molar masses), keeping the per-vector array types of AtmosphereProfile on top of main's renamed column_views.jl, and combining the compat entries (RRTMGP from main, SpeedyWeather 0.23 and Julia 1.11 from this branch). README keeps both the SpeedyWeather and the Breeze sections. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
main meanwhile gained the streaming column API (PR #20). Its TabulatedSurfaceEmission and its Adapt-based EcCKDTabulatedGasOpticsModel{FT} converter supersede this branch's surface_longwave_emission! and constructor based converter, so main's ecckd_forward.jl and exports are taken as is. ColumnAtmosphere keeps this branch's per-array type parameters on top of main's new constants field. The host-interface tests follow main's water_vapor_* names and lose the in-place emission testset; the plan and the src-changes justification record the decisions. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
main's streaming column API supersedes this branch's own additions: the extension now blends two TabulatedSurfaceEmission vectors for ocean and land instead of the dropped surface_longwave_emission!, passes main's ShortwaveColumnScratch built from seven column views instead of the dropped CloudlessShortwaveWorkspace, and takes gravity and the molar masses from the host's PhysicalConstants through ColumnAtmosphere.constants. The package change of this branch shrinks to an optional caller-owned scratch argument of radiative_fluxes!(…, CloudlessShortwave(), …); the two-stream singularity guard is re-applied to main's shortwave_reflectance_transmittance. Tests, README, API docs, validation README and the plan documents follow. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…DME.md The Phase 4 budget comparison, per-column benchmark and per-step NaN detector had been left untracked. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Pass constants as a keyword (a bare name in a call without ';' is positional),
and give all seven shortwave scratch work arrays the interface length so their
column views share one type, as main's ShortwaveColumnScratch{V} requires; the
test cross-check had the same keyword slip.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… 1.10 SpeedyWeather >= 0.23 (the Radiation bundle) is not registered yet and is pulled from its development branch through a [sources] entry, which Julia 1.10 ignores; with it in test/Project.toml the 1.10 CI jobs could not resolve the test environment. The extension tests now live in test/speedyweather/ with their own environment and a dedicated CI job on Julia 1.11, test/Project.toml is registry-only again, runtests.jl skips the extension tests when SpeedyWeather is absent, and the package's Julia compat goes back to 1.10. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…heir own environment, Julia 1.10 kept)
…eir own environment, Julia 1.10 kept)
Brings in the move of the SpeedyWeather coupling into SpeedyWeather's own extension (SpeedyWeatherNumericalRadiationExt) and the new host-neutral ClearSkyEcCKDRadiation scheme type; src_changes_for_speedyweather.md gains the entry for that type. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The SpeedyWeather coupling now lives in SpeedyWeather's extension SpeedyWeatherNumericalRadiationExt: the extension directory is removed here, the ecCKD coupling tests, the example and the validation scripts use ClearSkyEcCKDRadiation and AnalyticBandLongwave through SpeedyWeather (branch mg/numericalradiation-extension), the README and the plan documents follow. src_changes_for_speedyweather.md lists the new scheme type as change 8. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Apache 2.0 relicensing, README restyle, TagBot, CI on Julia 1.10 and 1.13, and main's own fix of the shortwave two-stream pole (54b6a75). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
main's fix of the shortwave two-stream pole (54b6a75) replaces this branch's guard for the same NaN; src_changes_for_speedyweather.md now justifies exactly the src diff against main (four changes) and records the superseded ones. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Codecov coverage upload and badge, docs install instructions for the registered package. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…nges document test_with_speedyweather.jl becomes test_speedyweather_analytic_band_longwave.jl and test_speedyweather_clear_sky_ecckd.jl (one module each), included by both test runners. src_changes_for_speedyweather.md: ColumnAtmosphere's constants parameter in the heading, test file names, GPU wording. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
Archived markdown plans ecckd_speedyweather.md |
… core matrix entry Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…n scripts and their README Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
@glwagner This removes the SpeedyWeather extension and is a companion to a new extension in SpeedyWeather SpeedyWeather/SpeedyWeather.jl#1271 It needs to adjust a few (but not many) things here for it to work, each is justified in the original (edited) post separately. Currently I would prefer to merge this in a quick and dirty way of letting this PR depend on the feature branch and vice versa, merge both and then remove feature branch dependencies in a very quick follow up and release new versions of both SpeedyWeather and NumericalRadiation. Let me know, if you want to do this in a more proper way. |
The registered 0.1.0 predates this PR; SpeedyWeather's extension needs ClearSkyEcCKDRadiation and the per-array types and sets its compat bound to 0.1.1. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
I'm fine with that. This code isn't properly evaluated yet. |
Package-side changes for coupling NumericalRadiation to SpeedyWeather.jl. The coupling code itself lives in SpeedyWeather as the package extension
SpeedyWeatherNumericalRadiationExt(SpeedyWeather branchmg/numericalradiation-extension, on top of theRadiationbundle of SpeedyWeather/SpeedyWeather.jl#1252); this PR makes the package usable from there and keeps the full tests and validation of the coupling here. It collapses the former stack #16, #17, #19 into one PR.Tests
test/test_ecckd_radiation.jl(the scheme type),test/test_host_interface.jl(host-style construction ofColumnAtmospherefrom views, zero-allocation shortwave call with a caller-owned scratch, two-stream singularity scan).test/speedyweather/: the full coupling tests against SpeedyWeather's extension (test_speedyweather_analytic_band_longwave.jl,test_speedyweather_clear_sky_ecckd.jl), in an environment of their own (SpeedyWeather ≥ 0.23 from its branch, Julia ≥ 1.11) with a dedicated CI job; the core suite runs on Julia 1.10 and 1.13.Example and validation:
examples/speedyweather_ecckd.jl(one-band vs ecCKD budgets),validation/speedyweather_ecckd_budget.jl,speedyweather_ecckd_benchmark.jl,speedyweather_nan_detector.jl; results and design indocs/plans/ecckd_speedyweather.md,speedyweather_upstream.md,extension_upstream.md.Merged with
mainas of 9c1e494;main's fix of the shortwave two-stream pole (54b6a75) supersedes the guard this work had added for the same NaN.Changes to
src/compared tomain, and whyCompanion to ecckd_speedyweather.md and
extension_upstream.md. The guiding rule for the
SpeedyWeather coupling is to keep changes to the package's core small and to
prefer the host side (SpeedyWeather's
SpeedyWeatherNumericalRadiationExt, wherethe coupling code lives) whenever that is possible without giving up
performance. This section lists every change to
src/that PR #16 makes relativeto
main(as of9c1e494, 2026-09-29), what forced it, what the alternativewould have been, and how it is tested. Nothing here changes numerical results
of existing code paths; every change is a type-parameter relaxation, an optional
argument, or an addition.
AtmosphereProfile: one array-type parameter per vectorsrc/column_views.jlColumnAtmosphere: one array-type parameter per arraysrc/runtime_interfaces.jlShortwaveColumnScratchargument ofradiative_fluxes!(…, CloudlessShortwave(), …)src/solvers/cloudless_shortwave.jlClearSkyEcCKDRadiationanddefault_ozone_profile: the configured clear-sky ecCKD column schemesrc/ecckd_radiation.jl(new),src/NumericalRadiation.jlAnalyticBandLongwave:ocean_emissivityandland_emissivityfields (default 1), closes #9src/longwave/williams_longwave.jlThree further changes that this work carried at some point were superseded by
mainand are no longer part of the diff; they are listed at the end.1.
AtmosphereProfile{NF, VT, VQ, VG}(was{NF, V})File:
src/column_views.jl.What forced it. The analytic-band adapter builds an
AtmosphereProfilefromviews into SpeedyWeather arrays. Since SpeedyWeather 0.22 the temperature and
humidity views come from stepped three-dimensional arrays
(
get_prognostic_step(vars.grid.temperature, ...)) while the geopotential viewcomes from a plain two-dimensional field. Their
SubArraytypes differ, andthe old struct forced all three vectors to share one type
V; constructionthrew a
MethodErrorinconvert.Alternative considered. Copying the geopotential into a scratch view of the
same shape as the temperature step view. That costs a copy per column per
step for a type-system artefact, and the scratch array would have to mirror
the time-step layout of the prognostic arrays.
Why acceptable. Only the type parameters changed; the constructor converts
surface_pressure,rain_rateandCO₂toNFas before. No code in thepackage dispatched on the second parameter (
radiative_transfer_column.jlandwilliams_longwave.jluseAtmosphereProfile{NF}).Tests. The coupling test
test/test_speedyweather_analytic_band_longwave.jlconstructs the profile from real SpeedyWeather views (through SpeedyWeather's
extension); the solver suite is unchanged.
2.
ColumnAtmosphere{FT, PL, PI, TL, TI, G, S, Geo, C}(was{FT, A, G, S, Geo, C})File:
src/runtime_interfaces.jl.What forced it. The same problem for the staged interface: layer
temperature is a view into a stepped prognostic array, layer pressure a view
into a
(npoints, nlayers)work array, interface quantities views into(npoints, nlayers + 1)work arrays. Three differentSubArraytypes wherethe struct demanded one.
Alternative considered. Copying the layer temperature into a work array
every column. Same objection as in 1.
Why acceptable.
heating_rates!,optical_properties!and the solvers readthe arrays element-wise and convert to their working precision; none of them
dispatched on the array type. The docstring states that
FTis the elementtype of
temperature_layers.Tests.
test/test_host_interface.jl, "ColumnAtmosphere from differentlyshaped host views": optics, fluxes and heating rates from views of a 2D/3D host
layout are identical to those from plain vectors.
3. Optional
scratchargument ofradiative_fluxes!(…, CloudlessShortwave(), …)File:
src/solvers/cloudless_shortwave.jl.What forced it. The Rayleigh (scattering) path of the clear-sky shortwave
solver needs seven work vectors. Before
main's refactor they were allocatedper g-point and per column (about 400 allocations per column per step for the
32-g-point model);
mainnow allocates oneShortwaveColumnScratchperradiative_fluxes!call, i.e. seven vectors per column per step. InsideSpeedyWeather's fused column kernel that would be the only allocation in the
hot loop on CPU (and allocation is not possible inside a GPU kernel), and the
extension cannot avoid it without dropping Rayleigh scattering.
Alternative considered. This work originally carried its own
CloudlessShortwaveWorkspacetype and a rewritten accumulation; at the firstmerge of
mainthat was dropped in favour ofmain's type.What changed.
radiative_fluxes!gained an optional last positionalargument
scratch::ShortwaveColumnScratch, defaulting to the allocationmainalready did. A host passes seven column views through the type'spositional constructor. No other line of the solver changed.
Tests.
test/test_host_interface.jl, "CloudlessShortwave with acaller-owned scratch": identical fluxes with and without the argument, zero
allocations with it, a scratch built from views.
4.
ClearSkyEcCKDRadiation(new filesrc/ecckd_radiation.jl, oneinclude)What forced it. With the coupling moved into SpeedyWeather, the extension there
adds methods to this package's scheme types instead of defining wrapper types of
its own (
AnalyticBandLongwaveneeded nothing). The ecCKD path had no schemetype:
EcCKDTabulatedGasOpticsModelis a coefficient table, and the couplingneeds configuration with no other home, the mole fractions of the gases a host
does not carry (ozone, further gases) and the surface emissivity, implicitly
also the solver pair.
ClearSkyEcCKDRadiationbundles gas optics,mole_fractions(numbers or functions of pressure,o3andco2defaults,every further gas of the model required) and
surface_emissivity; constructorsfrom a tabulated model, from a number format and reference model pair, and a
converting
{FT}form;default_ozone_profilemoved here from the extension.Alternative considered. Keeping a SpeedyWeather-side type (needs an exported
type, an
ArgumentErrorstub and asrcfile in SpeedyWeather for what isNumericalRadiation configuration), or extending the coefficient table directly
with hard-coded defaults (no user configuration, and a type change once clouds
or emissivities become configurable).
Why acceptable. Purely additive, host-neutral (Breeze needs the same
configuration), no existing code path changes. The SpeedyWeather extension
imports the name at load time, so it has to be part of this PR.Tests.
test/test_ecckd_radiation.jl(construction, defaults, conversion,missing-gas error);
test/test_speedyweather_clear_sky_ecckd.jland SpeedyWeather'stest/parameterizations/numericalradiation.jluse it end to end.5.
ocean_emissivityandland_emissivityofAnalyticBandLongwaveFile:
src/longwave/williams_longwave.jl.What forced it. #9: through SpeedyWeather there was no way to set the surface emissivities, which the solver reads from the
SurfaceStatea host builds per column (default 1). SpeedyWeather has no surface emissivity of its own (its one-band longwave keeps them as scheme parameters), and the scheme type is NumericalRadiation's, so the only place a user-chosen value can live is the scheme; the extension passes them into the surface state:AnalyticBandLongwave(spectral_grid; ocean_emissivity = 0.98, land_emissivity = 0.97).Alternative considered. A SpeedyWeather-side wrapper type (dropped for both schemes), or per-grid-point emissivity fields declared by the extension, which have no initialization hook for radiation components in SpeedyWeather.
Why acceptable. Two fields with default 1, so nothing changes unless set; the same pattern as
ClearSkyEcCKDRadiation.surface_emissivity. Hosts with their own surface emissivity keep passing it intoSurfaceStateand ignore the fields.Tests.
test/test_speedyweather_analytic_band_longwave.jl, "Ocean and land emissivity": the scheme's values reach the surface state, the ocean and land upward fluxes scale with them, the OLR drops.Superseded by
mainduring the mergesWritten here first, then dropped when
mainprovided the same:surface_longwave_emission!(in place).main's streaming column API(PR Kernel-facing column API and line-by-line validation for the Breeze coupling #20, Breeze coupling) added
TabulatedSurfaceEmission(model, T; emissivity),a lazy
AbstractVectorwhosegetindex(g)evaluates the source table for oneg point; a host passes it as
surface_longwave_up, or blends two of them per gpoint as the SpeedyWeather extension does for ocean and land, without any
allocation.
EcCKDTabulatedGasOpticsModel{FT}(model).mainadded the same method,converting every array through an
Adaptstorage adaptor, sharing arrays alreadyin
FT, and working on device arrays. The behavioural tests written here were kept.divide by
1 - (λμ₀)²; the old guard detected μ₀ within 1000 ulps of the pole butnudged it by 10 ulps, which in Float32 could land exactly on the pole and produced
NaN fluxes in a coupled SpeedyWeather run (found with
validation/speedyweather_nan_detector.jl). This work moved μ₀ to the nearer edgeof the band;
mainfixed the same pole independently (54b6a75, found in a BreezeFloat32 run) by stepping to the lower edge with a branch-free
ifelse. Same band,same escape size, so
main's version stands; the singularity scan intest/test_host_interface.jlstays as the regression test.Considered and deliberately not changed
@boundscheckaroundcheck_ecckd_optics_shapes. The checks are O(1)size comparisons, negligible on CPU, and
@boundscheckwould only removethem if
optical_properties!were inlined into the@inboundscaller, whichit is not. Their
throwpaths need a kernel-safe variant for GPU anyway;revisit with the first GPU run.
optical_properties!per stream. Unnecessary once SpeedyWeatherbundles both streams into one component (upstream change U1): one call fills
both streams, which is exactly what the component needs.
(ng, nlayers)-major work-array layout accessor. APermutedDimsArrayof the
(nlayers, ng)column slice gives the package's[ig, k]indexing atno cost in the package; only if that shows up in profiles is a change
warranted.
the extension (
gas_amounts!,interface_temperatures!).fluxes.longwave_up .= 0and the like).Fine on CPU; GPU kernels need scalar loops. Deferred to the first GPU run
together with the bounds checks.
🤖 Generated with Claude Code