#!/usr/bin/env python3 """The Sun and the solar wind in the 24 hours before the S0 News upload of November 1, 2026. Fact-Checked Science, "S0 News, November 1, 2026", section 9 ("Our analysis"). Standard library only. Run it python3 -I sun_window.py [folder] The folder (default: the script's own) must hold the four extracts published beside this page: goes-flares-2026-10-25-11-01.csv NOAA SWPC's GOES X-ray flare list, the 7 days read on Nov 1 at about 15:55 UTC swpc-events-2026-10-31-11-01.csv every line of SWPC's Edited Events for Oct 31 and Nov 1, as updated by 15:52 UTC on Nov 1 swpc-srs-2026-10-31-11-01.csv SWPC's Solar Region Summaries 304 and 305 solar-wind-2026-10-31-11-01.csv 15-minute medians of NOAA's real-time solar wind, active spacecraft, Oct 31, 15:55, to Nov 1, 15:50 UTC (the 1-minute series has isolated spikes; it also has gaps, notably 18-21 UT) NOAA's files roll over, so these extracts are the record we used. What it prints For the window 2026-10-31 12:09:09 to 2026-11-01 12:09:09 UTC: the flares by class in both lists, what came just after, the spotted regions, and the solar wind's highest speed and field, with their values at the upload. """ import csv import datetime as dt import os import sys UPLOAD = dt.datetime(2026, 11, 1, 12, 9, 9) START = UPLOAD - dt.timedelta(hours=24) def main(): sys.stdout.reconfigure(encoding="utf-8") folder = sys.argv[1] if len(sys.argv) > 1 else os.path.dirname(os.path.abspath(__file__)) def rd(name): with open(os.path.join(folder, name), encoding="utf-8") as f: return list(csv.DictReader(f)) flares = rd("goes-flares-2026-10-25-11-01.csv") events = rd("swpc-events-2026-10-31-11-01.csv") srs = rd("swpc-srs-2026-10-31-11-01.csv") wind = rd("solar-wind-2026-10-31-11-01.csv") print(f"Window: {START} to {UPLOAD} UTC, the 24 hours before the upload\n") def when(s): return dt.datetime.strptime(s, "%Y-%m-%dT%H:%M:%SZ") win = [f for f in flares if START <= when(f["max_utc"]) < UPLOAD] print(f"GOES flare list: {len(win)} flares peak in the window: " + ", ".join(f"{f['max_utc'][5:16]} {f['max_class']} (GOES-{f['satellite']})" for f in win)) c = [f for f in flares if f["max_class"][0] in "CMX" and when(f["max_utc"]) < UPLOAD] print(f" last C-class or larger before the upload: {c[-1]['max_class']} at {c[-1]['max_utc']}" if c else " none C-class or larger before the upload") after = [f for f in flares if when(f["max_utc"]) >= UPLOAD] print(" after the upload: " + ", ".join(f"{f['max_utc'][5:16]} {f['max_class']}" for f in after)) print("\nEdited Events, X-ray events (peak, class, observatory, region):") iw = [] for e in events: if e["type"] != "XRA": continue t = dt.datetime.strptime(f"{e['date']} {e['max']}", "%Y-%m-%d %H%M") inw = START <= t < UPLOAD cls = e["particulars"].split()[0] if inw: iw.append(cls) print(f" {'in window ' if inw else 'outside '} {t:%m-%d %H:%M} {cls} {e['observatory']} region {e['region'] or '-'}") print(f" in the window: {len(iw)}, all of class {sorted({x[0] for x in iw})}, largest {max(iw, key=lambda x: float(x[1:]))}") print("\nSolar Region Summaries:") for r in srs: print(" " + ", ".join(f"{k}={v}" for k, v in r.items() if v)) sp = [(r["time_utc"], float(r["speed_km_s_median"])) for r in wind if r["speed_km_s_median"]] bt = [(r["time_utc"], float(r["bt_nT_median"])) for r in wind if r["bt_nT_median"]] bz = [(r["time_utc"], float(r["bz_gsm_nT_median"])) for r in wind if r["bz_gsm_nT_median"]] print("\nSolar wind, 15-minute medians:") print(" highest speed", max(sp, key=lambda x: x[1]), "; highest Bt", max(bt, key=lambda x: x[1]), "; lowest Bz", min(bz, key=lambda x: x[1])) at = next(r for r in wind if r["time_utc"] == "2026-11-01T12:00") print(" at the upload (12:00 bin):", at) if __name__ == "__main__": main()