# Evidence for "The magic number that was right for exactly one grid size"

Every number in [the post](/blog/the-magic-number-that-was-right-for-exactly-one-grid-size/)
comes from a file in this folder, or is computed by a script here from one. `claims.csv` maps each
number in the post to its source. The folder sits in the site's `static/` directory, so Hugo copies it
unchanged to `/evidence/the-magic-number-that-was-right-for-exactly-one-grid-size/`: every file listed
below is at that path followed by its name.

The `.gz` files are gzipped CSVs; `gunzip -k *.gz forensic/*.gz` before running a script on them.

## What was measured

- **Library.** [shaia/CFD](https://github.com/shaia/CFD).
  - *Before*: commit `9aa06d1`, the last commit before the fix.
  - *February*: commit `554a988`, the parent of `be356a3`, which switched the OpenMP projection to CG.
  - *After*: branch `fix/neumann-sor-omega`, [PR #209](https://github.com/shaia/CFD/pull/209). The scalar
    SOR and Red-Black SOR sweeps measured in `runs_accept` and `runs_curves` are those of `29c8a47`; no
    later commit on the branch changes them. The per-backend runs used `48b9d28` for the CPU backends and
    `29c8a47` for CUDA, against master `b9f31e4`.
  - *Merged*: PR #209 was squash-merged on 2026-09-14 as
    [`23b196a`](https://github.com/shaia/CFD/commit/23b196a051ac7fdfed60cf6b2d66a84eb48994c1), so the branch
    commits above are not on master; the pull request keeps them. The branch gained 13 commits after the runs
    here: divergence reporting in the CUDA loops, argument checks in the example, docs and tests. Rebuilt
    against a GitHub archive of `23b196a` (SHA-256 in `meta.txt`) and rerun, `jobs_accept.py`'s 1,408 jobs
    reproduced `runs_accept.csv` byte for byte, SHA-256
    `bf797e3da51b3e58f380a18f586e8099a9ea935c83a54e668ef14e8e1d7f24e1` uncompressed for both.
    `sweep_example.txt` records the post's "Run it yourself" commands at `23b196a`.
- **Machine.** Intel Core i9-13980HX (8 performance and 16 efficiency cores, 32 threads), Windows 11
  (10.0.26200), MSVC 19.44.35228, CMake 4.2.1; CUDA 13.0 on an RTX 4090 Laptop GPU. `meta.txt` has the
  compiler path, flags and the SHA-256 of each exported source tree.
- **Problem, for every sweep count.** A right-hand side of splitmix64 noise from seed 20260913 on the
  interior with its interior mean removed, a zero initial guess, spacing h = 1/(n-1) on the unit square,
  and the library's stopping rule: the maximum-norm residual below 1e-6 times its starting value.
  Walls are the library's default zero-gradient copy (`bc` N) or held at zero through a no-op `apply_bc`
  installed before init (`bc` D).

## Files

| File | What it is |
|---|---|
| `sweep/sweep.c`, `sweep/CMakeLists.txt` | The harness: one solve per job line through the public API, with an independent replay through `poisson_solver_iterate` that must reproduce the library's count (`manual_match`), and the residual history's tail rate |
| `jobs.py`, `jobs_accept.py`, `jobs_curves.py` | Job generators for pass 1 and 2 (`9aa06d1`), the acceptance runs and the full scans on the fix |
| `runs1.csv.gz`, `runs2.csv.gz` | Pass 1 and pass 2 at `9aa06d1`: sets S1 (omega scans on 9x9 to 129x129), S2 (every grid from 5x5 to 17x17), S3 (257x257 and CG), S4 (a second problem and the March test's replica), S5 (3D), S6 (Gauss-Seidel, the zero and uniform right-hand side replicas, the residual floor) |
| `runs_accept.csv.gz`, `runs_curves.csv.gz` | The fix: automatic omega, omega 1.5 and fine scans (A1, A2), and full scans from 1.00 to 1.99 (C1) |
| `runs_options.csv` | The candidate automatic omegas compared before choosing the exact optimum |
| `sweep_example.txt` | The post's "Run it yourself" commands run at the merge commit `23b196a`, and the same example built onto `b9f31e4` for the "before" curve |
| `theory.py`, `theory.json`, `theory.txt` | Young's theory, the Jacobi spectra, the exact iteration matrices of the library's lagged sweep and of the corrected one, the Rayleigh quotient against the exact optimum, CG's condition numbers |
| `analyze.py` | Pass 1 and 2 summaries (best omega per grid, log-log slopes) |
| `pr_tables.py`, `pr_tables.md` | The before and after tables in the post and the pull request |
| `backend_runs.sh`, `backends/`, `backend_tables.py`, `backend_tables.md` | `examples/sor_omega_sweep.c` on every SOR and Red-Black SOR backend before (`b9f31e4`) and after; `diag-before-*` and `stab-after-*` are the AVX2 Block SOR stability scans |
| `forensic/forensic-554a988.patch` | The logging and experiment switches applied to an export of `554a988`: a per-call log in the solve loop, and CMake options `FORENSIC_OMEGA` (the textbook omega), `FORENSIC_MEAN` (remove the right-hand side's mean) and `FORENSIC_CG` (CG, as in `be356a3`) |
| `run_forensic.sh`, `forensic.py`, `forensic/` | The February arms: A0 as it was, A1 `FORENSIC_OMEGA`, A2 `FORENSIC_MEAN`, A3 both, B0 `FORENSIC_CG`, C0 the AVX2 test with `CFD_ENABLE_AVX2=ON`, and A0 on 1 and 8 threads; each arm's per-call log (`*-poisson.csv.gz`), its test output (`*-out.txt`), and `summary.csv` |
| `figure_data.py`, `figures/*.json`, `figures/check_figures.js` | The data embedded in the post's figures, written from the run files, and the Node replica that recomputes what each figure shows |
| `reducer/` | A standalone C reproduction of the library's lagged sweep, which matched the library to 17 digits on five solves, and a variant with the walls relaxed as the fix does |

## Rerunning

The shell scripts carry the absolute paths of the machine they ran on; change `W`, `WIN` and `ws` at their
tops. Python scripts expect to run from this folder.

- **Sweeps.** Export a CFD commit with `git archive`, point `sweep/CMakeLists.txt`'s `add_subdirectory` at
  it, build Release, then `python jobs.py > jobs.csv`, `sweep --header > runs.csv` and
  `sweep --jobs jobs.csv >> runs.csv`. `sweep --calibrate NX` estimates run time first.
- **Theory.** `python theory.py` (NumPy) writes `theory.json` and `theory.txt`.
- **February.** Export `554a988`, apply `forensic/forensic-554a988.patch`, configure one build per arm with
  the options above (Release, `BUILD_TESTS=ON`), run `run_forensic.sh`, then `python forensic.py`.
- **Backends.** Build `sor_omega_sweep` in an AVX2 build and a CUDA build of each side, then
  `bash backend_runs.sh` and `python backend_tables.py`.
- **Figures.** `python figure_data.py --embed-sor-sweep <site>/layouts/shortcodes/sor-sweep.html` (and
  `sor-scaling`, `sor-best`, `sor-kink`; the cover takes `--embed-sor-sweep` too), then
  `node figures/check_figures.js <site>/layouts/shortcodes`.
