MCPcopy Index your code
hub / github.com/YvZheng/pycwr

github.com/YvZheng/pycwr @v1.0.8

Chat with this repo
repository ↗ · DeepWiki ↗ · release v1.0.8 ↗ · + Follow
1,189 symbols 4,198 edges 75 files 527 documented · 44%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

pycwr

pycwr is a Python toolkit for operational Chinese weather radar workflows. It covers radar base-data reading, geometry, plotting, quality control, hydrometeor classification, single-radar wind retrieval, multi-radar compositing, and export.

Why 1.0.8 matters

1.0.8 continues the first stable release line intended to be usable for production-style usage and GitHub distribution. This refresh keeps the PA reader behavior aligned while improving the maintainability of the PA decode path and preserving the plotting colormap compatibility update in the public release line.

Highlights:

  • compatibility-focused readers for common Chinese weather radar formats
  • clearer PRD data model for sweep inspection and downstream processing
  • aligned and native reflectivity access kept side by side when low-level reflectivity range is longer than Doppler range
  • lighter default dependencies with plotting, QC, web viewer, and interop isolated into the optional full stack
  • stronger geometry and regression coverage
  • built-in single-radar wind retrieval workflows: VAD, VVP, VWP, and gridded horizontal wind volumes
  • improved multi-radar compositing and reference-style CR plotting
  • read_auto and read_PA now correctly recognize and build supported PA files
  • plot_ppi, plot_ppi_map, and the legacy Graph / GraphMap plotting paths now auto-register pycwr colormap names such as CN_ref and CN_vel
  • the PA reader implementation is cleaner internally, with explicit helpers for moment decoding, field padding, and range construction

Installation

Install from PyPI:

python -m pip install pycwr

Install the optional full stack from PyPI:

python -m pip install "pycwr[full]"

Base install:

python -m pip install -r requirements-core.txt
python -m pip install .

Full install:

python -m pip install -r requirements-full.txt
python -m pip install ".[full]"

Notes:

  • pycwr 1.0.8 requires Python >=3.9
  • python -m pip install pycwr is the recommended path for normal users
  • base install is enough for readers, PRD, geometry, interpolation, and NetCDF-style export
  • full install is recommended for plotting, map plotting, QC, Py-ART/xradar interop, and the web viewer
  • upstream arm_pyart and xradar currently require Python >=3.10, so on Python 3.9 the full install still covers plotting, QC, and the web viewer, but not those two optional interop stacks
  • pandas is pinned to <3 in 1.0.8 for release stability
  • source install is still useful when you are developing locally or rebuilding the Cython extension

Rebuild the extension after editing pycwr/core/RadarGridC.pyx:

python setup.py build_ext --inplace

Build release artifacts:

python -m build

5-minute quickstart

Read one radar file and inspect the returned volume:

from pycwr.io import read_auto

radar = read_auto("Z_RADR_I_Z9046_20260317065928_O_DOR_SAD_CAP_FMT.bin.bz2")
print(radar.summary())
print(radar.available_fields())
print(radar.sweep_summary()[0])

Get one field from one sweep:

dBZ0 = radar.get_sweep_field(0, "dBZ")
velocity0 = radar.get_sweep_field(0, "V")

Plot a PPI:

from pycwr.draw import plot_ppi

plot_ppi(radar, field="dBZ", sweep=0, show=True)

Plot a mapped PPI:

from pycwr.draw import plot_ppi_map

plot_ppi_map(radar, field="dBZ", sweep=0, show=True)

Extract a vertical section:

from pycwr.draw import plot_section

plot_section(radar, start=(-50, 0), end=(50, 0), field="dBZ", show=True)

Generate a simple product:

import numpy as np

x = np.arange(-150_000.0, 150_001.0, 1_000.0)
y = np.arange(-150_000.0, 150_001.0, 1_000.0)
radar.add_product_CR_xy(x, y)
print(radar.product)

API map

Module What it is for Recommended starting points
pycwr.io Read and write radar base data read_auto, read_WSR98D, read_SAB, read_CC, read_SC, read_PA
pycwr.core Central volume object, geometry, export helpers PRD, radar.summary(), radar.get_sweep_field()
pycwr.draw Plotting and quick-look figures plot_ppi, plot_ppi_map, plot_rhi, plot_section, plot_vvp, plot_wind_profile
pycwr.qc Dual-pol QC apply_dualpol_qc, run_dualpol_qc
pycwr.retrieve Hydrometeor and wind retrieval classify_hydrometeors, retrieve_vad, retrieve_vvp, retrieve_vwp, retrieve_wind_volume_xy, retrieve_wind_volume_lonlat
pycwr.interp Multi-radar compositing run_radar_network_3d
pycwr.GraphicalInterface Local web viewer create_app, launch

Core object model

All readers return pycwr.core.NRadar.PRD.

The most important parts of PRD are:

  • fields: one xarray.Dataset per sweep
  • scan_info: site and scan metadata
  • extended_fields: native sidecar fields when aligned and native ranges differ
  • product: computed product dataset

Useful inspection helpers:

  • summary(): compact full-volume summary
  • available_fields(sweep=None, range_mode="aligned")
  • sweep_summary()
  • get_sweep_field(sweep, field_name, range_mode="aligned", sort_by_azimuth=False)
  • get_native_sweep_field(sweep, field_name)
  • ordered_az(inplace=False)

Aligned vs native reflectivity

For some low sweeps, reflectivity may exist in two forms:

  • aligned: historical shared grid used by old processing chains
  • native: original reflectivity range before Doppler-driven truncation

Use:

  • range_mode="aligned" for historical compatibility
  • range_mode="native" when full low-level reflectivity coverage matters

Example:

aligned = radar.get_sweep_field(0, "dBZ", range_mode="aligned")
native = radar.get_sweep_field(0, "dBZ", range_mode="native")

Common workflows

Plotting

Recommended public plotting APIs:

from pycwr.draw import (
    plot,
    plot_ppi,
    plot_ppi_map,
    plot_rhi,
    plot_section,
    plot_section_lonlat,
    plot_vvp,
    plot_wind_profile,
)

These functions return an EasyPlotResult with fig, ax, and artist.

Default colormap strategy

pycwr.draw chooses a default colormap from the plotted field when you leave cmap=None.

  • dBZ uses the reflectivity palette such as CN_ref
  • V uses the velocity palette such as CN_vel
  • HCL uses a discrete hydrometeor classification palette
  • unknown fields fall back to viridis

This default strategy is shared by plot_ppi, plot_ppi_map, plot_rhi, plot_section, and the legacy Graph / GraphMap classes.

Recommended usage:

from pycwr.draw import plot_ppi, plot_ppi_map

plot_ppi(radar, field="dBZ", sweep=0, show=True)
plot_ppi_map(radar, field="dBZ", sweep=0, show=True)

Explicit colormap override:

plot_ppi(radar, field="dBZ", sweep=0, cmap="CN_ref", cmap_bins=16, show=True)
plot_ppi_map(radar, field="V", sweep=0, cmap="CN_vel", min_max=(-27.0, 27.0), show=True)

Legacy class-style usage is also supported:

from pycwr.draw.RadarPlot import GraphMap
from cartopy import crs as ccrs

display = GraphMap(radar, ccrs.PlateCarree())
display.plot_ppi_map(ax, 0, "dBZ", cmap="CN_ref")

Notes:

  • CN_ref, CN_vel, and the other pycwr colormap names are registered automatically when plotting starts. You no longer need to "warm up" the plotting module first.
  • If a field is missing in the radar file, plotting raises a field-not-found error. For example, some files may contain dBZ but not V.
  • If you need the raw colormap registry for custom matplotlib work, you can explicitly import pycwr.draw.colormap.

Products

Common PRD product methods:

  • add_product_CR_xy
  • add_product_CAPPI_xy
  • add_product_CAPPI_3d_xy
  • add_product_VIL_xy
  • add_product_ET_xy
  • add_product_CR_lonlat
  • add_product_CAPPI_lonlat
  • add_product_VIL_lonlat
  • add_product_ET_lonlat
  • add_product_VWP

Example:

radar.add_product_CAPPI_xy(x, y, 3000.0)
radar.add_product_VIL_xy(x, y, [1000.0, 2000.0, 3000.0])

Quality control

from pycwr.qc import apply_dualpol_qc

qc_radar = apply_dualpol_qc(radar, inplace=False, clear_air_mode="mask")

Common corrected fields include Zc, ZDRc, PhiDPc, KDPc, and mask fields such as QC_MASK and CLEAR_AIR_MASK when enabled.

Hydrometeor classification

hcl_radar = radar.classify_hydrometeors(
    inplace=False,
    band="C",
    profile_height=[0.0, 2000.0, 4000.0, 8000.0],
    profile_temperature=[24.0, 12.0, 2.0, -16.0],
    confidence_field="HCL_CONF",
)

You can also classify directly from arrays with pycwr.retrieve.classify_hydrometeors(...).

Wind retrieval

pycwr ships four single-radar wind workflows:

  • retrieve_vad: ring-wise harmonic fit on one or more sweeps
  • retrieve_vvp: local least-squares horizontal wind retrieval on one sweep
  • retrieve_vwp: vertical wind profile built from multiple VAD layers
  • retrieve_wind_volume_xy / retrieve_wind_volume_lonlat: fixed-height gridded horizontal wind volume built from multiple VVP sweeps

Quick example:

vad = radar.retrieve_vad(sweeps=[0, 1, 2], max_range_km=40.0, gate_step=4)
vvp = radar.retrieve_vvp(0, max_range_km=20.0, az_num=91, bin_num=5)
vwp = radar.retrieve_vwp(sweeps=[0, 1, 2], max_range_km=40.0, height_step=500.0)
wind = radar.retrieve_wind_volume_xy(
    XRange=np.arange(-20_000.0, 20_001.0, 10_000.0),
    YRange=np.arange(-20_000.0, 20_001.0, 10_000.0),
    level_heights=np.array([500.0, 1000.0, 1500.0]),
    sweeps=[0, 1, 2],
    max_range_km=30.0,
)

The stored profile product path is:

radar.add_product_VWP(sweeps=[0, 1, 2], max_range_km=40.0, height_step=500.0)

Store the gridded horizontal wind volume in radar.product:

radar.add_product_WIND_VOLUME_xy(
    XRange=np.arange(-20_000.0, 20_001.0, 10_000.0),
    YRange=np.arange(-20_000.0, 20_001.0, 10_000.0),
    level_heights=np.array([500.0, 1000.0, 1500.0]),
    sweeps=[0, 1, 2],
)

3-D wind volume: what it is

retrieve_wind_volume_xy and retrieve_wind_volume_lonlat return a single-radar fixed-height horizontal wind volume.

Treat it as:

  • gridded u/v wind on multiple height levels
  • one wind profile for each horizontal grid point
  • a robust diagnostic product built from Doppler radial velocity

Do not treat it as:

  • a full dynamic u/v/w retrieval
  • a dual-Doppler or multi-radar variational wind analysis
  • a substitute for vertical motion retrieval in convective dynamics studies

3-D wind volume: retrieval chain

The operational logic is:

  1. For each requested sweep, pycwr selects the velocity field, preferring corrected velocity when available.
  2. A local VVP is solved on each sweep with a moving azimuth-range window.
  3. Each sweep is summarized and, when sweeps=None or sweeps="auto", the strongest sweeps are selected automatically.
  4. The valid sweep-level VVP samples are interpolated horizontally onto the target grid with inverse-distance weighting.
  5. Fixed-height wind values are reconstructed from the selected sweeps with local vertical aggregation or short-gap linear interpolation.
  6. Diagnostics such as fit residual, support counts, and heuristic quality score are stored with the result.

This makes the product more stable on real radar volumes with missing velocity, weak echo regions, or sweep-to-sweep range differences.

3-D wind volume: recommended API

Use:

  • radar.retrieve_wind_volume_xy(...) for Cartesian x/y/z
  • radar.retrieve_wind_volume_lonlat(...) for regular lon/lat grids
  • radar.add_product_WIND_VOLUME_xy(...) or radar.add_product_WIND_VOLUME_lonlat(...) to store the result in radar.product

Recommended defaults for production-style usage:

  • use sweeps=None to enable auto sweep selection
  • use sweeps="all" only when you explicitly want every available sweep
  • set a realistic max_range_km instead of using the whole radar volume
  • increase workers on multi-core machines when the target grid is not tiny

Example with explicit operational settings:

wind = radar.retrieve_wind_volume_xy(
    XRange=np.arange(-60_000.0, 60_001.0, 5_000.0),
    YRange=np.arange(-60_000.0, 60_001.0, 5_000.0),
    level_heights=np.arange(500.0, 3_500.0, 500.0),
    sweeps=None,
    max_range_km=60.0,
    az_num=31,
    bin_num=5,
    azimuth_step=12,
    range_step=6,
    horizontal_radius_m=8_000.0,
    max_horizontal_radius_m=14_000.0,
    horizontal_min_neighbors=3,
    vertical_tolerance_m=400.0,
    max_vertical_gap_m=1_200.0,
    workers=4,
)

3-D wind volume: key arguments

  • XRange, YRange, level_heights: target output grid in meters
  • XLon, YLat: target output grid in degrees for lon/lat mode
  • sweeps: None or "auto" enables auto selection; "all" keeps all sweeps; a list such as [0, 1, 2] forces explicit sweeps
  • max_range_km: strongest first-order control for retrieval stability
  • az_num, bin_num: local VVP window size
  • azimuth_step, range_step: output thinning of the local sweep VVP
  • horizontal_radius_m: horizontal radius used when mapping sweep VVP samples to the target grid
  • max_horizontal_radius_m: optional expanded search radius when local support is sparse
  • vertical_tolerance_m: local height matching tolerance
  • max_vertical_gap_m: maximum allowed vertical interpolation gap
  • workers: process-based parallelism for sweep-level and grid-level work

Practical tuning rules:

  • use a wider az_num when velocity is sparse or noisy
  • keep bin_num modest; too small is noisy and too large oversmoo

Core symbols most depended-on inside this repo

get
called by 126
pycwr/GraphicalInterface/web_app.py
get_metadata
called by 52
pycwr/configure/pyart_config.py
as_numpy
called by 51
pycwr/draw/_plot_core.py
_resolve_network_value
called by 50
pycwr/interp/RadarInterp.py
read_auto
called by 49
pycwr/io/__init__.py
get_sweep_field
called by 38
pycwr/core/NRadar.py
_product_name
called by 35
pycwr/core/NRadar.py
copy
called by 34
pycwr/configure/pyart_lazydict.py

Shape

Method 617
Function 507
Class 57
Route 8

Languages

Python94%
TypeScript6%

Modules by API surface

pycwr/core/NRadar.py74 symbols
pycwr/GraphicalInterface/static/viewer.js67 symbols
pycwr/io/WSR98DFile.py64 symbols
pycwr/GraphicalInterface/web_app.py59 symbols
pycwr/retrieve/WindField.py55 symbols
pycwr/interp/RadarInterp.py52 symbols
pycwr/io/SABFile.py50 symbols
pycwr/draw/_plot_core.py47 symbols
pycwr/io/PAFile.py44 symbols
pycwr/core/PyartRadar.py41 symbols
pycwr/io/SCFile.py40 symbols
pycwr/io/CCFile.py40 symbols

For agents

$ claude mcp add pycwr \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact