# winds-engine: go/no-go analysis and plan
Status: revised 2026-10-24 (architecture decision recorded below)
Consumers: rockets-engine (trajectories, weathercock, dispersion), moving-batteries (artillery, via rockets-engine)
## Decision (2026-10-24)
Wind **modeling** is upgraded *inside* ballistics-engine's existing `wind`
module — **additively**: `WindSock`/`WindSegment` and every existing API stay
exactly as they are. New trait and profile types are pure additions; nothing
is replaced, no API expectations change.
**winds-engine is explicitly scoped to rocket modeling**: data acquisition
(GRIB2/HRRR/GFS ingestion), radiosonde validation, and feeding the kernel's
profile types to rockets-engine. It is not a general ballistics-ecosystem
wind crate and will not grow kernel-side modeling.
Rationale: the kernel's modeling pieces are small, pure physics, and belong
next to the atmosphere tables — but the ballistics-engine release train
(platform binaries, npm, PyPI, RubyGems, WASM terminal, docs) is too heavy
to couple to data-pipeline iteration. One deliberate kernel minor release
for the stable modeling core; everything that iterates lives here.
## What goes into ballistics-engine (one additive kernel release)
All in the existing `wind` module, alongside the untouched `WindSock`:
- `WindField` trait: `wind(time_s, position_m) -> Vec3` — stateless and
deterministic, the same sampling pattern the rockets-engine turbulence
work already relies on (RK4 substeps, snapshots, seed reproduction stay
bit-exact).
- `ConstantField(Vec3)` — today's uniform vector as a field.
- `ProfileField` — piecewise-linear vertical profile
`Vec<(altitude_m, speed_mps, direction_deg)>`.
- Analytic profiles: `LogProfile` (roughness boundary layer) and
`EkmanProfile` (geostrophic + latitude + roughness), consistent with the
kernel's atmosphere tables.
- An adapter so `WindSock` satisfies `WindField` — old and new interop;
no consumer changes required.
rockets-engine then gains an additive `BallisticForces::with_wind_field`
(keeping `with_wind_segments`), and the weathercock samples the same field
(the turbulence wiring already proved that path).
## What stays in winds-engine (rocket-modeling only)
1. GRIB2 ingestion (HRRR/GFS u/v on isobaric levels) → kernel
`ProfileField` at a launch coordinate: bilinear horizontal
interpolation, altitude conversion, offline cache, NOAA NOMADS subset
HTTP as the acquisition path (feature-gated `download`, off by default
so the crate stays embeddable/WASM-safe).
2. Radiosonde validation datasets (University of Wyoming sounding
archives): layer-mean speed/direction tolerances on a handful of
station-days.
3. rockets-engine-facing glue examples: `--wind-profile`, dispersion
profile perturbation with per-run seeding.
### GRIB2 risk item
1. Evaluate existing Rust GRIB crates for HRRR/GFS u/v-on-isobaric records.
2. Fall back to the NOAA NOMADS subset API (server-side extraction, small
downloads) with a checked-in golden file for tests.
3. Last resort: a tiny GRIB2 template decoder for exactly the two products
needed — explicitly not a general GRIB library.
## Explicitly out
- Tier C (WindNinja clone: FEM solver + weather init + GUI) — permanently.
- Diagnostic mass-consistent terrain flow (formerly Tier B): if ever
needed, its home is the kernel, not this crate; revisit only with a
concrete moving-batteries requirement.
- Replacing or wrapping existing ballistics-engine wind APIs.
## Determinism and dependency policy
- Kernel additions: no network, no wall-clock, no unseeded randomness;
everything reproducible from inputs.
- winds-engine: acquisition behind the non-default `download` feature;
the core decode/validate path is offline and deterministic.
- No AI/assistant attribution in commits; commit as the human developer.
## Effort and order
| `WindField` trait + constant/profile/analytic fields + `WindSock` adapter | ballistics-engine `wind` module | 3–4 days |
| Kernel minor release (run the full train, once) | ballistics-engine | 1 day |
| rockets-engine `with_wind_field` + `--wind-profile` + weathercock wiring | rockets-engine | 2–3 days |
| Radiosonde validation set | winds-engine | 2–3 days |
| GRIB acquisition + decode (one product) | winds-engine | 1–2 weeks (risk item) |
First milestone: kernel `WindField` release + rockets-engine
`--wind-profile` end to end, validated against two sounding days. Then
`winds-engine 0.1.0` with one GRIB product.
## Status: winds-engine 0.1.0 scaffolded (2026-10-24)
The crate exists with the modeling core **staged in it** rather than
waiting on the kernel release, because a useful initial crate needed the
types to exist somewhere. `src/field.rs` (`Wind`/`WindField`/
`ConstantField`/`ProfileField`), `src/analytic.rs` (`LogProfile`,
`EkmanProfile`) are byte-for-byte candidates for promotion into
ballistics-engine's `wind` module; at that kernel release this crate
re-exports them and drops its copies. Zero dependencies, 12 tests
including a real 115-level KMPX sounding (IEM RAOB archive, checked in as
`tests/data/mpx_20261023_00z.txt`), CI on GitHub.
Revised order: (1) promote the staged modeling core into ballistics-engine
(one kernel release), (2) rockets-engine `with_wind_field` /
`--wind-profile` against it, (3) GRIB acquisition here, (4) more sounding
validation days (one is checked in already).