Conversation
Adds engine='nexradlevel3' and open_nexradlevel3_datatree for NEXRAD Level 3 radial products (packets 16/AF1F/28) with values decoded to physical units, range-folded bins exposed as a separate mask, and SRMV/ACCUM/HCLASS added to the canonical moment names.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #392 +/- ##
==========================================
+ Coverage 94.20% 94.63% +0.43%
==========================================
Files 29 30 +1
Lines 6417 6934 +517
==========================================
+ Hits 6045 6562 +517
Misses 372 372
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
21 new synthetic tests covering malformed-file warnings/errors, the packet-16 ragged fallback, XDR parameters/multi-component/unknown-code paths, symbology offset override, surface-product geometry, RF mask variable, flag attrs, CF valid_min/max, reindex and drop_variables; drop two unused helpers. nexrad_level3.py: 100% line coverage from synthetic tests alone.
|
Hey @mgrover1 — really nice work on this one. I did a deep review pass: re-ran the suite in a clean env (all green), checked the coverage claim (it's real — 99% branch, 0 missed statements), and verified the decode math against the ICD (linear halfword scale/offset, float scale/offset, legacy-16 per-flag sign/scale, the unit conversions, and the hw36–38 flag-count handling all check out). The structured-dtype packet-16 path and the vectorized RLE expansion also hold up nicely. That said, I went adversarial on the edge cases and found a handful of things I think need fixing before merge. Everything in the first section I reproduced with the PR's own test helpers. Bugs (reproduced)1. Multi-component generic packets crash with a raw packet = _generic_packet28(2, 2, data, ncomponents=2)
NEXRADLevel3File(io.BytesIO(build_level3_file(msg_code=176, packet=packet)))
# AttributeError: 'list' object has no attribute 'radials'
2. So the decoded path and the raw+CF path disagree, and the raw path re-masks exactly the bins the PR says it rescues. 3.
Both come up naturally when someone globs 4. Truncated bz2 escapes the 5. Files truncated inside the headers leak raw 6. The nbins-override rule turns ICD halfword padding into a phantom bin 7. Raw-mode attrs vs decoded-mode mask disagree for While verifying this I hit a bigger problem with the raw-mode strategy itself: xarray's Design / consolidation (should-fix, not blocking)
Performance / memory
model.py side effects worth a look
Questions / scope
Test suggestions (they line up with the bugs)
Happy to open follow-up issues for the deferred items (legacy RF, WMO header flexibility) or help with any of the fixes. The core decode work here is really solid — most of the above is hardening around it. Edited: corrected the Py-ART header claim in "Questions / scope" (Py-ART requires |
Adds an xarray backend (
engine="nexradlevel3") andopen_nexradlevel3_datatreefor NEXRAD Level 3 (NIDS) radial products. Level 3 is available on AWS in real time and archived back to ~2020 (s3://unidata-nexrad-level3), so this opens a large, previously unreadable dataset to the xradar stack.Coverage
27 message codes across the three radial packet formats:
Deferred products (VIL/EET special encodings, TDWR, legacy hybrid variants) raise a clear
NotImplementedErrornaming the product; follow-up tracking issue to come. A multi-tilt volume of the same product (e.g. N0B-N3B) assembles into a DataTree ordered by fixed angle, following the IMD multi-file precedent.Deliberate divergences from Py-ART
Each verified against the ICD (2620001) and real data:
floor(1000*cos(elevation)), so ranges drift up to ~580 m by the last binflags[0]applied to all levels<moment>_range_foldedmaskraw < 1/raw < 2masks — on a real DPR file this marks ~92% of bins (genuine zero rain rate) as missingmodel.pygains SRMV/ACCUM/HCLASS (and RATE) in the canonical moment names; the twotest_io.pyexpectation updates reflect real moments in existing sample files that the canonical set previously missed.Verification
Performance (M-series laptop, warm, mean of 20)
Known limitation
Level 3 carries no per-ray times, so every ray holds the volume scan start time: multi-tilt volumes export to CfRadial2 but not CfRadial1 (documented in the docstring).