3Growth rate diagnostic — examines WHY the SIA peak doesn't move.
5At ~0.1 dpa, extracts the SIA distribution and computes:
6 - Net growth rate v(n) = K_grow(n)*c_1 - K_shrink(n)*c_v1 [size increments per second]
7 - Production contribution at each n
9 - Monomer SIA and vacancy concentrations
11This tells us whether loops are growing (v > 0) or shrinking (v < 0) at each size.
15from pathlib
import Path
18MODULE_ROOT = Path(__file__).resolve().parent.parent.parent
19REPO_ROOT = MODULE_ROOT.parent
20for p
in [str(REPO_ROOT), str(MODULE_ROOT)]:
25import radcluster_code.py_utils.defect_production
as _dp_mod
26import radcluster_code.py_utils.binding_energies
as _be_mod
27import radcluster_code.py_utils.bin_moment_rates
as _bmr
28import radcluster_code.py_utils.input_data
as _inp_mod
29import radcluster_code.py_utils.reaction_rates
as _rr_mod
30import radcluster_code.py_utils.rate_equations
as _re_mod
31import radcluster_code.py_utils.cpp_bridge
as _cb_mod
32import radcluster_code.py_utils.post_process
as _pp_mod
33import radcluster_code.py_utils.simulation
as _sim_mod
34for _m
in [_dp_mod, _be_mod, _bmr, _inp_mod, _rr_mod, _re_mod,
35 _cb_mod, _pp_mod, _sim_mod]:
37from radcluster_code.py_utils.simulation
import RadClusterSimulation
43 return 2.0 * np.sqrt(n * Omega / (np.pi * b_111)) * 1e9
47 't_span': (1e-6, 1e5),
55 'concentration_threshold': 1e-18,
'window_pad': 10,
59I_SIM, V_SIM = 3000, 3000
63 'eta': 0.3,
'f_cl_i': 0.3,
'f_cl_v': 0.25,
64 'E_m_1D': 0.4,
'i_mobile': i_mobile,
'L_hat': 71.8,
65 'c_C': 1.94e-4,
'E_b_C_SIA': 0.65,
66 'rho_d': 1e13,
'Z_i': 1.05,
'Z_ii': 1.2,
67 'shape_function':
'linear',
70print(
'Running baseline simulation to 0.1 dpa...')
71saved = sys.stdout, sys.stderr
72sys.stdout = sys.stderr = io.StringIO()
74 sim = RadClusterSimulation(
76 solver_mode=
'active_window', physics_option=
'full_CD_fission',
77 C_floor=1e-25, he_kinetics=
'quasi_steady_state',
78 i_mobile=i_mobile, v_mobile=5,
81 for key, val
in OVERRIDES.items():
83 for d
in [inp.production_fission, inp.production_fusion,
84 inp.diffusion, inp.reactions,
85 inp.energetics, inp.dissociation]:
90 inp.reactions[key] = val
91 inp.diffusion[
'i_mobile'] = i_mobile
92 inp.reactions[
'i_mobile'] = i_mobile
93 inp.reactions[
'i_discrete'] = i_mobile
94 inp.reactions[
'v_discrete'] = 5
95 inp.reactions[
'I_bin'] = 0
96 inp.reactions[
'V_bin'] = 0
97 inp._calculate_derived()
99 results = sim.run_adaptive(
100 solver_config=SOLVER_CONFIG, save_output=
False,
101 progress_callback=
None, boundary_threshold=0.1,
102 max_doublings=0, points_per_segment=10,
105 sys.stdout, sys.stderr = saved
108 print(
'Simulation failed!')
114dose = results[
'dose']
115idx_01 = np.argmin(np.abs(dose - 0.1))
116actual_dose = dose[idx_01]
117t_01 = results[
't'][idx_01]
119y = results[
'y'][:, idx_01]
120c_i = np.maximum(y[0:I_SIM], 0.0)
121c_v = np.maximum(y[I_SIM:2*I_SIM], 0.0)
126s2m = 1.0 / d[
'Omega']
128print(f
' GROWTH RATE DIAGNOSTIC at dose = {actual_dose:.4f} dpa (t = {t_01:.2e} s)')
130print(f
' C_i1 (SIA monomer) = {ci1:.4e} at.frac = {ci1*s2m:.4e} m^-3')
131print(f
' C_v1 (vac monomer) = {cv1:.4e} at.frac = {cv1*s2m:.4e} m^-3')
132print(f
' C_i1/C_v1 ratio = {ci1/max(cv1, 1e-300):.3f}')
134rr = sim.reaction_rates
135re = sim.rate_equations
144print(f
'\n{"n":>5} {"d(nm)":>7} {"c_n(m-3)":>11} {"K_grow":>10} {"K_shrink":>10} '
145 f
'{"v_net":>11} {"G_emit":>10} {"k2_sink":>10} {"dc/dt ratio":>11}')
148for n
in [2, 3, 4, 5, 8, 10, 11, 12, 15, 17, 20, 30, 50, 100, 200, 500]:
152 K_g = rr.K_SIA_grow[n-1]
153 K_s = rr.K_SIA_shrink[n-1]
157 v_net = K_g * ci1 - K_s * cv1
159 growth_flux = K_g * ci1 * cn
160 shrink_flux = K_s * cv1 * cn
161 ratio = growth_flux / max(shrink_flux, 1e-300)
if shrink_flux > 0
else float(
'inf')
163 print(f
'{n:5d} {n_to_d_nm(n):7.2f} {cn*s2m:11.3e} {K_g:10.3e} {K_s:10.3e} '
164 f
'{v_net:11.3e} {G_em:10.3e} {k2:10.3e} {ratio:11.3f}')
168print(f
' SIA MONOMER BALANCE (n=1)')
173print(f
' Production P_1: {Pr_1:.4e} at.frac/s')
176sink_loss = rr.k2_SIA[0] * ci1
177print(f
' Fixed sink loss (k2*c1): {sink_loss:.4e} at.frac/s')
180growth_loss = ci1 * np.dot(rr.K_SIA_grow, c_i)
181print(f
' Loop growth loss: {growth_loss:.4e} at.frac/s')
184recom_loss = rr.K_iv * ci1 * cv1
185print(f
' V-I recombination: {recom_loss:.4e} at.frac/s')
188K_3D = rr.K_3D_cav_pref
189m13 = np.arange(1, V_SIM+1)**(1./3.)
190cav_loss = ci1 * K_3D * np.dot(m13, c_v)
191print(f
' Cavity absorption: {cav_loss:.4e} at.frac/s')
193total_loss = sink_loss + growth_loss + recom_loss + cav_loss
194print(f
' ────────────────────')
195print(f
' Total loss: {total_loss:.4e} at.frac/s')
196print(f
' Production/Loss ratio: {Pr_1/max(total_loss,1e-300):.4f}')
197print(f
' Fixed sinks fraction: {sink_loss/max(total_loss,1e-300)*100:.1f}%')
198print(f
' Loop growth fraction: {growth_loss/max(total_loss,1e-300)*100:.1f}%')
199print(f
' Recombination fraction: {recom_loss/max(total_loss,1e-300)*100:.1f}%')
200print(f
' Cavity fraction: {cav_loss/max(total_loss,1e-300)*100:.1f}%')
204print(f
' GROWTH FRONT ANALYSIS')
206v_growth = np.array([rr.K_SIA_grow[n-1]*ci1 - rr.K_SIA_shrink[n-1]*cv1
for n
in range(1, I_SIM+1)])
207print(f
' v_net(n=10) = {v_growth[9]:.4e} s^-1 (growth velocity at mobility cutoff)')
208print(f
' v_net(n=50) = {v_growth[49]:.4e} s^-1')
209print(f
' v_net(n=100) = {v_growth[99]:.4e} s^-1')
210print(f
' v_net(n=500) = {v_growth[499]:.4e} s^-1')
211print(f
' At t={t_01:.0e}s, if uniform growth at v_net(100):')
212print(f
' distance = v_net * t ≈ {v_growth[99]*t_01:.1f} size increments')
213print(f
' → This means the growth front reaches n ≈ {v_growth[99]*t_01:.0f} by 0.1 dpa')
214print(f
' → Diameter = {n_to_d_nm(max(1,v_growth[99]*t_01)):.2f} nm')
218print(f
' SIA SIZE DISTRIBUTION (top 20 sizes by concentration)')
220sorted_idx = np.argsort(c_i)[::-1]
221for rank
in range(20):
222 n = sorted_idx[rank] + 1
223 cn = c_i[sorted_idx[rank]]
224 print(f
' #{rank+1:2d} n={n:5d} d={n_to_d_nm(n):6.2f} nm c_n={cn*s2m:.3e} m^-3')