The wizard GUI#
The graphical wizard guides you from a loaded trace to a retrieved pulse, dispersion analysis and uncertainty estimates. Its controls use the same calculations as the Python interface. If this is your first visit, follow installing the GUI, then try a synthetic trace whose pulse is known.
Every plot carries the standard matplotlib navigation toolbar (Home, Pan, Zoom-to-rectangle, Save, and a live cursor read-out), so you can zoom and pan to inspect a trace or marginal. A full redraw (changing a control, recomputing) resets the view; Home restores it.
Launching#
For a standalone tool installation:
croak-gui
Within a uv project or source checkout, use uv run croak-gui or
uv run python -m croak.gui. From Python, call croak.gui.run_wizard():
import croak.gui
croak.gui.run_wizard()
croak.gui.main() is the console-script entry point;
run_wizard() creates the QApplication and shows the window
maximised, because the plot pages need the room. Un-maximising gives the
window the size you last left it (remembered between launches and clamped to the
screen it opens on), or on a first run a window filling 90 % of the screen.
The wizard is laid out for displays of 1366×768 logical pixels and up (a
1920×1080 monitor at Windows’ 125 % scaling is 1536×864 logical pixels). Plot
text (titles, axis labels, tick labels, legends and their padding) shrinks as a
canvas gets smaller than the size its figure was designed for, down to 70 % of
the default size, and grows back when the window does. A laptop therefore shows
the same panels at a smaller type size rather than losing the axes behind their
labels. Figures written by a stage’s Save… button are drawn afresh at full
size, whatever the window size (PlotScale is the
mechanism).
Desktop integration#
The wizard carries its own icon (croak/gui/resources/croak.svg) and identifies
itself as croak rather than as the Python interpreter running it: that is the
icon in the macOS Dock, the Windows taskbar and the Linux dock, and the name in
the macOS menu bar. Two platform details remain:
On macOS the Cmd-Tab switcher and the Dock tooltip still say
python3. Those come from an application bundle, which a plaincroak-guiprocess does not have.On Windows
croak-guiis agui-scriptsentry point, so it opens no console window, but a crash before the window appears is then silent. Runuv run python -m croak.guifrom a terminal to see the traceback.
The stages#
The wizard is a stacked sequence of pages with shared navigation. The six linear stages — Load → Marginal check → Preprocess → Retrieve → Dispersion → Uncertainty — sit behind three entry pages (Welcome, Synthetic, Simulated); the synthetic generator and the simulated loader are each their session’s “stage 1” and hand the trace straight to the marginal-check stage.
Welcome — choose to load a measured file, load a numerically simulated scan, load a previous retrieval, or generate a synthetic trace.
Synthetic (optional) — build a test trace (pulse shape, interaction, delays, optional dispersive slab) using
maketrace(). Useful for trying the workflow without data. The duration is the intensity (power) FWHM of the temporal envelope — both Gaussian and sech² honour this convention (gaussian_pulse(),sech_pulse()). The delay and wavelength samplings each expose an interlocked count and step at fixed range (editing one updates the other), and a Spectrum (independent) group generates a fundamental spectrum that is passed downstream (range / step / # points); by default it mirrors the FROG-trace λ window, scaled to the fundamental (×2 for SHG). With it present, the marginal-check overlay and the SHG marginal correction work on synthetic traces too. The known pulse (its temporal intensity envelopeIt) is carried through and shown beneath the retrieved pulse, and saved with the result.Simulated (optional) — load a numerically simulated
scansavescan and preview it, then hand it to the marginal-check stage. A thickness dropdown selects which saved propagation depth to retrieve from a multi-thickness scan (disabled for older single-thickness files). See Loading numerically simulated traces.Load — pick an HDF5/NPZ file and the trace/wavelength/delay datasets, with unit and axis auto-detection (
croak.io). An optional independent spectrum can come from HDF5, NPZ or CSV, with background subtraction, and wavelength calibration curves from CSV. Transpose fixes a trace that loaded as (delay × wavelength), and Reverse delay axis flips the delay-sign convention — negating the scan axis, so the trace is mirrored about τ = 0. Which end of a stage scan is “gate late” is a wiring convention the file does not record. It matters for PG and SD, whose kernels fix the direction of time: a wrong setting time-reverses the retrieved pulse and flips the sign of every phase order. The error is invisible on a symmetric transform-limited pulse, so check the setting once against a pulse of known chirp (for example after adding a known glass thickness). An SHG trace is symmetric in delay even for a chirped pulse, so reversing it changes nothing and cannot settle SHG’s own direction-of-time ambiguity — that is resolved after retrieval instead (SHG: settle the direction of time first). It is the measured counterpart of the simulated loader’s switch of the same name. Optionally enter the measured pulse energy (J) here to rescale the temporal plots to absolute power (see the Dispersion stage). The trace preview uses the same colour scheme as the preprocessing stage (white→viridis for positive, white→red for negative), with the display scaled to the in-trust-band signal so an aggressive efficiency tilt cannot wash the image out.Marginal check — compare the trace’s frequency marginal with the marginal predicted from the independent spectrum (
croak.marginal_checks): exact for SHG (the spectral autoconvolution) and transform-limited for PG/TG/SD, within the thin local model; the marginal guide states its assumptions. It overlays the predicted marginal, marks the predicted vs measured centroid, reports the width-bracket check, and exposes the spectral trust region (defaults to 190–1000 nm for a measured trace, or the full loaded range for the noise-free simulated/synthetic traces), a frequency/wavelength axis toggle (default frequency, the space the retrieval works in; the wavelength view applies the \(1/\lambda^2\) density Jacobian, which shifts a broad marginal towards the blue), a log/linear marginal toggle (default linear, peak-normalised) and the 3rd-order(λ/µm)^expefficiency correction. Its Auto button picks the exponent whose corrected-marginal centroid matches the prediction (auto_third_order_exponent()). The SHG marginal correction is available here: it scales the trace’s frequency rows onto the predicted SHG autoconvolution (apply_marginal_correction()), is enabled only for an SHG trace with an independent spectrum, and previews the snap onto the prediction live (the correction itself is applied during Preprocess). All three entry paths (experimental, simulated, synthetic) pass through this stage; see Marginal consistency checks. The trace and the marginal comparison share a draggable divider, so either plot can be given the height.Preprocess — fringe/DC/low-pass filtering, windowing and regridding (
load_and_clean()), with a live before/after preview. The controls always show the settings that were actually applied: re-entering the stage re-seeds every widget from the current parameters, and those parameters survive a round trip back to the loader as long as the trace is unchanged (see Preprocessing). With Baseline (arPLS) on, the Auto button beside τ exclude (fs) sets the hold-out to twice the loaded trace’s delay-marginal FWHM. Use it on measured traces: without a hold-out the baseline bows into the τ ≈ 0 peak and removes signal, worst on the rows that constrain the fit least. The failure shows up as a thin stripe at one wavelength in the cleaned trace (see Peak suck-up).Retrieve — choose the algorithm (any of the ten, including the global
cma-es) and its parameters and run. Retrieval runs in a background thread, driven by the solvercallback, so the interface stays responsive. The result is shown on three pages of panels, as tabs: Traces (measured and retrieved trace, log and linear, with their colour bars), Pulse (retrieved pulse over the delay marginal, spectrum over the frequency marginal) and Diagnostics (convergence, residuals, spectral filter, spectrogram) — the same twelve panels asplot_retrieval(), two by two so each has room even on a laptop display. Pop out (top right of the tabs) opens the current page in its own window, which follows later results; each tab’s toolbar Save writes that page, and the stage’s Save… writes the full twelve-panel overview asretrieval.pdfbesideresult.h5andoptions.toml. The Display group’s Spectrum axis draws the spectrum panel against frequency (PHz, the default — the space the retrieval works in) or wavelength (nm, the spectrometer’s axis, carrying the \(1/\lambda^2\) density Jacobian); both are unit-peak densities in their own variable, the choice is remembered, and the exported PDF follows it.By default Live full-plot preview redraws the visible page as the run converges (other pages redraw when selected). Previews arrive at most four times a second, and less often on a machine where presenting one is slow: the wizard measures what each preview costs and stretches the interval so that presenting stays below about a quarter of the wall-clock time, so the preview never starves the solver. Until the first preview arrives, the convergence curve is shown on the visible page, redrawn at up to ten frames a second however fast the solver iterates. A page is laid out once per run and later snapshots update its existing curves, images and labels, so nothing shifts between frames; the spectrogram, the costliest panel, is computed only when the Diagnostics page is drawn. Pressing Stop halts the run at that point and leaves the latest full plot on screen as a (non-converged) result you can inspect, save or post-filter. Untick Live full-plot preview to fall back to the cheaper trace-error-vs-iteration curve (the full plot is still drawn once the run finishes). Every solver supports the preview, including
copra/copra-jaxand the COPRA warm-up ofwarm-lbfgs, which is 300 iterations by default.warm-lbfgsplots both stages on one convergence curve with a dashed rule at the handover; read the two sides as different units of work (COPRA iterations, then L-BFGS function evaluations). The Initial guess selector makes automatic, transform-limited, random, perfect-Gaussian, known truth, and reuse-previous starts mutually exclusive. Truth uses the complex field stored by ModelPNPS (and reports an explicit error for an older intensity-only file). Reuse previous result warm-starts one run from the last — handy for the global-then-local refine recipe. It seeds the previous spectrum as the initial guess and the extras that run fitted (thickness, τ₀, smearing width) as the starting values for fitting them again, naming them in the status line; an extra you are no longer fitting keeps the value set in the controls. When the run finishes, the status line reports the fitted extras alongside the error and FWHMs — including the geometric-smearing width as a delay σ in fs with its multiplier on the nominal mask geometry (which is left untouched). When the trace came from the synthetic or simulated workflow, the known-truth temporal intensity is drawn as a thin black line beneath the retrieved and transform-limited pulses. When its complex field is available, dotted truth-phase lines appear in both time and wavelength panels and are named in their legends. The truth arrays are also written into the savedresult.h5.Beneath the pages sits a numeric read-out that updates as the run converges; click its header to fold it away and give the plots its height (remembered between launches). Its Pulse parameters table has one row per curve in the figure, labelled to match the legends —
R(retrieved),M(the independent measured spectrum),TL(transform-limited) andtruth— and four columns: peak wavelength, spectral FWHM, temporal FWHM and peak power. Cells that are not defined show an en dash rather than a misleading number: the measured spectrum carries no phase, so it has no temporal envelope; the transform-limited pulse is the retrieved \(|E(\omega)|\) with the phase removed, so its spectrum is the retrieved one and is reported once, on theRrow. The peak-power column needs a measured pulse energy (set it on the Load or Dispersion stage) and shares one SI prefix, shown in its header, so the values compare at a glance.Below the table,
Iteration,ElapsedandError Rtrack the run itself, andτ₀,ThicknessandSmearingshow the forward model’s extra parameters, each marked (fit) or (fixed) so it is clear whether the retriever was free to move it. The three run counters tick every iteration; the table refreshes with the live full-plot preview, so with that preview turned off it stays blank until the run finishes.The last line,
ε(Iₜ),ε(I𝜔)andε(E𝜔), appears when the trace came from the synthetic or simulated workflow and so has a known pulse: the temporal intensity, spectral intensity and complex-field errors against it (croak.truth_metrics). They answer a different question from theError Rabove them — that scores the fit to the trace, these score the distance to the true pulse, and a FROG trace does not determine the pulse uniquely, so a smallRcan sit on a wrong answer. Read them together: a smallε(I𝜔)with a largeε(E𝜔)means the amplitude is right and the phase is not. The two spectral ones need the truth’s complex field, so an intensity-only file showsε(Iₜ)alone rather than a number derived from something else.Forward-model groups sit alongside the solver options. Dispersive propagation models a slab (material, thickness, Δz/Npoints — see The forward model). Geometrical smearing models the tilted-pulse-front temporal blur of a non-collinear BOXCARS setup from the mask hole diameter and edge-to-edge spacing, with the resulting delay width shown — and editable, which back-computes the spacing. Both groups grey out for solvers that cannot use them; smearing needs one of the autodiff solvers. The chromatic focal and collection settings are described in collection aperture modelling. See also geometric smearing for the reduced model.
Dispersion — apply and auto-tune residual dispersion (
croak.dispersion) to compress the retrieved pulse, including real glass (built-in or custom materials) and a mirror stack — add rows of(mirror, polarisation, bounces, remove/add)to back-propagate (remove) beam-path coating mirrors or add chirped-mirror bounces — with the temporal profile and spectrogram updating as you drag the sliders. Enter a pulse energy (J) (shared with the Load stage) to switch the temporal plot to absolute power and read off the current and transform-limited peak power; compressing the pulse raises the peak power towards the transform-limited maximum at fixed energy.Uncertainty — put an error bar on the retrieved FWHM (
croak.uncertainty). Choose the estimator (parametric or resampling bootstrap, the substrate-thickness systematic, or the fast analytic covariance), set the replicate count and confidence level, and run it on a worker thread with a progress bar. The stage reports the point estimate with its 68 % and 95 % intervals, draws the temporal confidence band over the retrieved profile, and saves everything alongside the result. See Estimating FWHM uncertainty.
Sessions#
The GUI persists its settings — preprocessing choices, algorithm and options — to
a TOML file via croak.save.save_options() / croak.save.load_options(),
so you can reload a prior session. Retrievals are saved to HDF5 with
croak.save.save_result(). Load previous retrieval reloads the saved
options.toml; if a result.h5 sits beside it, the retrieved result is
restored from the file (croak.save.result_from_saved()) after the cheap
load + preprocess steps replay — so the solver is not re-run (it falls back to
re-running only if the file is missing or its grid no longer matches). See
Saving and loading.
Scripting the same workflow#
Everything in the wizard maps to a few library calls — load → clean → retrieve → process → plot → save:
import croak
fd = croak.io.list_datasets("frog.h5")
lam = fd.load("wavelength") * croak.io.unit_to_si("nm")
delays = fd.load("delay") * croak.io.unit_to_si("fs")
trace = fd.load("trace")
td = croak.load_and_clean(trace, lam, delays, "shg", lam_min=730e-9, lam_max=900e-9,
input_unit="delay")
result = croak.retrieve_from_tracedata(td, algorithm="lbfgs", maxiters=200)
pr = croak.process_result(result, measured=td.trace)
croak.plot_retrieval(result, measured=td.trace).savefig("retrieval.png")
croak.save_result(result, "retrieval.h5", processed=pr)
The experimental-workflow tutorial walks through exactly this, end to end.