Laser Fields
GASFIR represents laser pulses as objects derived from the abstract base class
gasfir.Pulse. The concrete implementations are:
gasfir.CosNPulse— linearly polarized cosN envelope pulse.gasfir.EllipticalCosNPulse— elliptically polarized cosN envelope pulse.gasfir.DataPulse— pulse from user-supplied E(t) or A(t) numerical data.SumOfPulses— superposition of any number ofPulseobjects (gasfir.pulse).gasfir.TIPTOE— pump–probe delay scanner with Nyquist-sampled delay grid.
Creating a Pulse
The convenience factory gasfir.create_pulse() is the recommended entry point:
from gasfir import create_pulse
# create_pulse(wavelength_nm, intensity_W_per_cm2, CEP_rad, duration_optical_cycles)
laser = create_pulse(800, 1e14, 0, 6)
Constructing Directly
For full control over pulse parameters use the class constructor directly.
Note that the constructor takes the FWHM in atomic units (not optical
cycles); use gasfir.OptCyc_au() to convert from optical cycles:
from gasfir import CosNPulse, OptCyc_au
laser = CosNPulse(
central_wavelength=800, # nm
peak_intensity=1e14, # W/cm²
cep=0.0, # rad
FWHM=OptCyc_au(30, 800), # FWHM in a.u. (30 optical cycles)
envelope_N=2, # power of the cos envelope (default 8)
)
Accessing Field Properties
All Pulse objects expose cached accessors:
import numpy as np
t = laser.get_tgrid(dt=0.25) # time grid in atomic units
E = laser.get_electric_field(t) # electric field
A = laser.get_vector_potential(t) # vector potential
Results are cached internally by grid hash, so repeated calls with the same grid are free.
Units
All internal quantities are in atomic units unless stated otherwise.
Use the gasfir.AtomicUnits class for unit conversions:
from gasfir import AtomicUnits
t_fs = t * AtomicUnits.fs # convert from a.u. to femtoseconds
E_SI = E * AtomicUnits.Volts_per_meter # convert to V/m
Pump–probe delay scanning — TIPTOE
gasfir.TIPTOE holds a strong pump fixed and scans a weak probe in
time, returning a SumOfPulses at each delay. The
companion method return_delay_array() applies the
Nyquist sampling theorem to produce a correctly-sampled delay grid:
from gasfir import TIPTOE, create_pulse, get_diabatic_ionization_probability, get_parameters
pump = create_pulse(800, 3e14, 0, 3) # strong pump
probe = create_pulse(800, 1e13, 0, 1) # weak probe
scanner = TIPTOE(pump, probe)
# Nyquist-sampled delays: step = T₀/4 by default (4 samples/cycle)
delays = scanner.return_delay_array()
params = get_parameters("H_SFA")
probs = [
get_diabatic_ionization_probability(pulse=scanner.at_delay(tau), param_dict=params)
for tau in delays
]
The step size is T₀ / oversample where T₀ = 2π / max(ω_pump, ω_probe)
and oversample=4 (default, giving 2× Nyquist margin). Pass a larger
value for carrier-envelope resolved scans:
delays_fine = scanner.return_delay_array(oversample=8)
Numerical / data-driven pulses
gasfir.DataPulse wraps an arbitrary numerically-sampled field so it
can be used wherever a Pulse is accepted. Supply either the
electric field E(t) or the vector potential A(t) on a uniform time grid
— both in atomic units:
import numpy as np
from gasfir import DataPulse
# Build a simple sinusoidal A(t) that vanishes at both ends
t = np.linspace(0, 2000, 80_000) # atomic units
A = np.sin(np.pi * t / t[-1]) * np.sin(0.057 * t) # vanishes at t=0 and t=T
pulse = DataPulse(t=t, A=A)
# Use it exactly like any other Pulse
from gasfir import get_diabatic_ionization_probability, get_parameters
prob = get_diabatic_ionization_probability(pulse=pulse, param_dict=get_parameters("H_SFA"))
Physical requirement — boundary condition on A. The strong-field ionization integrals require \(A(t) \to 0\) at both the start and end of the time window. If you supply E(t) instead, GASFIR integrates it numerically to obtain A(t); ensure the pulse is switched on and off smoothly so the resulting A satisfies the boundary condition.
Combining pulses. gasfir.SumOfPulses adds two or more
Pulse objects together. To create a combination that no
analytic class supports, sample the sum onto a grid and feed the result back
as a DataPulse:
from gasfir import DataPulse, create_pulse
from gasfir.pulse import SumOfPulses
fundamental = create_pulse(800, 1e14, 0, 6)
harmonic = create_pulse(400, 2e13, 0, 6)
combined = SumOfPulses([fundamental, harmonic]) # takes a list
t = combined.get_tgrid(dt=0.25)
A = combined.get_vector_potential(t)
data_pulse = DataPulse(t=t, A=A) # now usable with any GASFIR routine