Modelling geometric time smearing#

Crossing beams have different local arrival times across a focal spot. A spectrometer that collects the isolated signal averages over those positions; ignoring this response can bias the inferred pulse. This page shows how to construct and use the reduced timing kernel. Start with the model overview, and use the worked example to compare retrievals.

The kernel assumes achromatic local profiles and full signal collection. For finite apertures or strongly chromatic focal fields, use the collection model. The derivation is retained in the forward model.

The quick version#

import croak
from croak.smearing import square_boxcars_kernel

kernel = square_boxcars_kernel(
    "pg",                    # PG (used for TG-FROG) or SD
    hole_diameter=1.0e-3,    # mask hole diameter D (m)
    hole_spacing=0.5e-3,     # edge-to-edge gap between holes (m)
    wavelength=260e-9,       # carrier
)
print(kernel.sigma_delta * 1e15)   # 0.357 fs

res = croak.retrieve(
    trace, g.omega, delays, "pg",
    algorithm="lbfgs-ad",
    smearing=kernel,
)

The same smearing= keyword works on maketrace() and maketrace_jax(), so you can generate a smeared trace and look at it without running a retrieval:

smeared = croak.maketrace(g.omega, delays, ew, "pg", smearing=kernel)

What the kernel is#

SmearingKernel holds three numbers: the rms of the gate-splitting parameter sigma_p, the rms of the delay offset sigma_delta, and their correlation rho. sigma_p changes the shape of the nonlinear signal and costs an explicit quadrature (npoints, default 5); sigma_delta is a pure translation of the delay axis, evaluated by a Fourier-domain convolution on a uniform delay grid. For correlated coordinates, the conditional mean and variance depend on the gate-splitting node.

Two constructors build one:

  • square_boxcars_kernel() — the standard folded square with four holes at \((\pm d,\pm d)\), \(d = (\text{spacing} + D)/2\). This is the usual case, and the arm assignment follows from the interaction: for "pg" the scanned delay rides the gate pair — croak’s operator convention \(E(t)\,|E(t-\tau)|^2\) delays both gate arms together (an instrument that delays the probe instead is equivalent up to a reversal of the delay axis); "sd" puts it on the conjugated arm.

  • kernel_from_arms() — fully general, for any three mask hole positions. Give them in the interaction’s role order: (probe, gate unconjugated, gate conjugated) for PG, (unconjugated A, unconjugated B, conjugated) for SD.

The focal length never appears: the crossing angle scales as \(1/f\) and the focal spot as \(f\), so it cancels. Within this ideal-hole approximation, the widths depend on \(d/D\) and wavelength.

The three numbers are arrangement-specific

For a folded square BOXCARS mask the closed forms depend on which arm carries the scanned delay, and they are not interchangeable:

Interaction

Delay on

\(\sigma_p\,/\,(d/D)(\lambda/c)\)

\(\sigma_\delta\,/\,(d/D)(\lambda/c)\)

\(\rho\)

"sd"

conjugated arm

0.69487

0.34743

0

"pg"

the gate pair (the usual TG-FROG case)

0.49135

0.54934

\(1/\sqrt5\)

So the convenient SD relations — \(\sigma_p = 2\sigma_\delta\) with the two uncorrelated — do not carry over to PG: there the pair is correlated and \(\sigma_\delta\) is 1.58× larger. square_boxcars_kernel() selects the right pair from the interaction argument, which matters if you are constructing a SmearingKernel by hand from numbers quoted for the other geometry.

Which way does your delay axis run?

The kernels are built in croak’s delay convention, where the PG signal is \(E(t)\,|E(t-\tau)|^2\). Only \(\rho\) depends on it: reversing the delay axis sends \(\delta \to -\delta\) and hence \(\rho \to -\rho\), for which square_boxcars_kernel(..., reverse_delay=True) is provided.

Which way it runs depends on which arm sits on the delay stage — the same \(\chi^{(3)}\) nonlinearity either way. Scanning the gate pair gives \(E(t)\,|E(t-\tau)|^2\); scanning the probe instead (as in the ModelPNPS reference simulation, and in TG setups generally) gives \(|E(t)|^2 E(t-\tau_{\rm exp})\), which is the same operator with \(\tau = -\tau_{\rm exp}\).

Retrieving a probe-scanned trace against croak’s convention without flipping the axis returns \(E^*(-t)\): the spectral amplitude and the duration are unchanged, but the temporal profile is mirrored and the spectral phase negated, so the sign of the retrieved GDD/TOD flips. Use the file’s documented delay convention (the simulated loader reads its delay_convention marker). Whenever the delay axis the retrieval sees is the probe-scanned one, build the kernel with reverse_delay=True, so that \(\rho\) matches the trace.

Note

Full collection permits an incoherent intensity average by Parseval’s identity. A finite aperture selects a coherent spatial field and can change delay-dependent structure. The reduced kernel is not a general upper bound on blur or duration; compare a suitable collection model.

Fixed or fitted#

Fixed geometry is the natural starting point: calculate the kernel from measured mask dimensions. Changing mask spacing provides a useful controlled test, but it can also change spatial collection and illumination. Model those changes before assigning a difference solely to smearing.

Warning

The kernel is independent of the medium thickness, but the duration offset it induces is not — how much the free pulse must absorb depends on the whole trace, whose sensitivity to the input duration changes with the slab. Do not use a “constant offset across thicknesses” test as the discriminator; use the mask dependence. examples/example_geometric_smearing.py runs both sweeps and shows the difference.

Fitted width adds a nuisance parameter. fit_smearing=True multiplies both widths by one dimensionless scale while retaining their correlation. It tests whether that reduced response can explain the trace; the estimate can also absorb other model errors:

res = croak.retrieve(
    trace, g.omega, delays, "pg",
    algorithm="lbfgs-ad",
    smearing=kernel,
    fit_smearing=True,
)
print(res.smear_scale)   # compare with the nominal scale of 1.0

A value near 1 is consistent with the nominal kernel; a value far from 1 usually means the reduced kernel is absorbing something else (collection, chromatic focal effects or another model error).

Warning

Do not fit the smearing width and the slab thickness in the same run. The smearing multiplier will soak up dispersion-model error, and the two are not separately identifiable from a single trace. Fit one, hold the other, and check the parameter covariance.

Uncertainty follows the same route as the other extra parameters: parameter_covariance() accepts smearing and fit_smearing and reports sigma_smear.

Splitting the two channels#

The kernel has two channels: the delay offset \(\delta\) (a pure blur along the delay axis) and the gate-shape parameter \(p\) (a symmetric split of the gate pair, which for a chirped gate shears the trace along frequency). The joint multiplier scales both together, which is the right physical prior — both derive from the same \(d/D\) — but as a diagnostic it conflates them. Setting fit_smearing_split=True fits the two widths as independent multipliers:

res = croak.retrieve(
    trace, g.omega, delays, "pg",
    algorithm="lbfgs-ad",
    smearing=kernel,
    fit_smearing=True,
    fit_smearing_split=True,
)
print(res.smear_scale)         # p (gate-shape) channel multiplier
print(res.smear_scale_delta)   # delta (delay-blur) channel multiplier

The split is exact, not an approximation: the bivariate-normal kernel scales channel-wise, with the \(p\) node positions carrying one factor and the conditional delay mean/width the other, at fixed correlation \(\rho\).

Note

For a transform-limited Gaussian field, splitting the gate changes only its amplitude, which a fitted intensity scale can absorb. This is not true for every transform-limited field: a sum of Gaussians gives a time-dependent gate ratio. A split-width estimate must therefore be interpreted using the pulse shape, phase and parameter correlations, not simply whether the field is chirped.

Temporal phase and spectral dispersion#

Write a Gaussian field as \(E(u)=\exp[-u^2/(4\sigma^2)]\exp[i b u^2/2]\). Its split gate is

\[E(u+p/2)E^*(u-p/2) =\exp[-u^2/(2\sigma^2)]\exp[-p^2/(8\sigma^2)]\exp[i bup].\]

For \(b=0\) this is attenuation only. With the forward Fourier sign \(\exp(+i\Omega t)\), the gate frequency shift is \(-bp\). For a general temporal phase \(\phi\), it is

\[\delta\omega_g(u;p)=-[\phi'(u+p/2)-\phi'(u-p/2)] =-p\phi''(u)-\frac{p^3}{24}\phi^{(4)}(u)+\cdots.\]

Temporal-phase derivatives are not the GDD, TOD and FOD, which are derivatives of spectral phase \(\Phi(\Omega)\). In particular, a symmetric spectral amplitude with pure odd spectral phase can give a real temporal field with phase jumps rather than continuous temporal chirp. For Gaussian intensity of rms width \(\sigma_t\), a quadratic spectral phase \(\Phi_2\) gives \(b=-\Phi_2/(4\sigma_{t,0}^4+\Phi_2^2)\), where \(\sigma_{t,0}\) is the transform-limited intensity width. This relation uses the Fourier convention above; it is not a general conversion for non-Gaussian fields.

parameter_covariance() accepts fit_smearing_split and additionally reports sigma_smear_delta.

Cost#

About \((K+1)/2\) times the unsmeared model, where \(K\) is npoints — roughly 3× at the default 5 nodes. The quadrature nodes are exactly antisymmetric, so each shifted field is built once and used twice, and the p axis is a jax.vmap batch. The delay integral adds nothing measurable.

The delay axis must be uniformly spaced (the delay integral is a convolution along it) and comfortably wider than the kernel; both are checked, and violations raise.

Solver support#

Option

Solvers

Requirements

smearing

warm-lbfgs, lbfgs-ad, lm, lm-optx, lbfgs-optx, cma-es

PG (TG) or SD; uniform delay axis

fit_smearing

warm-lbfgs, lbfgs-ad, lm, lm-optx, cma-es

a smearing kernel

fit_smearing_split

warm-lbfgs, lbfgs-ad, lm, lm-optx, cma-es

fit_smearing

copra, copra-jax, lbfgs and lbfgs-hand cannot model smearing: the incoherent average over the kernel breaks COPRA’s magnitude-replacement projection, and the hand-derived adjoint is not extended to it. Asking for smearing with them raises rather than silently dropping the kernel.

SHG raises too: geometric smearing here is defined for the three-arm PG/SD forms.

In the GUI#

The Retrieve stage has a Geometrical smearing group, greyed out unless the chosen solver supports it:

  • Enable geometrical smearing

  • Hole diameter (mm) and Hole spacing (mm) (edge to edge) — the physical inputs

  • σ delay (fs) — derived from them at the measured carrier, and editable: it back-computes the hole spacing, holding the diameter, so you can dial a width in directly. The same coupling as Thickness ↔ Δz ↔ Npoints in the group above. It has a floor at zero spacing (holes touching).

  • Quadrature nodes

Fit smearing width sits in Extra fit parameters and is enabled only once the kernel is on. When it has been fitted, the post-retrieval status line reports the result next to the other fitted extras, as the physical delay width and the multiplier it came from:

Done. R = 0.1459%   |   retrieved FWHM 1.12 fs   |   …   |   smearing σ 14.24 fs (×1.457)

The width shown is smear_scale × sigma_delta of the kernel the run used — the two kernel widths scale together with the multiplier (scaled()), so one number describes the fit. Selecting Reuse previous result on a re-run starts the fit from that multiplier instead of from 1.0.

The mask geometry is what is stored and what reaches the forward model; the displayed width is derived from it, always at the carrier of the loaded measurement. A fitted multiplier is deliberately not written back into the diameter/spacing controls: the geometry is a measured property of the instrument, not a readout to be overwritten by a fit. To adopt a fitted width as the new nominal, type it into σ delay (fs), which back-computes the spacing.

Worked example#

examples/example_geometric_smearing.py synthesises a trace with a known kernel and retrieves it with and without, printing the trace error, FWHM and rms width of each fit (see the gallery page):

uv run python examples/example_geometric_smearing.py