RadCluster
Graph-based cluster dynamics for irradiated materials
Loading...
Searching...
No Matches
check_binwise_moments.py
Go to the documentation of this file.
1"""Bin-by-bin comparison, with NO reconstruction anywhere.
2
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.
8"""
9import glob, sys
10import numpy as np
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
16
17OUT = "/Users/ghoni/Documents/GitHub/RadCluster/radcluster_code/output"
18I = 1000
19NB = int(sys.argv[1]) if len(sys.argv) > 1 else 32
20
21
22def load(pat):
23 f = sorted(glob.glob(f"{OUT}/{pat}/plots/plot_data.pkl"))
24 return viz.load_plot_data(f[-1]) if f else None
25
26
27# Bin edges: rebuild the same layout the run used.
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
37bins = list(RE.bins)
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}")
40
41ref = load("*_M1_D1000_*")
42bm = load(f"*_M2R_B{NB}_*")
43if ref is None or bm is None:
44 print("missing plot_data"); sys.exit(1)
45
46yr = np.asarray(ref["results"]["y"], float) # [N_eq, n_t] discrete
47yb = np.asarray(bm["results"]["y"], float)
48C_floor = float(ref["input_data"].reactions.get("C_floor", 1e-15))
49jr, jb = -1, -1
50print(f"ref dose {float(np.asarray(ref['results']['dose'])[jr]):.4g} "
51 f"bm dose {float(np.asarray(bm['results']['dose'])[jb]):.4g}")
52
53c_ref = np.maximum(yr[:I, jr], 0.0) # sizes 1..I
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}
57# discrete head, sizes 2..i_d
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
66
67for k, (nlo, nhi) in enumerate(bins):
68 # The last bin's nhi can exceed I -- build_bins walks floor(edge*r)+1 and
69 # the final edge overshoots the domain. Clip to what the exact arm
70 # actually holds, or the size vector and the spectrum slice disagree.
71 hi = min(nhi, I)
72 seg = c_ref[nlo - 1:hi] # sizes nlo..hi inclusive
73 sizes = np.arange(nlo, hi + 1)
74 e0 = float(np.sum(seg)); e1 = float(np.dot(sizes, seg))
75 off = i_d + P * k
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
83
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}%)")