1"""Bin-by-bin comparison, with NO reconstruction anywhere.
3Takes the exact arm's per-size 1/2<111> spectrum, SUMS it onto the binned
4run's own bin edges to form the exact mu0 and mu1 per bin, and compares those
5against the moments the binned run actually carries. Reconstruction is what
6the closure approximates, so comparing reconstructed spectra would measure the
7closure twice; comparing moments measures the dynamics alone.
11sys.path.insert(0,
"/Users/ghoni/Documents/GitHub/RadCluster")
12sys.path.insert(0,
"/Users/ghoni/Documents/GitHub/RadCluster/radcluster_code/digital_twin")
13from radcluster_code.py_utils
import visualization
as viz
14from radcluster_code.py_utils.simulation
import RadClusterSimulation
15import run_ensemble
as _re
17OUT =
"/Users/ghoni/Documents/GitHub/RadCluster/radcluster_code/output"
19NB = int(sys.argv[1])
if len(sys.argv) > 1
else 32
23 f = sorted(glob.glob(f
"{OUT}/{pat}/plots/plot_data.pkl"))
24 return viz.load_plot_data(f[-1])
if f
else None
28sim = RadClusterSimulation(I=I, V=I, solver_mode=
"full_system",
29 equations=
"bin_moment", cascade=
"fission",
30 he_kinetics=
"quasi_steady_state",
31 i_mobile=5, v_mobile=5)
32_re.apply_bin_config(sim, {
"equations":
"bin_moment",
"i_discrete": 50,
33 "v_discrete": 50,
"I_bin": NB,
"V_bin": NB,
34 "shape_function":
"linear"})
35sim.input_data._calculate_derived(); sim.rebuild_rates()
36RE = sim.rate_equations
38i_d, P = int(RE.i_discrete), int(RE.n_mom)
39print(f
"layout: i_discrete={i_d} I_bin={len(bins)} P={P} r={RE.r:.4f}")
43if ref
is None or bm
is None:
44 print(
"missing plot_data"); sys.exit(1)
46yr = np.asarray(ref[
"results"][
"y"], float)
47yb = np.asarray(bm[
"results"][
"y"], float)
48C_floor = float(ref[
"input_data"].reactions.get(
"C_floor", 1e-15))
50print(f
"ref dose {float(np.asarray(ref['results']['dose'])[jr]):.4g} "
51 f
"bm dose {float(np.asarray(bm['results']['dose'])[jb]):.4g}")
53c_ref = np.maximum(yr[:I, jr], 0.0)
54print(f
"\n{'bin':>4} {'sizes':>13} {'mu0 exact':>12} {'mu0 bm':>12} {'d%':>8} "
55 f
"{'mu1 exact':>12} {'mu1 bm':>12} {'d%':>8}")
56tot = {
"e0": 0.0,
"b0": 0.0,
"e1": 0.0,
"b1": 0.0}
58he = float(np.sum(np.maximum(c_ref[1:i_d] - C_floor, 0.0)))
59hb = float(np.sum(np.maximum(yb[1:i_d, jb] - C_floor, 0.0)))
60ns = np.arange(2, i_d + 1)
61he1 = float(np.dot(ns, np.maximum(c_ref[1:i_d] - C_floor, 0.0)))
62hb1 = float(np.dot(ns, np.maximum(yb[1:i_d, jb] - C_floor, 0.0)))
63print(f
"{'head':>4} {f'2-{i_d}':>13} {he:12.5g} {hb:12.5g} "
64 f
"{(hb-he)/he*100:+7.2f}% {he1:12.5g} {hb1:12.5g} {(hb1-he1)/he1*100:+7.2f}%")
65tot[
"e0"] += he; tot[
"b0"] += hb; tot[
"e1"] += he1; tot[
"b1"] += hb1
67for k, (nlo, nhi)
in enumerate(bins):
72 seg = c_ref[nlo - 1:hi]
73 sizes = np.arange(nlo, hi + 1)
74 e0 = float(np.sum(seg)); e1 = float(np.dot(sizes, seg))
76 b0 = float(yb[off, jb]); b1 = float(yb[off + 1, jb])
if P >= 2
else 0.0
77 d0 = (b0 - e0) / e0 * 100
if e0
else float(
"nan")
78 d1 = (b1 - e1) / e1 * 100
if e1
else float(
"nan")
79 if e0 > 1e-14
or abs(d0) > 1:
80 print(f
"{k:4d} {f'{nlo}-{nhi}':>13} {e0:12.5g} {b0:12.5g} {d0:+7.2f}% "
81 f
"{e1:12.5g} {b1:12.5g} {d1:+7.2f}%")
82 tot[
"e0"] += e0; tot[
"b0"] += b0; tot[
"e1"] += e1; tot[
"b1"] += b1
84print(f
"\nTOTAL mu0 exact={tot['e0']:.6g} bm={tot['b0']:.6g} "
85 f
"({(tot['b0']-tot['e0'])/tot['e0']*100:+.2f}%)")
86print(f
"TOTAL mu1 exact={tot['e1']:.6g} bm={tot['b1']:.6g} "
87 f
"({(tot['b1']-tot['e1'])/tot['e1']*100:+.2f}%)")
88print(f
"mean n exact={tot['e1']/tot['e0']:.4f} bm={tot['b1']/tot['b0']:.4f} "
89 f
"({(tot['b1']/tot['b0'])/(tot['e1']/tot['e0'])*100-100:+.2f}%)")