RadCluster
Graph-based cluster dynamics for irradiated materials
Loading...
Searching...
No Matches
check_path_equivalence_20dpa.py
Go to the documentation of this file.
1"""Is d_111's residual a CLOSURE error, or a PATH difference?
2
3CLAUDE.md S1: with I_bin = 0 and i_discrete = I every equation is discrete, so
4bin_moment reduces to full_CD. Run that against the real discrete arm on an
5identical grid at the dose where the problem shows:
6
7 agree -> the two paths solve the same equations; the -5% on d_111 is
8 genuine closure error and must be explained by the closure
9 disagree -> a path difference, which would ALSO explain why the error does
10 not refine away with bin count
11
12check_bm_vs_discrete.py already asks this, but at 1e-2 dpa. The M2R deviation
13is measured at 20 dpa, three orders of magnitude further out, so a difference
14that only opens up as the distribution broadens is invisible to it.
15"""
16import os, sys, time
17sys.path.insert(0, "/Users/ghoni/Documents/GitHub/RadCluster")
18sys.path.insert(0, "/Users/ghoni/Documents/GitHub/RadCluster/radcluster_code/digital_twin")
19os.environ["OMP_NUM_THREADS"] = "6"
20import numpy as np
21from radcluster_code.py_utils.simulation import RadClusterSimulation
22import run_ensemble as re_mod
23
24I = 1000
25DOSE = float(os.environ.get("PDOSE", "20"))
26
27
28def run(mode):
29 sim = RadClusterSimulation(I=I, V=I, solver_mode="full_system",
30 equations=mode, cascade="fission",
31 he_kinetics="quasi_steady_state",
32 i_mobile=5, v_mobile=5)
33 cfg = {"equations": mode, "i_discrete": I, "v_discrete": I,
34 "I_bin": 0, "V_bin": 0, "shape_function": "linear"}
35 re_mod.apply_bin_config(sim, cfg)
36 sim.input_data.reactions["LOOP_NETWORK_LOSS"] = 1
37 sim.input_data._calculate_derived()
38 sim.rebuild_rates()
39 G = float(sim.input_data.reactions["G"])
40 scfg = {"t_span": (1e-6, DOSE / G), "n_points": 45, "log_time": True,
41 "required_times": (20.0 / G,), "rtol": 1e-5, "atol": 1e-20,
42 "timeout_s": 7200,
43 "solver_method": {"linsol": "gmres", "preconditioner": "woodbury",
44 "concentration_threshold": 1e-22},
45 "loop_conversion": 1}
46 t0 = time.time()
47 res = sim.run_adaptive(solver_config=scfg, save_output=False,
48 timeout_s=7200, max_doublings=0)
49 w = time.time() - t0
50 cfg2 = dict(cfg); cfg2.update({"I": I, "V": I, "dose": DOSE,
51 "C_floor": float(sim.input_data.reactions.get("C_floor", 1e-15))})
52 o = re_mod.observe(res, sim, cfg2, 1.0)
53 return w, o, res
54
55
56KEYS = ["N_loops_111", "d_111_nm", "N_loops_100", "d_100_nm",
57 "N_voids", "d_cavity_nm", "mean_n_111", "delta_FP"]
58out = {}
59for mode in ("discrete", "bin_moment"):
60 w, o, res = run(mode)
61 out[mode] = o
62 print(f"{mode:11s} wall={w:6.1f}s reached={o['dose_reached']:.4g} dpa")
63
64print(f"\n{'observable':14s} {'discrete':>14s} {'bin_moment':>14s} {'rel diff':>10s}")
65for k in KEYS:
66 a, b = out["discrete"].get(k), out["bin_moment"].get(k)
67 if a is None or b is None:
68 continue
69 rel = (b - a) / a * 100.0 if a else float("nan")
70 print(f"{k:14s} {a:14.6g} {b:14.6g} {rel:+9.3f}%")