Skip to content

Commit 504cf34

Browse files
FIX: benchmark, caching, fasthmath, opt. gpu/cuda
1 parent 8d6af75 commit 504cf34

21 files changed

Lines changed: 253 additions & 93 deletions

README.md

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,29 @@ uv sync --all-extras
4646
pip install -e .
4747
```
4848

49+
### GPU Acceleration (optional)
50+
51+
If you have an NVIDIA GPU, install the GPU extra to enable CUDA acceleration.
52+
This installs `cupy-cuda12x` (GPU array ops) and `numba-cuda` (Numba `@cuda.jit`
53+
kernels). No separate CUDA toolkit download needed — only an NVIDIA driver
54+
(version 450+). The code automatically detects GPU availability and falls back
55+
to CPU if CUDA is not present.
56+
57+
**With uv (recommended):**
58+
```bash
59+
uv pip install -e ".[gpu]"
60+
```
61+
62+
**With pip:**
63+
```bash
64+
pip install -e ".[gpu]"
65+
```
66+
67+
**Manual install (if you only want the packages directly):**
68+
```bash
69+
pip install cupy-cuda12x numba-cuda
70+
```
71+
4972
### Development
5073

5174
```bash
@@ -85,9 +108,10 @@ pulsesuite/
85108
- NumPy ≥1.26.4
86109
- SciPy ≥1.15.2
87110
- Matplotlib ≥3.10.0
88-
- pyFFTW ≥0.15.0 (recommended for performance)
89-
- Numba ≥0.61.2 (optional, for JIT acceleration)
90-
- Numba-CUDA==0.23.0 (optional, for CUDA acceleration)
111+
- pyFFTW ≥0.15.0
112+
- Numba ≥0.61.2
113+
- CuPy-CUDA12x ≥13.5.0 (optional, GPU array ops — install via `pip install -e ".[gpu]"`)
114+
- numba-cuda ≥0.6.0 (optional, Numba CUDA kernels — included in `[gpu]` extra)
91115

92116
## Running a simulation
93117

pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ test = [
7272
"pytest-cov>=4.0.0",
7373
"pytest-xdist>=3.0.0",
7474
]
75+
gpu = [
76+
"cupy-cuda12x>=13.5.0", # GPU array ops (requires CUDA 12 driver)
77+
"numba-cuda>=0.6.0", # Numba @cuda.jit kernels
78+
]
7579
jupyter = ["notebook", "ipywidgets>=7.6"]
7680
doc = [
7781
"httpx",

run_pulsesuite_sbetestpropnov30.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ cd $PBS_O_WORKDIR/src/pulsesuite/PSTD3D
1010
export PATH="$HOME/.local/bin:$PATH"
1111

1212
# Match Numba thread count to PBS allocation
13-
export NUMBA_NUM_THREADS=8
14-
export OMP_NUM_THREADS=8
15-
export MKL_NUM_THREADS=8
13+
export NUMBA_NUM_THREADS=48
14+
export OMP_NUM_THREADS=48
15+
export MKL_NUM_THREADS=48
1616

1717
uv run python -u -m pulsesuite.PSTD3D.sbetestpropnov30

src/pulsesuite/PSTD3D/SBEs.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -805,7 +805,7 @@ def CalcH(Meh, Wee, Whh, C, D, p, VC, Heh, Hee, Hhh):
805805
)
806806

807807

808-
@jit(nopython=True, cache=True, parallel=True)
808+
@jit(nopython=True, cache=True, parallel=True, fastmath=True)
809809
def _CalcH_jit(
810810
Meh, Wee, Whh, C, D, p, VC, Heh, Hee, Hhh, Nk, Ee, Eh, gap, excitons, lf, freepot
811811
):
@@ -1938,7 +1938,7 @@ def dpdt(C, D, p, Heh, Hee, Hhh, GamE, GamH, OffP):
19381938
return _dpdt_fallback(C, D, p, Heh, Hee, Hhh, GamE, GamH, OffP)
19391939

19401940

1941-
@jit(nopython=True, cache=True, parallel=True)
1941+
@jit(nopython=True, cache=True, parallel=True, fastmath=True)
19421942
def _dpdt_jit(C, D, p, Heh, Hee, Hhh, GamE, GamH, OffP, ii_val, hbar_val):
19431943
"""JIT-compiled dpdt calculation."""
19441944
Nk = p.shape[0]
@@ -2305,7 +2305,7 @@ def dCdt(Cee, Dhh, Phe, Heh, Hee, Hhh, GamE, GamH, OffE):
23052305
return _dCdt_fallback(Cee, Dhh, Phe, Heh, Hee, Hhh, GamE, GamH, OffE)
23062306

23072307

2308-
@jit(nopython=True, cache=True, parallel=True)
2308+
@jit(nopython=True, cache=True, parallel=True, fastmath=True)
23092309
def _dCdt_jit(Cee, Dhh, Phe, Heh, Hee, Hhh, GamE, GamH, OffE, ii_val, hbar_val):
23102310
"""JIT-compiled dCdt calculation."""
23112311
Nk = Cee.shape[0]
@@ -2603,7 +2603,7 @@ def dDdt(Cee, Dhh, Phe, Heh, Hee, Hhh, GamE, GamH, OffH):
26032603
return _dDdt_fallback(Cee, Dhh, Phe, Heh, Hee, Hhh, GamE, GamH, OffH)
26042604

26052605

2606-
@jit(nopython=True, cache=True, parallel=True)
2606+
@jit(nopython=True, cache=True, parallel=True, fastmath=True)
26072607
def _dDdt_jit(Cee, Dhh, Phe, Heh, Hee, Hhh, GamE, GamH, OffH, ii_val, hbar_val):
26082608
"""JIT-compiled dDdt calculation."""
26092609
Nk = Dhh.shape[0]
@@ -3161,7 +3161,7 @@ def CalcXqw(iq, w, kr, fe, fh, Ee, Eh, gap, area, game, gamh, dcv):
31613161
return _CalcXqw_fallback(iq, w, kr, fe, fh, Ee, Eh, gap, area, game, gamh, dcv)
31623162

31633163

3164-
@jit(nopython=True)
3164+
@jit(nopython=True, fastmath=True, cache=True)
31653165
def _CalcXqw_jit(
31663166
iq,
31673167
w,
@@ -3713,7 +3713,7 @@ def GetArrays(x, qx, kx):
37133713
_NQ0 = GetArray0Index(qx)
37143714

37153715

3716-
@jit(nopython=True, parallel=True)
3716+
@jit(nopython=True, parallel=True, fastmath=True, cache=True)
37173717
def _MakeKKP_jit(Nk, kr, dkr, NQ0, kkp):
37183718
"""JIT-compiled parallel version of MakeKKP computation."""
37193719
from numba import prange

src/pulsesuite/PSTD3D/coulomb.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636
# JIT-compatible K0 approximation for modified Bessel function
3737
# nopython friendly version of K03 which is in usefulsubs.py
38-
@jit(nopython=True, cache=True)
38+
@jit(nopython=True, cache=True, fastmath=True)
3939
def _K03_jit(x):
4040
"""
4141
JIT-compatible approximation of modified Bessel function K0(x).
@@ -66,7 +66,7 @@ def _K03_jit(x):
6666
return np.sqrt(np.pi / (2.0 * x)) * np.exp(-x)
6767

6868

69-
@jit(nopython=True, cache=True)
69+
@jit(nopython=True, cache=True, fastmath=True)
7070
def _Vint_jit(Qyk, y, alphae, alphah, Delta0, N1, N2, Ny):
7171
"""JIT-compiled version of Vint calculation."""
7272
Vint_val = 0.0
@@ -782,7 +782,7 @@ def GetEps1Dqw(alphae, alphah, Delta0, epsr, me, mh, n1D, q, w):
782782
###############################################################################
783783

784784

785-
@jit(nopython=True, parallel=True)
785+
@jit(nopython=True, parallel=True, fastmath=True, cache=True)
786786
def _CalcMVeh_core(p, Veh, MVeh, k3, UnDel):
787787
"""Core JIT-compiled computation for CalcMVeh."""
788788
N = p.shape[0]
@@ -809,7 +809,7 @@ def _CalcMVeh_core(p, Veh, MVeh, k3, UnDel):
809809
)
810810

811811

812-
@jit(nopython=True, parallel=True)
812+
@jit(nopython=True, parallel=True, fastmath=True, cache=True)
813813
def _BGRenorm_core(ne, nh, Vee, Vhh, BGR, UnDel):
814814
"""Core JIT-compiled computation for BGRenorm."""
815815
N = len(ne)
@@ -830,7 +830,7 @@ def _BGRenorm_core(ne, nh, Vee, Vhh, BGR, UnDel):
830830
BGR[k, kp] = -sum_hh - sum_ee
831831

832832

833-
@jit(nopython=True, parallel=True)
833+
@jit(nopython=True, parallel=True, fastmath=True, cache=True)
834834
def _EeRenorm_core(ne, Vee, BGR, UnDel):
835835
"""Core JIT-compiled computation for EeRenorm."""
836836
N = len(ne)
@@ -862,7 +862,7 @@ def _EeRenorm_core(ne, Vee, BGR, UnDel):
862862
)
863863

864864

865-
@jit(nopython=True, parallel=True)
865+
@jit(nopython=True, parallel=True, fastmath=True, cache=True)
866866
def _EhRenorm_core(nh, Vhh, BGR, UnDel):
867867
"""Core JIT-compiled computation for EhRenorm."""
868868
N = len(nh)
@@ -894,7 +894,7 @@ def _EhRenorm_core(nh, Vhh, BGR, UnDel):
894894
)
895895

896896

897-
@jit(nopython=True, parallel=True)
897+
@jit(nopython=True, parallel=True, fastmath=True, cache=True)
898898
def _MBCE2_core(ne, nh, Veh2, Vee2, Win, Wout, k3, Ceh, Cee):
899899
"""Core JIT-compiled computation for MBCE2."""
900900
Nk = len(ne) - 1
@@ -953,7 +953,7 @@ def _MBCE2_core(ne, nh, Veh2, Vee2, Win, Wout, k3, Ceh, Cee):
953953
)
954954

955955

956-
@jit(nopython=True, parallel=True)
956+
@jit(nopython=True, parallel=True, fastmath=True, cache=True)
957957
def _MBCE_core(ne, nh, Veh2, Vee2, Win, Wout, k3, Ceh, Cee):
958958
"""Core JIT-compiled computation for MBCE (identical to MBCE2)."""
959959
Nk = len(ne) - 1
@@ -1012,7 +1012,7 @@ def _MBCE_core(ne, nh, Veh2, Vee2, Win, Wout, k3, Ceh, Cee):
10121012
)
10131013

10141014

1015-
@jit(nopython=True, parallel=True)
1015+
@jit(nopython=True, parallel=True, fastmath=True, cache=True)
10161016
def _MBCH_core(ne, nh, Veh2, Vhh2, Win, Wout, k3, Ceh, Chh):
10171017
"""Core JIT-compiled computation for MBCH."""
10181018
Nk = len(ne) - 1

src/pulsesuite/PSTD3D/dcfield.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ def DC_Step_FD(ne, nh, nemid, nhmid, ky, Edc, dt, me, mh):
335335
# ============================================================================
336336

337337

338-
@jit(nopython=True)
338+
@jit(nopython=True, fastmath=True, cache=True)
339339
def _EkReNorm_jit(n, En, V):
340340
Nk = len(n)
341341
Ec = np.zeros(Nk)
@@ -344,7 +344,7 @@ def _EkReNorm_jit(n, En, V):
344344
return Ec
345345

346346

347-
@jit(nopython=True)
347+
@jit(nopython=True, fastmath=True, cache=True)
348348
def _DriftVt_jit(n, Ec, dkk, hbar_val):
349349
Nk = len(n)
350350
dEdk = np.zeros(Nk, dtype=np.complex128)
@@ -359,19 +359,19 @@ def _DriftVt_jit(n, Ec, dkk, hbar_val):
359359
return np.real(v)
360360

361361

362-
@jit(nopython=True)
362+
@jit(nopython=True, fastmath=True, cache=True)
363363
def _Lrtz_jit(a, b):
364364
pi_val = 3.141592653589793
365365
return (b / pi_val) / (a**2 + b**2)
366366

367367

368-
@jit(nopython=True)
368+
@jit(nopython=True, fastmath=True, cache=True)
369369
def _theta_jit(x):
370370
small = 1e-200
371371
return (np.abs(x) + x) / 2.0 / (np.abs(x) + small)
372372

373373

374-
@jit(nopython=True)
374+
@jit(nopython=True, fastmath=True, cache=True)
375375
def _ThetaEM_jit(Ephn, m, g, ky, n, Cq2, v, N0, q, k, hbar_val, pi_val):
376376
q_idx = q - 1 if q > 0 else 0
377377
k_idx = k - 1 if k > 0 else 0
@@ -408,7 +408,7 @@ def _ThetaEM_jit(Ephn, m, g, ky, n, Cq2, v, N0, q, k, hbar_val, pi_val):
408408
return ThetaEM_val
409409

410410

411-
@jit(nopython=True)
411+
@jit(nopython=True, fastmath=True, cache=True)
412412
def _ThetaABS_jit(Ephn, m, g, ky, n, Cq2, v, N0, q, k, hbar_val, pi_val):
413413
q_idx = q - 1 if q > 0 else 0
414414
k_idx = k - 1 if k > 0 else 0
@@ -445,7 +445,7 @@ def _ThetaABS_jit(Ephn, m, g, ky, n, Cq2, v, N0, q, k, hbar_val, pi_val):
445445
return ThetaABS_val
446446

447447

448-
@jit(nopython=True)
448+
@jit(nopython=True, fastmath=True, cache=True)
449449
def _dndEk_jit(Ephn, m, q, dndq, hbar_val):
450450
x0 = Ephn * 2.0 * m / hbar_val**2
451451
N = len(q)
@@ -455,7 +455,7 @@ def _dndEk_jit(Ephn, m, q, dndq, hbar_val):
455455
return dndEk
456456

457457

458-
@jit(nopython=True)
458+
@jit(nopython=True, fastmath=True, cache=True)
459459
def _ThetaEMABS_jit(Ephn, m, q, dndk, Cq2, v, hbar_val, pi_val):
460460
x0 = Ephn * 2.0 * m / hbar_val**2
461461
N = len(q)

src/pulsesuite/PSTD3D/dephasing.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
# ── JIT kernels (module-level — Numba requirement) ──────────────────
2626

2727

28-
@jit(nopython=True)
28+
@jit(nopython=True, fastmath=True, cache=True)
2929
def _CalcGammaE_jit(
3030
Vee2, Veh2, ne, nh, se, sh, k_p_q, k_m_q, k1p_m_q, k1p, xe, xh, pi_val, hbar_val
3131
):
@@ -64,7 +64,7 @@ def _CalcGammaE_jit(
6464
return GammaE
6565

6666

67-
@jit(nopython=True)
67+
@jit(nopython=True, fastmath=True, cache=True)
6868
def _CalcGammaH_jit(
6969
Vhh2, Veh2, ne, nh, se, sh, k_p_q, k_m_q, k1_m_q, k1, xe, xh, pi_val, hbar_val
7070
):
@@ -103,7 +103,7 @@ def _CalcGammaH_jit(
103103
return GammaH
104104

105105

106-
@jit(nopython=True)
106+
@jit(nopython=True, fastmath=True, cache=True)
107107
def _CalcOffDiagDeph_E_jit(
108108
ne, nh, Veh2, Vee2, Ee, Eh, k_p_q, k_m_q, gee, geh, pi_val, hbar_val
109109
):
@@ -146,7 +146,7 @@ def _CalcOffDiagDeph_E_jit(
146146
return D * pi_val / hbar_val
147147

148148

149-
@jit(nopython=True)
149+
@jit(nopython=True, fastmath=True, cache=True)
150150
def _CalcOffDiagDeph_H_jit(
151151
ne, nh, Veh2, Vhh2, Ee, Eh, k_p_q, k_m_q, ghh, geh, pi_val, hbar_val
152152
):
@@ -189,7 +189,7 @@ def _CalcOffDiagDeph_H_jit(
189189
return D * pi_val / hbar_val
190190

191191

192-
@jit(nopython=True)
192+
@jit(nopython=True, fastmath=True, cache=True)
193193
def _OffDiagDephasing_jit(Dh, De, pt, pp, k_p_q, undel, ii_val, hbar_val):
194194
"""JIT-compiled version of OffDiagDephasing inner loops."""
195195
Nk = Dh.shape[0]
@@ -217,13 +217,13 @@ def _OffDiagDephasing_jit(Dh, De, pt, pp, k_p_q, undel, ii_val, hbar_val):
217217
return x * ii_val * hbar_val
218218

219219

220-
@jit(nopython=True)
220+
@jit(nopython=True, fastmath=True, cache=True)
221221
def _Lrtz_jit(a, b, pi_val):
222222
"""JIT-compiled version of Lrtz."""
223223
return (b / pi_val) / (a**2 + b**2)
224224

225225

226-
@jit(nopython=True)
226+
@jit(nopython=True, fastmath=True, cache=True)
227227
def _CalcOffDiagDeph_E2_jit(ne, nh, Vsq, Ee, Eh, gee, geh, pi_val, hbar_val):
228228
"""JIT-compiled version of CalcOffDiagDeph_E2."""
229229
Nk = len(ne)
@@ -270,7 +270,7 @@ def _CalcOffDiagDeph_E2_jit(ne, nh, Vsq, Ee, Eh, gee, geh, pi_val, hbar_val):
270270
return D * pi_val / hbar_val
271271

272272

273-
@jit(nopython=True)
273+
@jit(nopython=True, fastmath=True, cache=True)
274274
def _CalcOffDiagDeph_H2_jit(ne, nh, Vsq, Ee, Eh, ghh, geh, pi_val, hbar_val):
275275
"""JIT-compiled version of CalcOffDiagDeph_H2."""
276276
Nk = len(ne)
@@ -317,7 +317,7 @@ def _CalcOffDiagDeph_H2_jit(ne, nh, Vsq, Ee, Eh, ghh, geh, pi_val, hbar_val):
317317
return D * pi_val / hbar_val
318318

319319

320-
@jit(nopython=True)
320+
@jit(nopython=True, fastmath=True, cache=True)
321321
def _OffDiagDephasing2_jit(Dh, De, p, pt, undel, ii_val, hbar_val):
322322
"""JIT-compiled version of OffDiagDephasing2 inner loops."""
323323
Nk = p.shape[0]

src/pulsesuite/PSTD3D/epsrtl.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def atanhc(x):
142142
return 0.5 * np.log((1.0 + x) / (1.0 - x))
143143

144144

145-
@jit(nopython=True)
145+
@jit(nopython=True, fastmath=True, cache=True)
146146
def _PiT_jit(
147147
w, me, mh, Te, Th, dk, Ek, Ekq, n00_val, hbar_val, kB_val, twopi_val, pi_val
148148
):
@@ -300,7 +300,7 @@ def GetEpsrLEpsrT(n1D, dcv0, Te, Th, me, mh, Eg, ky):
300300
# Note: stop statement removed in Python version
301301

302302

303-
@jit(nopython=True)
303+
@jit(nopython=True, fastmath=True, cache=True)
304304
def _RecordEpsrT_loop_jit(
305305
ky_size,
306306
Nw,
@@ -694,7 +694,7 @@ def QqGq(ky, Nk, dk, dw, EpsR, EpsI, eh):
694694
f.write(f"{ky[q]} {Omega[q]} {Gam[q]}\n")
695695

696696

697-
@jit(nopython=True)
697+
@jit(nopython=True, fastmath=True, cache=True)
698698
def _ZeroT_L_loop_jit(
699699
qy_size, Nw, dw, m, kf, qy, dq, Vc, hbar_val, e0_val, pi_val, ii_real, ii_imag
700700
):
@@ -848,7 +848,7 @@ def ZeroT_L(B, m, qy, kf):
848848
f.write(f"{eps[qq, ww_idx].real} {eps[qq, ww_idx].imag}\n")
849849

850850

851-
@jit(nopython=True)
851+
@jit(nopython=True, fastmath=True, cache=True)
852852
def _ZeroT_T_loop_jit(
853853
qy_size,
854854
Nw,
@@ -1066,7 +1066,7 @@ def ZeroT_T(me, mh, Egap, dcv, qy, kf):
10661066
f.write(f"{Pi2[qq, ww_idx]} {-Pi3[qq, ww_idx].real}\n")
10671067

10681068

1069-
@jit(nopython=True)
1069+
@jit(nopython=True, fastmath=True, cache=True)
10701070
def _RecordEpsrL_loop_jit(
10711071
ky_size,
10721072
Nw,

0 commit comments

Comments
 (0)