By default every element is one-dimensional in space: the beam enters through its size (an effective area for the Kerr effect, an overlap for χ(2) coupling) but has no transverse structure. A transverse model resolves the field across the beam, A(r, ω) or A(x, y, ω), so diffraction, self-focusing, spatial depletion, walk-off and beam-quality changes come out of the simulation.
Set it per element ("transverse"), for all elements of a run (physics.transverse in the run spec), or as an
override. Worked example: 11 Spatial Kerr; crystals: 08 radial SHG.
model |
representation | cost | use for |
|---|---|---|---|
"none" |
1-D (default) | 1x | everything where the beam stays Gaussian and its size is known |
"radial" |
cylindrical symmetry, quasi-discrete Hankel transform of order 0 (Guizar-Sicairos & Gutiérrez-Vega), points radial samples |
~points x | self-focusing, Kerr lensing, spatial depletion in SHG/OPA, M² of round beams |
"cartesian" |
x-y grid, 2-D FFT, nx x ny |
~nx·ny x | walk-off, non-collinear beams, decentred or elliptical beams, anything not round |
Options (all optional):
| key | meaning | default |
|---|---|---|
points |
radial samples (radial); also nx = ny for cartesian if those are omitted | 64 |
nx, ny |
cartesian samples | points |
radiusM |
radial grid extent | extentFactor x the largest expected radius |
halfWidthM (halfWidthXM, halfWidthYM) |
cartesian half-width | as above |
extentFactor |
grid extent / expected beam radius | 3.5 |
paraxial |
use the paraxial diffraction operator −k⊥²/2k instead of the exact √(k² − k⊥²) − k | false |
transport |
how a profile is brought in from before (below): auto, exact, rescale, beam |
auto |
polarization |
auto (vector when the beam carries a y component), scalar, vector: Jones fields on the grid (below) |
auto |
equation |
nonlinear coupling: envelope (paraxial) or uppe (n ω / c k_z per plane wave) |
envelope |
initialProfile |
the field at the first spatial element (below) | from the element's beam |
absorbingBoundary |
linear elements: absorb the light that leaves the grid -- true (outer 15 %) or {"widthFraction": 0.15} (below) |
false |
spectralSamples |
frequencies at which the carried profile is stored (and linear elements propagate it); fewer is faster and smaller for narrowband pulses | 48 |
Resolution rule of thumb: about 15-20 samples across the smallest beam radius the beam reaches, and an extent of 3-4x the largest radius. The Hankel grid is not uniform (denser near the axis); its k-space extent is j_points/radius. Its orthogonal transform matrix depends only on the number of points and is built once per size (about 1 s for 2048 points) and shared by every element and run in the process.
initialProfile makes the field at the first spatial element (or at an element with "transport": "beam"):
| key | meaning |
|---|---|
kind |
gaussian (default), superGaussian (field exp(−(r²/w²)^order), order 4), flatTop (super-Gaussian of order 10), tophat (hard edge: uniform inside r = w, or |x| ≤ w_x, |y| ≤ w_y; edge cells carry the area fraction inside), or a structured beam below |
geometry |
round (default: a function of x²/w_x² + y²/w_y²) or square (separable in x and y: square or rectangular beams; cartesian grids) |
radiusM, radiusXM, radiusYM |
1/e² radius (tophat: edge radius / half-width); replaces the size of the element's beam, so the element needs no beam |
radiusOfCurvatureM |
wavefront radius of curvature (negative: converging); replaces the beam's |
decenterXM, decenterYM, tiltXRad, tiltYRad |
offset and tilt of the field |
ripple, noise |
seeds of small-scale structure (cartesian; below) |
Structured beams (w is radiusM or the element's beam):
kind |
field | keys | grid |
|---|---|---|---|
hermiteGauss |
H_m(√2 x/w) H_n(√2 y/w) exp(−(x² + y²)/w²) | modeX, modeY |
cartesian (radial only for 0, 0) |
laguerreGauss |
u^(|l|/2) L_p^|l|(u) exp(−u/2) exp(i l φ), u = 2r²/w² | modeP, modeL |
cartesian; radial for modeL = 0 |
besselGauss |
J0(k_r r) exp(−r²/w²), k_r = k sin(angle) at the centre of the spectrum | coneAngleRad or radialWavenumberPerM |
both |
airy |
Ai(x/x0) exp(a x/x0) x the same in y, x0 = radiusM |
truncation (a, 0.1) |
cartesian; "ny": 1 for one transverse dimension |
measured |
a near field from an NPZ: x_m, y_m ([x][y] maps) or r_m, with fluence or amplitude, optionally phase_rad |
file (relative to the file that names it), background (a number, or "auto": the median of the map's edge; fluence only) |
both (x-y maps: cartesian) |
A measured map is sampled bilinearly and is zero outside its axes; its size is the map's, so no radiusM is needed if the grid is explicit. It may also be
an 8- or 16-bit grayscale PNG with pixelSizeM. threshold zeroes fluence below that fraction of the peak, and center moves the fluence centroid to the axis.
Gouy phases are not added: the field is the mode at its waist (with radiusOfCurvatureM, the curvature of that plane).
dA/dz = [ L_1D(ω) + i (√(k(ω)² − k⊥²) − k(ω)) ] A + N(A) A(r or x,y, ω), √(W/m²)
L_1D: exact dispersion and loss as in 1-D. The diffraction term uses the material index at each frequency (chromatic diffraction).- N: Kerr (n2 I, with the local intensity), delayed Raman, self-steepening and any user time responses -- the same
physics as 1-D, evaluated point by point across the beam (
ypolarisation is propagated linearly). - The integrator and its error control are the same as in 1-D; one extra FFT/Hankel transform pair per stage.
- Diffraction is unitary (the Hankel matrix is made exactly orthogonal): linear spatial propagation conserves
energy to rounding. If more than 0.01 % of the output energy lies in the outer 15 % of the grid the engine warns
("reached the edge of the transverse grid -- enlarge transverse.radiusM"): beyond that the field would wrap
(cartesian) or reflect (radial).
absorbingBoundaryremoves that light instead (below). - Linear elements (no Kerr or other nonlinearity, no position-dependent properties, a scalar field) propagate
only the carried profile at its stored frequencies: the operator factorises into the 1-D dispersion of the
spectrum and the diffraction exp(i (k_z − k) L) of the profile. Between the stored frequencies the change of the
spectrum (power lost to evanescent waves or to the absorbing boundary, area-weighted phase) is interpolated, as the
profile is. At the stored frequencies the result equals the full propagation (
DiffractionTests: 10⁻⁴ of the peak). The element'srepresentationsays "linear: the profile is propagated in n steps".
χ(2) crystals with a transverse model propagate all fields this way, each with its own index (and walk-off and non-collinear drift in the cartesian model), coupled locally (Crystals).
With "polarization": "vector" (or auto and a pulse that is not linearly polarised along x) the grid holds both
Jones components, A_x(r, ω) and A_y(r, ω). The Kerr term then is the full isotropic χ(3) tensor (cross-phase
modulation 2/3, coherent coupling 1/3; circular light has 2/3 of the linear self-focusing), the rotational Raman
tensor of molecular gases acts on both components (Gases), and waveplates, polarisers, Faraday
rotators and coatings act on the carried profile with their Jones matrices. Collapsing to 1-D keeps both components.
"equation": "uppe" replaces the paraxial drive of the nonlinear polarisation, i ω n₂ I A / c, by the
unidirectional pulse propagation equation's i ω² P_NL / (2 ε₀ c² k_z(ω, k⊥)) for every plane-wave component. It
matters when the beam is focused tightly or collapses (filaments, Ionisation); for paraxial beams
both agree (test Uppe_AgreesWithEnvelopeForParaxialBeams_AndPlasmaClampsTheFocus).
After a spatial element the pulse carries a transverse profile P(r, ω) = E/A normalised so that the 1-D spectrum A(ω) is exactly the integrated spectrum. The next element decides what to do with it:
- transverse-aware elements use it as is: a spatial bulk/gap/crystal, a
lens(focalLengthM, chromatic if amaterialis given), amirrorwithradiusOfCurvatureM/focalLengthM, and zero-length surfaces. Profiles are moved between different grids by interpolation with the quadratic phase taken out and put back (exact for focusing/diverging beams); between identical grids they are copied. Between its frequency samples the profile is interpolated and renormalised, so E = A·P always carries the exact energy. The note "profile carried exactly from '…'" confirms this. - 1-D elements with a length (a 1-D bulk, a compressor, ...) do not propagate the profile. With
transportautothe profile is kept in shape and rescaled to the next spatial element's beam size (its curvature replaced by that beam's);exactrefuses to proceed through such elements (error),beamdiscards the profile and starts from the next element'sbeam.
Apertures act on a carried profile (below). Without a profile, the aperture clips the beam model as before.
The per-element record then reports beam.radiusXM, radiusYM (second-moment, 1/e² equivalent) and beam.m2
(M² from the second moments in space and spatial frequency, along x on a cartesian grid). Moments are central: a tilted or decentred
beam keeps its size and M². The beamProfile instrument gives the fluence across the beam, m2, and on a 2-D grid m2Y.
An aperture on a carried profile cuts the field; the spatial elements after it diffract what passes:
{ "id": "source", "type": "gap", "lengthM": 0, "material": "Vacuum",
"transverse": { "model": "radial", "points": 1024, "radiusM": 12e-3, "absorbingBoundary": true,
"initialProfile": { "kind": "tophat", "radiusM": 3e-3 } } },
{ "id": "iris", "type": "aperture", "radiusM": 1e-3 },
{ "id": "screen", "type": "gap", "lengthM": 0.97, "material": "Vacuum", "transverse": { ...the same grid... } }- Openings:
radiusM(round),halfWidthXM/halfWidthYM(rectangle; cartesian grids -- on a radial grid it becomes a circle of radius min(half-widths), with a warning),softRadiusM+order(field transmission exp(−(r/a)^2n)), anddecenterXM/decenterYM(cartesian). The profile is masked at every stored frequency and the spectrum is scaled by the frequency-resolved transmission, booked asclip; the element reportstransmissionandapertureSamples. - Hard edges between samples: each grid cell on the edge transmits the fraction of its area inside the opening
(annuli on a radial grid, pixels on a cartesian one). A 0/1 mask moves the edge by up to a cell; the averaged mask puts it
in the right place, and the patterns converge quickly with the sampling (
DiffractionTests). Light diffracted beyond the grid's largest angle is not represented, so the transmission converges to the geometric value from below, by about dx / 3a times the edge fluence (0.2 % for a Gaussian cut at half its radius with 85 samples, 1.6 % for a square flat-top with 21). - Resolution: the engine warns when the opening spans fewer than 10 samples across its radius / half-width. The pattern's finest feature is the outermost Fresnel zone at the edge, λz / 2a wide; the grid must be several times wider than the pattern (the first dark ring of a round iris of radius a lies at r = 0.61 λz/a, and the rings beyond carry a few % of the energy).
absorbingBoundary: a hard edge sends light to large angles. On a radial grid it reflects at the edge and focuses back onto the axis; on a cartesian one it wraps around. With"absorbingBoundary": truea layer in the outer 15 % of the grid (widthFraction) multiplies the field by exp(−5 u⁴) (u from 0 at the layer's inner edge to 1 at the grid's edge) after each step, and a linear element is split into steps short enough for the steepest wave on the grid to cross the layer within one. The absorbed energy is booked asgrid boundaryand reported asabsorbedAtBoundaryJ/absorbedAtBoundaryFraction; the edge warning is not given. In nonlinear elements the layer is an absorption in the same region (power 10 nepers across the layer for a wave at a third of the grid's steepest angle), stepped with the propagation, booked the same way. Such an element stays on the CPU.- Narrowband pulses:
spectralSamples(default 48) sets how many frequencies the profile holds. A ps pulse needs a few; a 2-D grid's memory scales with them.
Worked example and checks: Example 30 (Gaussian, round flat-top, uniform and square beams at N = 3, 2, 1 and in the far zone, against the Fresnel integral) and Tutorial 6.
initialProfile.kind = "tiles" builds the input beam from N coherent sub-beams (a tiled-aperture amplifier array, or any laser
array): the complex sum of the tiles' fields, normalised to unit energy at every stored frequency, so the tiles share the pulse energy.
It needs a cartesian grid; the grid sets the size (no radiusM or beam model is needed).
| key | default | meaning |
|---|---|---|
tileRadiusM |
required | 1/e² radius of one tile (hard radius for tileShape tophat) |
tileShape |
gaussian |
gaussian, tophat or superGaussian (order) |
layout |
square |
square (countX × countY, or count × count), hex (rings: 1, 7, 19, 37, 61 tiles, ordered by j then i in axial coordinates), ring (count tiles on a circle of ringRadiusM), or explicit positionsM [[x, y], ...] |
pitchM |
centre-to-centre distance (square, hex) | |
amplitudes, pistonRad, tileTiltXRad, tileTiltYRad, delaysS |
1, 0, 0, 0, 0 | a number, or one value per tile |
pistonRmsRad, seed |
0, 1 | random Gaussian piston error added to pistonRad |
The piston is the phase at the carrier; a delay τ adds (ω − ω_centre) τ (the envelope moves, spectral factor e^{+iωτ}). The profile is
stored at spectralSamples frequencies, so they must resolve e^{iωτ}: keep (spacing of stored frequencies) × τ under about 0.5 rad
(24 samples for 30 fs pulses and delays to 30 fs, 96 for 60 fs).
Reference behaviour (TileSourceTests): N in-phase equal tiles give an on-axis far-field peak N × that of one beam carrying all the energy
(N² for equal power per tile), 2 %; the far field has lobes at λ/pitch, 1.5 %; with piston errors the peak is |Σ e^{iφ}|²/N² of the in-phase
peak (exact for the draw; the expectation over draws is 1/N + (1 − 1/N) e^{−σ²}), 1.5 %; a delay mismatch between two Gaussian pulses of FWHM T costs
(1 + e^{−ln2 (τ/T)²}) / 2 of the energy on axis, 3 %; hex, ring and explicit layouts place the tiles as stated, exact; a tilted tile focuses at
F θ, 1 %; a tophat tile on a line grid has the slit far field (first zero at F λ / 2a), one grid step; amplitudes [1, 0.5] give 0.9 of the
equal-amplitude peak, |Σ a|² / (N Σ a²), 1 %; delaysS = τ > 0 makes the tile's pulse arrive later (its field moves by +τ in the frame,
as a delay element moves the arrival time by +τ), 3 %.
Two settings keep long chains on large grids (a full 1920 × 1080 SLM panel, many stored frequencies) within memory. Neither changes a result.
transverse.frequencyBlock(default 0 = all at once): a linear spatial element propagates the stored frequencies in blocks of this many. The stored frequencies are independent in a linear element, so the result does not depend on the block size (tested to 1e-12); the propagator then holds one block instead of all K frequencies, and a work buffer of one block is added, so blocks under K/2 save memory (B = 1 saves almost one profile). The element notes "streamed over frequency".outputs.keepProfiles"all"(default) or"used": withused, the profile after an element is dropped as soon as the next element has run (or, if the next element passes the same profile on, once the beam has moved past), unless an instrument reads it (an instrument point or its element forid#port, the element before it, or the last element). The 1-D pulse at every element stays; probes keep their 1-D snapshots. A prefix-cached study keeps every profile of its nominal run, because the points resume from it. Measured on a 1024 × 1024 grid with 16 stored frequencies and five elements: 1587 MB held after the run withall, 336 MB withused(one profile is 268 MB), bit-identical instruments.
Peak memory during a run is set by the .NET garbage collector, which returns large freed arrays late: on the 1920 × 1080 × 16 case of the
spikes the process peak stayed at 2.7-3.4 GB whatever the block size, so frequencyBlock lowers the live set
but not the reported peak. A 1920 × 1080 single-frequency panel on a 2048 × 1280 grid (modulator, 0.2 m gap, beam profile) takes 1.9 s
and holds 260 MB after the run with keepProfiles: "used", 491 MB with all (slow test FullPanel_1920x1080_SingleFrequency_RunsWithinTheSpikeBudget).
One stored frequency (spectralSamples: 1, the cw / narrow-band fast path) is the bin nearest the energy centroid of the spectrum, i.e.
the carrier (before 2026-11-10 it was the low edge of the band, 3e-6 off for a 1 ns pulse); if no light falls there (two colours, a notch),
it is the nearest bin above 1e-3 of the peak. A cw source's single bin is snapped to its grid
lattice: the run notes print its wavelength (1.2e-6 from the request at 1030 nm).
Cylindrical lens: lens with focalLengthXM and focalLengthYM; give the axis without power a very long focal length (1e30). Tested:
x focuses to λ f / (π w) while y diffracts freely, 2 % (CylindricalLens_FocusesOneAxisOnly).
A mask element multiplies the carried profile at every stored frequency. Without a profile it warns and does nothing; the tracked
Gaussian beam does not change.
{ "id": "zp", "type": "mask", "kind": "zonePlate", "focalLengthM": 0.1, "binaryPhase": true }kind |
transmission | keys | grid |
|---|---|---|---|
axicon |
exp(−i k r sin(angle)): a Bessel core of first zero 2.405 / (k sin(angle)) behind it | coneAngleRad (the deflection) |
both |
zonePlate |
Fresnel zones r² / (λ f): even zones open, odd zones closed, or shifted by π with binaryPhase |
focalLengthM, designWavelengthM (default the carrier), binaryPhase |
both |
vortex |
exp(i l φ) | charge |
cartesian |
slits |
count slits along x |
widthM, separationM, heightM (default unlimited) |
cartesian |
file |
an NPZ map: x_m, y_m or r_m, amplitude, optionally phase_rad (at designWavelengthM; the phase scales with the frequency, as an optical path does) |
file |
both (x-y maps: cartesian) |
decenterXM, decenterYM move the mask (cartesian). Each cell takes the average of the mask over the cell, so edges fall between samples as for
apertures; axicon and vortex masks keep unit magnitude (a smooth phase). A binaryPhase zone plate does not: a cell that holds both a 0 and a π zone averages to less than unit magnitude, and the missing power is spatial frequency the grid cannot carry. On average a cell split by an edge transmits 1/3 of its power, so the loss is 2/3 of the power in the edge cells, T ≈ 1 − (2/3) Σ_edges 4 r dr / w² exp(−2r²/w²) for a Gaussian beam of radius w; it falls in proportion to the grid step (2.3 %, 0.9 %, 0.6 % for 512, 1024, 2048 radial points with w = 1 mm, f = 0.2 m). Lost energy is booked as clip, and the element reports transmission;
a zone plate also reports outerZoneWidthM and warns below 3 samples per zone across the beam. Maps come from NPZ files: convert images in Python
(np.savez("mask.npz", x_m=x, y_m=y, amplitude=a)).
A top-level "coherence" in the run spec makes the source a Gaussian-Schell beam: an incoherent sum of Hermite-Gauss modes with weights tⁿ, t = (M² − 1) / (M² + 1),
and mode radius w₀ / √M² (Starikov & Wolf, J. Opt. Soc. Am. 72, 923 (1982)).
"coherence": { "m2": 2.5, "modes": 40 } // also "dimensions": 1 | 2 (1 for a grid with ny = 1), "element": the element whose initialProfile they replace- w₀ comes from
initialProfile.radiusM(or the element'sbeam); the grid must be cartesian and wide enough for the highest mode (radius ~ w₀ √(2 n / M²)). - Each mode is one complete run with its share of the energy, so the run takes as many times longer as there are modes (modes below 10⁻⁴ of the energy are skipped). Element
energies and beam radii, and
beamProfilefluences and radii, are the sums. Thecoherenceentry among the instruments reports the modes andcapturedWeight. Other outputs, such as spectra andresult.npzfields, belong to the lowest mode. - It is a run mode around the engine: elements know nothing of it. In a nonlinear element each mode propagates alone (a warning says so), so coupling between modes is missing.
Example: 31 Cavity and coherence. Check: w(z) = w₀ √(1 + (M² z / z_R)²) to 2 %.
A beam above the critical power breaks up into filaments from its small-scale structure. The cartesian model ("model": "cartesian") follows
this in 2-D (x, y) when the field starts with a seed:
"transverse": { "model": "cartesian", "nx": 128, "ny": 128, "halfWidthM": 0.6e-3, // 128 x 128 over 4 beam radii (w = 0.3 mm)
"initialProfile": { "kind": "flatTop",
"noise": { "relativeAmplitudeRms": 0.01, "correlationLengthM": 40e-6, "seed": 1 }, // seeded smooth random amplitude (and phaseRmsRad)
"ripple": { "periodM": 67e-6, "relativeAmplitude": 0.01, "angleDeg": 0 } } } // or one sinusoidal ripple: the textbook seednoise:N(x, y) = sqrt(2/K) sum cos(kx x + ky y + phi_k), wavevectors Gaussian with rms 1 / correlation length, unit variance, scaled byrelativeAmplitudeRms(field amplitude; the fluence contrast is twice that) andphaseRmsRad. Reproducible fromseed.ripple: the field amplitude is multiplied by1 + a cos(2 pi (x cos t + y sin t) / period + phase).- The element reports
fluenceContrastIn/fluenceContrastOut(rms / mean of the fluence over the core r < w/2),smallScaleGain(their ratio),peakPowerOverCritical(P / P_cr) and the cutsfluence_in_x,fluence_out_x,x_m(arraysel_<id>_...inresult.npz) along y = 0.
Reference: Bespalov-Talanov. For a plane wave of intensity I with an amplitude ripple of wavenumber K, linearising the envelope equation gives
u' = q v, v' = (2 Delta - q) u with q = K^2 / 2k, Delta = k0 n2 I, hence u(z) = u(0) cosh(g z), g^2 = q (2 Delta - q) (a cosine for
q > 2 Delta): the most unstable ripple, q = Delta, grows as cosh(B), B = Delta L. SmallScaleTests compares the engine with this, integrated over
the Gaussian pulse in time: the fluence ripple after 3 mm at q = Delta and q = 3 Delta agrees to 6 % (observed < 1 %), and without Kerr the
ripple follows the Talbot law 2 eps |cos(q L)|.
Size and speed. The state is nx x ny x N_t complex numbers per field: 128 x 128 x 512 is 134 MB, and the 10 mm example below ran in 57 s on a
laptop CPU (4 cores). 256 x 256 at 2^13 time points is 64 x larger (8.6 GB per field array, several of them held by the solver):
use a short time window and the GPU plugin ("compute": {"backend": "auto"} runs the cartesian model on the device), or reduce
nx and N_t and check that edge warnings stay quiet. The grid must resolve the most unstable ripple (period 2 pi / sqrt(2 k Delta) -- about 90 um
in the example, 9 samples) and the correlation length of the seed; the box (4 beam radii) must hold the beam. Example 29.
The pulse's 1-D spectrum after a spatial element is the spectrum integrated over the beam (exact energy) with
the phase of the area-weighted field. So output_power_w, spectra and instruments describe the whole beam, as a
power meter or a spectrometer with a large integrating aperture would see it; spatially varying chirp or
spectra show up as a reduced temporal Strehl, not in the 1-D phase. The true peak fluence is
elements[i].peakFluenceOutJPerM2 / the beamProfile instrument.
| check | result | test |
|---|---|---|
| Hankel quadrature and round trip | < 1e-12 | SpatialTests.RadialBasis_QuadratureAndTransformAreExact |
| Gaussian beam w(z), radial and cartesian | < 0.5-1 %, M² = 1.00 | SpatialTests.Diffraction_* |
| lens + gap focus vs Gaussian optics | < 2 % | SpatialTests.LensAndGap_* |
| thin Kerr lens phase vs n2 I(r) k L | < 2 % | SpatialTests.ThinKerrLens_* |
| Bespalov-Talanov ripple growth (cosh(g z)), q = Delta and q = 3 Delta; Talbot ripple without Kerr | 6 % (observed < 1 %) | SmallScaleTests.SmallScale_RippleGrowth_*, ..._TheRippleDiffractsAsTheTalbotEffect |
| seeded noise reproducible and linear in its amplitude; P / P_cr | 2 % | SmallScaleTests.SmallScale_Seeded*, ..._ReportsPeakPowerOverCritical |
| beamProfile instrument vs w(z) | < 1 % | Chi2Tests.BeamProfileInstrument_* |
| plane wave through a round iris: on-axis 4 F sin²(πN/2) at N = 3, 2, 1 | 1.5 % (dark centre < 0.4 % of 4 F) | DiffractionTests.UniformBeamThroughRoundIris_* |
| Gaussian / flat-top through an iris vs the Fresnel integral, whole profile; transmission 1 − exp(−2a²/w²) | 1 % of the peak (observed 0.02-0.5 %); 0.3 % | DiffractionTests.GaussianAndFlatTopThroughRoundIris_* |
| square flat-top through a square aperture vs the separable Fresnel integral (512²) | 2 % of the peak (observed 0.4-0.7 %) | DiffractionTests.SquareBeamThroughSquareAperture_* |
| far zone: first Airy ring 0.61 λz/a, first sinc² zero λz/2a | 2 % | DiffractionTests.FarField_* |
| hard edge on coarse / fine grids; warning under 10 samples | dx / 2a | DiffractionTests.HardAperture_* |
| absorbing boundary: wrapped light removed, loss budget closes | 2 % / 1e-6 | DiffractionTests.AbsorbingBoundary_* |
| linear profile-only path vs full-field path | 1e-4 of the peak | DiffractionTests.LinearSpatialGap_* |
| Hermite-Gauss and Laguerre-Gauss radii w(z) √(2m + 1), dark vortex axis, LG node | 0.5-5 % | BeamSourceTests.HermiteGauss_*, LaguerreGauss_* |
| Bessel-Gauss core unchanged over 6 Rayleigh ranges of its spot (Durnin); Airy lobe on x = z² / 4k²x0³ (Siviloglou & Christodoulides) | 2 %, 3 % | BeamSourceTests.BesselGauss_*, AiryBeam_* |
| zone plate on the axis 4 N² I0 (amplitude), 16 N² I0 (binary phase); axicon core; Young's fringes | 5 %, 2 %, 2 % | BeamSourceTests.ZonePlate_*, Axicon_*, DoubleSlit_* |
| radial χ(2) vs 1-D Gaussian overlap (low conversion) | < 1 % | Chi2Tests.Shg_RadialModelAgrees* |
{ "id": "plate", "type": "bulk", "material": "FusedSilica", "lengthM": 10e-3, "beam": { "kind": "gaussian", "w0M": 100e-6 }, "transverse": { "model": "radial", "points": 96, "radiusM": 0.45e-3 } }