2binding_energies.py — Defect cluster binding energies for bcc Fe / EUROFER97.
4All equations and tables cite:
5 Ghoniem, N.M. (2026), "A Cluster Dynamics Model for Radiation Damage
6 Evolution in Ferritic-Martensitic Steels" (Rate_Equations.pdf).
10- Vacancy cluster of size m (pure void): c_{m,0}
11- He-vacancy cluster: c_{m,ℓ} — m vacancies + ℓ He atoms
12- Interstitial cluster of size n: c_n
13- All energies in eV; lengths in m unless stated.
24def atomic_radius(Omega):
25 """Wigner-Seitz radius r_0 = (3Ω/4π)^{1/3} [m]."""
26 return (3.0 * Omega / (4.0 * np.pi)) ** (1.0 / 3.0)
30 """r_n = r_0 · n^{1/3} [m]. Eq. 86 (equivalent form)."""
31 return atomic_radius(Omega) * np.asarray(n, dtype=float) ** (1.0 / 3.0)
38_A_void = {0: 1.2353, 1: 2.9064, 2: 3.4147, 3: 2.1504, 4: -0.1590}
42def E_b_void(m, E_f_v, gamma_s, Omega, lambda_void=None, A_void_0=None):
44 Binding energy of the m-th vacancy in a spherical void cluster.
46 Capillary formula with atomistic correction (Eqs. 66-67):
47 E_b(m) = E_f_v − A_cap · [m^{2/3} − (m−1)^{2/3}]
48 + A_void(0) · exp(−λ·(m−1)) [Eq. 67 correction]
50 where A_cap = 4π·γ_s·r_0² [eV], r_0 = (3Ω/4π)^{1/3}.
52 Special case m=1: return E_f_v (monomer formation energy).
58 gamma_s : float [J/m²]
60 lambda_void : float or None [vac^-1]
61 Blend decay constant λ. ``None`` (default) uses the module constant
62 ``_lambda_void`` = ln(100)/8, preserving legacy behaviour. Supplied
63 from the ``dissociation`` sheet key ``lambda`` by ReactionRates so the
64 workbook value is actually honoured — before this argument existed the
65 workbook carried ``lambda`` and ``A_void_0`` but nothing read them.
66 A_void_0 : float or None [eV]
67 Atomistic correction amplitude A(0). ``None`` uses ``_A_void[0]``.
71 E_b : float or array [eV]
73 r0 = atomic_radius(Omega)
74 A_cap = 4.0 * np.pi * gamma_s * r0**2 * _J_eV
75 A_atm = _A_void[0]
if A_void_0
is None else float(A_void_0)
76 _lam = _lambda_void
if lambda_void
is None else float(lambda_void)
78 m = np.asarray(m, dtype=float)
79 capillary = A_cap * (m**(2.0/3.0) - (m - 1.0)**(2.0/3.0))
80 correction = A_atm * np.exp(-_lam * np.maximum(m - 1.0, 0.0))
82 Eb = np.where(m <= 1.0,
84 E_f_v - capillary + correction)
85 return float(Eb)
if Eb.ndim == 0
else Eb
97 He pressure in a bubble with m vacancies and ℓ He atoms.
99 P·V = N_He·k_B·T · [1 + B2·(N_He/V) + B3·(N_He/V)²]
101 V = m·Ω (bubble volume).
112 P = float(ell) * kB_SI * T / V * (1.0 + _B2 * rho + _B3 * rho**2)
116def E_b_bubble(m, ell, E_f_v, gamma_s, Omega, T):
118 Effective vacancy binding energy for a He-vacancy bubble (m, ℓ).
120 Capillary model with He-pressure correction (Eqs. 70-73):
121 E_b_bub(m, ℓ) = E_f_v − A_cap·[m^{2/3}−(m−1)^{2/3}]
122 + P_He(m,ℓ)·Ω [He pressure work term]
124 The pressure term is POSITIVE: removing a vacancy from a gas-filled
125 cavity compresses the gas, costing work +P·Ω, so He raises the
126 vacancy binding energy and stabilizes bubbles against thermal
127 dissociation (equivalently c_v^eq(surface) ∝ exp[(2γ/r − P)Ω/kT]).
128 This term creates the stable-bubble branch and critical radius r*
129 of critical-bubble theory (Mansur & Coghlan).
134 ell : int or float (can be fractional mean loading)
136 gamma_s : float [J/m²]
144 r0 = atomic_radius(Omega)
145 A_cap = 4.0 * np.pi * gamma_s * r0**2 * _J_eV
147 capillary = A_cap * (m_f**(2.0/3.0) - max(m_f - 1.0, 0.0)**(2.0/3.0))
149 dE_P = P * Omega * _J_eV
150 return E_f_v - capillary + dE_P
157_A_He = {2: 0.55, 3: 0.40, 4: 0.75}
160def E_b_He(m, ell, E_s_He, Omega, T, B2=_B2, B3=_B3):
162 He binding energy: energy to remove one He from bubble (m, ℓ).
164 Combined continuum + atomistic formula (Eqs. 76-77, Table 19):
166 E_b^He(m, ℓ) = E_s^He + P_He(m,ℓ)·Ω [continuum, Eq. 76]
167 + f_blend · A^He(α) · exp(−μ·(ℓ−α)) [atomistic, Eq. 77]
169 where α = ℓ_max(m) = floor(alpha_He · m^{2/3}) is the trap-mutation limit,
170 and f_blend fades the atomistic correction for large ℓ.
175 ell : int — current He content
176 E_s_He : float [eV] — He interstitial solution energy (= 2.35 eV, Table 5)
185 E_cont = E_s_He + P * Omega * _J_eV
188 alpha = int(round(ell))
189 A_atm = _A_He.get(alpha, 0.0)
191 E_atm = A_atm * np.exp(-_mu_He * max(ell - alpha, 0.0))
195 return E_cont + E_atm
199 """He binding energy to a monovacancy (first He, Table 5). Eq. 76."""
204 """He binding energy to a monovacancy (second He, Table 5). Eq. 76."""
211 nu=0.29, gamma_sf=0.6, Omega=1.18e-29):
213 Continuum binding energy of the n-th SIA to an interstitial loop.
215 E_b^{i,cont}(n) = E_f^i − μ_loop^{(i)}(n) [Eqs. i_binding, chem_pot_i_loop]
217 where μ = Gb·Ω/(4π(1−ν)R)·[ln(R/r_c)+1] + γ_sf·Ω/b,
218 R = sqrt(n·Ω/(π·b)), r_c = b/3.
220 n = np.asarray(n, dtype=float)
221 n_safe = np.maximum(n, 2.0)
223 R = np.sqrt(n_safe * Omega / (np.pi * b))
224 ln_fac = np.log(R / r_c) + 1.0
225 mu = (G_shear * b * Omega / (4.0 * np.pi * (1.0 - nu) * R)) * ln_fac \
226 + gamma_sf * Omega / b
232def A_111_from_E_b_i2(E_b_i2, B_111):
234 Loop-binding amplitude ``A_111`` implied by a measured **di-interstitial
235 binding energy** ``E_b^i(2)``.
237 The small-n branch of :func:`E_b_loop_i` is the DFT power-law fit
238 ``E_b^fit(n) = A_111 · n^{+B_111}`` (note the exponent is POSITIVE — binding
239 *increases* with loop size). Evaluated at n = 2 this is the binding of the
240 second SIA to a mono-interstitial, i.e. the di-interstitial binding energy.
243 A_111 = E_b^i(2) · 2^{−B_111}
245 Anchoring on ``E_b^i(2)`` rather than on ``A_111`` directly is preferable
246 for calibration: ``A_111`` has no independent measurement, whereas
247 ``E_b^i(2)`` is reported by DFT for bcc Fe (≈0.8 eV).
249 Note that :func:`E_b_loop_i` returns the *blended* value at n = 2, which
250 differs from ``E_b_i2`` by the continuum admixture (1 − w) ≈ 1e-4 at the
251 default blend (n_tr = 25, σ_tr = 5) — negligible, but not identically zero.
255 E_b_i2 : float [eV] — di-interstitial binding energy
256 B_111 : float — power-law exponent
262 return float(E_b_i2) * 2.0 ** (-float(B_111))
265def E_b_loop_i(n, A_111=0.7501, B_111=0.3873, A_100=0.7160, B_100=0.3581,
266 n_tr=25.0, sigma_tr=5.0,
267 E_f_i=3.77, G_shear=82e9, b_111=2.49e-10, nu=0.29,
268 gamma_sf=0.6, Omega=1.18e-29):
270 Binding energy of the n-th SIA to an interstitial loop of size n−1.
272 Small-n DFT fit blended to continuum limit (Eqs. Eb_smalln_fit, Eb_blended):
273 E_b^fit(n) = A · n^{+B} [Eq. Eb_smalln_fit]
274 E_b^cont(n) = E_f^i − μ_loop(n) [continuum elasticity]
275 w(n) = 0.5·(1 + tanh((n_tr − n)/σ_tr)) [Eq. weight_function]
276 E_b(n) = w·E_b^fit + (1−w)·E_b^cont [Eq. Eb_blended]
278 For small n, w→1 and E_b follows the DFT power-law fit.
279 For large n, w→0 and E_b approaches E_f^i (formation energy).
283 n : int or array (≥ 2)
284 A_111, B_111 : float — ½⟨111⟩ loop fit (Table 18)
285 A_100, B_100 : float — ⟨100⟩ loop fit (Table 18)
286 n_tr : float — blend center (25 SIAs)
287 sigma_tr : float — blend width (5 SIAs)
288 E_f_i : float — SIA formation energy [eV]
289 G_shear : float — shear modulus [Pa]
290 b_111 : float — ½⟨111⟩ Burgers vector [m]
291 nu : float — Poisson ratio
292 gamma_sf : float — stacking fault energy [J/m²]
293 Omega : float — atomic volume [m³]
297 E_b : float or array [eV]
299 n = np.asarray(n, dtype=float)
300 n_safe = np.maximum(n, 2.0)
303 Eb_fit = A_111 * n_safe**(B_111)
310 w = 0.5 * (1.0 + np.tanh((n_tr - n_safe) / sigma_tr))
311 Eb = w * Eb_fit + (1.0 - w) * Eb_cont
313 return float(Eb)
if Eb.ndim == 0
else Eb
316def E_b_loop_100(n, A_100=0.7160, B_100=0.3581,
317 n_tr=25.0, sigma_tr=5.0,
318 E_f_i=3.77, G_shear=82e9, b_100=2.8665e-10, nu=0.29,
319 gamma_sf=0.6, Omega=1.18e-29):
321 Binding energy of the n-th SIA to a ⟨100⟩ interstitial loop of size n−1.
323 Sibling of :func:`E_b_loop_i` for the sessile ⟨100⟩ population
324 (½⟨111⟩→⟨100⟩ loop-conversion work). Same small-n DFT fit blended to the
325 continuum limit (Eqs. Eb_smalln_fit, Eb_blended), but with the ⟨100⟩ fit
326 amplitudes ``A_100, B_100`` (Table 18 — already carried, dormant, by
327 :func:`E_b_loop_i`) and the ⟨100⟩ Burgers vector ``b_100 = a`` in the
330 E_b^fit(n) = A_100 · n^{+B_100}
331 E_b^cont(n) = E_f^i − μ_loop(n; b_100)
332 w(n) = 0.5·(1 + tanh((n_tr − n)/σ_tr))
333 E_b(n) = w·E_b^fit + (1−w)·E_b^cont
335 ⟨100⟩ loops are slightly less bound than ½⟨111⟩ at small n
336 (``A_100 < A_111``); emission is therefore slow but nonzero, feeding the
337 ⟨100⟩ ``DISSOCIATION`` edge.
339 Parameters mirror :func:`E_b_loop_i`; ``b_100`` defaults to the bcc-Fe
340 lattice parameter ``a = 2.8665 Å`` (= ⟨100⟩ Burgers vector magnitude).
342 n = np.asarray(n, dtype=float)
343 n_safe = np.maximum(n, 2.0)
344 Eb_fit = A_100 * n_safe**(B_100)
347 w = 0.5 * (1.0 + np.tanh((n_tr - n_safe) / sigma_tr))
348 Eb = w * Eb_fit + (1.0 - w) * Eb_cont
349 return float(Eb)
if Eb.ndim == 0
else Eb
352def E_b_loop_v(m, E_f_v, gamma_sf, Omega, b_111):
354 Vacancy loop binding energy (Eqs. 104-105).
356 Continuum (Frank loop) with DFT anchor at m=2:
357 E_b^vloop(m) = E_f_v − A_sf · [m^{1/2} − (m−1)^{1/2}]
359 where A_sf = 2·b_111 · sqrt(π·γ_sf·Ω/√3) · _J_eV.
363 m : int or array (≥ 1)
365 gamma_sf: float [J/m²] — stacking fault energy
367 b_111 : float [m] — ½⟨111⟩ Burgers vector
371 E_b : float or array [eV]
373 A_sf = 2.0 * b_111 * np.sqrt(np.pi * gamma_sf * Omega / np.sqrt(3.0)) * _J_eV
374 m = np.asarray(m, dtype=float)
378 E_f_v - A_sf * (m**(1.0/2.0) - (m - 1.0)**(1.0/2.0))
380 return float(Eb)
if Eb.ndim == 0
else Eb
399 Trap mutation barrier (Table 27, Eq. 83).
401 Returns the barrier E_TM(m, ℓ) [eV].
402 If (m, ℓ) not in the table, returns `default` (effectively disables TM).
408 default : float [eV] — barrier for unknown (m, ℓ)
410 return _E_TM_table.get((int(m), int(ell)), default)
413def Gamma_TM(m, ell, T, nu0_TM=1e12):
415 Trap mutation rate from bubble (m, ℓ) → (m+1, ℓ-1) [s^-1].
417 Γ_TM(m, ℓ) = ν_0_TM · exp(−E_TM(m,ℓ) / k_B T) (Eq. 142)
423 nu0_TM : float [s^-1] — attempt frequency (Table 27)
428 return nu0_TM * np.exp(-E / (_kB * T))
433def Gamma_res(ell, phi_dot, b0):
435 Radiation re-solution rate for He from bubble (m, ℓ) [s^-1].
437 Γ_res(m, ℓ) = b_0 · ℓ · φ̇ (Eq. 143)
441 ell : int or float — He content
442 phi_dot : float [dpa/s] — displacement rate
443 b0 : float [dpa^-1 He^-1] — re-solution parameter (Table 29)
445 return b0 * float(ell) * phi_dot
450def ell_max(m, alpha_He=1.7):
452 Maximum He loading for cluster with m vacancies.
453 Trap-mutation limit: ℓ_max = floor(alpha_He · m^{2/3}). Table 29.
455 return int(np.floor(alpha_He * float(m)**(2.0 / 3.0)))
He_pressure(m, ell, Omega, T)
E_TM(m, ell, default=1e10)
E_b_He_first(E_b_hV_1=2.30)
E_b_He_second(E_b_hV_2=2.00)
E_b_He(m, ell, E_s_He, Omega, T, B2=_B2, B3=_B3)
_E_b_loop_i_continuum(n, E_f_i=3.77, G_shear=82e9, b=2.49e-10, nu=0.29, gamma_sf=0.6, Omega=1.18e-29)