84 def __init__(self, input_data, reaction_rates):
86 self.
rr = reaction_rates
93 po = input_data.physics_option
97 self.
C_floor = float(input_data.reactions.get(
'C_floor', 1e-15))
100 raw = str(input_data.reactions.get(
'he_kinetics',
'dynamic')).lower()
101 if raw
not in _VALID_HE_KINETICS:
103 warnings.warn(f
"Unknown he_kinetics='{raw}'. Using 'dynamic'.")
106 self.
qss_He = (raw ==
'quasi_steady_state')
109 kBT_val = input_data.derived[
'kBT']
110 E_b_hV1 = float(input_data.energetics.get(
'E_b_hV_1', 2.30))
111 E_m_h = float(input_data.derived[
'E_m_h'])
112 nu_h = float(input_data.derived[
'nu_h'])
113 self.
beta_He = nu_h * np.exp(-(E_b_hV1 + E_m_h) / kBT_val)
125 n_phys = I + 2 * V + 1
126 self.
i_He = I + 2 * V
137 self.
i_He = I + V + 1
150 spectrum = input_data.derived[
'spectrum']
151 G = input_data.derived[
'G']
152 G_He_r = input_data.derived[
'G_He_r']
153 Pr_SIA, Pr_VAC, G_He = production_rates(
154 G, spectrum, I, V, G_He_r,
155 spec_over=(input_data.production_fission
if 'fiss' in spectrum.lower()
156 else input_data.production_fusion))
167 self.
_denom_m = 1.0 + reaction_rates.B_rot * reaction_rates.L_hat**2 / self.
_m13
169 print(f
"RateEquations: he_mode='{self.he_mode}' he_kinetics='{self.he_kinetics}'"
170 f
" N_eq={self.N_eq} I={I} V={V}"
171 f
" G_He={G_He:.3e} at.frac/s"
172 f
" C_floor={self.C_floor:.1e} beta_He={self.beta_He:.3e}")
225 Decoupled He-reduction (Case 2, fission, Eq. 175).
227 he_kinetics='dynamic':
228 State: [c_1..c_I | c_{-1}..c_{-V} | Q_tot | c_h]
229 he_kinetics='quasi_steady_state':
230 State: [c_1..c_I | c_{-1}..c_{-V} | Q_tot]
231 c_h = (G_He + β·Q_tot) / (Σ K_HeV·c_v + k2_He)
237 dydt = np.zeros(self.
N_eq)
245 c_i = np.maximum(y_SIA, C_floor)
246 c_v = np.maximum(y_VAC, C_floor)
247 Q_tot = max(float(y_Qtot), C_floor)
254 c_h = max(float(y_He), C_floor)
260 C_vac_tot = np.sum(c_v)
261 ell_bar = Q_tot / max(C_vac_tot, 1e-200)
264 G_VAC_eff = rr.G_VAC.copy()
266 for m
in range(1, min(V, 20) + 1):
267 G_VAC_eff[m - 1] = rr.alpha_bubble_fn(m, ell_bar * m**(2.0/3.0))
271 i_mobile = self.
inp.derived[
'i_mobile']
272 reflect = self.
inp.derived.get(
'boundary_flux',
'absorption') ==
'reflection'
280 dci[:-1] += rr.G_SIA[1:] * c_i[1:]
281 dci -= rr.G_SIA * c_i
284 dci -= rr.K_SIA_grow * ci1 * c_i
285 dci[1:] += rr.K_SIA_grow[:-1] * ci1 * c_i[:-1]
287 dci[-1] += rr.K_SIA_grow[-1] * ci1 * c_i[-1]
288 dci[0] += ci1 * rr.K_SIA_grow[-1] * c_i[-1]
292 dci[0] -= ci1 * np.dot(rr.K_SIA_grow, c_i)
296 dci[0] -= rr.K_iv * cv1 * ci1
298 dci[1:] -= rr.K_SIA_shrink[1:] * cv1 * c_i[1:]
299 dci[:-1] += rr.K_SIA_shrink[1:] * cv1 * c_i[1:]
304 sum_K3D_cv_m2 = rr.K_3D_cav_pref * np.dot(m13[1:], c_v[1:])
305 dci[0] -= ci1 * sum_K3D_cv_m2
308 sum_K3D_cv = rr.K_3D_cav_pref * np.dot(m13, c_v)
309 for n
in range(2, min(4, i_mobile + 1)):
310 dci[n - 1] -= c_i[n - 1] * sum_K3D_cv
313 for n
in range(4, min(I, i_mobile) + 1):
314 k_pref = rr.K_1D_pref[n - 1]
317 dci[n - 1] -= c_i[n - 1] * k_pref * np.dot(m13 / denom_m, c_v)
320 dci -= rr.k2_SIA * c_i
330 dcv -= G_VAC_eff * c_v
331 dcv[:-1] += G_VAC_eff[1:] * c_v[1:]
332 dcv[0] += np.sum(G_VAC_eff[1:] * c_v[1:])
335 dcv -= rr.K_VAC_grow * cv1 * c_v
336 dcv[1:] += rr.K_VAC_grow[:-1] * cv1 * c_v[:-1]
338 dcv[-1] += rr.K_VAC_grow[-1] * cv1 * c_v[-1]
339 dcv[0] += cv1 * rr.K_VAC_grow[-1] * c_v[-1]
341 dcv[0] -= cv1 * np.dot(rr.K_VAC_grow, c_v)
346 dcv[0] -= rr.K_iv * ci1 * cv1
349 dcv[0] += rr.K_VAC_shrink[1] * ci1 * c_v[1]
352 dcv[1:] -= rr.K_VAC_shrink[1:] * ci1 * c_v[1:]
354 dcv[1:-1] += rr.K_VAC_shrink[2:] * ci1 * c_v[2:]
358 dcv[0] -= cv1 * np.dot(rr.K_SIA_shrink[1:], c_i[1:])
361 for n
in range(2, min(4, i_mobile + 1)):
363 dcv -= rr.K_VAC_shrink * cn * c_v
365 dcv[:V - n] += rr.K_VAC_shrink[n:] * cn * c_v[n:]
368 for n
in range(4, min(I, i_mobile) + 1):
369 k_pref = rr.K_1D_pref[n - 1]
373 k_loss = k_pref * m13 / denom_m
374 dcv -= k_loss * cn * c_v
376 mp = self.
_m_arr[:V - n] + float(n)
377 mp13 = mp ** (1.0 / 3.0)
378 denom = 1.0 + rr.B_rot * rr.L_hat**2 / mp13
379 dcv[:V - n] += (k_pref * mp13 / denom) * cn * c_v[n:]
382 v_mobile = self.
inp.derived[
'v_mobile']
383 mask_mobile_v = np.zeros(V)
384 mask_mobile_v[:min(v_mobile, V)] = 1.0
385 dcv -= rr.k2_vac_scalar * c_v * mask_mobile_v
388 He_uptake = np.sum(rr.K_HeV * c_h * c_v)
389 He_release = self.
beta_He * Q_tot
391 ms_arr = np.arange(1.0, V + 1.0)
392 ell_m_arr = ell_bar * ms_arr ** (2.0 / 3.0)
393 He_sink = rr.k2_vac_scalar * np.sum(ell_m_arr[:v_mobile] * c_v[:v_mobile])
394 dydt[self.
i_Qtot] = He_uptake - He_release - He_sink
400 - rr.k2_He_scalar * c_h
404 ns_all = np.arange(1.0, I + 1)
410 mutual = rr.K_iv * ci1 * cv1
411 mutual += ci1 * sum_K3D_cv_m2
412 mutual += cv1 * np.sum(rr.K_SIA_shrink[1:] * c_i[1:])
413 for n
in range(2, min(4, i_mobile + 1)):
414 mutual += n * c_i[n - 1] * sum_K3D_cv
415 for n
in range(4, min(I, i_mobile) + 1):
416 k_pref = rr.K_1D_pref[n - 1]
419 mutual += n * c_i[n - 1] * k_pref * np.dot(m13 / denom_m, c_v)
423 vm = min(v_mobile, V)
424 ms_mob = np.arange(1.0, vm + 1)
425 dydt[self.
i_J_VAC_fixed] = rr.k2_vac_scalar * np.dot(ms_mob, c_v[:vm])
437 he_sink_acc = rr.k2_He_scalar * c_h
438 if C_vac_tot > 1e-300
and Q_tot > 0:
439 ell_m_mob = ell_bar * ms_mob ** (2.0 / 3.0)
440 he_sink_acc += rr.k2_vac_scalar * np.dot(ell_m_mob, c_v[:vm])
449 Mean-field He-reduction (Case 1, fusion, Eq. 174).
451 he_kinetics='dynamic':
452 State: [c_1..c_I | c_{-1}..c_{-V} | Q_1..Q_V | c_h]
453 he_kinetics='quasi_steady_state':
454 State: [c_1..c_I | c_{-1}..c_{-V} | Q_1..Q_V]
455 c_h = (G_He + β·Σ Q_m) / (Σ K_HeV·c_v + k2_He)
461 dydt = np.zeros(self.
N_eq)
465 y_Q = y[self.
i_Q:self.
i_Q + V]
467 c_i = np.maximum(y_SIA, C_floor)
468 c_v = np.maximum(y_VAC, C_floor)
469 Q_m = np.maximum(y_Q, C_floor)
471 ell_bar_m = Q_m / np.maximum(c_v, 1e-200)
478 c_h = max(float(y_He), C_floor)
483 i_mobile = self.
inp.derived[
'i_mobile']
487 G_VAC_eff = np.array([
488 rr.alpha_bubble_fn(m + 1, ell_bar_m[m])
for m
in range(V)
493 reflect = self.
inp.derived.get(
'boundary_flux',
'absorption') ==
'reflection'
498 dci[:-1] += rr.G_SIA[1:] * c_i[1:]
499 dci -= rr.G_SIA * c_i
500 dci -= rr.K_SIA_grow * ci1 * c_i
501 dci[1:] += rr.K_SIA_grow[:-1] * ci1 * c_i[:-1]
503 dci[-1] += rr.K_SIA_grow[-1] * ci1 * c_i[-1]
504 dci[0] += ci1 * rr.K_SIA_grow[-1] * c_i[-1]
506 dci[0] -= ci1 * np.dot(rr.K_SIA_grow, c_i)
510 dci[0] -= rr.K_iv * cv1 * ci1
512 dci[1:] -= rr.K_SIA_shrink[1:] * cv1 * c_i[1:]
513 dci[:-1] += rr.K_SIA_shrink[1:] * cv1 * c_i[1:]
517 sum_K3D_cv_m2 = rr.K_3D_cav_pref * np.dot(m13[1:], c_v[1:])
518 dci[0] -= ci1 * sum_K3D_cv_m2
520 sum_K3D_cv = rr.K_3D_cav_pref * np.dot(m13, c_v)
521 for n
in range(2, min(4, i_mobile + 1)):
522 dci[n - 1] -= c_i[n - 1] * sum_K3D_cv
523 for n
in range(4, min(I, i_mobile) + 1):
524 k_pref = rr.K_1D_pref[n - 1]
527 dci[n - 1] -= c_i[n - 1] * k_pref * np.dot(m13 / denom_m, c_v)
529 dci -= rr.k2_SIA * c_i
534 dcv -= G_VAC_eff * c_v
535 dcv[:-1] += G_VAC_eff[1:] * c_v[1:]
536 dcv[0] += np.sum(G_VAC_eff[1:] * c_v[1:])
537 dcv -= rr.K_VAC_grow * cv1 * c_v
538 dcv[1:] += rr.K_VAC_grow[:-1] * cv1 * c_v[:-1]
540 dcv[-1] += rr.K_VAC_grow[-1] * cv1 * c_v[-1]
541 dcv[0] += cv1 * rr.K_VAC_grow[-1] * c_v[-1]
543 dcv[0] -= cv1 * np.dot(rr.K_VAC_grow, c_v)
547 dcv[0] -= rr.K_iv * ci1 * cv1
549 dcv[0] += rr.K_VAC_shrink[1] * ci1 * c_v[1]
551 dcv[1:] -= rr.K_VAC_shrink[1:] * ci1 * c_v[1:]
553 dcv[1:-1] += rr.K_VAC_shrink[2:] * ci1 * c_v[2:]
555 for n
in range(2, min(4, i_mobile + 1)):
557 dcv -= rr.K_VAC_shrink * cn * c_v
559 dcv[:V - n] += rr.K_VAC_shrink[n:] * cn * c_v[n:]
560 for n
in range(4, min(I, i_mobile) + 1):
561 k_pref = rr.K_1D_pref[n - 1]
565 k_loss = k_pref * m13 / denom_m
566 dcv -= k_loss * cn * c_v
568 mp = self.
_m_arr[:V - n] + float(n)
569 mp13 = mp ** (1.0 / 3.0)
570 denom = 1.0 + rr.B_rot * rr.L_hat**2 / mp13
571 dcv[:V - n] += (k_pref * mp13 / denom) * cn * c_v[n:]
575 dcv[0] -= cv1 * np.dot(rr.K_SIA_shrink[1:], c_i[1:])
578 v_mobile = self.
inp.derived[
'v_mobile']
579 mask_mobile_v = np.zeros(V)
580 mask_mobile_v[:min(v_mobile, V)] = 1.0
581 dcv -= rr.k2_vac_scalar * c_v * mask_mobile_v
584 dQ = dydt[self.
i_Q:self.
i_Q + V]
588 for m
in range(1, V + 1):
593 he_cap_m = rr.K_HeV[k] * c_h * cm
595 He_cap_total += he_cap_m
597 alpha_h = rr.alpha_He_emit_fn(m, max(int(round(ell)), 0))
598 he_emit_m = alpha_h * Q_m[k]
600 He_emit_total += he_emit_m
604 dQ[k] -= rr.k2_vac_scalar * Q_m[k]
610 - rr.k2_He_scalar * c_h
614 ns_all = np.arange(1.0, I + 1)
620 mutual = rr.K_iv * ci1 * cv1
621 mutual += ci1 * sum_K3D_cv_m2
622 mutual += cv1 * np.sum(rr.K_SIA_shrink[1:] * c_i[1:])
623 for n
in range(2, min(4, i_mobile + 1)):
624 mutual += n * c_i[n - 1] * sum_K3D_cv
625 for n
in range(4, min(I, i_mobile) + 1):
626 k_pref = rr.K_1D_pref[n - 1]
629 mutual += n * c_i[n - 1] * k_pref * np.dot(m13 / denom_m, c_v)
633 vm = min(v_mobile, V)
634 ms_mob = np.arange(1.0, vm + 1)
635 dydt[self.
i_J_VAC_fixed] = rr.k2_vac_scalar * np.dot(ms_mob, c_v[:vm])
641 for mp
in range(2, vm + 1):
645 for n
in range(1, min(mp, I + 1)):
650 K_s = rr.K_3D_cav_pref * float(mp) ** (1.0 / 3.0)
652 k_pref = rr.K_1D_pref[n - 1]
655 K_s = k_pref * float(mp) ** (1.0 / 3.0) / (
656 1.0 + rr.B_rot * rr.L_hat**2 / float(mp) ** (1.0 / 3.0))
657 vac_mutual += (mp - n) * K_s * c_mp * cn
663 he_sink_acc = rr.k2_He_scalar * c_h
664 he_sink_acc += rr.k2_vac_scalar * np.sum(np.maximum(Q_m[:vm], 0.0))