Performance Optimization

Numba JIT Compilation

GASFIR’s inner loops are JIT-compiled with Numba. The first call to any kernel function triggers compilation, which may take several seconds. Subsequent calls run at near-native speed. To warm up the cache before a long computation:

from gasfir import create_pulse, get_parameters, get_diabatic_ionization_probability

laser = create_pulse(800, 1e14, 0, 5)   # short pulse just for warm-up
params = get_parameters("Hydrogen_SFA")
_ = get_diabatic_ionization_probability(pulse=laser, param_dict=params)
# From here on, compiled kernels are cached in memory.

Pulse Result Caching

Each Pulse instance caches field/potential evaluations keyed by the grid hash. Reusing the same time grid avoids redundant computation:

t = laser.get_tgrid(dt=0.25)
E1 = laser.get_electric_field(t)   # computed
E2 = laser.get_electric_field(t)   # returned from cache

Batch Pre-Computation

When computing probabilities for many pulses, use get_diabatic_ionization_probability_vec(). It calls precompute_pulse_batch() once to strip the NumPy arrays out of the pulse objects, then evaluates the kernel for every pulse across all CPU cores:

from gasfir import get_diabatic_ionization_probability_vec, get_parameters

params = get_parameters("Hydrogen_SFA")
probs  = get_diabatic_ionization_probability_vec(pulses, params)

During fitting, the grid pre-computation should happen once and be reused across every parameter update. ret_gasfir_P_for_dataFrame() (and gasfir.fitting.ret_residual_function()) do exactly this: they run precompute_pulse_batch() at construction time and return a fast closure that only re-evaluates the kernel:

from gasfir import ret_gasfir_P_for_dataFrame

P_func = ret_gasfir_P_for_dataFrame(df)   # pre-computes once
probs  = P_func(params)                   # cheap to call per parameter set

Choosing dt and dT

  • dT (fine grid) controls accuracy of the phase integral. Default 0.25 a.u. is safe for most conditions; increase to 0.5 for a ~2× speedup with mild accuracy loss.

  • dt (coarse grid) controls the pulse envelope sampling. Default 2 a.u. is sufficient for pulses longer than ~5 optical cycles.