37for _m
in [_dp_mod, _be_mod, _bmr, _inp_mod, _rr_mod, _re_mod,
139 """Run one simulation and return the SIA peak info at ~0.1 dpa."""
140 params = dict(BASE_PARAMS)
141 params.update(overrides)
143 i_mob = int(params.get(
'i_mobile', 10))
148 _fission_backup = dict(_FISSION_DICT)
149 for key
in [
'eta',
'f_cl_i',
'f_cl_v',
's_i',
's_v',
'i_cascade',
'v_cascade']:
151 _FISSION_DICT[key] = params[key]
154 saved = sys.stdout, sys.stderr
155 sys.stdout = sys.stderr = io.StringIO()
157 sim = RadClusterSimulation(
159 solver_mode=
'active_window',
160 physics_option=
'full_CD_fission',
162 he_kinetics=
'quasi_steady_state',
169 for key, val
in params.items():
171 for d
in [inp.production_fission, inp.production_fusion,
172 inp.diffusion, inp.reactions,
173 inp.energetics, inp.dissociation]:
178 inp.reactions[key] = val
179 inp.diffusion[
'i_mobile'] = i_mob
180 inp.reactions[
'i_mobile'] = i_mob
182 inp.reactions[
'i_discrete'] = i_mob
183 inp.reactions[
'v_discrete'] = v_mob
184 inp.reactions[
'I_bin'] = 0
185 inp.reactions[
'V_bin'] = 0
186 inp._calculate_derived()
189 results = sim.run_adaptive(
190 solver_config=SOLVER_CONFIG,
192 progress_callback=
None,
193 boundary_threshold=0.1,
195 points_per_segment=10,
198 sys.stdout, sys.stderr = saved
200 _FISSION_DICT.update(_fission_backup)
206 dose = results[
'dose']
208 G_dpa = inp.derived[
'G']
209 Omega_val = inp.derived[
'Omega']
210 s2m = 1.0 / Omega_val
214 idx_01 = np.argmin(np.abs(dose - target_dose))
215 actual_dose = dose[idx_01]
219 y_full = results[
'y']
220 c_i_at_01 = np.maximum(y_full[0:I, idx_01], 0.0) * s2m
222 N_sia = c_i_at_01.shape[0]
223 ns = np.arange(1, N_sia + 1)
227 c_trimmed = c_i_at_01[1:]
231 if np.max(c_trimmed) <= 0:
235 ipeak = np.argmax(c_trimmed)
236 peak_n = ns_trimmed[ipeak]
237 peak_d = d_trimmed[ipeak]
238 peak_c = c_trimmed[ipeak]
241 mask = c_trimmed > 1e10
243 mean_d = np.sum(d_trimmed[mask] * c_trimmed[mask]) / np.sum(c_trimmed[mask])
244 total_N = np.sum(c_trimmed[mask])
251 sorted_idx = np.argsort(c_trimmed)[::-1]
252 cum_c = np.cumsum(c_trimmed[sorted_idx])
253 half_total = cum_c[-1] * 0.5
254 top_half_mask = cum_c <= half_total
255 if top_half_mask.any():
256 top_half_n = ns_trimmed[sorted_idx[top_half_mask]]
259 d_range = (peak_d, peak_d)
263 'dose_actual': actual_dose,
264 'peak_n': int(peak_n),
268 'total_N_m3': total_N,
269 'd_range_nm': d_range,
270 'mean_n_i': results[
'mean_n_i'][idx_01],
271 'N_loops': results[
'N_loops'][idx_01],
272 'swelling_pct': results[
'swelling'][idx_01] * 100,
273 'delta_FP': results[
'delta_FP'][idx_01],