Weather Radar Reflectivity Explained: dBZ, Tilts and Beam Height

On this page

Problem statement

dBZ, the unit of the green-yellow-red map on every weather app, is ten times the logarithm of the radar reflectivity factor. It is logarithmic: every 10 dBZ is ten times more echo, so 40 dBZ is not a third more than 30 but ten times as much, about 11.5 mm of rain an hour against 2.7 by the usual Marshall-Palmer relation. And the map is not a horizontal slice of the atmosphere: each pixel sits at a height that grows with distance from the radar, the beam is kilometres wide where a storm is far away, and the radar scans at a stack of elevation angles, which is why the same storm looks different at each tilt.

This page uses one complete volume scan from the KHGX radar near Houston, taken 20 minutes after Hurricane Beryl made landfall on 8 July 2024, to show what reflectivity is, where in the sky each value was measured, and what the radar cannot see.

Quick answer

dBZ is ten times the logarithm of the reflectivity factor Z (mm⁶ m⁻³). Every 10 dBZ is a tenfold increase in Z, and through a Z–R relationship roughly a fourfold to fivefold increase in rain rate:

for dbz in (20, 30, 40, 50):
    Z = 10 ** (dbz / 10)
    R = (Z / 200) ** (1 / 1.6)                  # Marshall-Palmer
    print(f"{dbz} dBZ: Z = {Z:>9,.0f} mm6/m3, rain rate {R:5.1f} mm/h")
20 dBZ: Z =       100 mm6/m3, rain rate   0.6 mm/h
30 dBZ: Z =     1,000 mm6/m3, rain rate   2.7 mm/h
40 dBZ: Z =    10,000 mm6/m3, rain rate  11.5 mm/h
50 dBZ: Z =   100,000 mm6/m3, rain rate  48.6 mm/h
Three radar maps of Hurricane Beryl from KHGX at 09:01 UTC on 8 July 2024. At the 0.5 degree tilt the full rain shield and the eye to the south-west fill the 230 km circle; at 3.1 degrees the shield is smaller and weaker; at 8 degrees only a disc about 60 km across around the radar shows echo. Three radar maps of Hurricane Beryl from KHGX at 09:01 UTC on 8 July 2024. At the 0.5 degree tilt the full rain shield and the eye to the south-west fill the 230 km circle; at 3.1 degrees the shield is smaller and weaker; at 8 degrees only a disc about 60 km across around the radar shows echo.
The same moment at three tilts. The higher the beam, the less of the storm it intersects. Data: NOAA NEXRAD Level II, KHGX, rings at 100 and 200 km.

List the tilts, find the beam, read the distribution

1. Read the volume and list its tilts

A NEXRAD volume contains several sweeps: a full circle at each of a set of elevation angles, the volume coverage pattern. Some low tilts are scanned twice — once for reflectivity and polarimetric fields at long range, once for velocity — and the lowest is revisited during the volume:

tilts = {}
for name in sorted((c for c in dt.children if c.startswith("sweep")), key=lambda c: int(c.split("_")[1])):
    angle = round(float(dt[name].ds.sweep_fixed_angle), 2)
    tilts.setdefault(angle, name)
print(f"{sum(c.startswith('sweep') for c in dt.children)} sweeps, {len(tilts)} distinct tilts:", list(tilts))
23 sweeps, 14 distinct tilts: [0.48, 0.88, 1.32, 1.8, 2.42, 3.12, 4.0, 5.1, 6.42, 8.0, 10.02, 12.48, 15.6, 19.51]

2. Where is the beam?

The beam leaves the radar at a small angle and the Earth curves away beneath it; refraction bends it back down a little. The standard model treats the Earth as having four-thirds its real radius. The beam is also about 0.95° wide, so its footprint grows linearly with range:

def beam_height(r_km, elev_deg, ke=4 / 3, a=6371.0):
    return np.sqrt(r_km**2 + (ke * a) ** 2 + 2 * r_km * ke * a * np.sin(np.radians(elev_deg))) - ke * a

for r in (50, 124, 200):
    print(f"{r:3d} km: 0.48 deg beam centre {beam_height(r, 0.48):.2f} km up, "
          f"{2 * r * np.tan(np.radians(0.95 / 2)):.2f} km wide")
 50 km: 0.48 deg beam centre 0.57 km up, 0.83 km wide
124 km: 0.48 deg beam centre 1.94 km up, 2.06 km wide
200 km: 0.48 deg beam centre 4.03 km up, 3.32 km wide

At 124 km — the distance of Beryl's eye from KHGX at landfall — the lowest beam is centred about 2 km above the ground and 2 km deep. Whatever rain happens below that, the radar measures only what falls through the beam.

Height of the beam centre above the radar for four KHGX tilts at four ranges, with the beam depth. how high is each pixel?50 km100 km150 km200 km0.48°0.6 km1.4 km2.6 km4.0 km1.32°1.3 km2.9 km4.8 km7.0 km3.12°2.9 km6.0 km9.5 km13.2 km6.42°5.7 km11.8 km18.1 kmnot recordedbeam depth0.83 km1.66 km2.49 km3.32 km4/3 Earth radius model; 0.95° beam width spatialworkflow.io
Where each pixel of a reflectivity map really is: the beam's height and depth grow with range.

3. Read the distribution, not just the colours

sw = dt["sweep_0"].ds
z = sw.DBZH.values
echo = z > 5
for lo, hi in ((5, 20), (20, 30), (30, 40), (40, 50), (50, 80)):
    print(f"{lo:2d}-{hi:2d} dBZ: {((z >= lo) & (z < hi)).sum() / echo.sum():5.1%} of echo")
print(f"maximum {np.nanmax(z):.0f} dBZ")
 5-20 dBZ: 13.6% of echo
20-30 dBZ: 30.9% of echo
30-40 dBZ: 41.7% of echo
40-50 dBZ: 13.1% of echo
50-80 dBZ:  0.7% of echo
maximum 66 dBZ

Most of a hurricane's rain shield sits between 20 and 40 dBZ — moderate rain by any Z–R relationship. Values above 50 dBZ cover less than 1% of the echo; at 66 dBZ the maximum is beyond what rain alone produces, which points at clutter or hail, not a 200 mm/h downpour.

4. Look at the storm in the vertical

Taking the same azimuth from every tilt and drawing each gate at its true height and depth gives a pseudo-RHI — a vertical cross-section:

Vertical cross-section along azimuth 220 degrees from KHGX to 200 km. Fourteen fan-shaped beams rise from the radar with widening gaps between them; reflectivity of 30 to 45 dBZ fills the lowest 4 km out to 100 km, the eye from about 110 to 145 km is empty, and the eyewall beyond rises to about 6 to 7 km. Vertical cross-section along azimuth 220 degrees from KHGX to 200 km. Fourteen fan-shaped beams rise from the radar with widening gaps between them; reflectivity of 30 to 45 dBZ fills the lowest 4 km out to 100 km, the eye from about 110 to 145 km is empty, and the eyewall beyond rises to about 6 to 7 km.
A pseudo-RHI through Beryl's eye built from all 14 tilts, each gate drawn at its true height and beam depth. Data: NOAA NEXRAD Level II, KHGX, 09:01 UTC on 8 July 2024.

The cross-section through Beryl's eye shows what a plan view hides: the gaps between the tilts, which widen with range; the cone of silence above the radar, where no tilt looks; the eye, empty from about 110 to 145 km at every tilt; and the echo tops. Find the highest tilt with at least 18 dBZ at each range:

tops = {}
for r in range(20, 201, 20):
    top = None
    for angle, name in tilts.items():
        ds = dt[name].ds
        i = np.argmin(np.abs(ds.azimuth.values - 220))
        j = np.argmin(np.abs(ds.range.values / 1000 - r))
        if abs(ds.range.values[j] / 1000 - r) < 1 and ds.DBZH.values[i, j] >= 18:
            top = max(top or 0, beam_height(r, angle))
    tops[r] = None if top is None else round(float(top), 1)
print("18 dBZ echo top (km) along azimuth 220:", tops)
18 dBZ echo top (km) along azimuth 220: {20: 5.4, 40: 5.7, 60: 5.5, 80: 6.0, 100: 3.7, 120: None, 140: None, 160: 6.5, 180: 6.1, 200: 5.4}

Echo tops of 5–7 km are typical of tropical rain, which is shallow but extremely efficient. Continental thunderstorms push 18 dBZ to 12–15 km. Beyond 150 km the lowest tilt is already above 2.5 km, so the echo top measured there is a lower bound on what the radar could have seen.

How far each tilt can see the storm

The data used on this page

NOAA NEXRAD Level II, KHGX (Houston/Galveston), volume 2024-07-08 09:01:25 UTC, from the Unidata NEXRAD Level II archive on AWS.

from pathlib import Path

import numpy as np
import requests
import xradar as xd

Path("data").mkdir(exist_ok=True)
KEY = "2024/07/08/KHGX/KHGX20240708_090125_V06"
path = Path("data") / KEY.split("/")[-1]
if not path.exists():
    path.write_bytes(requests.get("https://unidata-nexrad-level2.s3.amazonaws.com/" + KEY, timeout=300).content)
dt = xd.io.open_nexradlevel2_datatree(str(path))
print(path.name, f"{path.stat().st_size / 1e6:.1f} MB")
KHGX20240708_090125_V06 19.4 MB

Example 1: how far each tilt can see the storm

Each tilt is recorded to a maximum range, and the higher ones are shorter because their beams leave the troposphere:

for angle, name in tilts.items():
    ds = dt[name].ds
    r_max = float(ds.range.values[-1]) / 1000
    print(f"{angle:5.2f} deg: recorded to {r_max:3.0f} km, where the beam is {beam_height(r_max, angle):4.1f} km up")
 0.48 deg: recorded to 460 km, where the beam is 16.3 km up
 0.88 deg: recorded to 460 km, where the beam is 19.5 km up
 1.32 deg: recorded to 430 km, where the beam is 20.8 km up
 1.80 deg: recorded to 387 km, where the beam is 20.9 km up
 2.42 deg: recorded to 336 km, where the beam is 20.8 km up
 3.12 deg: recorded to 296 km, where the beam is 21.2 km up
 4.00 deg: recorded to 252 km, where the beam is 21.3 km up
 5.10 deg: recorded to 210 km, where the beam is 21.2 km up
 6.42 deg: recorded to 174 km, where the beam is 21.2 km up
 8.00 deg: recorded to 137 km, where the beam is 20.1 km up
10.02 deg: recorded to 117 km, where the beam is 21.1 km up
12.48 deg: recorded to  96 km, where the beam is 21.2 km up
15.60 deg: recorded to  78 km, where the beam is 21.3 km up
19.51 deg: recorded to  63 km, where the beam is 21.2 km up

Every tilt stops where its beam is about 21 km up, well above the tropopause: there is no weather beyond that point, so the radar stops listening. The 19.5° tilt sees only to 63 km.

How far each KHGX tilt is recorded: every tilt stops where its beam is about 21 km up. the higher the tilt, the shorter the ray0.48°460 km1.80°387 km3.12°296 km5.10°210 km8.00°137 km12.48°96 km19.51°63 km0230 km460 kmSeven of the 14 tilts in the 09:01 UTC volume spatialworkflow.io
Each tilt is recorded only until its beam is about 21 km up, so the higher tilts are short.

Example 2: reflectivity at the same place, three tilts

ds_hi = dt[tilts[3.12]].ds
i0, i1 = np.argmin(np.abs(sw.azimuth.values - 220)), np.argmin(np.abs(ds_hi.azimuth.values - 220))
for r in (40, 80, 160):
    j0, j1 = np.argmin(np.abs(sw.range.values - r * 1000)), np.argmin(np.abs(ds_hi.range.values - r * 1000))
    print(f"{r:3d} km at 220 deg: {sw.DBZH.values[i0, j0]:5.1f} dBZ at {beam_height(r, 0.48):.1f} km, "
          f"{ds_hi.DBZH.values[i1, j1]:5.1f} dBZ at {beam_height(r, 3.12):.1f} km")
 40 km at 220 deg:  38.0 dBZ at 0.4 km,  33.5 dBZ at 2.3 km
 80 km at 220 deg:  43.5 dBZ at 1.0 km,  32.0 dBZ at 4.7 km
160 km at 220 deg:  32.5 dBZ at 2.8 km,  15.0 dBZ at 10.2 km

The higher tilt measures a different part of the cloud. At 160 km the 3.1° beam is 10 km up, far above the melting level, and reads 15 dBZ where the lowest tilt reads 32.5 dBZ. This is why rainfall products use the lowest unblocked tilt.

The radar equation in brief

A radar sends out a short pulse and listens. Anything in the beam — raindrops, hail, snowflakes, insects, buildings — scatters some energy back. The radar equation converts the returned power and the range into the reflectivity factor Z, which for small raindrops is the sum of the sixth power of their diameters per cubic metre. That sixth power is why a few large drops dominate: one 4 mm drop scatters as much as 4,096 drops of 1 mm.

From a radar pulse to a coloured pixel on a reflectivity map. from pulse to pixelpulse out0.95° beampower backfrom every targetradar equationZ = Σ D⁶ per m³dBZ10 log₁₀ Zcolour on amapdrawn as if atgroundOne 4 mm drop scatters as much as 4,096 drops of 1 mm spatialworkflow.io
From pulse to pixel. The sixth power means a few large drops dominate the signal.

Because Z spans many orders of magnitude, it is reported as dBZ = 10 log₁₀ Z. A plan-position display then paints each gate of one tilt on a map, as if it were at ground level. It is not: the 0.5° tilt is 1.4 km up at 100 km and 4 km up at 200 km, and each gate is a volume kilometres deep. Reading a reflectivity map well means remembering three things: it is logarithmic, it is tilted, and it is blurred with range.

Range folding, the bright band and attenuation

Common misreadings of a reflectivity map and what is really happening. what the map does not tell youIt looks likeIt is10 dBZ more = a bit more rain10× Z, about 4× the rain ratea slice at ground level4 km up at 200 km on the lowesttiltno rain far from the radarbeam overshoots shallow rain66 dBZ = torrential rainclutter or hail: check ρhvNumbers from Beryl’s 09:01 UTC volume spatialworkflow.io
What a reflectivity map does not tell you on its own.
  • Range folding and clutter produce echo that is not weather; dual-pol fields help separate them.
  • The bright band, where snow melts into rain, inflates reflectivity in a layer.
  • Hail gives very high dBZ that no Z–R relationship should turn into rain.
  • Attenuation is minor at S band (NEXRAD) and serious at C and X band.
  • Different radars use different colour maps; compare numbers, not colours.
  • Anomalous propagation bends beams into the ground on clear, stable nights.

FAQ

What does dBZ mean?

Decibels of Z, the radar reflectivity factor: dBZ = 10 log₁₀(Z), with Z in mm⁶ per cubic metre. Each 10 dBZ is ten times more reflectivity.

What dBZ is heavy rain?

Around 40–50 dBZ is heavy rain (roughly 10–50 mm/h by Marshall–Palmer). Above about 55 dBZ, hail is likely.

Why does a radar image change between tilts?

Each tilt samples a different height. At 100 km the 0.5° tilt is 1.4 km up and the 3.1° tilt is 6 km up — often above the melting level.

Why can the radar not see the ground far away?

The Earth curves away beneath the beam. At 200 km the lowest beam is centred 4 km above the radar's height.