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\) |
|---|---|---|---|---|
|
conjugated arm |
0.69487 |
0.34743 |
0 |
|
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
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
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 |
|---|---|---|
|
|
PG (TG) or SD; uniform delay axis |
|
|
a |
|
|
|
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