Laser Fields ============ GASFIR represents laser pulses as objects derived from the abstract base class :class:`gasfir.Pulse`. The concrete implementations are: * :class:`gasfir.CosNPulse` — linearly polarized cos\ :sup:`N` envelope pulse. * :class:`gasfir.EllipticalCosNPulse` — elliptically polarized cos\ :sup:`N` envelope pulse. * :class:`gasfir.DataPulse` — pulse from user-supplied E(t) or A(t) numerical data. * ``SumOfPulses`` — superposition of any number of ``Pulse`` objects (``gasfir.pulse``). * :class:`gasfir.TIPTOE` — pump–probe delay scanner with Nyquist-sampled delay grid. Creating a Pulse ---------------- The convenience factory :func:`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 :func:`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 :class:`~gasfir.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 :class:`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 ------------------------------------ :class:`gasfir.TIPTOE` holds a strong pump fixed and scans a weak probe in time, returning a :class:`~gasfir.pulse.SumOfPulses` at each delay. The companion method :meth:`~gasfir.TIPTOE.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 ------------------------------- :class:`gasfir.DataPulse` wraps an arbitrary numerically-sampled field so it can be used wherever a :class:`~gasfir.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 :math:`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.** :class:`gasfir.SumOfPulses` adds two or more :class:`~gasfir.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 :class:`~gasfir.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