"""Repeat every number of ours on https://factchecked.science/planet-alignments-earthquakes/ in one go. Needs Python 3 and NumPy (for the steps that take the singular value decomposition). Keep the page's scripts in one folder and run, from anywhere: python3 -I run_all.py [--usgs ] It downloads about 280 MB from JPL Horizons (156 hourly tables for 2000-2025 and 24 one-minute check tables; tables already in the work folder are kept) and three USGS lists, unless --usgs names a folder holding the page's snapshot, then runs the steps below and writes /results/. It takes about half an hour, mostly downloading. At the end it compares its planet-dates-results.json with the page's copy, if that sits beside the scripts. Horizons' replies carry the time of the request in their header, so the comparison uses the SHA-256 of their ephemeris lines; the USGS revises its catalogue now and then, so a fresh list can differ from the snapshot of 2026-10-28. Steps: fetch_horizons.py, fetch_usgs.py; select_dates.py (the paper's dates, 2000-2025, and four variants for 2010-2012); overlap_test.py; diagnose_dates.py; outsample_test.py; explore.py; why_2011.py; intervals.py; tides.py; check_interpolation.py; mirror_coords.py; then the further, exploratory test with the threshold rule of the author's code (author_rule.py, scored by the same scripts, with three variants, in /results/author-rule/) and the two blocks taken together (combine_blocks.py); summarize.py. """ import json, pathlib, shutil, subprocess, sys HERE = pathlib.Path(__file__).resolve().parent BODIES = ["199", "299", "399", "301", "499", "599"] CHECK_DAYS = [("2010-01-06", "2010-01-07"), ("2010-04-30", "2010-05-01"), ("2011-09-16", "2011-09-17"), ("2012-10-28", "2012-10-29")] USGS = {"in": ("2010-01-01", "2013-01-01"), "new": ("2013-01-01", "2026-01-01"), "old": ("2000-01-01", "2010-01-01")} VARIANTS = [("0.35", "svd", "sunlast"), ("0.35", "svd", "reversed"), ("0.35", "eigh", "sunfirst"), ("1.0", "svd", "sunfirst")] AUTHOR_VARIANTS = [("svd", "sunlast"), ("svd", "reversed"), ("eigh", "sunfirst")] BLOCKS = ((2010, 2012), (2013, 2025), (2000, 2009)) def run(script, *args, out=None): cmd = [sys.executable, "-I", str(HERE / script), *map(str, args)] print("+", script, *map(str, args), flush=True) r = subprocess.run(cmd, check=True, capture_output=out is not None, text=True) if out is not None: with open(out, "w", encoding="utf-8", newline="\n") as f: f.write(r.stdout) def main(): args = sys.argv[1:]; usgs_dir = None if "--usgs" in args: i = args.index("--usgs"); usgs_dir = pathlib.Path(args[i + 1]); del args[i:i + 2] work = pathlib.Path(args[0]); hz, hz1, us, res = work / "horizons", work / "horizons-1min", work / "usgs", work / "results" for d in (hz, hz1, us, res): d.mkdir(parents=True, exist_ok=True) for y in range(2000, 2026): run("fetch_horizons.py", hz, "1h", f"{y - 1}-12-31T23:00", f"{y + 1}-01-01T01:00", *BODIES) for a, b in CHECK_DAYS: run("fetch_horizons.py", hz1, "1m", f"{a}T00:00", f"{b}T00:00", *BODIES) csv = {} for k, (a, b) in USGS.items(): name = f"usgs-m6.5-{a}-{b}.csv" if usgs_dir is not None: shutil.copyfile(usgs_dir / name, us / name) elif not (us / name).exists(): run("fetch_usgs.py", us, a, b) csv[k] = us / name for y0, y1 in ((2010, 2012), (2013, 2025), (2000, 2009)): run("select_dates.py", hz, res, y0, y1) for alpha, method, order in VARIANTS: run("select_dates.py", hz, res, 2010, 2012, alpha, method, order) run("overlap_test.py", res / "dates-2010-2012.json", csv["in"], out=res / "overlap-2010-2012.json") for alpha, method, order in VARIANTS: tag = f"2010-2012-a{alpha}-{method}-{order}" run("overlap_test.py", res / f"dates-{tag}.json", csv["in"], out=res / f"overlap-{tag}.json") for tag in ("2010-2012", "2013-2025", "2000-2009"): run("diagnose_dates.py", hz, res / f"selection-{tag}.json", res / f"diagnose-{tag}.json", out=res / f"diagnose-{tag}.txt") run("outsample_test.py", res, csv["new"], res / "dates-2013-2025.json", res / "diagnose-2013-2025.json", out=res / "outsample-2013-2025.txt") run("outsample_test.py", res, csv["old"], res / "dates-2000-2009.json", res / "diagnose-2000-2009.json", out=res / "outsample-2000-2009.txt") run("explore.py", hz, res, csv["in"], csv["new"], csv["old"], "--out", res / "explore.json", out=res / "explore.txt") run("why_2011.py", hz, res / "why-2011.json", out=res / "why-2011.txt") run("intervals.py", res / "outsample-2013-2025.json", res / "outsample-2000-2009.json", "--out", res / "intervals.json", out=res / "intervals.txt") run("tides.py", hz, res / "tides.json", out=res / "tides.txt") run("mirror_coords.py", hz, res / "mirror-2010-02-18.json", out=res / "mirror-2010-02-18.txt") run("combine_blocks.py", res / "dates-2013-2025.json", csv["new"], res / "dates-2000-2009.json", csv["old"], "--out", res / "combined-preregistered-rule.json", out=res / "combined-preregistered-rule.txt") ar = res / "author-rule" # the author's code's threshold rule: exploratory, run afterwards ar.mkdir(exist_ok=True) for y0, y1 in BLOCKS: run("author_rule.py", hz, ar, y0, y1) for method, order in AUTHOR_VARIANTS: run("author_rule.py", hz, ar, y0, y1, method, order) run("overlap_test.py", ar / "dates-2010-2012.json", csv["in"], out=ar / "overlap-2010-2012.json") for tag in ("2010-2012", "2013-2025", "2000-2009"): run("diagnose_dates.py", hz, ar / f"selection-{tag}.json", ar / f"diagnose-{tag}.json", out=ar / f"diagnose-{tag}.txt") run("outsample_test.py", ar, csv["new"], ar / "dates-2013-2025.json", ar / "diagnose-2013-2025.json", out=ar / "outsample-2013-2025.txt") run("outsample_test.py", ar, csv["old"], ar / "dates-2000-2009.json", ar / "diagnose-2000-2009.json", out=ar / "outsample-2000-2009.txt") run("intervals.py", ar / "outsample-2013-2025.json", ar / "outsample-2000-2009.json", "--out", ar / "intervals.json", out=ar / "intervals.txt") run("combine_blocks.py", ar / "dates-2013-2025.json", csv["new"], ar / "dates-2000-2009.json", csv["old"], "--out", ar / "combined.json", out=ar / "combined.txt") for method, order in AUTHOR_VARIANTS: vd = ar / f"{method}-{order}"; vd.mkdir(exist_ok=True) run("overlap_test.py", ar / f"dates-2010-2012-{method}-{order}.json", csv["in"], out=vd / "overlap-2010-2012.json") run("outsample_test.py", vd, csv["new"], ar / f"dates-2013-2025-{method}-{order}.json", out=vd / "outsample-2013-2025.txt") run("outsample_test.py", vd, csv["old"], ar / f"dates-2000-2009-{method}-{order}.json", out=vd / "outsample-2000-2009.txt") run("check_interpolation.py", hz, hz1, res / "selection-2010-2012.json", *[a for a, _ in CHECK_DAYS], out=res / "check-interpolation.txt") run("summarize.py", res, hz, HERE, res / "planet-dates-results.json") ours, page = res / "planet-dates-results.json", HERE / "planet-dates-results.json" if page.exists(): a, b = json.load(open(ours, encoding="utf-8")), json.load(open(page, encoding="utf-8")) diff = [k for k in b if k not in ("about",) and a.get(k) != b.get(k)] print("Compared with the page's planet-dates-results.json:", "identical" if not diff else "differs in " + ", ".join(diff)) print("Done. Results in", res) if __name__ == "__main__": main()