Repository navigation
ENH: NEXRAD Level 2 nyquist_velocity/unambiguous_range per sweep, mask below-threshold and range-folded codes - #457
Conversation
Add nyquist_velocity and unambiguous_range per sweep from the MSG_31 radial data block or the MSG_1 header. Decode the special moment codes below threshold (0) and range folded (1) to NaN via _FillValue when mask_and_scale=True; raw codes are kept with mask_and_scale=False.
…-special-codes # Conflicts: # docs/history.md
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #457 +/- ##
==========================================
+ Coverage 96.02% 96.03% +0.01%
==========================================
Files 30 30
Lines 7472 7494 +22
==========================================
+ Hits 7175 7197 +22
Misses 297 297
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:
|
|
Great, my brain failed me. So I always was under the impression that in the appending changelog case this will go without creating merge conflicts, but alas... 🤷 Nevermind @syedhamidali, but maybe adding to the end is still preferable. |
|
@kmuehlbauer yes, git sees two PRs adding a line at the same place as a conflict, wherever that place is. I tried the options in a small repo:
So what actually works is changelog fragments, e.g. with towncrier (used by pip, pytest, attrs, twisted; GitLab moved to per-MR changelog files and reported ~90% fewer merge conflicts):
Setup is a |
# Conflicts: # docs/history.md
# Conflicts: # docs/history.md
aladinor
left a comment
There was a problem hiding this comment.
Thanks @syedhamidali, this works well. One suggestion on CFP (inline), not a blocker. Approving.
What I ran, at c20fa00 merged locally with main (a8f2dac): NEXRAD, CfRadial1/2 and model tests, 278 passed, black/ruff clean. The 11 new or changed NEXRAD tests fail on main. nyquist_velocity matches the RAD block on every sweep of KATX 2013, KILX 2023 and KLBB 2016 (e.g. 8.81/35.09/29.97/32.33 m s-1 on KATX), and the KLIX MSG_1 surveillance cut gives NaN. The moments stay lazy, and mask_and_scale={"VRADH": False} and False work through xr.open_dataset, xr.open_datatree, xd.open_datatree and open_nexradlevel2_datatree. to_cfradial1 writes both values as (time,) and they read back; the moments are the same after both round trips.
Before merging: main needs to be merged in once more. The conflict in NexradLevel2Store.__init__ is only main's _ManagedStoreMixin base (from #433) next to the new mask_and_scale argument, and the history entry moves from 0.13.0 to Development (unreleased). The closing fixture in the test suggestion comes with that merge.
Nit: the new tests don't close what they open; main's NEXRAD tests use the closing fixture now.
On your first open point, I'm fine with range folded masked by default: the IRIS reader also returns NaN for its no-data codes (#465), and the raw codes stay available with mask_and_scale=False. @kmuehlbauer may see it differently.
Co-authored-by: Alfonso Ladino <aladinor@unal.edu.co>
Co-authored-by: Alfonso Ladino <aladinor@unal.edu.co>
Co-authored-by: Alfonso Ladino <aladinor@unal.edu.co>
|
Thanks for applying the CFP suggestions. The new test uses the |
# Conflicts: # docs/history.md # xradar/io/backends/nexrad_level2.py
# Conflicts: # docs/history.md
Either way is fine. Finally, the user will tell us. |
|
Thanks all! |
|
Thank you! |
history.mdThe NEXRAD Level 2 reader had two gaps that downstream processing (e.g. velocity dealiasing, QC) had to work around by reading the raw headers:
Nyquist velocity / unambiguous range were not exposed. Both are in the MSG_31 radial data block (
RAD, ICD 2620002 Table XVII-H, scaled by 0.01 m s-1 / 0.1 km) and in the MSG_1 header for legacy files.Each sweep now carries scalar
nyquist_velocity(m s-1,get_nyquist_velocity_attrs, same as the ODIM/GAMIC readers from add nyquist_velocity as coordinate in ODIM H5/GAMIC reader #291) andunambiguous_range(meters, newget_unambiguous_range_attrsinxradar.model). Both names are already listed inoptional_sweep_metadata_vars(CfRadial2/FM301). Surveillance-only MSG_1 cuts store zero, which is returned as NaN.Example (KATX20130717_195021_V06): sweep_0 (CS) 8.81 m s-1 / 466 km, sweep_1 (CD) 35.09 m s-1 / 117 km.
Special moment codes were decoded as data. Per ICD Table XVII-I, raw
0= below threshold and1= range folded for all moments, so they showed up as e.g. DBZH −33/−32.5 dBZ, VRADH −64.5/−64 m s-1, ZDR −8 dB, RHOHV 0.20.The moments now get
_FillValue = 0. Because xarray can only round-trip a single fill value cleanly (several values inmissing_valuewarn on every open and conflict with_FillValueon encode), the array wrapper maps range folded (1) to below threshold (0) when masking is active, so both decode to NaN while the data stay lazy. Padding of shorter rays also uses the fill code.With
mask_and_scale=False(globally or per variable, e.g.mask_and_scale={"VRADH": False}) the raw codes are returned unchanged, so range folded gates stay distinguishable (raw.VRADH == 1). This is documented in the docstring and indocs/importers.md.Export: the new
_FillValueencoding round-trips throughto_cfradial2andto_cfradial1(NaN is written as0, values identical after reopening).Open points for review
_Undetecthandling), which needs a second fill value. Happy to change this if you prefer that.CCORH(CFP, clutter filter power removed) has more special codes (2–7 in the ICD). Only 0 and 1 are masked here, as for all other moments.to_cfradial2currently does not writenyquist_velocity/unambiguous_rangeto the sweep groups. Not changed here.nyquist_velocity,unambiguous_range); the two existing tests checking the count are updated.Tests
mask_and_scale=False, per-variablemask_and_scalemapping.pytest tests: 686 passed. Pre-commit: all hooks pass exceptzizmor, which reports the same findings onmain.