RadCluster
Graph-based cluster dynamics for irradiated materials
Loading...
Searching...
No Matches
test_lapack_omp_small.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2"""
3test_lapack_omp_small.py — Quick verification of LAPACK + OMP auto-pick.
4
5Small problem (I=V=1000, N_eq~2002), short time span (t_end=1e-2).
6Runs once with Jacobi, once with Woodbury, using the already-built
7solver.exe. Does NOT rebuild — uses whatever's in build/Release/.
8
9Verifies:
10 1. Auto-thread picker fires (expect "auto-selected 4 threads ...")
11 2. Woodbury works (no "binary built without LAPACK" fallback)
12 3. Speedup of Woodbury over Jacobi
13"""
14
15import os
16# Drop OMP_NUM_THREADS BEFORE importing any solver bridge so the spawned
17# solver.exe does not inherit the machine-scope value.
18os.environ.pop('OMP_NUM_THREADS', None)
19
20import sys, io, time, importlib
21from pathlib import Path
22
23MODULE_ROOT = Path(__file__).resolve().parent.parent.parent
24REPO_ROOT = MODULE_ROOT.parent
25for p in [str(REPO_ROOT), str(MODULE_ROOT)]:
26 if p not in sys.path:
27 sys.path.insert(0, p)
28
29import radcluster_code.py_utils.input_data as _inp
30import radcluster_code.py_utils.reaction_rates as _rr
31import radcluster_code.py_utils.rate_equations as _re
32import radcluster_code.py_utils.cpp_bridge as _cb
33import radcluster_code.py_utils.post_process as _pp
34import radcluster_code.py_utils.simulation as _sim
35for _m in [_inp, _rr, _re, _cb, _pp, _sim]:
36 importlib.reload(_m)
37from radcluster_code.py_utils.simulation import RadClusterSimulation
38
39I = 1000
40V = 1000
41
42OVERRIDES = {
43 'eta': 0.3, 'f_cl_i': 0.2, 'f_cl_v': 0.15,
44 'E_m_1D': 0.4, 'i_mobile': 1, 'L_hat': 5,
45 'c_C': 0.001, 'E_b_C_SIA': 0.65,
46 'rho_d': 1e15, 'Z_i': 1.08, 'Z_ii': 1.01,
47 'shape_function': 'linear',
48 'i_discrete': I, 'v_discrete': V,
49 'I_bin': 0, 'V_bin': 0,
50}
51
52
54 saved = sys.stdout, sys.stderr
55 sys.stdout = sys.stderr = io.StringIO()
56 try:
57 sim = RadClusterSimulation(
58 I=I, V=V, solver_mode='full_system',
59 physics_option='full_CD_fission',
60 C_floor=1e-25, he_kinetics='quasi_steady_state',
61 i_mobile=1, v_mobile=1,
62 )
63 inp = sim.input_data
64 for key, val in OVERRIDES.items():
65 placed = False
66 for d in [inp.production_fission, inp.production_fusion,
67 inp.diffusion, inp.reactions,
68 inp.energetics, inp.dissociation]:
69 if key in d:
70 d[key] = val
71 placed = True
72 if not placed:
73 inp.reactions[key] = val
74 inp._calculate_derived()
75 sim.rebuild_rates()
76 finally:
77 sys.stdout, sys.stderr = saved
78 return sim
79
80
81def run_case(prec_type):
82 cfg = {
83 't_span': (1e-6, 1e-2),
84 'n_points': 50,
85 'log_time': True,
86 'rtol': 1e-6,
87 'atol': 1e-25,
88 'solver_method': {
89 'linsol': 'gmres',
90 'window_width': 50,
91 'concentration_threshold': 1e-20, 'window_pad': 100,
92 'prec_type': prec_type,
93 }
94 }
95 name = 'Woodbury' if prec_type == 1 else 'Jacobi'
96 print(f"\n{'='*60}\n {name} (prec_type={prec_type})\n{'='*60}", flush=True)
97 sim = make_sim()
98 t0 = time.perf_counter()
99 results = sim.run(solver_config=cfg, save_output=False)
100 wall = time.perf_counter() - t0
101 if results is None:
102 print(f" FAILED after {wall:.2f}s", flush=True)
103 return None
104 md = results.get('metadata', {})
105 n_thr = md.get('omp_threads_used', '?')
106 print(f" wall = {wall:.2f}s omp_threads_used = {n_thr}", flush=True)
107 return {'wall': wall, 'n_thr': n_thr, 'prec': name}
108
109
110print(f"OMP_NUM_THREADS env at script start: "
111 f"{os.environ.get('OMP_NUM_THREADS', '<unset>')}", flush=True)
112print(f"N_eq for I=V={I}: ~{2 * I + 2}", flush=True)
113
116
117if j and w:
118 print(f"\n{'='*60}\n SUMMARY\n{'='*60}")
119 print(f" Jacobi wall = {j['wall']:.2f}s ({j['n_thr']} threads)")
120 print(f" Woodbury wall = {w['wall']:.2f}s ({w['n_thr']} threads)")
121 if w['wall'] > 0:
122 print(f" Speedup (Jacobi / Woodbury) = {j['wall'] / w['wall']:.2f}x")