Materials and mirrors#
Use material data to describe a nonlinear medium or to propagate a retrieved pulse through a known optical path. Use mirror data to apply or remove the spectral phase and attenuation of reflections during post-processing. These operations have different roles: the forward model fits a measurement, while dispersion tuning explores the pulse after retrieval.
The optical data come from five sources:
built-in materials (fused silica, BK7, CaF₂, sapphire, …), from optmat;
custom materials you register yourself, from a formula or measured data;
gases (air, helium, argon, …), also from optmat, at a chosen pressure and path length;
chirped mirrors, from bundled data or your own measurement;
beam-path coating mirrors (Si, MgF₂/Al) to back-propagate out of the pulse.
Each dataset has a valid wavelength range, and mirror data are given for a polarisation and angle of incidence.
Built-in materials#
croak takes its refractive indices from optmat, a curated library of optical constants. Each built-in name uses optmat’s recommended model of that material. optmat records the model’s sources, its validity range and why it was chosen. Its indices are absolute (relative to vacuum). The uniaxial crystals use their ordinary index, as a plate cut with the optic axis along its normal shows at normal incidence.
Name |
Material |
optmat model |
|---|---|---|
|
fused silica |
Franta et al.’s universal dispersion model, refitted to absolute refractometry (24.8 nm – 125 µm) |
|
SCHOTT N-BK7 |
SCHOTT’s catalogue Sellmeier formula |
|
calcium fluoride |
Daimon & Masumura (2002), at 20 °C |
|
barium fluoride |
Malitson (1964) |
|
magnesium fluoride, ordinary ray |
Dodge (1984) |
|
sapphire, ordinary ray |
Weber (2003) |
|
crystalline silicon (opaque below 1.1 µm) |
Franta et al.’s model refitted at 20 °C |
import croak
print(croak.materials.MATERIALS) # SiO2, BK7, CaF2, BaF2, MgF2, Al2O3, Si
print(croak.materials.material_model("BK7").citation) # the model's source
croak.materials.material_model() returns the optmat model behind a name, with
its name, citation, wavelength_range and identity, for recording what a
result was computed with.
Pass a name as material= to any solver or to maketrace() to
propagate through a slab (see Dispersive retrieval),
or to apply_dispersion() to add or remove a known thickness
of glass:
# add 2 mm of fused silica to a retrieved spectrum
ew = croak.apply_dispersion(g.omega, res.omega0, res.spectrum,
material_thicknesses={"SiO2": 2e-3})
Names are resolved case-insensitively. The raw index and propagation constant are
available directly as croak.materials.refractive_index() and
croak.materials.beta().
The frequency grid of a retrieval spans far beyond any model’s stated range, where
the pulse carries no energy. There the models are extrapolated without a
warning. A grid point at zero or negative frequency has no index: it gives nan,
and beta() sets its phase to zero.
Earlier croak versions also offered SiO2-Franta, Franta et al.’s tabulated fused
silica. optmat’s SiO2 is Franta’s model refitted to absolute measurements, so the
name now resolves to SiO2, and saved sessions that use it load unchanged.
Custom materials#
To use a material croak doesn’t ship, register a callable n(wavelength_m) that
returns the real refractive index (returning nan outside its validity range so
the propagation constant is masked there):
import numpy as np
def n_glass(lam_m): # a Cauchy formula, λ in metres
lam_um = lam_m * 1e6
return 1.5046 + 0.00420 / lam_um**2
croak.materials.register_material("MyGlass", n_glass)
A registered material is then usable everywhere a built-in name is — including
material="MyGlass" in croak.retrieve(). Registered names take precedence
over the built-in set and are resolved case-insensitively; remove one with
croak.materials.unregister_material().
Measured data are noisy, and the dispersion quantities differentiate \(n(\lambda)\)
up to third order, which amplifies that noise. Build the callable from samples with
croak.materials.make_tabulated_material(), which fits a smoothing spline
(in log-wavelength, with its smoothing chosen by generalised cross-validation) and
returns nan outside the sampled range. croak.materials.read_nk_csv() reads
the stacked wl,n / wl,k CSV layout some optical-constants databases export:
lam_m, n = croak.materials.read_nk_csv("my_glass.csv")
n_func, (lo, hi) = croak.materials.make_tabulated_material(lam_m, n)
croak.materials.register_material("MyMeasuredGlass", n_func)
Gases#
A gas is dispersive like a solid, but its refractive index depends on the gas
density — and hence on pressure and temperature — as well as on wavelength.
croak.gases.gas_refractive_index() builds an n(wavelength_m) callable for a
gas at a chosen pressure (bar) and temperature (K, defaulting to 20 °C). The
dispersion is optmat’s recommended model of the gas’s polarisability \(\alpha\); for
air, optmat’s dry air, the sum of its constituents’ models. The index follows from
the Lorentz–Lorenz relation at the real-gas density \(N = P/(Z k_B T)\), with the
compressibility factor \(Z\) of the gas’s reference equation of state (CoolProp,
through optmat):
import croak
# 1.5 bar of helium, at room temperature
n_he = croak.gases.gas_refractive_index("He", 1.5)
croak.materials.register_material("He-cell", n_he)
# add 30 cm of that helium to a retrieved spectrum (path length is the "thickness")
ew = croak.apply_dispersion(g.omega, res.omega0, res.spectrum,
material_thicknesses={"He-cell": 0.30})
Because a gas is registered as an ordinary material, it works everywhere a
material name does — including the dispersion auto-compressor, which sweeps the
path length at the set pressure. The available gases are in
croak.gases.GASES: Air and optmat’s species (He, Ne, Ar, Kr,
Xe, H2, D2, N2, O2, CH4, SF6, N2O, CO2, O3, CO). The earlier
name Helium still resolves to He. croak.gases.gas_model() returns the
optmat model behind a name. The real gas matters most for the heavy gases: at 20 °C
and 10 bar its index differs from the ideal gas’s by 0.5 % for helium and air, 6 %
for CO₂ and xenon and 15 % for SF₆. A state where the gas would condense raises a
ValueError.
The GUI’s Gases group selects the gas, pressure and path length. Pressure ranges from 0–10 bar in 1 mbar steps; path length ranges to ±999 cm. Negative length removes the modelled phase, and Auto optimises over the allowed range.
Chirped mirrors#
Chirped (dispersive) mirrors apply a designed spectral phase per reflection. Their
dispersion is data-dependent, so croak.dispersion.MIRRORS starts empty and
you populate it with register_mirror(). Once registered, a
mirror is applied by number of bounces through mirror_bounces.
Built-in mirror data#
Several mirrors are bundled (ported from Luna.jl), keyed by name in
croak.mirrors.BUILTIN_MIRRORS. The mirrors submodule is not pulled into
the top-level namespace by import croak, so import it explicitly:
from croak import mirrors
print(list(mirrors.BUILTIN_MIRRORS))
# ['PC70', 'HD59', 'PC147', 'PC1611', 'PC1821', 'HD120', 'ThorlabsUMC']
croak.mirrors.load_builtin() reads the data and returns a per-bounce
phase_fn, an application-ready amplitude (reflectivity) amp_fn and the
mirror’s valid wavelength range. Register both, then add bounces when tuning;
each bounce applies the phase and attenuates by the measured reflectivity:
phase_fn, amp_fn, valid_range = mirrors.load_builtin("PC70")
croak.dispersion.register_mirror("PC70", phase_fn, amp_fn)
# compress a retrieved pulse with 6 bounces off the PC70 pair
compressed = croak.apply_dispersion(g.omega, res.omega0, res.spectrum,
mirror_bounces={"PC70": 6})
For good compressor mirrors R ≳ 99 % in band, so the loss is small; pass only
phase_fn to register_mirror for a phase-only calculation.
mirror_bounces can be combined with gdd/tod/fod and material_thicknesses
in a single apply_dispersion() call, and is one of the knobs
the dispersion-tuning auto-compressor can
sweep.
Custom mirrors#
For a mirror you measured yourself, build the phase function from arrays with
croak.mirrors.mirror_from_arrays() — either a sampled spectral phase
(mode="phase") or GDD vs wavelength (mode="gdd", double-integrated for you).
The overall group delay is removed automatically (it only shifts the pulse in time):
lam_m = ... # wavelength samples (m)
gdd = ... # GDD per bounce (s²) at each wavelength
phase_fn, valid_range = mirrors.mirror_from_arrays(lam_m, gdd, mode="gdd")
croak.dispersion.register_mirror("MyMirror", phase_fn)
Beam-path coating mirrors (back-propagation)#
Simple beam-path optics — bare silicon, MgF₂-coated aluminium — are not used to
compress a pulse but rather sit in the measurement path; you usually want to
remove (back-propagate) their dispersion to recover the pulse upstream. These
are keyed in croak.mirrors.BUILTIN_COATINGS (by coating and angle of
incidence) and loaded with croak.mirrors.load_coating(), which returns the
per-bounce phase, an amplitude (reflectivity) function for the chosen
polarisation, and the valid range:
phase_fn, amp_fn, valid_range = mirrors.load_coating("MgF2/Al @ 0°", "s")
croak.dispersion.register_mirror("MgF2Al", phase_fn, amp_fn) # phase + amplitude
phase_fn, amp_fn, _ = mirrors.load_coating("Si @ 45°", "s")
croak.dispersion.register_mirror("Si45", phase_fn, amp_fn)
A negative bounce count removes the mirror: apply_dispersion then applies
exp(-iNφ) and divides the spectrum by r(λ)^N (the net amplitude gain is
clamped so a small reflectivity cannot blow it up). Several registered coatings
back-propagate in one call:
upstream = croak.apply_dispersion(
g.omega, res.omega0, res.spectrum,
mirror_bounces={"MgF2Al": -6, "Si45": -2},
)
Back-propagation cannot recover spectral content where the reflectivity is close to zero; the clamp limits the noise it amplifies there. Keep the spectrum inside the coating’s valid wavelength range.
At normal incidence the s and p data coincide; at oblique incidence they
differ, so pick the polarisation that matches your beam.
See also#
Dispersion tuning — compressing a pulse by removing residual dispersion (Taylor, material and mirror).
The dispersive forward model — retrieving through a material slab.
croak.materials,croak.gases,croak.mirrorsandcroak.dispersionin the API reference.