73def ladder(B, md, tbl, prefix, bins, dom, refid):
74 """One binning ladder, with deviations only if its exact arm completed."""
75 ref_rec = B.get(refid)
if refid
else None
77 md.append(f
"## {tbl} — binning convergence at I = V = {dom}\n")
78 md.append(
"`i_discrete = v_discrete = 50` throughout; only the bin count "
79 "moves, so a deviation below is closure error and nothing else.\n")
81 why = (f
"`{refid}` did not complete" if refid
else
82 "the discrete solve at this domain diverges")
83 md.append(f
"> **No exact arm: {why}.** The rungs are listed so their "
84 "mutual spread can be read, but the deviation columns are "
85 "WITHHELD. Scoring a ladder against its own finest rung is "
86 "how one converges confidently to the wrong answer.\n")
88 md.append(
"| rung | I_bin=V_bin | r | N_eq | "
89 +
" | ".join(o[1]
for o
in OBS) +
" |")
90 md.append(
"|" +
"---|" * (4 + len(OBS)))
92 md.append(f
"| **{refid} (exact)** | — | — | {ref_rec.get('N_eq', '—')} | "
93 +
" | ".join(f[2].format(ref[f[0]])
for f
in OBS) +
" |")
101 got = r.get(
"dose_reached")
102 why = (f
"reached {got:.4g} dpa" if isinstance(got, (int, float))
103 else r.get(
"status",
"not run"))
104 md.append(f
"| {rid} | {nb} | — | — | _{why}_ | "
105 +
" | ".join([
"—"] * (len(OBS) - 1)) +
" |")
107 md.append(f
"| {rid} | {rec.get('bin_I_bin', nb)} "
108 f
"| {rec.get('bin_r', float('nan')):.3f} "
109 f
"| {rec.get('N_eq', '—')} | "
110 +
" | ".join(f[2].format(v[f[0]])
for f
in OBS) +
" |")
111 rows.append((nb, rec, v))
114 if ref
is not None and rows:
115 md.append(f
"### {tbl}: deviation from the exact arm (%)\n")
116 md.append(
"| rung | N_eq | r | "
117 +
" | ".join(o[1].split(
" ")[0]
for o
in OBS) +
" | max |")
118 md.append(
"|" +
"---|" * (4 + len(OBS)))
119 for nb, rec, v
in rows:
120 devs = [((v[k] - ref[k]) / ref[k] * 100.0)
if ref[k]
else float(
"nan")
122 worst = max(abs(d)
for d
in devs)
123 md.append(f
"| {prefix}{nb} | {rec.get('N_eq', '—')} "
124 f
"| {rec.get('bin_r', float('nan')):.2f} | "
125 +
" | ".join(f
"{d:+.1f}" for d
in devs)
126 + f
" | **{worst:.1f}** |")
131 ap = argparse.ArgumentParser()
132 ap.add_argument(
"--out")
133 a = ap.parse_args(argv)
135 ent = {e[
"run_id"]: e
for e
in manifest_mod.manifest()}
137 ref_id = f
"M1_D{manifest_mod.M_REF_DOMAIN}"
138 ref_rec = B.get(ref_id)
141 md = [
"# Campaign M — system size and binning",
""]
142 md.append(f
"Production mobility (`i_mobile = v_mobile = 5`), fission "
143 f
"cascade, `LOOP_COAL` and `prec_bw` at their defaults in BOTH "
144 f
"arms. All rows share one 45-point log grid to "
145 f
"{SCORE_DOSE} dpa, so the comparison dose is the same point in "
146 f
"every column (plan S3.2).\n")
149 md.append(
"## M1 — cost against domain (fully discrete)\n")
150 md.append(
"| rung | I = V | N_eq | dose reached | wall |")
151 md.append(
"|---|---|---|---|---|")
152 for rid, e
in ent.items():
153 if e[
"table"]
not in (
"M1",
"M1REF"):
157 md.append(f
"| {rid} | {e['I']} | — | _not run_ | — |")
159 st = r.get(
"status",
"?")
161 md.append(f
"| {rid} | {e['I']} | {r.get('N_eq', '—')} "
162 f
"| {r.get('dose_reached', float('nan')):.4g} / {e['dose']:g} dpa "
163 f
"| {(w/60 if w else float('nan')):.1f} min ({st}) |")
169 for tbl, prefix, bins, dom, refid
in (
170 (
"M2R",
"M2R_B", manifest_mod.M2R_BINS, manifest_mod.M2R_DOMAIN,
171 manifest_mod.M_REF_RUN),
172 (
"M2",
"M2_B", manifest_mod.M2_BINS, manifest_mod.M_BIN_DOMAIN,
174 ladder(B, md, tbl, prefix, bins, dom, refid)
176 md.append(
"## What must be stated\n")
177 md.append(f
"1. **The domain is truncated.** V = {manifest_mod.M_BIN_DOMAIN} "
178 "sits below the mean cavity size at production dose, so d_cav "
179 "reads the ceiling at the coarse rungs rather than the model. "
180 "Both arms see the identical truncation, so it cancels out of the "
181 "closure error — but nothing here may be compared to experiment.")
182 md.append(
"2. **`delta_FP` runs ~0.05 throughout**, against the model's own "
183 "1e-2 gate. A separate, disclosed defect: closure error and "
184 "conservation error are different quantities. Do not conflate "
186 md.append(
"3. **The reference is a single trajectory, not a converged "
187 "domain.** M1 shows how cost grows with I; it does not show that "
188 "I = V = 10000 is itself converged in domain. A rung agreeing "
189 "with it agrees with the exact solution ON THIS DOMAIN.\n")
193 Path(a.out).write_text(out, encoding=
"utf-8")
194 print(f
"wrote {a.out}")