✈ Contrail Formation Predictor

Atmospheric data at cruise altitudes • Schmidt-Appleman criterion • Updated 2026-08-30 09:59 UTC

Headline result

Comparing forecast classes only within each city (Mantel–Haenszel, 33 cities carrying both classes), the PERSISTENT–EPHEMERAL detection gap is +26pp (95% CI [+23, +30]pp) on n = 5420 city-hours across 48 observation days.

Honest caveat: about 10% of the raw pooled +29pp gap is city mix, not forecast class; stratifying by city removes it. The interval clears zero on the current sample — but only just, so the significance is fresh and still thin. Every figure here is recomputed live from scorecard.json — see the rolling scorecard below for the full derivation.

Current Status — Major Cities
Columbus, OH
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
19:00–23:00 UTC (PERSISTENT)
Denver, CO
Persistent
PERSISTENT 2/3 levels PERSISTENT
right now
CONTRAIL WINDOW (48h)
22:00–23:00 UTC (PERSISTENT)
Seattle, WA
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
07:00–19:00 UTC (PERSISTENT)
Chicago, IL
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
21:00–23:00 UTC (PERSISTENT)
New York, NY
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
23:00–23:00 UTC (PERSISTENT)
Miami, FL
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
23:00–23:00 UTC (PERSISTENT)
Los Angeles, CA
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
13:00–16:00 UTC (PERSISTENT)
Dallas, TX
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
00:00–23:00 UTC (EPHEMERAL)
Anchorage, AK
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
20:00–22:00 UTC (PERSISTENT)
Mexico City, MX
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
19:00–23:00 UTC (PERSISTENT)
Atlanta, GA
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
00:00–23:00 UTC (EPHEMERAL)
Houston, TX
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
22:00–23:00 UTC (PERSISTENT)
St. Louis, MO
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
18:00–21:00 UTC (PERSISTENT)
Kansas City, MO
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
14:00–16:00 UTC (PERSISTENT)
Memphis, TN
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
00:00–23:00 UTC (EPHEMERAL)
Nashville, TN
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
00:00–23:00 UTC (EPHEMERAL)
Indianapolis, IN
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
23:00–23:00 UTC (PERSISTENT)
Detroit, MI
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
10:00–23:00 UTC (PERSISTENT)
Minneapolis, MN
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
21:00–23:00 UTC (PERSISTENT)
Charlotte, NC
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
02:00–03:00 UTC (PERSISTENT)
Pittsburgh, PA
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
12:00–17:00 UTC (PERSISTENT)
Boston, MA
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
23:00–23:00 UTC (PERSISTENT)
Philadelphia, PA
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
16:00–17:00 UTC (PERSISTENT)
Washington, DC
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
19:00–23:00 UTC (PERSISTENT)
New Orleans, LA
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
18:00–20:00 UTC (PERSISTENT)
Oklahoma City, OK
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
00:00–23:00 UTC (EPHEMERAL)
Omaha, NE
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
15:00–19:00 UTC (PERSISTENT)
Milwaukee, WI
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
16:00–23:00 UTC (PERSISTENT)
Phoenix, AZ
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
03:00–06:00 UTC (PERSISTENT)
San Francisco, CA
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
00:00–23:00 UTC (EPHEMERAL)
Portland, OR
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
06:00–13:00 UTC (PERSISTENT)
Salt Lake City, UT
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
22:00–23:00 UTC (PERSISTENT)
San Diego, CA
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
00:00–23:00 UTC (EPHEMERAL)
Las Vegas, NV
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
16:00–01:00 UTC (PERSISTENT)
São Paulo, BR
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
13:00–14:00 UTC (PERSISTENT)
London, UK
Persistent
PERSISTENT 1/3 levels PERSISTENT
right now
CONTRAIL WINDOW (48h)
17:00–22:00 UTC (PERSISTENT)
Paris, FR
Persistent
PERSISTENT 1/3 levels PERSISTENT
right now
CONTRAIL WINDOW (48h)
22:00–23:00 UTC (PERSISTENT)
Frankfurt, DE
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
22:00–00:00 UTC (PERSISTENT)
Reykjavík, IS
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
16:00–23:00 UTC (PERSISTENT)
Moscow, RU
Persistent
PERSISTENT 1/3 levels PERSISTENT
right now
CONTRAIL WINDOW (48h)
22:00–23:00 UTC (PERSISTENT)
Cairo, EG
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
00:00–23:00 UTC (EPHEMERAL)
Dubai, AE
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
00:00–23:00 UTC (EPHEMERAL)
Johannesburg, ZA
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
05:00–23:00 UTC (PERSISTENT)
Delhi, IN
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
00:00–23:00 UTC (EPHEMERAL)
Tokyo, JP
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
00:00–23:00 UTC (EPHEMERAL)
Singapore, SG
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
06:00–07:00 UTC (PERSISTENT)
Sydney, AU
Ephemeral
EPHEMERAL
right now
CONTRAIL WINDOW (48h)
00:00–23:00 UTC (EPHEMERAL)
All Locations — 47 cities, worldwide

Every row below is a full Schmidt-Appleman forecast, precomputed from Open-Meteo pressure-level data. The forecaster is not limited to these cities — the same core runs for any coordinate on Earth:

python predictor.py --latlon 52.52 13.40 "Berlin, DE"

Add --json for a machine-readable payload (location, 48h contrail window, per-hour classification series). This static page ships a curated set so no live backend is needed; the forecast_location(lat, lon, name) function behind it will forecast anywhere on demand.

Location ↕ Lat, Lon ↕ Now ↕ Persist ↕ Contrail window (48h) ↕ Forecast horizon ↕
Columbus, OH39.96, -82.99EPHEMERAL0/319:00–23:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Denver, CO39.74, -104.98PERSISTENT2/322:00–23:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Seattle, WA47.61, -122.33EPHEMERAL0/307:00–19:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Chicago, IL41.88, -87.63EPHEMERAL0/321:00–23:00 UTC (PERSISTENT)to 09-01 23:00 UTC
New York, NY40.71, -74.01EPHEMERAL0/323:00–23:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Miami, FL25.76, -80.19EPHEMERAL0/323:00–23:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Los Angeles, CA34.05, -118.24EPHEMERAL0/313:00–16:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Dallas, TX32.78, -96.80EPHEMERAL0/300:00–23:00 UTC (EPHEMERAL)to 09-01 23:00 UTC
Anchorage, AK61.22, -149.90EPHEMERAL0/320:00–22:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Mexico City, MX19.43, -99.13EPHEMERAL0/319:00–23:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Atlanta, GA33.75, -84.39EPHEMERAL0/300:00–23:00 UTC (EPHEMERAL)to 09-01 23:00 UTC
Houston, TX29.76, -95.37EPHEMERAL0/322:00–23:00 UTC (PERSISTENT)to 09-01 23:00 UTC
St. Louis, MO38.63, -90.20EPHEMERAL0/318:00–21:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Kansas City, MO39.10, -94.58EPHEMERAL0/314:00–16:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Memphis, TN35.15, -90.05EPHEMERAL0/300:00–23:00 UTC (EPHEMERAL)to 09-01 23:00 UTC
Nashville, TN36.16, -86.78EPHEMERAL0/300:00–23:00 UTC (EPHEMERAL)to 09-01 23:00 UTC
Indianapolis, IN39.77, -86.16EPHEMERAL0/323:00–23:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Detroit, MI42.33, -83.05EPHEMERAL0/310:00–23:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Minneapolis, MN44.98, -93.27EPHEMERAL0/321:00–23:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Charlotte, NC35.23, -80.84EPHEMERAL0/302:00–03:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Pittsburgh, PA40.44, -79.99EPHEMERAL0/312:00–17:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Boston, MA42.36, -71.06EPHEMERAL0/323:00–23:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Philadelphia, PA39.95, -75.16EPHEMERAL0/316:00–17:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Washington, DC38.91, -77.04EPHEMERAL0/319:00–23:00 UTC (PERSISTENT)to 09-01 23:00 UTC
New Orleans, LA29.95, -90.07EPHEMERAL0/318:00–20:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Oklahoma City, OK35.47, -97.52EPHEMERAL0/300:00–23:00 UTC (EPHEMERAL)to 09-01 23:00 UTC
Omaha, NE41.26, -95.93EPHEMERAL0/315:00–19:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Milwaukee, WI43.04, -87.91EPHEMERAL0/316:00–23:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Phoenix, AZ33.45, -112.07EPHEMERAL0/303:00–06:00 UTC (PERSISTENT)to 09-01 23:00 UTC
San Francisco, CA37.77, -122.42EPHEMERAL0/300:00–23:00 UTC (EPHEMERAL)to 09-01 23:00 UTC
Portland, OR45.52, -122.68EPHEMERAL0/306:00–13:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Salt Lake City, UT40.76, -111.89EPHEMERAL0/322:00–23:00 UTC (PERSISTENT)to 09-01 23:00 UTC
San Diego, CA32.72, -117.16EPHEMERAL0/300:00–23:00 UTC (EPHEMERAL)to 09-01 23:00 UTC
Las Vegas, NV36.17, -115.14EPHEMERAL0/316:00–01:00 UTC (PERSISTENT)to 09-01 23:00 UTC
São Paulo, BR-23.55, -46.63EPHEMERAL0/313:00–14:00 UTC (PERSISTENT)to 09-01 23:00 UTC
London, UK51.51, -0.13PERSISTENT1/317:00–22:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Paris, FR48.86, 2.35PERSISTENT1/322:00–23:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Frankfurt, DE50.11, 8.68EPHEMERAL0/322:00–00:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Reykjavík, IS64.15, -21.94EPHEMERAL0/316:00–23:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Moscow, RU55.76, 37.62PERSISTENT1/322:00–23:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Cairo, EG30.04, 31.24EPHEMERAL0/300:00–23:00 UTC (EPHEMERAL)to 09-01 23:00 UTC
Dubai, AE25.20, 55.27EPHEMERAL0/300:00–23:00 UTC (EPHEMERAL)to 09-01 23:00 UTC
Johannesburg, ZA-26.20, 28.05EPHEMERAL0/305:00–23:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Delhi, IN28.61, 77.21EPHEMERAL0/300:00–23:00 UTC (EPHEMERAL)to 09-01 23:00 UTC
Tokyo, JP35.68, 139.69EPHEMERAL0/300:00–23:00 UTC (EPHEMERAL)to 09-01 23:00 UTC
Singapore, SG1.35, 103.82EPHEMERAL0/306:00–07:00 UTC (PERSISTENT)to 09-01 23:00 UTC
Sydney, AU-33.87, 151.21EPHEMERAL0/300:00–23:00 UTC (EPHEMERAL)to 09-01 23:00 UTC

Click a column header to sort; type above to filter.

72-Hour Timeline — Columbus, OH

Each pixel = 1 hour. Hover for time + status.

2026-08-30T00:00 UTC: Ephemeral2026-08-30T01:00 UTC: Ephemeral2026-08-30T02:00 UTC: Ephemeral2026-08-30T03:00 UTC: Ephemeral2026-08-30T04:00 UTC: Ephemeral2026-08-30T05:00 UTC: Ephemeral2026-08-30T06:00 UTC: Ephemeral2026-08-30T07:00 UTC: Ephemeral2026-08-30T08:00 UTC: Ephemeral2026-08-30T09:00 UTC: Ephemeral2026-08-30T10:00 UTC: Ephemeral2026-08-30T11:00 UTC: Ephemeral2026-08-30T12:00 UTC: Ephemeral2026-08-30T13:00 UTC: Ephemeral2026-08-30T14:00 UTC: Ephemeral2026-08-30T15:00 UTC: Ephemeral2026-08-30T16:00 UTC: Ephemeral2026-08-30T17:00 UTC: Ephemeral2026-08-30T18:00 UTC: Ephemeral2026-08-30T19:00 UTC: Ephemeral2026-08-30T20:00 UTC: Ephemeral2026-08-30T21:00 UTC: Ephemeral2026-08-30T22:00 UTC: Persistent2026-08-30T23:00 UTC: Persistent2026-08-31T00:00 UTC: Persistent2026-08-31T01:00 UTC: Persistent2026-08-31T02:00 UTC: Ephemeral2026-08-31T03:00 UTC: Ephemeral2026-08-31T04:00 UTC: Persistent2026-08-31T05:00 UTC: Persistent2026-08-31T06:00 UTC: Ephemeral2026-08-31T07:00 UTC: Ephemeral2026-08-31T08:00 UTC: Ephemeral2026-08-31T09:00 UTC: Persistent2026-08-31T10:00 UTC: Ephemeral2026-08-31T11:00 UTC: Persistent2026-08-31T12:00 UTC: Persistent2026-08-31T13:00 UTC: Persistent2026-08-31T14:00 UTC: Persistent2026-08-31T15:00 UTC: Persistent2026-08-31T16:00 UTC: Persistent2026-08-31T17:00 UTC: Persistent2026-08-31T18:00 UTC: Ephemeral2026-08-31T19:00 UTC: Persistent2026-08-31T20:00 UTC: Persistent2026-08-31T21:00 UTC: Persistent2026-08-31T22:00 UTC: Persistent2026-08-31T23:00 UTC: Persistent2026-09-01T00:00 UTC: Ephemeral2026-09-01T01:00 UTC: Ephemeral2026-09-01T02:00 UTC: Persistent2026-09-01T03:00 UTC: Persistent2026-09-01T04:00 UTC: Persistent2026-09-01T05:00 UTC: Persistent2026-09-01T06:00 UTC: Persistent2026-09-01T07:00 UTC: Persistent2026-09-01T08:00 UTC: Persistent2026-09-01T09:00 UTC: Persistent2026-09-01T10:00 UTC: Persistent2026-09-01T11:00 UTC: Persistent2026-09-01T12:00 UTC: Persistent2026-09-01T13:00 UTC: Persistent2026-09-01T14:00 UTC: Persistent2026-09-01T15:00 UTC: Ephemeral2026-09-01T16:00 UTC: Ephemeral2026-09-01T17:00 UTC: Ephemeral2026-09-01T18:00 UTC: Ephemeral2026-09-01T19:00 UTC: Ephemeral2026-09-01T20:00 UTC: Persistent2026-09-01T21:00 UTC: Persistent2026-09-01T22:00 UTC: Persistent2026-09-01T23:00 UTC: Persistent08/3008/3109/01NOW
No contrails
Ephemeral (dissolve quickly)
Persistent (contrail cirrus)
48-Hour Detail — Columbus, OH (3 pressure levels)

T = temperature at pressure level • RH = relative humidity w.r.t. ice • [N/3] = persistent level count

Time (UTC)200 hPa (FL387)250 hPa (FL344)300 hPa (FL295)Status
08-30 00:00 UTC-57.5°C / 64% RH_ice-45.5°C / 81% RH_ice-34.5°C / 75% RH_iceEphemeral
08-30 01:00 UTC-57.5°C / 59% RH_ice-45.5°C / 95% RH_ice-35.0°C / 100% RH_iceEphemeral
08-30 02:00 UTC-57.0°C / 67% RH_ice-45.5°C / 67% RH_ice-35.0°C / 92% RH_iceEphemeral
08-30 03:00 UTC-57.5°C / 73% RH_ice-45.0°C / 61% RH_ice-34.5°C / 78% RH_iceEphemeral
08-30 04:00 UTC-57.0°C / 59% RH_ice-45.0°C / 44% RH_ice-34.0°C / 25% RH_iceEphemeral
08-30 05:00 UTC-56.5°C / 67% RH_ice-45.0°C / 50% RH_ice-34.0°C / 25% RH_iceEphemeral
08-30 06:00 UTC-57.0°C / 72% RH_ice-45.0°C / 61% RH_ice-34.0°C / 28% RH_iceEphemeral
08-30 07:00 UTC-56.5°C / 67% RH_ice-44.5°C / 55% RH_ice-33.5°C / 30% RH_iceEphemeral
08-30 08:00 UTC-57.0°C / 76% RH_ice-44.5°C / 44% RH_ice-34.0°C / 33% RH_iceEphemeral
08-30 09:00 UTC-57.0°C / 84% RH_ice-44.5°C / 27% RH_ice-34.0°C / 30% RH_iceEphemeral
08-30 10:00 UTC-56.5°C / 72% RH_ice-44.5°C / 61% RH_ice-34.0°C / 33% RH_iceEphemeral
08-30 11:00 UTC-56.5°C / 72% RH_ice-44.5°C / 47% RH_ice-34.0°C / 36% RH_iceEphemeral
08-30 12:00 UTC-56.5°C / 64% RH_ice-44.5°C / 30% RH_ice-34.0°C / 39% RH_iceEphemeral
08-30 13:00 UTC-57.0°C / 81% RH_ice-44.5°C / 33% RH_ice-34.0°C / 39% RH_iceEphemeral
08-30 14:00 UTC-57.0°C / 92% RH_ice-44.5°C / 41% RH_ice-34.0°C / 39% RH_iceEphemeral
08-30 15:00 UTC-57.0°C / 98% RH_ice-44.5°C / 44% RH_ice-34.0°C / 44% RH_iceEphemeral
08-30 16:00 UTC-57.0°C / 84% RH_ice-44.0°C / 47% RH_ice-34.0°C / 53% RH_iceEphemeral
08-30 17:00 UTC-56.5°C / 80% RH_ice-44.0°C / 36% RH_ice-33.5°C / 36% RH_iceEphemeral
08-30 18:00 UTC-56.0°C / 67% RH_ice-44.0°C / 36% RH_ice-33.0°C / 38% RH_iceEphemeral
08-30 19:00 UTC-56.5°C / 75% RH_ice-44.0°C / 71% RH_ice-33.5°C / 55% RH_iceEphemeral
08-30 20:00 UTC-56.0°C / 75% RH_ice-44.0°C / 67% RH_ice-33.0°C / 55% RH_iceEphemeral
08-30 21:00 UTC-56.0°C / 75% RH_ice-44.0°C / 71% RH_ice-33.0°C / 82% RH_iceEphemeral
08-30 22:00 UTC-56.0°C / 100% RH_ice-44.0°C / 101% RH_ice-33.0°C / 85% RH_icePersistent[2/3]
08-30 23:00 UTC-56.5°C / 109% RH_ice-44.0°C / 64% RH_ice-33.0°C / 71% RH_icePersistent[1/3]
08-31 00:00 UTC-56.5°C / 106% RH_ice-44.5°C / 88% RH_ice-33.0°C / 74% RH_icePersistent[1/3]
08-31 01:00 UTC-56.5°C / 109% RH_ice-44.0°C / 74% RH_ice-33.0°C / 41% RH_icePersistent[1/3]
08-31 02:00 UTC-56.5°C / 75% RH_ice-44.0°C / 47% RH_ice-33.5°C / 50% RH_iceEphemeral
08-31 03:00 UTC-56.5°C / 72% RH_ice-43.5°C / 63% RH_ice-33.0°C / 36% RH_iceEphemeral
08-31 04:00 UTC-57.0°C / 118% RH_ice-44.0°C / 97% RH_ice-33.0°C / 33% RH_icePersistent[1/3]
08-31 05:00 UTC-57.0°C / 92% RH_ice-44.0°C / 104% RH_ice-33.0°C / 82% RH_icePersistent[1/3]
08-31 06:00 UTC-56.0°C / 80% RH_ice-44.0°C / 97% RH_ice-33.0°C / 104% RH_iceEphemeral
08-31 07:00 UTC-56.5°C / 89% RH_ice-44.0°C / 60% RH_ice-33.0°C / 66% RH_iceEphemeral
08-31 08:00 UTC-56.0°C / 88% RH_ice-44.0°C / 97% RH_ice-32.5°C / 98% RH_iceEphemeral
08-31 09:00 UTC-56.0°C / 97% RH_ice-44.0°C / 101% RH_ice-33.0°C / 58% RH_icePersistent[1/3]
08-31 10:00 UTC-56.0°C / 97% RH_ice-44.0°C / 77% RH_ice-32.5°C / 44% RH_iceEphemeral
08-31 11:00 UTC-56.5°C / 122% RH_ice-44.0°C / 91% RH_ice-33.0°C / 41% RH_icePersistent[1/3]
08-31 12:00 UTC-56.5°C / 109% RH_ice-44.0°C / 64% RH_ice-33.0°C / 47% RH_icePersistent[1/3]
08-31 13:00 UTC-56.5°C / 109% RH_ice-44.0°C / 58% RH_ice-33.0°C / 52% RH_icePersistent[1/3]
08-31 14:00 UTC-56.5°C / 109% RH_ice-44.0°C / 60% RH_ice-33.0°C / 52% RH_icePersistent[1/3]
08-31 15:00 UTC-56.0°C / 100% RH_ice-44.0°C / 71% RH_ice-33.0°C / 52% RH_icePersistent[1/3]
08-31 16:00 UTC-56.0°C / 105% RH_ice-44.0°C / 88% RH_ice-33.0°C / 52% RH_icePersistent[1/3]
08-31 17:00 UTC-56.0°C / 108% RH_ice-44.0°C / 85% RH_ice-33.0°C / 60% RH_icePersistent[1/3]
08-31 18:00 UTC-56.0°C / 97% RH_ice-44.0°C / 94% RH_ice-33.0°C / 58% RH_iceEphemeral
08-31 19:00 UTC-56.0°C / 105% RH_ice-44.0°C / 74% RH_ice-33.0°C / 41% RH_icePersistent[1/3]
08-31 20:00 UTC-56.0°C / 108% RH_ice-43.5°C / 63% RH_ice-32.5°C / 22% RH_icePersistent[1/3]
08-31 21:00 UTC-56.5°C / 117% RH_ice-44.0°C / 101% RH_ice-33.0°C / 30% RH_icePersistent[2/3]
08-31 22:00 UTC-56.0°C / 108% RH_ice-44.0°C / 104% RH_ice-33.0°C / 38% RH_icePersistent[2/3]
08-31 23:00 UTC-56.5°C / 106% RH_ice-44.0°C / 104% RH_ice-33.0°C / 68% RH_icePersistent[2/3]
Validation — METAR High-Cloud Correlation (15-Day Hindcast)

Method: 15-day hindcast via Open-Meteo + IEM ASOS hourly sky conditions. High cloud = any sky layer ≥ 20,000 ft (FEW/SCT/BKN/OVC) in the METAR report of the nearest major airport. The table shows: when we predict PERSISTENT or EPHEMERAL, how often does the METAR show high-altitude cloudiness?


Note: METAR high cloud ≠ contrail observation. Natural cirrus and contrail cirrus are indistinguishable. London EGLL uses automated ceilometers (NCD = No Cloud Detected) that cannot detect thin cirrus — excluded from aggregate. Miami shows equal high-cloud rates for both classes due to tropical convective cirrus (not sensitive to upper-tropospheric humidity). This is a directional sanity check, not a precision/recall measurement.

City Predicted Hours High Cloud When Predicted
Columbus, OHPERSISTENT6162%
Columbus, OHEPHEMERAL28740%
Denver, COPERSISTENT3686%
Denver, COEPHEMERAL31256%
Seattle, WAPERSISTENT7478%
Seattle, WAEPHEMERAL27436%
Chicago, ILPERSISTENT4070%
Chicago, ILEPHEMERAL30859%
New York, NYPERSISTENT4185%
New York, NYEPHEMERAL30770%
Miami, FLPERSISTENT8685%
Miami, FLEPHEMERAL26285%
Los Angeles, CAPERSISTENT3020%
Los Angeles, CAEPHEMERAL3181%
London, UKPERSISTENT187 (63% NCD — ceilometer blind to thin cirrus)
London, UKEPHEMERAL173 (87% NCD — ceilometer blind to thin cirrus)
All 7 cirrus-capable stationsPERSISTENT36873%
All 7 cirrus-capable stationsEPHEMERAL206849%

6/7 cities show higher high-cloud agreement for PERSISTENT than EPHEMERAL. Aggregate: PERSISTENT = 73% vs EPHEMERAL = 49% (Δ = +24pp, ex-London). Miami is a special case: tropical convective cirrus saturates both classes at ~85%. This layer cannot separate contrail cirrus from natural cirrus — see the GOES-19 satellite layer below, which can.

Validation — GOES-19 Linear-Contrail Detection (Satellite)

Why a second layer: the METAR table above cannot tell a contrail from natural cirrus. This layer can, because it keys on the one thing natural cirrus is not: linearity.


Method: GOES-19 ABI split-window brightness-temperature difference (C13 10.3µm − C15 12.3µm) over a ~200 km box per city. Thin ice cloud shows a positive BTD. A bank of oriented line filters then scores each pixel by how much more it responds along its best orientation than along a typical one — large for a long, narrow contrail, ~0 for a diffuse cirrus deck — normalised by local BTD variability so the test does not simply re-flag any high-contrast scene.


Data: s3://noaa-goes19 ABI-L1b-RadC (public, no credentials). Window 2026-06-15 → 2026-06-29, 4×/day, 406 city-hours. GOES-19 is used rather than GOES-16 — GOES-16 ABI stopped producing CONUS imagery on 2025-04-07 when it was retired as GOES-East. GOES-19 carries the same instrument in the same slot. Only cities GOES-East can actually see are scored — membership is computed from a geometric limb test, not a hand-kept list (see the data-integrity note in the robustness panel for why that distinction cost us four bad city-hours).

Predicted City-Hours Linear Contrail Detected Mean Thin-Ice Cover
PERSISTENT5466.7%34.5%
EPHEMERAL35248.6%21.5%
City PERSISTENT EPHEMERAL Ordered?
columbus60% (10)56% (48)
denver75% (4)74% (54)
seattle92% (12)70% (46)
chicago67% (6)56% (52)
nyc71% (7)53% (51)
miami58% (12)33% (46)
la0% (3)2% (55)·

When we predict PERSISTENT, GOES-19 finds a linear contrail overhead 67% of the time, vs 49% for EPHEMERAL (Δ = +18pp). 6/7 cities show the correct ordering — the same score the independent METAR layer reaches, from a different instrument.

Null test: the classes are very unequal (54 PERSISTENT vs 352 EPHEMERAL), so a gap this size could in principle be small-sample noise. Shuffling the predicted labels against the observations 20,000 times reproduces a gap this large in p = 0.010 of shuffles — so the signal is unlikely to be chance.

Honest caveat: the city-level rates above (does this city-hour show a linear contrail overhead, yes/no) are a coarser measure than pixel-level precision/recall, so they are not restated as calibrated numbers here. The same detector has since been calibrated pixel-by-pixel against OpenContrails human labels — see the calibration panel below (6.3% precision, 1.9% recall, 32× lift over chance at the shipped threshold) — so the detector overall is no longer uncalibrated, even though this panel's own city-level rates are not the calibrated figures. Only the relative ordering between classes at this city-level granularity is claimed from this panel alone. Los Angeles is the outlier in both layers — June marine-layer subsidence leaves it with almost no upper-level ice cloud for either class to work with.

Detector calibration — scored against human labels

The panel above establishes that PERSISTENT scenes score higher than EPHEMERAL ones. It never established that what the detector flags is a contrail — so the standing caveat was “uncalibrated; only the class ordering is meaningful.” This panel replaces that caveat with a measured number by scoring the detector directly against all 151 human-labelled scenes (40 containing contrails) in the OpenContrails validation shards on disk — OpenContrails (Ng et al. 2023, CC BY 4.0). Labels are pixel-level; we score the aggregated ground truth (a pixel counts as contrail when >50% of annotators marked it).

anisotropy threshold precision recall F1 lift vs chance
0.250.33%18.95%0.006
0.51.09%9.31%0.020
0.752.94%4.17%0.03515×
1.0 ← shipped6.30%1.94%0.03032×
1.2511.77%0.91%0.01760×
1.517.39%0.39%0.00888×
1.7530.25%0.18%0.004153×
2.042.86%0.08%0.002217×
2.560.00%0.03%0.001304×
3.0100.00%0.01%0.000506×
0%25%50%75%100%shipped 0.251.02.03.0 anisotropy threshold (local σ) precision recall

What this says, plainly. At the shipped operating point the detector reaches 6.3% precision and 1.9% recall (F1 0.030). In absolute terms that is a weak segmenter — it finds a small minority of contrail pixels and most of what it flags is not one. But contrail pixels are only 0.20% of all pixels, so that precision is 32× better than chance. Both halves of that sentence are the result: the detector is emphatically not a random number generator, which is what licenses the class-ordering claim above — and it is also nowhere near good enough to call a contrail segmenter.

How sure are we? Bootstrapping over scenes (10,000 resamples with replacement, seed 0) puts a 95% confidence interval of 2.0–11.3% on precision and 0.7–3.2% on recall. The interval is wide — contrail pixels are rare, so the numbers ride on the 40 labelled scenes that contain one — but even the lower precision bound of 2.0% is about 10× the 0.20% base rate, so “far above chance” survives the uncertainty; “a good segmenter” does not.

It behaves like a real detector. Sweeping the anisotropy threshold traces a clean precision/recall trade-off — precision climbs monotonically from 1.1% to 100% as the threshold tightens, while recall falls. A noise source has no such curve. That monotonic ordering, not any single point, is the strongest single piece of evidence that the line filter is keying on real linear structure.

Where the signal comes from. The brightness-temperature candidate gate alone scores 0.14% precision — at or below the 0.20% base rate, i.e. no better than guessing. The oriented line filter supplies essentially all of the discriminative power. That is a load-bearing detail: the physics gate finds cold thin ice, which over CONUS is mostly ordinary cirrus; only the anisotropy test asks the question that separates a contrail from the cirrus around it.

Where the recall goes. The candidate gate keeps only 25% of human-labelled contrail pixels before the line filter ever runs, so it — not the line filter — is the ceiling on recall. The gate demands cold (BT13 < 273 K) and thin-ice (BTD > 0.5 K); a young or semi-transparent contrail over warm ground satisfies neither cleanly. Raising recall means loosening the physics gate, not sharpening the anisotropy test.

Not results-shopping. The +18pp separation above was measured at the shipped threshold (1.0) and is not restated at any other value. Best F1 over the sweep is 0.035 at threshold 0.75; that is reported as an observation for future work, not retro-fitted to the published headline.

Honest caveat — GOES-16 labels, GOES-19 detector. OpenContrails labels GOES-16 scenes; the dataset predates GOES-16's retirement as GOES-East on 2025-04-07. Our detector runs on GOES-19, which took the same 75.2°W slot with the same ABI instrument and the same bands — so the calibration should transfer, but it was measured on GOES-16 and applied to GOES-19. These are not numbers measured on our own scenes.

Session 28 — a second satellite (GOES-West / GOES-18). Western and Pacific-facing cities (LA, Seattle, Phoenix, San Francisco, Portland, Salt Lake City, San Diego, Las Vegas) sit deep in the GOES-East limb, where a fixed-degree box foreshortens badly. They are now scored by GOES-18 (137.2°W), which gives them near-nadir geometry. Each city is assigned to its best-geometry satellite by the same derived limb test that keeps Tokyo out — never a hand-kept list. G18 carries the identical ABI instrument, product and C13/C15 bands, so the detector is band-for-band applicable, and as of Session 29 the transfer is measured, not assumed: the same city-hours in the FOV overlap (8 western cities, 2026-07-18 to 2026-07-24) were scored through both satellites with the shared calibration — Cohen's kappa 0.64 (95% CI [0.57, 0.71]), concordance 83.0%, n=448 paired city-hours, detection rate G19 43.5% vs G18 31.5%. That is at least moderate agreement at the interval floor — the transfer holds on real scenes, with the interval still to tighten. West cities enter as new-roster strata and accrue forward-only, so they accelerate future evidence rather than tightening today's CI.

Rolling forecast-vs-GOES scorecard — a growing track record

The satellite panels above are a one-time check over a fixed June window. This one turns that into an ongoing, append-only track record: every day a scheduled job takes the forecast that was already archived before that day (a genuine forecast with real lead time, not a hindcast) and scores it against what the calibrated GOES-19 detector actually observed over each CONUS city's footprint. It grows one day at a time.

Read this as small-sample, weak-detector evidence — because it is. The detector is a weak segmenter (~6% pixel precision; see the calibration panel), so a single day's per-city detection bool is noisy. Daily samples are a handful of cities × a few hours, so early numbers carry almost no statistical power — only the accumulated contrast means anything. At cruise altitude in summer nearly every hour forecasts EPHEMERAL and NONE is essentially never forecast, so the literal PERSISTENT-vs-NONE table is near-empty; the useful signal is whether the detection rate rises from EPHEMERAL to PERSISTENT. And the detector is calibrated on GOES-16 labels but run on GOES-19.

Cumulative over 48 day(s) (2026-06-28 → 2026-08-29) — how often GOES saw a linear contrail, by forecast class:

forecast class city-hours contrail seen detection rate 95% CI (Wilson)
None22100%
Ephemeral4467163837%[35–38%]
Persistent95162766%[63–69%]

The detection rate rises from 37% (EPHEMERAL, n = 4467) to 66% (PERSISTENT, n = 951), a +29pp gap (bootstrap 95% CI [+26, +33]pp) that is now statistically distinguishable from zero — two-sided Fisher's exact p < 0.001, and the bootstrap gap CI excludes zero. That pooled number is confounded, though — see the city-stratified check below, which is the one to believe.

Robustness check: the pooled gap is inflated by which city, not just by forecast class

Baseline detectability varies enormously between cities — cirrus climatology, viewing geometry and GOES scan angle differ, and the table below shows per-city detection rates spanning nearly the full range. The PERSISTENT hours are not spread evenly across those cities, so pooling every city-hour together lets “this sample came from an easy-to-see city” masquerade as “this sample was forecast PERSISTENT.” That is Simpson's paradox, and it is the obvious way this panel could fool itself.

Comparing classes only within each city and averaging (Mantel–Haenszel risk difference, 33 cities carrying both classes) gives +26pp (95% CI [+23, +30]pp) — against the pooled +29pp. 10% of the headline gap was city mix. On the current sample the within-city gap survives stratification: the honest reading is that the ordering is real but smaller than the pooled number suggests.

city PERSISTENT seen EPHEMERAL seen within-city gap
nyc69% (n=58)44% (n=154)+26pp
miami41% (n=54)39% (n=158)+2pp
columbus73% (n=51)35% (n=168)+38pp
seattle87% (n=47)47% (n=172)+40pp
mexico83% (n=46)47% (n=118)+36pp
chicago68% (n=37)44% (n=183)+24pp
pittsburgh70% (n=37)43% (n=115)+27pp
saltlakecity95% (n=37)57% (n=103)+37pp
philadelphia76% (n=33)40% (n=119)+36pp
indianapolis62% (n=32)42% (n=120)+20pp
boston81% (n=31)43% (n=121)+38pp
milwaukee77% (n=31)40% (n=121)+37pp
sanfrancisco45% (n=31)11% (n=109)+34pp
washington84% (n=31)45% (n=121)+38pp
minneapolis75% (n=28)44% (n=124)+31pp
omaha79% (n=28)48% (n=124)+31pp
stlouis75% (n=28)46% (n=124)+29pp
charlotte64% (n=25)44% (n=127)+20pp
detroit72% (n=25)39% (n=127)+33pp
kansascity72% (n=25)48% (n=127)+24pp
lasvegas48% (n=25)24% (n=115)+24pp
atlanta58% (n=24)41% (n=128)+18pp
neworleans33% (n=24)26% (n=128)+8pp
denver74% (n=23)63% (n=197)+11pp
phoenix67% (n=21)36% (n=119)+30pp
sandiego24% (n=21)16% (n=119)+8pp
houston17% (n=18)19% (n=134)-2pp
la28% (n=18)14% (n=194)+14pp
nashville56% (n=18)40% (n=134)+16pp
portland56% (n=16)22% (n=124)+34pp
memphis57% (n=14)32% (n=138)+25pp
oklahomacity70% (n=10)21% (n=142)+49pp
dallas25% (n=4)12% (n=160)+12pp

Two kinds of stratum carry no trustworthy within-city signal and are held out of the estimate separately. Single-class (none): only one forecast class was ever observed, so there is no contrast at all. Small-cell (none): both classes appear but one has fewer than 3 observations, so its rate is one or two coin-flips — exactly the noise that let a lone PERSISTENT hour in Denver drag the average by tens of points. Leave-one-day-out over the 48 observation days moves the pooled gap between +29pp and +30pp, and the city-stratified gap between +26pp and +27pp (both sign-stable): no single day flips the direction. The point estimate is robust to dropping any one day; what is fresh is that the interval only just cleared zero, so the significance — not the sign — is what the new roster still has to confirm.

How the estimate got here: the CONUS roster was tripled to 33+ GOES-East cities (adding humid mid-continent and eastern locations that generate PERSISTENT hours), a minimum-cell rule (3 observations per class) was added to drop noise strata like Denver's single PERSISTENT hour, and every reachable trailing day of archived forecasts was scored. Effect on the honest headline: the city-stratified estimate moved to +26.5pp (95% CI [+23, +30]pp), which now separates from zero — the deadlock the earlier straddling-CI reported is broken on the current sample.

Is Mantel–Haenszel itself the right pooling choice? A second, independently-weighted stratified estimator — inverse-variance-weighted fixed-effect meta-analysis, which weights each city's within-city gap by 1/variance instead of MH's sample-size-only weight — run over the same 33 informative cities gives +27.4pp (95% CI [+24, +30]pp) against MH's +26.5pp. The two agree in sign with overlapping intervals, so the estimator choice is not carrying the result.

New-roster watch (since 2026-07-22): 29 additional GOES-East cities joined the roster but hold no honest history — a forecast must predate its observation, so they can only validate forward via the daily timer, never by backfill. So far they have accrued 38 obs day(s) and 3576 city-hour(s), and on the current sample they are corroborating it — folding them in lifts the stratified gap (a shift of +1.6pp). This line updates itself from scorecard.json as the new cities accumulate.

Per-day record (detected / forecast, newest first):

obs date (UTC) P+E+N city-hrs median lead PERSISTENT EPHEMERAL
2026-08-2913226.7h26/3541/96
2026-08-2713226.7h18/2156/111
2026-08-2613226.7h19/3140/101
2026-08-2513226.0h12/3619/96
2026-08-2413226.7h16/2340/109
2026-08-2313226.7h18/2443/108
2026-08-2213226.7h22/3049/102
2026-08-2113226.7h18/3334/99
2026-08-2013226.7h15/1846/114
2026-08-1913226.7h27/5824/74
2026-08-1813226.7h16/3136/101
2026-08-1713226.7h22/3025/102
2026-08-1613226.7h16/2839/104
2026-08-1513226.7h16/2443/108
2026-08-1413226.7h27/3643/96
2026-08-1313226.7h27/3249/100
2026-08-1213226.0h27/3838/94
2026-08-1113226.7h16/2950/103
2026-08-1013226.7h15/2048/112
2026-08-0913226.7h5/1151/121
2026-08-0813226.7h9/1351/119
2026-08-0713216.5h11/1545/117
2026-08-0613214.9h13/1745/115
2026-08-0513215.6h6/744/125
2026-08-0413226.7h5/823/124
2026-08-0313226.7h9/1325/119
2026-08-0213226.7h13/1947/113
2026-08-0113226.7h29/3534/97
2026-07-3113226.7h14/2428/108
2026-07-3013226.4h4/1025/122
2026-07-2913226.1h7/1335/119
2026-07-2813220.9h10/1440/118
2026-07-2713220.6h13/2339/109
2026-07-2613220.0h8/1144/121
2026-07-2513226.7h12/1643/116
2026-07-2410826.7h22/2728/81
2026-07-2310826.7h2/440/104
2026-07-2210826.7h8/1434/94
2026-07-213626.7h10/137/23
2026-07-203625.6h3/49/32
2026-07-193624.2h0/214/34
2026-07-185630.2h9/1127/45
2026-07-175615.7h2/232/54
2026-07-025645.9h9/1313/43
2026-07-015621.9h8/913/47
2026-06-305646.9h6/1612/40
2026-06-295622.9h6/812/48
2026-06-283213.5h1/215/29

The full 2×2 (PERSISTENT vs not-PERSISTENT), per-day samples, and lead times are in scorecard.json. Refreshed daily by the contrail-scorecard systemd timer; no manual step.

Research: ground truth beyond satellite (Session 9)

Two queued research tasks asked whether ground cameras and ADS-B flight tracks could give the predictor a third validation layer. Both were surveyed and both came back don't build it. The reasons are more interesting than the verdict.


Cameras — NO-GO. Two premises failed before feasibility even came up:


“Near airports” is backwards. Contrails form at 8–12 km. Aircraft within ~30 km of an airport are climbing through 0–3 km — far below the layer. The planes easiest to identify from an airport camera are exactly the ones making nothing. Jets reach contrail altitude roughly 110–220 km downrange. What matters is enroute traffic overhead, not the airport.


Aviation weather cameras cannot see the sky. FAA cameras are aimed by compass azimuth — the API schema has no elevation or tilt field at all. 83% have a 45° FOV, putting the top of frame at ~25° elevation. Zenith is never in view. They are built to judge ceiling and visibility, which is a horizon question.


Coverage settled it regardless: 1 of the 8 cities checked has an archive-grade sky camera (SRRL Golden CO, 17 km from Denver, verified live). Columbus — the default location — has none. And the field is occupied: Schumann et al. (2013) did this with four cameras and aircraft matching; EUROCONTROL's GVCCS (2025) published 24,228 labelled frames.


ADS-B — scoped, then built as a narrow negative control. A free ODbL backfill (adsb.lol) covers real flight tracks over CONUS. The ceiling on full attribution is why this was never built as one: a contrail is only observable ~41 minutes after it forms — young ones are ~100 m wide against ABI's 2 km pixels — by which point it has drifted 60–150 km. Google's own matching, human-checked over 1,000 segments, confirms only 50%.


That asymmetry is the useful part: “an aircraft was plausibly there” is weak evidence (over CONUS, one nearly always was), while “no aircraft at any plausible offset” is strong evidence a detection is not a contrail. So the version that got built is exactly that one number, not an attribution pipeline: across 72 city-hours (2026-08-01, 24 CONUS cities), positive detections had no plausible aircraft nearby 19.6% of the time, versus 26.9% for pixels the detector called clear — the opposite direction from what cirrus contamination would predict, though the gap is not statistically significant at this sample size (Fisher's exact p=0.56). It does not support cirrus contamination as the dominant driver of the detector's precision. Full method and caveats: NEGATIVE_CONTROL.md in the project source.


The finding that outranked both tasks. This project's notes recorded that calibrating the detector to real precision/recall was blocked behind Kaggle terms a human had to accept. It isn't. The OpenContrails human labels sit in a public Google Cloud Storage bucket — anonymous, CC BY 4.0, a 78 GB validation split at 780 MB per shard.


This is the second false blocker found in two sessions. The previous one claimed the satellite archive needed credentials; NOAA publishes all of it openly, and the layer shown above got built once someone checked. Both blockers were inherited assumptions, recorded in the doc used to make decisions, and never tested. Both took one command to disprove.


The standing caveat below — “uncalibrated; only the ordering is meaningful” — is therefore fixable, with no permission needed.

The Physics

Contrails form when the mixing line between hot engine exhaust and cold ambient air passes through or above the ice saturation curve — the Schmidt-Appleman criterion.


The critical temperature Tc is the ambient temperature below which contrails must form. Whether they persist depends on whether the air is supersaturated with respect to ice (RHice ≥ 100%). Open-Meteo reports RH w.r.t. liquid water, so we convert: RHice = RHliq × (esat,liq / esat,ice). At −45°C this factor is ~1.53 — meaning 65% RH (liquid) is already supersaturated w.r.t. ice.


Engine parameters used: EIH₂O = 1.25 kg/kg • LHV = 43.2 MJ/kg • η = 0.30


PressureAltitudeG (Pa/K)Tc (contrails form below)
200 hPa~11.8 km / FL3871.334 Pa/K-44.1°C
250 hPa~10.5 km / FL3441.668 Pa/K-41.8°C
300 hPa~9.0 km / FL2952.002 Pa/K-39.9°C
350 hPa~7.9 km / FL2592.335 Pa/K-38.2°C
Try It — Schmidt-Appleman Calculator

Drag the sliders to set the ambient pressure, temperature, and humidity a contrail-forming aircraft might fly through. The classification below updates instantly — the same formulas as the forecast above, computed live in your browser.

Tc (critical temp)
RHice
Classification