RadCluster
Graph-based cluster dynamics for irradiated materials
Loading...
Searching...
No Matches
diagnose_stall.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2"""Diagnose bin-moment stall: run to t=8000 (just before stall) and inspect state."""
3
4import sys, os, io, importlib, numpy as np
5from pathlib import Path
6
7MODULE_ROOT = Path(__file__).resolve().parent.parent.parent
8REPO_ROOT = MODULE_ROOT.parent
9for p in [str(REPO_ROOT), str(MODULE_ROOT)]:
10 if p not in sys.path:
11 sys.path.insert(0, p)
12
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}')
17 importlib.reload(m)
18from radcluster_code.py_utils.simulation import RadClusterSimulation
19
20I, V = 10000, 10000
21i_mobile, v_mobile = 10, 5
22i_discrete, v_discrete = 200, 50
23
24saved = sys.stdout, sys.stderr
25sys.stdout = sys.stderr = io.StringIO()
26try:
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,
32 )
33 overrides = {
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,
40 'r_ratio': 1.3,
41 }
42 inp = sim.input_data
43 for key, val in overrides.items():
44 placed = False
45 for d in [inp.production_fission, inp.production_fusion,
46 inp.diffusion, inp.reactions,
47 inp.energetics, inp.dissociation]:
48 if key in d:
49 d[key] = val
50 placed = True
51 if not placed:
52 inp.reactions[key] = val
53 inp.diffusion['i_mobile'] = i_mobile
54 inp.reactions['i_mobile'] = i_mobile
55 inp._calculate_derived()
56 sim.rebuild_rates()
57finally:
58 sys.stdout, sys.stderr = saved
59
60re = sim.rate_equations
61PM = re.n_mom
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)
64
65# Run to t=8000 (just before stall at ~9000)
66config = {
67 't_span': (1e-6, 8e3),
68 'n_points': 100,
69 'log_time': True,
70 'rtol': 1e-5,
71 'atol': 1e-20,
72 'solver_method': {'linsol': 'dense'},
73}
74
75print("Running to t=8000...", flush=True)
76results = sim.run(solver_config=config, save_output=False, timeout_s=60)
77
78if results is None:
79 print("FAILED — solver returned None", flush=True)
80 sys.exit(1)
81
82t = results['t']
83y = results['y']
84print(f"Completed {len(t)} points, t_final={t[-1]:.4e}", flush=True)
85
86i_d = re.i_discrete
87i_VAC = i_d + PM * re.I_bin
88
89# Analyze SIA bin moments
90print(f"\n=== SIA bin moments at t={t[-1]:.2e} ===", flush=True)
91# Compute bin edges with r_ratio=1.3
92edges = []
93edge = i_d + 1
94for k in range(re.I_bin):
95 n_lo = edge
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))
99 edge = n_hi
100
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]
106 bw = n_hi - n_lo
107
108 # Check linear reconstruction at bin edges
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
112 c_lo = c_hi = 0
113 if abs(det) > 1e-30:
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
120
121 flag = ""
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):
127 flag += f" NBAR_OUT"
128
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)
132
133# Same for VAC
134v_d = re.v_discrete
135vac_start = i_VAC + v_d
136print(f"\n=== VAC bin moments at t={t[-1]:.2e} ===", flush=True)
137v_edges = []
138edge = v_d + 1
139for k in range(re.V_bin):
140 m_lo = edge
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))
144 edge = m_hi
145
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]
151 bw = m_hi - m_lo
152
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
156 c_lo = c_hi = 0
157 if abs(det) > 1e-30:
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
164
165 flag = ""
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):
171 flag += f" NBAR_OUT"
172
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)
176
177# Check discrete concentrations
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)
181
182# Find the discrete SIA concentration at the boundary (n=i_discrete)
183c_boundary = y[i_d - 1, -1]
184print(f"c_SIA[{i_d}] = {c_boundary:.3e} (discrete/bin boundary)", flush=True)
185
186print(f"\ndelta_FP = {results['delta_FP'][-1]:.3e}", flush=True)