65 """Recompute the observables for one run from its saved solution."""
66 from radcluster_code.py_utils
import visualization
as viz
67 import run_ensemble
as re_mod
69 d = viz.load_plot_data(str(run_dir /
"plots" /
"plot_data.pkl"))
70 res, idata = d[
"results"], d[
"input_data"]
71 sim = types.SimpleNamespace(input_data=idata)
73 cfg = {
"I": entry[
"I"],
"V": entry[
"V"],
"dose": float(entry[
"dose"]),
74 "C_floor": float(idata.reactions.get(
"C_floor", 1e-15))}
75 out = re_mod.observe(res, sim, cfg, 1.0)
77 reads = entry[
"dose_read"]
78 reads = reads
if isinstance(reads, (list, tuple))
else [reads]
79 lad = out.get(
"at_dose")
or {}
82 key = f
"{float(target):g}"
84 scored[key] = got
if got
else {
"missing":
True,
85 "dose_reached": out.get(
"dose_reached")}
86 out[
"scored"] = scored
87 out[
"reached_all_scoring_doses"] = all(
88 not v.get(
"missing")
for v
in scored.values())
93 ap = argparse.ArgumentParser(description=__doc__,
94 formatter_class=argparse.RawDescriptionHelpFormatter)
95 ap.add_argument(
"--dry-run", action=
"store_true",
96 help=
"report what would change; publish nothing")
97 ap.add_argument(
"--only", default=
None, help=
"restrict to one run_id")
98 ap.add_argument(
"--branch", default=
"campaign-verification")
99 a = ap.parse_args(argv)
101 entries = {e[
"run_id"]: e
for e
in manifest_mod.manifest()}
104 found = {k: v
for k, v
in found.items()
if k == a.only}
106 print(
"no re-scoreable runs on this machine "
107 "(need output/<...>/plots/plot_data.pkl)")
112 claims = sync.claims()
114 print(f
"{len(found)} run(s) with a saved solution on this machine\n")
116 for rid
in sorted(found):
117 entry, run_dir = entries[rid], found[rid]
119 new =
rescore(rid, run_dir, entry)
120 except Exception
as exc:
121 print(f
" {rid:14s} RESCORE FAILED: {type(exc).__name__}: {exc}")
123 old = claims.get(rid, {})
124 old_sc, new_sc = old.get(
"scored")
or {}, new[
"scored"]
126 for k
in sorted(new_sc, key=float):
127 o, n = old_sc.get(k)
or {}, new_sc[k]
128 od, nd = o.get(
"dose"), n.get(
"dose")
129 if od
is None or nd
is None:
130 moved.append(f
"{k}: {'MISSING' if o.get('missing') else od} -> "
131 f
"{'MISSING' if n.get('missing') else nd}")
132 elif abs(od - nd) > 1e-9 * max(abs(od), 1.0):
133 moved.append(f
"{k} dpa: scored at {od:.4g} -> {nd:.4g}"
134 f
" (d100 {o.get('d_100_nm', float('nan')):.3f}"
135 f
" -> {n.get('d_100_nm', float('nan')):.3f} nm)")
136 flag =
"" if new[
"reached_all_scoring_doses"]
else " [did not reach all]"
137 print(f
" {rid:14s} {'CHANGED' if moved else 'unchanged'}{flag}")
141 changed.append((rid, new))
144 print(f
"\ndry run: {len(changed)} record(s) would be republished")
147 for rid, new
in changed:
148 rec = dict(claims.get(rid, {}))
150 rec[
"rescored_at"] = time.strftime(
"%Y-%m-%dT%H:%M:%S%z")
151 rec[
"rescore_reason"] = (
"dose ladder fix b5e9a51: end-point rounding "
152 "and off-grid checkpoints scored the wrong dose")
153 ok = sync.publish(rid, rec, {
"observables.json":
154 json.dumps(rec, indent=2, sort_keys=
True,
155 default=str) +
"\n"})
156 print(f
" republished {rid}: {'ok' if ok else 'PUSH FAILED'}")
157 print(f
"\n{len(changed)} record(s) corrected")