#!/usr/bin/env python3 """The Sun in the 24 hours before the S0 News upload of October 28, 2026. Fact-Checked Science, "S0 News, October 28, 2026", section 3 ("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: goes18-flares-2026-10-21-28.csv NOAA SWPC's GOES-18 X-ray flare list for the 7 days to Oct 28, 12:52 UTC swpc-events-2026-10-27-28.csv every line of SWPC's Edited Events for Oct 27 and 28, as updated at 15:27 and 15:57 UTC on Oct 28 swpc-srs-2026-10-27-28.csv the "Regions with Sunspots" of SWPC's Solar Region Summaries 300 and 301 cactus-cmes-2026-10-22-28.csv the CME list of CACTus (SIDC, Royal Observatory of Belgium), from SOHO/LASCO C2 and C3, issued Oct 28, 13:54 UTC NOAA's files roll over, so these extracts, made on October 28, 2026, at about 16:00 UTC, are the record we used. What it prints For the window 2026-10-27 10:45:55 to 2026-10-28 10:45:55 UTC (the video went up at 03:45:55 Arizona time): the flares by class (A < 1e-7 <= B < 1e-6 <= C < 1e-5 <= M < 1e-4 <= X, peak 0.1-0.8 nm flux in W/m2), any M- or X-class flare in the whole 7-day list, the Edited Events lines in the window with their regions, the event types on the two days (DSF would be a disappearing filament), the spotted regions in the two Solar Region Summaries, and the CACTus CMEs whose onset falls in the window. CACTus's position angle runs counterclockwise from solar north, so about 0-180 degrees is the east limb and 180-360 the west limb. """ import csv import datetime as dt import os import sys UPLOAD = dt.datetime(2026, 10, 28, 10, 45, 55, tzinfo=dt.timezone.utc) START = UPLOAD - dt.timedelta(hours=24) def utc(s, fmt): return dt.datetime.strptime(s, fmt).replace(tzinfo=dt.timezone.utc) 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__)) rd = lambda name: list(csv.DictReader(open(os.path.join(folder, name), encoding="utf-8"))) flares = rd("goes18-flares-2026-10-21-28.csv") events = rd("swpc-events-2026-10-27-28.csv") srs = rd("swpc-srs-2026-10-27-28.csv") cmes = rd("cactus-cmes-2026-10-22-28.csv") print(f"Window: {START:%Y-%m-%d %H:%M:%S} to {UPLOAD:%Y-%m-%d %H:%M:%S} UTC, the 24 hours before the upload\n") win = [f for f in flares if START <= utc(f["max_utc"], "%Y-%m-%dT%H:%M:%SZ") < UPLOAD] print(f"GOES-18 flare list, {flares[0]['max_utc']} to {flares[-1]['max_utc']}: {len(win)} flares peak in the window") by = {} for f in win: by[f["max_class"][0]] = by.get(f["max_class"][0], 0) + 1 print(f" peak {f['max_utc'][5:16].replace('T', ' ')} {f['max_class']}") print(" by class: " + ", ".join(f"{k} {v}" for k, v in sorted(by.items()))) mx = [f for f in flares if f["max_class"][0] in "MX"] print(" M- or X-class flares anywhere in the 7-day list: " + (", ".join(f["max_class"] for f in mx) or "none")) before = [f for f in flares if utc(f["max_utc"], "%Y-%m-%dT%H:%M:%SZ") < START and f["max_class"][0] in "CMX"] if before: print(f" last C-class or larger flare before the window: {before[-1]['max_class']} at {before[-1]['max_utc']}") print("\nSWPC Edited Events in the window (XRA = X-ray event, RBR/RSP = radio bursts):") types = {} for e in events: types[e["type"]] = types.get(e["type"], 0) + 1 hhmm = e["max"] if e["max"] != "////" else e["begin"] when = utc(f"{e['date']} {hhmm}", "%Y-%m-%d %H%M") if START <= when < UPLOAD: print(f" {when:%m-%d %H:%M} event {e['event']} {e['type']} {e['particulars']}" f" region {e['region'] or '-'}") xra = [e for e in events if e["type"] == "XRA" and START <= utc(f"{e['date']} {e['max']}", "%Y-%m-%d %H%M") < UPLOAD] print(f" X-ray events in the window: {len(xra)}, of which from Region 4561: " f"{sum(1 for e in xra if e['region'] == '4561')}, with no region: {sum(1 for e in xra if not e['region'])}") print(" event types on October 27-28, all hours: " + ", ".join(f"{k} {v}" for k, v in sorted(types.items())) + " (no DSF, no disappearing filament)") print("\nSolar Region Summaries, regions with sunspots:") for r in srs: print(f" SRS {r['srs_number']} ({r['issued_utc']}, valid {r['valid']}): Region {r['region']} at " f"{r['location']}, {int(r['area_msh'])} millionths, {r['class']}, {int(r['spots'])} spot(s), " f"{r['magnetic_type']}") print("\nCACTus CMEs with onset in the window:") found = [c for c in cmes if c["table"] == "CME" and START <= utc(c["onset_utc"], "%Y-%m-%d %H:%M") < UPLOAD] for c in found: print(f" CME {c['number']} onset {c['onset_utc']}, position angle {c['position_angle_deg']}") if not found: last = max((c for c in cmes if c["table"] == "CME"), key=lambda c: c["onset_utc"]) print(f" none; the latest CME in the list began {last['onset_utc']} UTC " f"(list issued {last['cactus_issued']})") if __name__ == "__main__": main()