2Stage-4 validation — the two-layer RadCluster_2_1 on the im5vm2 case.
4This is the capstone regression test for the graph-based two-layer
5refactor. It reproduces the RadCluster_1_0 reference case
7 full_system / full_CD_fission, I = V = 1000, i_mobile = 5,
8 v_mobile = 2, T = 673 K, G = 1e-6 dpa/s, t in [1e-6, 1e-2] s
12 (A) STRUCTURE — that a RadClusterSimulation now carries the live
13 two-layer description: an abstract-core ReactionAdmissibilityGraph
14 instantiated by the EUROFER-97 material layer (Layer 1 + Layer 2).
16 (B) PHYSICS — that running through the restructured (core/ + materials/)
17 C++ solver still reproduces the locked post-Stage-2 baseline, i.e.
18 the refactor is behaviour-preserving.
20Reference values are the post-Stage-2 baseline (the G_He single-source
21fix is included by design); agreement to 1e-5 relative — comfortably
22above the ~5e-7 solver-tolerance noise — counts as a pass.
24Run: python codes/Python_Testing/test_two_layer_im5vm2.py
25Exit code 0 = PASS, 1 = FAIL.
28from pathlib
import Path
30ROOT = Path(
r"d:/GitHub/RadCluster/radcluster_code")
31sys.path.insert(0, str(ROOT))
33from py_utils.core import ReactionAdmissibilityGraph, StateLayout, EdgeClass
38 "C_SIA_tot": 1.49422818e20,
39 "C_VAC_tot": 2.37571338e20,
40 "C_He_tot": 1.58830031e13,
41 "mean_n_i": 7.11591710,
42 "mean_n_v": 3.09365893,
43 "swelling": 2.80334179e-9,
51 mark =
"PASS" if ok
else "FAIL"
52 print(f
" [{mark}] {name}{(' -- ' + detail) if detail else ''}")
58print(
"Stage-4 validation: two-layer RadCluster_2_1 on the im5vm2 case")
63 I=1000, V=1000, solver_mode=
"full_system",
64 physics_option=
"full_CD_fission",
65 C_floor=1e-25, he_kinetics=
"quasi_steady_state",
66 i_mobile=5, v_mobile=2,
68sim.input_data.reactions[
"T"] = 673.0
69sim.input_data._calculate_derived()
73print(
"\n(A) Two-layer structure")
74rag = getattr(sim,
"material_rag",
None)
75layout = getattr(sim,
"state_layout",
None)
77check(
"material RAG present", isinstance(rag, ReactionAdmissibilityGraph))
78check(
"state layout present", isinstance(layout, StateLayout))
80if isinstance(rag, ReactionAdmissibilityGraph):
84 except Exception
as exc:
86 check(
"RAG validates",
False, repr(exc))
88 check(
"RAG validates",
True)
89 check(
"2 populations (1 SIA + 1 vacancy bulk)",
90 len(rag.populations) == 2,
91 f
"got {len(rag.populations)}")
92 active = {ec
for ec
in EdgeClass
if rag.edges_of_class(ec)}
93 expected = {EdgeClass.GROWTH, EdgeClass.SHRINKAGE, EdgeClass.DISSOCIATION,
94 EdgeClass.RECOMBINATION, EdgeClass.ANNIHILATION,
95 EdgeClass.COALESCENCE, EdgeClass.SOURCE, EdgeClass.SINK}
96 check(
"all P1-P6 edge classes present", expected <= active,
97 f
"missing {expected - active}")
98 check(
"P7/P8 edges declared (growth>=3 or solute_trapping)",
99 len(rag.edges_of_class(EdgeClass.GROWTH)) >= 3
or
100 bool(rag.edges_of_class(EdgeClass.SOLUTE_TRAPPING)),
101 f
"{len(rag.edges)} edges total")
102 print(f
" {rag.summary()}")
105print(
"\n(B) Behaviour-preserving run")
106cfg = sim._default_solver_config()
107cfg[
"t_span"] = (1e-6, 0.01)
109cfg[
"log_time"] =
True
112cfg[
"solver_method"][
"linsol"] =
"gmres"
114results = sim.run_adaptive(solver_config=cfg, save_output=
False,
115 max_doublings=0, points_per_segment=200)
118 check(
"solver run completed",
False,
"run returned None")
120 check(
"solver run completed",
True)
126 except (TypeError, IndexError):
129 print(f
" {'quantity':12s} {'baseline':>16s} {'this run':>16s} {'rel.diff':>11s}")
130 for key, ref
in BASELINE.items():
132 rel = abs(val - ref) / abs(ref)
133 print(f
" {key:12s} {ref:16.6e} {val:16.6e} {rel:11.2e}")
134 check(f
"{key} within {RTOL:g}", rel < RTOL, f
"rel.diff {rel:.2e}")
139 check(
"delta_FP finite and < 1e-3",
140 math.isfinite(dfp)
and abs(dfp) < 1e-3, f
"delta_FP={dfp:.2e}")
141 check(
"delta_He finite and < 1e-3 (NaN bug fixed)",
142 math.isfinite(dhe)
and abs(dhe) < 1e-3, f
"delta_He={dhe:.2e}")
145print(
"\n" +
"=" * 72)
147 print(f
"RESULT: FAIL ({len(failures)} check(s) failed: {failures})")
149print(
"RESULT: PASS -- two-layer RadCluster_2_1 reproduces the im5vm2 "
150 "baseline\n and carries the abstract-core + EUROFER-97 RAG "
check(name, ok, detail="")