2"""Diagnose bin-moment stall: run to t=8000 (just before stall) and inspect state."""
4import sys, os, io, importlib, numpy
as np
5from pathlib
import Path
7MODULE_ROOT = Path(__file__).resolve().parent.parent.parent
8REPO_ROOT = MODULE_ROOT.parent
9for p
in [str(REPO_ROOT), str(MODULE_ROOT)]:
13for mod_name
in [
'defect_production',
'binding_energies',
'bin_moment_rates',
14 'input_data',
'reaction_rates',
'rate_equations',
15 'cpp_bridge',
'post_process',
'simulation']:
16 m = importlib.import_module(f
'radcluster_code.py_utils.{mod_name}')
18from radcluster_code.py_utils.simulation
import RadClusterSimulation
21i_mobile, v_mobile = 10, 5
22i_discrete, v_discrete = 200, 50
24saved = sys.stdout, sys.stderr
25sys.stdout = sys.stderr = io.StringIO()
27 sim = RadClusterSimulation(
28 I=I, V=V, solver_mode=
'full_system',
29 physics_option=
'bin_moment_CD_fission',
30 C_floor=1e-20, he_kinetics=
'quasi_steady_state',
31 i_mobile=i_mobile, v_mobile=v_mobile,
34 'eta': 0.3,
'f_cl_i': 0.6,
'f_cl_v': 0.15,
35 'E_m_1D': 0.4,
'i_mobile': i_mobile,
'L_hat': 71.8,
36 'c_C': 1.94e-4,
'E_b_C_SIA': 0.65,
37 'rho_d': 1e15,
'Z_i': 1.05,
'Z_ii': 1.2,
38 'shape_function':
'linear',
39 'i_discrete': i_discrete,
'v_discrete': v_discrete,
43 for key, val
in overrides.items():
45 for d
in [inp.production_fission, inp.production_fusion,
46 inp.diffusion, inp.reactions,
47 inp.energetics, inp.dissociation]:
52 inp.reactions[key] = val
53 inp.diffusion[
'i_mobile'] = i_mobile
54 inp.reactions[
'i_mobile'] = i_mobile
55 inp._calculate_derived()
58 sys.stdout, sys.stderr = saved
60re = sim.rate_equations
62print(f
"N_eq={re.N_eq} i_d={re.i_discrete} I_bin={re.I_bin} "
63 f
"v_d={re.v_discrete} V_bin={re.V_bin} PM={PM}", flush=
True)
67 't_span': (1e-6, 8e3),
72 'solver_method': {
'linsol':
'dense'},
75print(
"Running to t=8000...", flush=
True)
76results = sim.run(solver_config=config, save_output=
False, timeout_s=60)
79 print(
"FAILED — solver returned None", flush=
True)
84print(f
"Completed {len(t)} points, t_final={t[-1]:.4e}", flush=
True)
87i_VAC = i_d + PM * re.I_bin
90print(f
"\n=== SIA bin moments at t={t[-1]:.2e} ===", flush=
True)
94for k
in range(re.I_bin):
96 n_hi = max(int(edge * 1.3), edge + 1)
97 n_hi = min(n_hi, I + 1)
98 edges.append((n_lo, n_hi))
101for k
in range(re.I_bin):
102 mu0 = y[i_d + PM*k, -1]
103 mu1 = y[i_d + PM*k + 1, -1]
if PM >= 2
else 0
104 n_bar = mu1 / max(abs(mu0), 1e-300)
105 n_lo, n_hi = edges[k]
109 S1 = sum(range(n_lo, n_hi))
110 S2 = sum(n*n
for n
in range(n_lo, n_hi))
111 det = bw * S2 - S1 * S1
114 phi0_lo = (S2 - S1 * n_lo) / det
115 phi1_lo = (bw * n_lo - S1) / det
116 c_lo = phi0_lo * mu0 + phi1_lo * mu1
117 phi0_hi = (S2 - S1 * (n_hi-1)) / det
118 phi1_hi = (bw * (n_hi-1) - S1) / det
119 c_hi = phi0_hi * mu0 + phi1_hi * mu1
122 if mu0 < 0: flag +=
" MU0_NEG"
123 if mu1 < 0
and mu0 > 1e-30: flag +=
" MU1_NEG"
124 if c_lo < -1e-30: flag += f
" c_lo={c_lo:.1e}_NEG"
125 if c_hi < -1e-30: flag += f
" c_hi={c_hi:.1e}_NEG"
126 if mu0 > 1e-30
and (n_bar < n_lo * 0.8
or n_bar > n_hi * 1.2):
129 print(f
" bin {k:2d}: [{n_lo:5d},{n_hi:5d}) bw={bw:4d} "
130 f
"mu0={mu0:+10.2e} mu1={mu1:+10.2e} nbar={n_bar:8.1f} "
131 f
"c_lo={c_lo:+.2e} c_hi={c_hi:+.2e}{flag}", flush=
True)
135vac_start = i_VAC + v_d
136print(f
"\n=== VAC bin moments at t={t[-1]:.2e} ===", flush=
True)
139for k
in range(re.V_bin):
141 m_hi = max(int(edge * 1.3), edge + 1)
142 m_hi = min(m_hi, V + 1)
143 v_edges.append((m_lo, m_hi))
146for k
in range(re.V_bin):
147 mu0 = y[vac_start + PM*k, -1]
148 mu1 = y[vac_start + PM*k + 1, -1]
if PM >= 2
else 0
149 n_bar = mu1 / max(abs(mu0), 1e-300)
150 m_lo, m_hi = v_edges[k]
153 S1 = sum(range(m_lo, m_hi))
154 S2 = sum(m*m
for m
in range(m_lo, m_hi))
155 det = bw * S2 - S1 * S1
158 phi0_lo = (S2 - S1 * m_lo) / det
159 phi1_lo = (bw * m_lo - S1) / det
160 c_lo = phi0_lo * mu0 + phi1_lo * mu1
161 phi0_hi = (S2 - S1 * (m_hi-1)) / det
162 phi1_hi = (bw * (m_hi-1) - S1) / det
163 c_hi = phi0_hi * mu0 + phi1_hi * mu1
166 if mu0 < 0: flag +=
" MU0_NEG"
167 if mu1 < 0
and mu0 > 1e-30: flag +=
" MU1_NEG"
168 if c_lo < -1e-30: flag += f
" c_lo={c_lo:.1e}_NEG"
169 if c_hi < -1e-30: flag += f
" c_hi={c_hi:.1e}_NEG"
170 if mu0 > 1e-30
and (n_bar < m_lo * 0.8
or n_bar > m_hi * 1.2):
173 print(f
" bin {k:2d}: [{m_lo:5d},{m_hi:5d}) bw={bw:4d} "
174 f
"mu0={mu0:+10.2e} mu1={mu1:+10.2e} nbar={n_bar:8.1f} "
175 f
"c_lo={c_lo:+.2e} c_hi={c_hi:+.2e}{flag}", flush=
True)
178sia_neg = np.sum(y[:i_d, -1] < -1e-30)
179vac_neg = np.sum(y[i_VAC:i_VAC+v_d, -1] < -1e-30)
180print(f
"\nNegative discrete: SIA={sia_neg}/{i_d}, VAC={vac_neg}/{v_d}", flush=
True)
183c_boundary = y[i_d - 1, -1]
184print(f
"c_SIA[{i_d}] = {c_boundary:.3e} (discrete/bin boundary)", flush=
True)
186print(f
"\ndelta_FP = {results['delta_FP'][-1]:.3e}", flush=
True)