Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: Add a reader for nexrad level2 files #147

Closed
wants to merge 50 commits into from
Closed
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
c9d88ec
ENH: Add first cut at nexrad reader
mgrover1 Jan 5, 2024
f7e42d2
FIX: Fix incorrect module registration
mgrover1 Jan 8, 2024
34b52b0
FIX: Fix the requirements for the package
mgrover1 Jan 8, 2024
9330b17
ADD: Add new xarray dataset builder
mgrover1 Jan 18, 2024
fbd942c
FIX: Fix last few steps
mgrover1 Jan 19, 2024
3f8f8cf
ADD: Add testing suite
mgrover1 Jan 19, 2024
e042f1f
FIX: Fix the manifest
mgrover1 Jan 19, 2024
0c2d0dc
FIX: Fix local import nexrad_interpolate
mgrover1 Jan 19, 2024
4f1d06c
FIX: Fix import of interpolation
mgrover1 Jan 19, 2024
1af4649
ADD: Add missing cython depend
mgrover1 Jan 19, 2024
2e37130
ADD: Add updated manifest
mgrover1 Jan 19, 2024
7ae79bd
ADD: Ensure cython is packaged
mgrover1 Jan 19, 2024
ec2261c
ADD: Add specific submodules
mgrover1 Jan 19, 2024
568e65b
force reinstall
mgrover1 Jan 19, 2024
0a56163
make sure more files are included
mgrover1 Jan 19, 2024
a904c03
FIX: Fix build of cython extensions
mgrover1 Jan 25, 2024
14aa01c
FIX: Fix lowercase letter
mgrover1 Jan 25, 2024
888fb1b
revert couple of settings
mgrover1 Jan 25, 2024
44647ac
fix installation line
mgrover1 Jan 25, 2024
2b8d9b9
ADD: Add proper import
mgrover1 Jan 25, 2024
0550e2c
move interpolation to its own submodule
mgrover1 Jan 25, 2024
78fada3
ADD: Update setup
mgrover1 Jan 25, 2024
cb94350
Move to hidden module
mgrover1 Jan 25, 2024
c3824d6
FIX: Fix all imports in backends
mgrover1 Jan 25, 2024
5e0da73
fix manifest
mgrover1 Jan 25, 2024
d16e2e3
include all cython
mgrover1 Jan 25, 2024
3a0a76f
Add other types of cython files
mgrover1 Jan 25, 2024
62db259
debug submodule
mgrover1 Jan 25, 2024
2edb91c
ADD: add this back in
mgrover1 Jan 25, 2024
f0ec0e7
Be explicit about submodules
mgrover1 Jan 25, 2024
f1af9cd
ADD: Add clear submodule
mgrover1 Jan 25, 2024
6c7b824
ADD: Add imports
mgrover1 Jan 25, 2024
f162928
remove name in setup
mgrover1 Jan 25, 2024
9592148
remove check on version
mgrover1 Jan 25, 2024
045ba99
make sure submodule is blank
mgrover1 Jan 25, 2024
2f26565
ADD: Add extra line
mgrover1 Jan 25, 2024
d7d65b2
be more explicit
mgrover1 Jan 25, 2024
3b49bc3
add setup.py run
mgrover1 Jan 26, 2024
9c11f0b
ADD: Add proper installation to other parts
mgrover1 Jan 26, 2024
e1a59df
ADD: add test for lazy dict
mgrover1 Jan 26, 2024
98090ad
DEL: Remove unused sections of common
mgrover1 Jan 26, 2024
528a16d
DEL: Remove the interpolation step
mgrover1 Jan 30, 2024
1b0d97b
FIX: Fix linting
mgrover1 Jan 30, 2024
bae3d19
Only use ruff for linting
mgrover1 Jan 30, 2024
aee82ed
DOC: Add addition to history file
mgrover1 Jan 30, 2024
fc03120
ADD: add original ci back in
mgrover1 Jan 30, 2024
c33d521
DEL: Delete extra init
mgrover1 Jan 30, 2024
5c7bd7e
Update xradar/io/backends/common.py
mgrover1 Jan 31, 2024
091a7c0
Merge latest updates on main
mgrover1 Feb 28, 2024
2a1c46e
Merge branch 'add-nexrad-reader' of https://github.com/mgrover1/xrada…
mgrover1 Feb 28, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ jobs:
- name: Install xradar
run: |
python -m pip install . --no-deps
python setup.py build_ext --inplace
- name: Version Info
run: |
python -c "import xradar; print(xradar.version.version)"
Expand Down Expand Up @@ -104,6 +105,7 @@ jobs:
- name: Install xradar
run: |
python -m pip install . --no-deps
python setup.py build_ext --inplace
- name: Version Info
run: |
python -c "import xradar; print(xradar.version.version)"
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/upload_pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ jobs:
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
python -m build
python setup.py build_ext --inplace
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wont be needed anymore.

twine upload dist/*
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ recursive-include tests *
recursive-exclude * __pycache__
recursive-exclude * *.py[co]

global-include *.pyx *pxd
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be removed too.

recursive-include docs *.rst conf.py Makefile make.bat *.jpg *.png *.gif
1 change: 1 addition & 0 deletions ci/notebooktests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ dependencies:
- cmweather
- codecov
- coverage
- cython
- dask
- h5netcdf
- h5py
Expand Down
1 change: 1 addition & 0 deletions ci/unittests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ dependencies:
- codecov
- cmweather
- coverage
- cython
- dask
- h5netcdf
- h5py
Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,15 @@ gamic = "xradar.io.backends:GamicBackendEntrypoint"
iris = "xradar.io.backends:IrisBackendEntrypoint"
odim = "xradar.io.backends:OdimBackendEntrypoint"
rainbow = "xradar.io.backends:RainbowBackendEntrypoint"
nexradlevel2 = "xradar.io.backends:NexradLevel2BackendEntrypoint"

[build-system]
requires = [
"setuptools>=45",
"wheel",
"setuptools_scm[toml]>=7.0",
"cython",
"numpy"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

numpy can be removed?

]
build-backend = "setuptools.build_meta"

Expand Down
24 changes: 24 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from Cython.Build import cythonize
from Cython.Compiler import Options
from setuptools import Extension, setup

# These are optional
Options.docstrings = True
Options.annotate = False

# Modules to be compiled and include_dirs when necessary
extensions = [
Extension(
"xradar.interpolate._nexrad_interpolate",
sources=["xradar/interpolate/_nexrad_interpolate.pyx"],
),
]


# This is the function that is executed
setup(
# external to be compiled
ext_modules=cythonize(
extensions, compiler_directives={"language_level": "3", "cpow": True}
),
)
5 changes: 5 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,8 @@ def iris0_file():
@pytest.fixture(scope="session")
def iris1_file():
return DATASETS.fetch("SUR210819000227.RAWKPJV")


@pytest.fixture(scope="session")
def nexradlevel2_file():
return DATASETS.fetch("KATX20130717_195021_V06")
9 changes: 9 additions & 0 deletions tests/io/backends/test_common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from xradar.io.backends import common


def test_lazy_dict():
d = common.LazyLoadDict({"key1": "value1", "key2": "value2"})
assert d["key1"] == "value1"
lazy_func = lambda: 999
d.set_lazy("lazykey1", lazy_func)
assert d["lazykey1"] == 999
30 changes: 30 additions & 0 deletions tests/io/test_nexrad_level2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env python
# Copyright (c) 2024, openradar developers.
# Distributed under the MIT License. See LICENSE for more info.

"""Tests for `xradar.io.nexrad_archive` module."""

import xarray as xr

from xradar.io.backends import open_nexradlevel2_datatree


def test_open_nexradlevel2_datatree(nexradlevel2_file):
dtree = open_nexradlevel2_datatree(nexradlevel2_file)
ds = dtree["sweep_0"]
assert ds.attrs["instrument_name"] == "KATX"
assert ds.attrs["nsweeps"] == 16
assert ds.attrs["Conventions"] == "CF/Radial instrument_parameters"
assert ds["DBZH"].shape == (719, 1832)
assert ds["DBZH"].dims == ("azimuth", "range")
assert int(ds.sweep_number.values) == 0


def test_open_nexrad_level2_backend(nexradlevel2_file):
ds = xr.open_dataset(nexradlevel2_file, engine="nexradlevel2")
assert ds.attrs["instrument_name"] == "KATX"
assert ds.attrs["nsweeps"] == 16
assert ds.attrs["Conventions"] == "CF/Radial instrument_parameters"
assert ds["DBZH"].shape == (719, 1832)
assert ds["DBZH"].dims == ("azimuth", "range")
assert int(ds.sweep_number.values) == 0
1 change: 1 addition & 0 deletions xradar/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
# import subpackages
from . import accessors # noqa
from . import georeference # noqa
from . import interpolate # noqa
from . import io # noqa
from . import model # noqa
from . import util # noqa
Expand Down
11 changes: 11 additions & 0 deletions xradar/interpolate/__init__.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env python
# Copyright (c) 2024, openradar developers.
# Distributed under the MIT License. See LICENSE for more info.

"""
XRadar Interpolation
====================

"""

from xradar.interpolate._nexrad_interpolate cimport * # noqa
8 changes: 8 additions & 0 deletions xradar/interpolate/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env python
# Copyright (c) 2024, openradar developers.
# Distributed under the MIT License. See LICENSE for more info.

"""
XRadar Interpolation
====================
"""
110 changes: 110 additions & 0 deletions xradar/interpolate/_nexrad_interpolate.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
"""
Interpolation of NEXRAD moments from 1000 meter to 250 meter gate spacing.

"""

def _fast_interpolate_scan_4(
float[:, :] data, float[:] scratch_ray, float fill_value,
int start, int end, int moment_ngates, int linear_interp):
""" Interpolate a single NEXRAD moment scan from 1000 m to 250 m. """
# This interpolation scheme is only valid for NEXRAD data where a 4:1
# (1000 m : 250 m) interpolation is needed.
#
# The scheme here performs a linear interpolation between pairs of gates
# in a ray when the both of the gates are not masked (below threshold).
# When one of the gates is masked the interpolation changes to a nearest
# neighbor interpolation. Nearest neighbor is also performed at the end
# points until the new range bin would be centered beyond half of the range
# spacing of the original range.
#
# Nearest neighbor interpolation is performed when linear_interp is False,
# this is equivalent to repeating each gate four times in each ray.
#
# No transformation of the raw data is performed prior to interpolation, so
# reflectivity will be interpolated in dB units, velocity in m/s, etc,
# this may not be the best method for interpolation.
#
# This method was adapted from Radx and Py-ART
cdef int ray_num, i, interp_ngates
cdef float gate_val, next_val, delta

interp_ngates = 4 * moment_ngates # number of gates interpolated

for ray_num in range(start, end+1):

# repeat each gate value 4 times
for i in range(moment_ngates):
gate_val = data[ray_num, i]
scratch_ray[i*4 + 0] = gate_val
scratch_ray[i*4 + 1] = gate_val
scratch_ray[i*4 + 2] = gate_val
scratch_ray[i*4 + 3] = gate_val

if linear_interp:
# linear interpolate
for i in range(2, interp_ngates - 4, 4):
gate_val = scratch_ray[i]
next_val = scratch_ray[i+4]
if gate_val == fill_value or next_val == fill_value:
continue
delta = (next_val - gate_val) / 4.
scratch_ray[i+0] = gate_val + delta * 0.5
scratch_ray[i+1] = gate_val + delta * 1.5
scratch_ray[i+2] = gate_val + delta * 2.5
scratch_ray[i+3] = gate_val + delta * 3.5

for i in range(interp_ngates):
data[ray_num, i] = scratch_ray[i]


def _fast_interpolate_scan_2(
float[:, :] data, float[:] scratch_ray, float fill_value,
int start, int end, int moment_ngates, int linear_interp):
""" Interpolate a single NEXRAD moment scan from 300 m to 150 m. """
# This interpolation scheme is only valid for NEXRAD TWDR data where a 2:1
# (300 m : 150 m) interpolation is needed.
#
# The scheme here performs a linear interpolation between pairs of gates
# in a ray when the both of the gates are not masked (below threshold).
# When one of the gates is masked the interpolation changes to a nearest
# neighbor interpolation. Nearest neighbor is also performed at the end
# points until the new range bin would be centered beyond half of the range
# spacing of the original range.
#
# Nearest neighbor interpolation is performed when linear_interp is False,
# this is equivalent to repeating each gate four times in each ray.
#
# No transformation of the raw data is performed prior to interpolation, so
# reflectivity will be interpolated in dB units, velocity in m/s, etc,
# this may not be the best method for interpolation.
#
# This method was adapted from Radx
cdef int ray_num, i, interp_ngates
cdef float gate_val, next_val, delta

interp_ngates = 2 * moment_ngates - 1 # number of gates interpolated

for ray_num in range(start, end+1):

# repeat each gate value 4 times
for i in range(moment_ngates):
gate_val = data[ray_num, i]
if i == moment_ngates - 1:
scratch_ray[i*2 + 0] = gate_val
else:
scratch_ray[i*2 + 0] = gate_val
scratch_ray[i*2 + 1] = gate_val

if linear_interp:
# linear interpolate
for i in range(1, interp_ngates - 2, 2):
gate_val = scratch_ray[i]
next_val = scratch_ray[i+2]
if gate_val == fill_value or next_val == fill_value:
continue
delta = (next_val - gate_val) / 2.
scratch_ray[i+0] = gate_val + delta * 0.5
scratch_ray[i+1] = gate_val + delta * 1.5

for i in range(interp_ngates):
data[ray_num, i] = scratch_ray[i]
19 changes: 18 additions & 1 deletion xradar/io/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,24 @@
.. automodule:: xradar.io.export

"""
from .backends import * # noqa
from .backends import (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can do this, but we should be sure, that we do not need any other things exported from the subpackages. At least for wradlib, I need some of the sigmet/iris defines. Sure, I can import those directly using the deep path to the definition.

CfRadial1BackendEntrypoint, # noqa
FurunoBackendEntrypoint, # noqa
GamicBackendEntrypoint, # noqa
IrisBackendEntrypoint, # noqa
NexradLevel2BackendEntrypoint, # noqa
OdimBackendEntrypoint, # noqa
RainbowBackendEntrypoint, # noqa
open_cfradial1_datatree, # noqa
open_furuno_datatree, # noqa
open_gamic_datatree, # noqa
open_iris_datatree, # noqa
open_nexradlevel2_datatree, # noqa
open_odim_datatree, # noqa
open_rainbow_datatree, # noqa
)

# noqa
from .export import * # noqa

__all__ = [s for s in dir() if not s.startswith("_")]
7 changes: 5 additions & 2 deletions xradar/io/backends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
.. automodule:: xradar.io.backends.furuno
.. automodule:: xradar.io.backends.rainbow
.. automodule:: xradar.io.backends.iris
.. automodule:: xradar.io.backends.nexrad_level2

"""

Expand All @@ -24,5 +25,7 @@
from .iris import * # noqa
from .odim import * # noqa
from .rainbow import * # noqa

__all__ = [s for s in dir() if not s.startswith("_")]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove the __all__ here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure... it is back in. thanks!!

from .nexrad_level2 import (
NexradLevel2BackendEntrypoint, # noqa
open_nexradlevel2_datatree, # noqa
)
Loading
Loading