FORECASTEAGLE.COM · NOAA / NWS DATA · NORTH AMERICA HOMERRFSHRRRRTMAGFSNDFDNBMALERTSMODELSNOTESCOLOURSBUILDERSTATUSABOUT

FIFTEEN-MINUTE STEPS, AND THE END OF THE 3-HOURLY BARRIER

What a cycle publishes is not what the file names suggest.

For a long time the RRFS explorer showed one frame an hour, and for a long time it was quietly stale. Both problems had the same cause, and fixing one fixed the other.

What a cycle actually publishes

Walk the NOMADS listings for the RRFS and the pattern is not the one the file names suggest:

cyclepublishes
every hoursub-hourly files, f001 to f018, and nothing else
00 / 03 / 06 / 09 / 12 / 15 / 18 / 21Zplain hourly files f000 to f018 as well
00 / 06 / 12 / 18Zplain hourly files all the way to f084

The ingest looked for the newest cycle by probing for a plain file, and two cycles in three do not publish one. So the explorer sat on the last cycle that did. The log was unambiguous: the 09Z run was re-rendered at 10:40, 11:40, 12:40 and 13:40. The sub-hourly files land every hour, so reading them fixes the freshness as much as the time step.

Inside a sub-hourly file

A sub-hourly file holds four complete field sets — :15, :30, :45 and the hour — back to back, 157 messages over about 165 MB. Until recently there was no index to ask for one message by; you could only ask for a byte range. The layout turned out to be strikingly regular: measured across f001, f010 and f017, the four blocks begin at 0.000, 0.249, 0.499 and 0.749 of the file, never off by more than 0.6%, with reflectivity always the first message of a block.

The first version fetched each block as its own range and pulled only the first 13% of the three intermediate blocks, so the quarter-hours carried just the fast-moving fields — a threefold bandwidth saving, paid for by aligning buffers to message boundaries by hand and a window that had to be sized as a fraction of a block, which it originally was not, silently dropping cloud ceiling after f007. This host has a 3 Gbit path to NOAA, so that was a bad trade: pulling the whole file was one request, needed no alignment, could not drop a field, and gave every field at every quarter-hour — about 185 MB and 45 seconds a forecast hour.

Then, on 17 August 2026, the premise expired. The NOMADS listing for that day's 21Z cycle carried 205 .idx sidecars — one for every file, CONUS and every nest. With an index, the scatter of fields through a file is free: the twelve fields this site draws sit anywhere from 0.0% (reflectivity) to 40.7% (ceiling) into a plain file, and covering the last one used to mean pulling everything before it. Now a sub-hourly file costs 77 MB in eight ranges rather than 181 MB whole. Verified field by field, not by size: 36 of 36 field-steps bit-identical, which was the check that mattered, because those files hold four sets and a field pattern legitimately matches four times. Take only the first match and the quarter-hours would still have rendered — from the wrong data.

Two fields came with the sub-hourly files: echo tops and visibility, both natives of them. Wind is computed from the u and v components, because the sub-hourly files carry no speed field.

Fields that do not share a clock

Once radar runs every fifteen minutes and CAPE runs every hour, the fields no longer share a time axis. So frames are keyed by minutes from the cycle rather than a forecast hour, the catalog publishes the union of every field's steps, and switching field keeps you at the same moment instead of throwing you back to the start. The lead reads F06 while the steps are hourly and +2:15 once they are not.

cadencerange
Radar, echo tops, temp, dewpoint, wind, gusts, visibility, ceiling, total precip15 min18 h
CAPE, PWAT, 1-h precip60 min3-hourly cycles only
Everything from the plain fileshourlyto 84 h at 00/06/12/18

The HRRR, and the sidecar that changes everything

The HRRR page is the same instrument as the RRFS page, because the two models publish CONUS on the same 1799 by 1059 Lambert grid. What differs is the source. The HRRR on AWS has always shipped an index: 10 kB of byte offsets for a 160 MB file. So that ingest reads the index and fetches only the messages the site draws — about 10 MB of 160, six percent, and roughly 200 MB for a whole 19-hour cycle, where the RRFS prefix path once cost 3.2 GB.

The HRRR publishes a sub-hourly series too, and the index makes it cheaper in a way that matters: each block's messages are labelled with their own valid time, so exactly one message is fetched per field and step, where the RRFS path pulls all four blocks and sorts them out afterwards. Not every field is worth having every fifteen minutes: the sub-hourly messages are the same size as the hourly ones and there are four per file, so each costs about four times what it costs hourly. The ones taken are the ones whose shape changes inside an hour — a squall line, a gust front, the temperature falling through an outflow boundary: radar, temperature, dewpoint, humidity, gust, wind and both precipitation fields. CAPE, PWAT and the ceiling do not, and stay hourly.

Two of those are built rather than read. The sub-hourly files carry no relative humidity at all, so it is derived from temperature and dewpoint — at every step, hourly ones included, so the series is one quantity computed one way. And precipitation appears only as native 15-minute buckets, so the trailing hour and the run total are accumulated across the run here — the one sequential thing in the ingest: a bucket missing at f007 makes every total after it wrong, so the chain stops rather than publishing a number that looks fine.

Racing the model

Making the fetch faster exposed a bug the slow version had been hiding. One 16Z RRFS run came out ending at +16 hours instead of +18: the last two files returned 404 because NOMADS had not written them yet. Nothing was broken by speed — the old 17-minute pass used to reach the tail after the model published it, and a 6-minute pass arrives in front.

Two things were wrong. The cycle was adopted too early. The readiness probe asked for six forecast hours, but the RRFS publishes its sub-hourly series over roughly two hours, so a cycle taken at +1 h is inevitably short. The test is now the last hour, and a cycle that has not got there is passed over for the one before it — about an hour of freshness for a loop that is always its full length. And a run that outran the model did not come back for the files it missed. It does now, in four sweeps two and a half minutes apart, with a 404 read as "not written yet" rather than as an error to retry.