The folder tests/components/ runs each component of the engine alone, through the Python API, and compares it with an
analytic or published answer. Every check prints its numbers, and most also draw a figure a person can read. The C#
component sheet (tests/UltrafastEngine.Tests/Components/) asserts the same numbers in the test suite.
python3 python/tools/build_engine.py # after any C# change
python3 tests/components/run_all.py # all families, figures, tests/components/report/index.html
python3 tests/components/run_all.py --no-plots # numbers only (what CI runs)
python3 tests/components/run_all.py linear crystals # some families
python3 tests/components/linear.py # one family; exit code 1 if a check failsThe scripts find python/src and the hosted engine build (python/src/ultrafast/bin/hosted) themselves. They set
ULTRAFAST_STRICT_OPTIONS=1, so an unknown key is an error. Figures need matplotlib; --no-plots needs only NumPy.
| script | what is checked | reference |
|---|---|---|
linear.py |
bulk and gap: group delay, GDD, TOD at 515 nm, 1030 nm and 2 µm, for 4 glasses and air; the whole applied φ(ω) over a 10 fs band; no spectral narrowing at 1 ps and 10 fs; plate: Fresnel transmission for p and s, tilted-plate GDD; GDD-coated mirror, chirpedMirror, dispersion; both leakMirror ports in Sequential and Branched systems; gratingPair, 1 and 2 passes |
Sellmeier (Malitson, Schott) × Ciddor air; Fresnel; plane-wave k_z d; Taylor phase; Treacy 1969 |
splitters_masks.py |
beamSplitter port energies from R = 0 to 1 at three carriers; Sequential = Branched (field to 1e-12); Mach-Zehnder (Stokes relations); polarizingSplitter against the input angle; dichroic port energies and spectra; a 515 nm beam fed into port b beside a 1030 nm beam; hard aperture on a Gaussian beam |
energy conservation, (t² − r²)², 4r²t², ∫R S dω, 1 − exp(−2a²/w²) |
polarisation.py |
design waveplates (pulse-averaged Stokes parameters); polariser (Malus with extinction); vector Kerr: B of linear light, B(χ)/B_lin = 1 − sin²2χ/3 (circular 2/3), ellipse rotation B sin2χ/3 at the pulse peak, circular light at 1.5× the power has the linear spectrum | Jones calculus; Maker, Terhune & Savage 1964 |
crystals.py |
BBO type-I SHG at three carriers: phase-matching angle, absolute conversion efficiency, η ∝ L² with depletion, η(Δk)/η(0) = sinc²(ΔkL/2), energy conservation at high conversion, the carrier on each output port | Eimerl 1987 and Tamošauskas 2018 Sellmeier; Armstrong et al. 1962 |
instruments.py |
intensity autocorrelator (FWHM ratio √2 Gaussian, 1.5427 sech², and the whole trace); spectrometer resolution (FWHMs add in quadrature) and its energy; SHG-FROG marginals; third-order cross-correlator (FWHM √(3/2) τ, floor, trace in dB) | Diels & Rudolph; closed-form correlations |
| optics_misc.py | lens and curved / off-axis mirror (ABCD, tangential and sagittal focus), prism (Snell, Brewster, prism-pair GDD), single grating (angle, Littrow, pulse-front tilt, efficiency), ndFilter and its ghost, plate and bulk ghosts, interface and coatings (Fresnel, quarter-wave AR, Bragg mirror), delay, rotator, faradayRotator (non-reciprocal double pass), thinFilmPolarizer, saturableAbsorber, filter, zone-plate mask, wavefront Zernike terms, aperture | Snell, Fork / Martinez, Treacy, Hebling, transfer matrix, Malus, Frantz-Nodvik-type ODE, Fresnel |
| media.py | fiber (SPM phase γPL, broadening, β2 / β3, D(λ), solitons, Raman shift against Gordon, loss and gain, dispersive wave), gain (small signal, Frantz-Nodvik, extraction), gases (Ciddor, n2 and density, rotational Raman), ionisation (ADK, PPT), bulk Kerr (B, self-steepening) | Agrawal, Frantz-Nodvik 1963, Gordon 1986, PPT / ADK |
| crystals_chi2.py | SFG, DFG, OPA / OPCPA gain and Manley-Rowe, quasi-phase matching, non-collinear OPA, BBO phase-matching angles, walk-off | cosh² and sinc² solutions, Tamošauskas Sellmeier |
| spatial.py | Gaussian w(z), round and square apertures against the Fresnel integral, lens focus, Hermite- and Laguerre-Gauss beams, axicon, Kerr lens, Young slits, resonator eigenmodes, soliton oscillator | Fresnel integral, Kogelnik-Li, Fox-Li, area theorem |
| instruments_more.py | energy meter, scope, polarimeter, interferometric autocorrelator, d-scan, Wigner, camera, wavefront sensor, beam profile, delay scan, FROG retrieval, spectrometer detector models | closed-form traces and Stokes vectors |
Each reproduction of an engine disagreement is in tests/components/repro/; the check that shows it is marked KNOWN.
tests/components/report/index.html (not committed) has one section per family:
- Figures. The title states the check. The engine is drawn solid (with markers where it is a set of runs), the analytic answer dashed black. The lower panel is the residual: engine − analytic in the unit of the upper axis, or the relative residual when the panel says so. A good check has a flat residual near zero, small against the quantity above it.
- Formula. Under each figure: the analytic expression that was used.
- Table. Every check, with the engine value, the analytic value, the relative error, the tolerance and the result:
ok,FAIL, orKNOWN.KNOWNis a reported engine limitation that has not been fixed yet; the note says what it is. It does not fail the run. When aKNOWNcheck starts to pass, the table says so and the marker should be removed.
Tolerances are as tight as the reference allows; where a tolerance is looser than the numerics, a comment in the script gives the reason (for example, group-velocity mismatch at 515 nm in BBO, or the 1 fs delay sampling of a cross-correlation on a −100 dB flank).
Decide first whether the engine or the analytic expectation is wrong. The conventions that matter most are in
Concepts: elements use absolute indices (a plate does not remove the air it replaces), the envelope sits
on a grid centred on the carrier snapped to the frequency step (the reported GDD and TOD are those at the requested
carrier, grid.reportOmega0 in the summary; a fit to the field gives the values at the grid centre, so it includes
TOD (ω_c − ω)), and pulse-averaged quantities (Stokes parameters of a chromatic waveplate, a pulse's SHG
efficiency) need the average over the spectrum or the pulse, not the value at the carrier. If the engine is wrong, fix
it with a C# test in tests/UltrafastEngine.Tests/Components/.
Add a function to the family script that calls fam.check(name, engine, analytic, rel=..., abs=..., unit=..., formula=...)
for each number, and draws its figure inside with fam.figure(key, title, formula) as f: with
overlay(f, x, engine, analytic, ...). The harness is tests/components/_common.py; independent references
(Sellmeier, Ciddor air, Treacy, Fresnel, tilted plate) are in _analytic.py, which does not import the engine. A new
family script also goes into FAMILIES in run_all.py. In C#, a linear element gets the whole sheet (three carriers,
two bandwidths, energy, reported GDD, Sequential = Branched) by being listed in LinearComponentTests.Cases.