physicskit.optics#
physicskit.optics: classical and quantum optics – rays, waves, Gaussian beams, and quantized light.
Typical usage:
import physicskit as pk
import numpy as np
beam = pk.optics.GaussianBeam(wavelength=1.064e-3, w0=0.05)
system = pk.optics.OpticalSystem([
pk.optics.OpticalElement(pk.optics.free_space(1.0), length=1.0),
pk.optics.OpticalElement(pk.optics.thin_lens(0.5)),
])
q_out = pk.optics.propagate_q(beam.q_parameter(0.0), system.system_matrix())
physicskit.optics.ray– geometric ray optics and ABCD matrices: free space, thin/thick lenses, curved and flat interfaces, spherical mirrors, GRIN media, cascaded optical systems, and laser-cavity stability.physicskit.optics.wave– scalar wave optics: circular/slit apertures and Fraunhofer, Fresnel, and angular-spectrum diffraction.physicskit.optics.gaussian– Gaussian beam propagation via the complex beam parameter \(q\), Hermite-Gaussian and Laguerre-Gaussian higher-order modes, and \(M^2\) beam-quality propagation.physicskit.optics.quantum_optics– quantum optical states (Fock, coherent, squeezed) in a truncated photon-number basis, Wigner quasi-probability distributions, and the Jaynes-Cummings model of a two-level atom coupled to a quantized cavity mode.physicskit.optics.lasers– laser rate equations (threshold, gain clamping, relaxation oscillations) and the Maxwell-Bloch (Haken-Lorenz) single-mode laser with its chaotic second threshold.physicskit.optics.thin_films– interference in layered media by the characteristic-matrix method: antireflection coatings, Bragg mirrors, Fabry-Pérot cavities and the band gaps of 1D photonic crystals.physicskit.optics.nonlinear– second-harmonic generation: the coupled-amplitude equations, phase mismatch, quasi-phase matching and birefringent phase matching.physicskit.optics.visualizers– ray-trace, beam-envelope, diffraction-pattern, and interactive Wigner-surface plots.
- class physicskit.optics.GaussianBeam(wavelength, w0, z0=0.0)[source]#
Bases:
objectA fundamental (\(\mathrm{TEM}_{00}\)) Gaussian laser beam.
- Parameters:
- property divergence_angle#
Far-field half-angle divergence, \(\theta = \lambda/(\pi w_0)\).
- gouy_phase(z)[source]#
Gouy phase at axial position
z, \(\zeta(z) = \arctan[(z-z_0)/z_R]\).- Parameters:
z (float or array_like) – Axial position(s).
- Returns:
float or ndarray
- q_parameter(z)[source]#
Complex beam parameter at axial position
z, \(q(z) = (z-z_0) + i z_R\).- Parameters:
z (float) – Axial position.
- Returns:
complex
- radius_of_curvature(z)[source]#
Wavefront radius of curvature at axial position
z.\[R(z) = (z - z_0)\left[1 + \left(\frac{z_R}{z - z_0}\right)^2\right]\]with \(R(z_0) = \infty\) (the wavefront is flat at the waist), handled explicitly rather than relying on the indeterminate-form arithmetic of the formula above.
- Parameters:
z (float or array_like) – Axial position(s).
- Returns:
float or ndarray
- property rayleigh_range#
Rayleigh range, \(z_R = \pi w_0^2/\lambda\).
- class physicskit.optics.JaynesCummingsModel(omega_c, omega_a, g, cutoff=10)[source]#
Bases:
objectThe Jaynes-Cummings model: a two-level atom coupled to a single quantized cavity mode.
In the rotating-wave approximation, the Hamiltonian is
\[\hat H = \omega_c\, \hat a^\dagger \hat a \otimes \hat I + \frac{\omega_a}{2}\, \hat I \otimes \hat\sigma_z + g\left(\hat a \otimes \hat\sigma_+ + \hat a^\dagger \otimes \hat\sigma_-\right)\]with the cavity (dimension
cutoff) as the left tensor factor and the atom (dimension 2, basis \(\{\lvert e\rangle, \lvert g\rangle\}\), so \(\hat\sigma_z = \mathrm{diag}(+1,-1)\), \(\hat\sigma_+ = \lvert e\rangle\langle g\rvert\), \(\hat\sigma_- = \lvert g\rangle\langle e\rvert\)) as the right factor, i.e.H = kron(H_cav, H_atom)terms throughout. A combined basis state \(\lvert n, e/g\rangle\) therefore sits at flat index2*n(excited) or2*n+1(ground) of the2*cutoff-dimensional Hilbert space.- Parameters:
- evolve(psi0, t_array)[source]#
Time-evolve a state under the Jaynes-Cummings Hamiltonian.
Diagonalizes \(\hat H\) once and reconstructs \(\lvert\psi(t)\rangle = e^{-i\hat H t}\lvert\psi_0\rangle\) at every requested time from the eigendecomposition, which is much faster than exponentiating \(\hat H\) separately for each
t.- Parameters:
psi0 (ndarray of shape (2*cutoff,)) – Initial state.
t_array (ndarray of shape (T,)) – Times at which to evaluate the evolved state.
- Returns:
ndarray of shape (T, 2*cutoff) – Complex states \(\lvert\psi(t)\rangle\) at each requested time.
- excited_state_population(t_array, n_photons=0)[source]#
Atomic excited-state population \(P_e(t)\) starting from \(\lvert e, n\rangle\).
Starts from the initial state \(\lvert e, n_\text{photons}\rangle\) (atom excited,
n_photonsphotons in the cavity) and returns\[P_e(t) = \sum_n \left\lvert \langle e, n \rvert \psi(t)\rangle \right\rvert^2.\]On resonance (\(\omega_c = \omega_a\)) with
n_photons=0, this reduces to the textbook vacuum Rabi formula \(P_e(t) = \cos^2(gt)\).- Parameters:
- Returns:
ndarray of shape (T,) – Real-valued excited-state population at each time.
Examples
>>> import numpy as np >>> jc = JaynesCummingsModel(omega_c=1.0, omega_a=1.0, g=0.5, cutoff=10) >>> t = np.array([0.0, np.pi / 2 / 0.5]) >>> Pe = jc.excited_state_population(t, n_photons=0) >>> np.round(Pe, 6) array([1., 0.])
- hamiltonian()[source]#
The Jaynes-Cummings Hamiltonian as a dense matrix.
- Returns:
ndarray of shape (2*cutoff, 2*cutoff) – The Hamiltonian \(\hat H\), in the
cavity (x) atombasis ordering documented on the class.
Examples
>>> import numpy as np >>> jc = JaynesCummingsModel(omega_c=1.0, omega_a=1.0, g=0.1, cutoff=3) >>> H = jc.hamiltonian() >>> H.shape (6, 6) >>> bool(np.allclose(H, H.conj().T)) True
- class physicskit.optics.LaserRateEquations(pump=2.0, tau=1.0, tau_c=0.01, B=1.0, beta=0.0)[source]#
Bases:
objectSingle-mode laser rate equations for the inversion \(N\) and photon number \(q\).
\[\frac{dN}{dt} = P - \frac{N}{\tau} - BNq, \qquad \frac{dq}{dt} = BNq - \frac{q}{\tau_c} + \beta\frac{N}{\tau},\]with pump rate \(P\), upper-level lifetime \(\tau\), cavity photon lifetime \(\tau_c\), stimulated-emission coefficient \(B\), and fraction \(\beta\) of spontaneous emission going into the lasing mode (Siegman, Lasers, Eqs. 24.8-24.9; Milonni and Eberly, Laser Physics, 2010, Sec. 6.3). Gain equals loss at the threshold inversion \(N_\mathrm{th} = 1/(B\tau_c)\), reached at pump \(P_\mathrm{th} = N_\mathrm{th}/\tau\) – the condition first formulated by Schawlow and Townes (Phys. Rev. 112, 1940 (1958)). For \(\beta = 0\), above threshold the inversion clamps at \(N_\mathrm{th}\) and every extra pumped excitation becomes a photon: \(q = \tau_c(P - P_\mathrm{th})\).
- Parameters:
Examples
>>> laser = LaserRateEquations(pump=300.0, tau=1.0, tau_c=0.01, B=1.0) >>> laser.threshold_pump 100.0 >>> N, q = laser.steady_state() >>> round(N, 10), round(q, 10) (100.0, 2.0)
- integrate(t_max, dt, N0=0.0, q0=1.0)[source]#
Integrate the rate equations with RK4 from \((N_0, q_0)\) at \(t = 0\).
A small seed \(q_0 > 0\) stands in for spontaneous emission when \(\beta = 0\) (with no photons, stimulated emission never starts).
- relaxation_oscillation()[source]#
Frequency and damping rate of the relaxation oscillations (\(\beta = 0\), above threshold).
Linearizing about the lasing steady state, with pump ratio \(r = P/P_\mathrm{th}\), perturbations evolve as \(e^{-\Gamma t}\cos\Omega t\) with
\[\Gamma = \frac{r}{2\tau}, \qquad \Omega = \sqrt{\frac{r - 1}{\tau\tau_c} - \Gamma^2}\](Siegman, Eq. 25.14).
- Return type:
- Returns:
Omega, Gamma (float)
- Raises:
ValueError – Below threshold, or if the oscillations are overdamped.
- steady_state()[source]#
Stationary inversion and photon number \((N_s, q_s)\).
Setting both derivatives to zero gives \(q = \tau_c[P - (1-\beta)N/\tau]\) and the quadratic \(\frac{(1-\beta)B\tau_c}{\tau}N^2 - \left(PB\tau_c + \frac1\tau\right)N + P = 0\), whose smaller root is the physical one. For \(\beta = 0\) it is \((P\tau, 0)\) below threshold and \((N_\mathrm{th}, \tau_c(P - P_\mathrm{th}))\) above.
- class physicskit.optics.MaxwellBloch(kappa=3.0, gamma_perp=1.0, gamma_par=0.25, r=2.0)[source]#
Bases:
objectSemiclassical single-mode laser: the resonant Maxwell-Bloch equations in Haken’s form.
For the (real, resonant) field \(E\), atomic polarization \(P\), and inversion \(D\), normalized so that lasing starts at pump parameter \(r = 1\),
\[\dot E = \kappa(P - E), \qquad \dot P = \gamma_\perp(ED - P), \qquad \dot D = \gamma_\parallel(r - D - EP)\](Haken 1975; Narducci and Abraham, Laser Physics and Laser Instabilities, 1988, Ch. 7). With \(\sigma = \kappa/\gamma_\perp\), \(b = \gamma_\parallel/\gamma_\perp\), time \(\gamma_\perp t\), and \(x = \sqrt b\,E\), \(y = \sqrt b\,P\), \(z = r - D\), these are exactly the Lorenz equations \(\dot x = \sigma(y - x)\), \(\dot y = x(r - z) - y\), \(\dot z = xy - bz\). Below \(r = 1\) the field decays; above it the stable lasing state is \(E = P = \pm\sqrt{r-1}\), \(D = 1\). In the “bad cavity” regime \(\sigma > b + 1\) that state loses stability at the second threshold
\[r_H = \frac{\sigma(\sigma + b + 3)}{\sigma - b - 1},\]beyond which the laser output is chaotic.
- Parameters:
Examples
>>> mb = MaxwellBloch(kappa=10.0, gamma_perp=1.0, gamma_par=8 / 3, r=28.0) >>> round(mb.second_threshold, 4) # the Lorenz value for sigma=10, b=8/3 24.7368
- jacobian(state)[source]#
Jacobian \(\partial\dot{\mathbf y}/\partial\mathbf y\) at
state = (E, P, D).
- class physicskit.optics.OpticalElement(matrix, name='', length=0.0)[source]#
Bases:
objectA single named optical element wrapping one ABCD matrix.
- Parameters:
matrix (array_like, shape (2, 2)) – The element’s ray transfer matrix, e.g. from
thin_lens()orfree_space().name (str, default="") – Human-readable label (e.g.
"f=50mm lens").length (float, default=0.0) – Physical length occupied by this element along the optical axis (zero for a “thin” element such as a lens or mirror).
Examples
>>> elem = OpticalElement(thin_lens(0.05), name="focusing lens", length=0.0) >>> elem.name 'focusing lens'
- class physicskit.optics.OpticalSystem(elements)[source]#
Bases:
objectAn ordered sequence of
OpticalElementforming a compound optical system.- Parameters:
elements (list of OpticalElement) – The elements in the order light passes through them:
elements[0]is hit first.
Examples
A single thin lens followed by propagation over its focal length focuses any parallel ray bundle back to the axis:
>>> f = 0.1 >>> sys = OpticalSystem([ ... OpticalElement(thin_lens(f), name="lens"), ... OpticalElement(free_space(f), name="propagate to focus"), ... ]) >>> trajectory = sys.trace_ray(y0=0.01, theta0=0.0) >>> abs(float(trajectory[-1, 0])) < 1e-12 True
- is_stable()[source]#
Whether the system satisfies the resonator stability condition \(|A+D| \le 2\).
- Returns:
bool
- property stability_parameter#
Resonator stability parameter \((A+D)/2\) of the system matrix.
- Returns:
float
- system_matrix()[source]#
Total ABCD matrix of the system, \(M = M_n \cdots M_2 M_1\).
elements[0]is applied first (it is the rightmost factor), so it acts on the incoming ray state before any later element.- Returns:
ndarray of shape (2, 2)
- physicskit.optics.airy_transmission(delta, R)[source]#
Fabry-Pérot (Airy) transmission of two lossless mirrors of reflectance \(R\).
\[T = \frac{1}{1 + F\sin^2(\delta/2)}, \qquad \delta = \frac{4\pi n L\cos\theta}{\lambda},\]where \(\delta\) is the round-trip phase.
- Parameters:
- Return type:
- Returns:
ndarray
Examples
>>> float(airy_transmission(0.0, 0.99)) # every resonance transmits fully 1.0
- physicskit.optics.angular_spectrum_propagate(U0, wavelength, z, dx)[source]#
Exact scalar diffraction propagation via the angular spectrum method.
Decomposes the input field into plane-wave components (its 2D Fourier transform), advances each by its own longitudinal propagation phase, and re-synthesizes:
\[\begin{split}U(x,y;z) = \mathcal{F}^{-1}\!\left[ \mathcal{F}[U_0](f_x,f_y)\; e^{ik_z z} \right], \qquad k_z = \begin{cases} \sqrt{k^2 - k_x^2 - k_y^2} & k_x^2+k_y^2 \le k^2 \quad \text{(propagating)}\\ i\sqrt{k_x^2+k_y^2 - k^2} & k_x^2+k_y^2 > k^2 \quad \text{(evanescent)} \end{cases}\end{split}\]with \(k=2\pi/\lambda\), \(k_x = 2\pi f_x\), \(k_y = 2\pi f_y\). Evanescent orders get a purely imaginary \(k_z\), so \(e^{ik_z z}\) decays exponentially rather than producing
nan/inf. Unlikefresnel_diffraction()andfraunhofer_diffraction(), this is not a paraxial approximation and the output remains on the same grid (shape and spacingdx) as the input.- Parameters:
- Returns:
ndarray of shape (Ny, Nx), complex – The propagated field, on the same grid as the input.
Examples
>>> ap = circular_aperture((64, 64), dx=0.01, radius=0.05) >>> U = angular_spectrum_propagate(ap, wavelength=0.5e-3, z=0.05, dx=0.01) >>> U.shape (64, 64) >>> bool(np.isclose(intensity(U).sum(), intensity(ap).sum(), rtol=0.02)) True
- physicskit.optics.animate_diffraction_propagation(aperture, wavelength, z_values, dx, log_scale=False, interval=100, ax=None)[source]#
Animate the diffraction pattern developing as propagation distance
zincreases.Steps
physicskit.optics.wave.angular_spectrum_propagate()over each distance inz_values(treatingzas the animation’s “time” axis – the standard way to visualize the Fresnel-to-Fraunhofer development of a diffraction pattern), and animates the resulting intensity as an imshow heatmap: a double-slit aperture’s near-field wavefronts visibly evolve into the far-field interference fringes aszgrows.- Parameters:
aperture (ndarray of shape (Ny, Nx)) – Input field immediately after the aperture (e.g. from
physicskit.optics.wave.double_slit_aperture()).wavelength (float) – Wavelength.
z_values (ndarray) – Sequence of propagation distances to sweep over.
dx (float) – Grid spacing (same for input and output, since
angular_spectrum_propagate()stays on the same grid).log_scale (bool, default=False) – If True, plot
log10(intensity + eps)to reveal faint fringes.interval (int, default=100) – Delay between frames, in milliseconds.
ax (matplotlib.axes.Axes, optional) – Axes to draw into; a new figure is created if omitted.
- Returns:
matplotlib.animation.FuncAnimation
Examples
>>> import numpy as np >>> from physicskit.optics.wave import double_slit_aperture >>> ap = double_slit_aperture((64, 64), dx=1e-3, width=2e-3, separation=1e-2).astype(complex) >>> z_values = np.linspace(0.01, 2.0, 5) >>> anim = animate_diffraction_propagation(ap, wavelength=0.5e-3, z_values=z_values, dx=1e-3) >>> isinstance(anim, FuncAnimation) True
- physicskit.optics.bloch_wavenumber(n_layers, d_layers, wavelength)[source]#
Bloch wavenumber \(K\) of an infinite periodic stack at normal incidence.
For one period of layers, \(\cos(K\Lambda) = \tfrac12 \operatorname{tr} M_{\text{period}}\); for two layers this is
\[\cos(K\Lambda) = \cos\delta_1\cos\delta_2 - \tfrac12\left(\frac{n_1}{n_2} + \frac{n_2}{n_1}\right)\sin\delta_1\sin\delta_2.\]- Parameters:
- Return type:
- Returns:
ndarray of complex – \(K\Lambda\), with real part in \([0, \pi]\) and a nonzero imaginary part (the decay per period) inside band gaps.
Examples
>>> KL = bloch_wavenumber([1.0, 1.0], [0.25, 0.25], 1.0) # uniform medium: K = 2 pi n / lambda >>> round(float(KL.real), 6) == round(np.pi, 6) True
- physicskit.optics.cavity_round_trip_matrix(elements)[source]#
Round-trip ABCD matrix of a laser cavity, given its elements in traversal order.
Equivalent to
OpticalSystem(elements).system_matrix(); provided as a standalone function for cavities analyzed without constructing a fullOpticalSystem.- Parameters:
elements (list of OpticalElement) – The elements encountered over one full round trip, in order.
- Returns:
ndarray of shape (2, 2)
See also
cavity_stabilityTest the resulting matrix for resonator stability.
Examples
>>> M = cavity_round_trip_matrix([ ... OpticalElement(spherical_mirror(2.0), name="M1"), ... OpticalElement(free_space(1.0), name="gap"), ... OpticalElement(spherical_mirror(2.0), name="M2"), ... OpticalElement(free_space(1.0), name="gap"), ... ]) >>> cavity_stability(M) True
- physicskit.optics.cavity_stability(M)[source]#
Resonator stability test \(|A+D| \le 2\) (equivalently \(|\operatorname{tr} M| \le 2\)).
A laser cavity with round-trip matrix
Msupports stable, non-diverging paraxial ray bundles if and only if this holds.- Parameters:
M (array_like, shape (2, 2)) – A round-trip ABCD matrix, e.g. from
cavity_round_trip_matrix()orOpticalSystem.system_matrix().- Returns:
bool
Examples
>>> cavity_stability(np.array([[1.0, 0.0], [0.0, 1.0]])) True >>> cavity_stability(np.array([[3.0, 0.0], [0.0, 3.0]])) False
- physicskit.optics.circular_aperture(shape, dx, radius)[source]#
A circular (disk) aperture, transmittance 1 inside, 0 outside.
- Parameters:
- Returns:
ndarray of shape (Ny, Nx) – Real-valued array of 1.0 (open) and 0.0 (blocked).
Examples
>>> ap = circular_aperture((5, 5), dx=1.0, radius=1.5) >>> float(ap[2, 2]) 1.0 >>> float(ap[0, 0]) 0.0
- physicskit.optics.coherent_state(alpha, cutoff)[source]#
The coherent state \(\lvert\alpha\rangle\), truncated to a finite Fock basis.
\[\lvert\alpha\rangle = e^{-\lvert\alpha\rvert^2/2} \sum_{n=0}^{\infty} \frac{\alpha^n}{\sqrt{n!}}\,\lvert n\rangle\]The sum is truncated to
cutoffterms and the result renormalized, so it is a good approximation only while \(\lvert\alpha\rvert^2 \ll\)cutoff(i.e. the mean photon number is well within the truncated space). Amplitudes are built iteratively in log-space viascipy.special.gammaln()to avoid overflow for largecutoff.- Parameters:
- Returns:
ndarray of shape (cutoff,) – Complex, L2-normalized state vector.
Examples
>>> psi = coherent_state(0.0, 8) >>> psi array([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j])
- physicskit.optics.compute_wigner_function(state_vector, x_grid, p_grid)[source]#
Wigner quasi-probability distribution of a Fock-basis state.
Computed from the density matrix \(\rho = \lvert\psi\rangle\langle\psi\rvert\) and the closed-form Fock-basis Wigner matrix elements,
\[W(x,p) = \sum_{n,m} \rho_{nm}\, K_{nm}(x,p),\]with \(K_{nm}\) built from associated Laguerre polynomials of \(2(x^2+p^2)\) (see
scipy.special.eval_genlaguerre()). Uses dimensionless quadratures \(x = (a+a^\dagger)/\sqrt2\), \(p = (a-a^\dagger)/(i\sqrt2)\), for which \(\iint W(x,p)\,dx\,dp = 1\) for any normalized state.- Parameters:
state_vector (ndarray of shape (cutoff,)) – Fock-basis ket.
x_grid (ndarray of shape (Nx,)) – Grid of \(x\) quadrature values.
p_grid (ndarray of shape (Np,)) – Grid of \(p\) quadrature values.
- Returns:
ndarray of shape (Nx, Np) – Real-valued Wigner function \(W(x,p)\).
Examples
>>> import numpy as np >>> vac = fock_state(0, 8) >>> xg = np.linspace(-4, 4, 41) >>> pg = np.linspace(-4, 4, 41) >>> W = compute_wigner_function(vac, xg, pg) >>> W.shape (41, 41) >>> round(float(W[20, 20] * np.pi), 6) 1.0
- physicskit.optics.curved_interface(R, n1, n2)[source]#
ABCD matrix for refraction at a spherical interface of radius
R, indexn1ton2.\[\begin{split}M = \begin{pmatrix} 1 & 0 \\ (n_1-n_2)/(R n_2) & n_1/n_2 \end{pmatrix}\end{split}\]- Parameters:
- Returns:
ndarray of shape (2, 2)
See also
flat_interfaceThe \(R \to \infty\) limit.
Examples
>>> np.allclose(curved_interface(1.0e12, 1.0, 1.5), flat_interface(1.0, 1.5), atol=1e-6) True
- physicskit.optics.double_slit_aperture(shape, dx, width, separation)[source]#
Two parallel slits of the given
width, centers separated byseparation.- Parameters:
- Returns:
ndarray of shape (Ny, Nx) – Real-valued array of 1.0 (open) and 0.0 (blocked).
Examples
>>> ap = double_slit_aperture((3, 11), dx=1.0, width=1.5, separation=6.0) >>> ap[0].tolist() [0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0]
- physicskit.optics.extraordinary_index(theta, n_o, n_e)[source]#
Index of the extraordinary wave at angle \(\theta\) to the optic axis of a uniaxial crystal.
\[\frac{1}{n_e(\theta)^2} = \frac{\cos^2\theta}{n_o^2} + \frac{\sin^2\theta}{n_e^2}\]
- physicskit.optics.fabry_perot_free_spectral_range(length, n=1.0, wavelength=None)[source]#
Free spectral range \(c/2nL\) in frequency, or \(\lambda^2/2nL\) in wavelength.
- physicskit.optics.field_grid(shape, dx)[source]#
Centered physical-coordinate grid for an array of the given
shape.- Parameters:
- Returns:
X, Y (ndarray of shape (Ny, Nx)) – Cartesian coordinates, with the origin at index
(Ny//2, Nx//2).
Examples
>>> X, Y = field_grid((4, 4), dx=1.0) >>> X[0].tolist() [-2.0, -1.0, 0.0, 1.0]
- physicskit.optics.finesse(R)[source]#
Reflectance finesse \(\mathcal F = \pi\sqrt R/(1 - R)\): free spectral range over linewidth.
Examples
>>> round(float(finesse(0.9)), 2) 29.8
- physicskit.optics.flat_interface(n1, n2)[source]#
ABCD matrix for refraction at a flat interface, index
n1ton2.\[\begin{split}M = \begin{pmatrix} 1 & 0 \\ 0 & n_1/n_2 \end{pmatrix}\end{split}\]- Parameters:
- Returns:
ndarray of shape (2, 2)
Examples
>>> flat_interface(1.0, 1.5) array([[1. , 0. ], [0. , 0.66666667]])
- physicskit.optics.fock_state(n, cutoff)[source]#
The number (Fock) state \(\lvert n\rangle\).
- Parameters:
- Returns:
ndarray of shape (cutoff,) – Complex state vector, all zeros except a
1at indexn.- Raises:
ValueError – If
nis negative orn >= cutoff.
Examples
>>> fock_state(1, 4) array([0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j])
- physicskit.optics.fraunhofer_diffraction(aperture, wavelength, z, dx)[source]#
Far-field (Fraunhofer) diffraction pattern of
apertureat distancez.Uses the standard single-Fourier-transform Fraunhofer formula (Goodman convention),
\[U(x',y') = \frac{e^{ikz}}{i\lambda z} e^{i\frac{k}{2z}(x'^2+y'^2)} \iint U_0(x,y)\, e^{-i\frac{2\pi}{\lambda z}(x x' + y y')}\,dx\,dy,\]with \(k = 2\pi/\lambda\). The double integral is exactly a 2D Fourier transform of
apertureevaluated at spatial frequency \((f_x, f_y) = (x'/(\lambda z), y'/(\lambda z))\), computed here with a single FFT (the discrete sum is converted to a continuum integral by thedx**2pixel-area factor). The output array lives on its own natural grid of spacing \(\lambda z / (N\,dx)\), not the input grid.- Parameters:
- Returns:
ndarray of shape (Ny, Nx), complex – The far-field complex amplitude.
See also
fresnel_diffractionThe near-field approximation this is a further limit of.
Examples
>>> ap = circular_aperture((64, 64), dx=0.01, radius=0.05) >>> U = fraunhofer_diffraction(ap, wavelength=0.5e-3, z=2.0, dx=0.01) >>> U.shape (64, 64) >>> bool(intensity(U)[32, 32] == intensity(U).max()) True
- physicskit.optics.free_space(d)[source]#
ABCD matrix for propagation through a distance
dof free space (or any homogeneous medium).\[\begin{split}M = \begin{pmatrix} 1 & d \\ 0 & 1 \end{pmatrix}\end{split}\]A ray’s height changes in proportion to its angle and the distance traveled; its angle is unchanged.
- Parameters:
d (float) – Propagation distance.
- Returns:
ndarray of shape (2, 2)
Examples
>>> free_space(2.0) array([[1., 2.], [0., 1.]])
- physicskit.optics.fresnel_diffraction(aperture, wavelength, z, dx)[source]#
Near-field (Fresnel) diffraction pattern of
apertureat distancez.The single-Fourier-transform (“one-step”) Fresnel propagation method: multiply the input field by the Fresnel quadratic phase, Fourier transform, then multiply by the corresponding output quadratic phase and prefactor,
\[U(x',y') = \frac{e^{ikz}}{i\lambda z} e^{i\frac{k}{2z}(x'^2+y'^2)}\, \mathcal{F}\!\left[U_0(x,y)\, e^{i\frac{k}{2z}(x^2+y^2)}\right]_{f_x=x'/(\lambda z),\ f_y=y'/(\lambda z)},\]the paraxial approximation to exact scalar diffraction. As with
fraunhofer_diffraction(), the output lives on its own natural grid of spacing \(\lambda z/(N\,dx)\).- Parameters:
- Returns:
ndarray of shape (Ny, Nx), complex – The propagated complex amplitude.
See also
angular_spectrum_propagateThe exact (non-paraxial) alternative, on the same input grid.
Examples
>>> ap = circular_aperture((64, 64), dx=0.01, radius=0.05) >>> U = fresnel_diffraction(ap, wavelength=0.5e-3, z=5.0, dx=0.01) >>> U.shape (64, 64)
- physicskit.optics.grin_medium(n0, n2_coeff, d)[source]#
ABCD matrix for a graded-index (GRIN) medium of length
d.For the standard quadratic radial index profile
\[n(r) = n_0\left(1 - \frac{n_2 r^2}{2}\right),\]paraxial rays oscillate sinusoidally about the axis, giving
\[A = D = \cos(\sqrt{n_2}\,d), \qquad B = \frac{\sin(\sqrt{n_2}\,d)}{n_0\sqrt{n_2}}, \qquad C = -n_0\sqrt{n_2}\,\sin(\sqrt{n_2}\,d).\]Input and output ray angles are measured outside the rod, in a medium of index 1: this is the inside-the-rod solution (\(A=D=\cos\), \(B=\sin/\sqrt{n_2}\), \(C=-\sqrt{n_2}\sin\)) sandwiched between the flat entrance and exit faces,
flat_interface(n0, 1) @ M_inside @ flat_interface(1, n0), which is where the \(n_0\) factors come from. Asn2_coeff\(\to 0\), \(A=D\to 1\), \(C\to 0\), and \(B \to d/n_0\) – the familiar reduced thickness of a homogeneous slab of index \(n_0\) in air, equal tofree_space()only for \(n_0 = 1\).- Parameters:
- Returns:
ndarray of shape (2, 2)
Examples
A tiny gradient barely distinguishable from a homogeneous medium of index 1 reduces to plain free-space propagation:
>>> np.allclose(grin_medium(n0=1.0, n2_coeff=1e-8, d=2.0), free_space(2.0), atol=1e-4) True
- physicskit.optics.hermite_gaussian_mode(x, y, z, beam, m, n)[source]#
Hermite-Gaussian \(\mathrm{TEM}_{mn}\) mode amplitude.
\[u_{mn}(x, y, z) = \frac{w_0}{w(z)}\, H_m\!\left(\frac{\sqrt2\,x}{w(z)}\right) H_n\!\left(\frac{\sqrt2\,y}{w(z)}\right) \exp\!\left[-\frac{x^2+y^2}{w(z)^2}\right] \exp\!\left[-\frac{ik(x^2+y^2)}{2R(z)}\right] \exp\!\left[i(m+n+1)\zeta(z)\right] \exp(-ikz)\]with \(k = 2\pi/\lambda\). \(m=n=0\) reduces to the fundamental Gaussian mode carried by
beam.- Parameters:
x (array_like) – Transverse coordinates (broadcastable).
y (array_like) – Transverse coordinates (broadcastable).
z (float) – Axial position.
beam (GaussianBeam) – The underlying fundamental-mode beam (sets \(w_0\), \(z_0\), \(\lambda\)).
m (int) – Transverse mode indices along \(x\) and \(y\).
n (int) – Transverse mode indices along \(x\) and \(y\).
- Returns:
ndarray – Complex field amplitude, broadcast shape of
xandy.
Examples
>>> import numpy as np >>> beam = GaussianBeam(wavelength=1.0, w0=1.0, z0=0.0) >>> u00 = hermite_gaussian_mode(0.0, 0.0, 0.0, beam, 0, 0) >>> round(float(abs(u00)), 6) 1.0
- physicskit.optics.intensity(U)[source]#
Optical intensity \(|U|^2\) of a complex field.
- Parameters:
U (ndarray) – Complex (or real) field amplitude.
- Returns:
ndarray – Real-valued intensity, same shape as
U.
Examples
>>> intensity(np.array([3.0 + 4.0j])) array([25.])
- physicskit.optics.interactive_wigner_surface(W, x_grid, p_grid, title=None)[source]#
An interactive 3D Plotly surface plot of a Wigner quasi-probability distribution.
- Parameters:
W (ndarray of shape (Nx, Np)) – Wigner function, e.g. from
physicskit.optics.quantum_optics.compute_wigner_function().x_grid (ndarray of shape (Nx,)) – Grid of x quadrature values.
p_grid (ndarray of shape (Np,)) – Grid of p quadrature values.
title (str, optional) – Plot title.
- Returns:
plotly.graph_objects.Figure
Examples
>>> import numpy as np >>> from physicskit.optics.quantum_optics import coherent_state, compute_wigner_function >>> x = np.linspace(-4, 4, 40) >>> p = np.linspace(-4, 4, 40) >>> W = compute_wigner_function(coherent_state(1.0, 20), x, p) >>> fig = interactive_wigner_surface(W, x, p) >>> isinstance(fig, go.Figure) True
- physicskit.optics.laguerre_gaussian_mode(r, phi, z, beam, l, p)[source]#
Laguerre-Gaussian \(\mathrm{LG}_p^l\) mode amplitude.
\[u_{lp}(r, \phi, z) = \frac{w_0}{w(z)} \left(\frac{r\sqrt2}{w(z)}\right)^{|l|} L_p^{|l|}\!\left(\frac{2r^2}{w(z)^2}\right) \exp\!\left[-\frac{r^2}{w(z)^2}\right] \exp\!\left[-\frac{ikr^2}{2R(z)}\right] \exp(il\phi) \exp\!\left[i(|l|+2p+1)\zeta(z)\right] \exp(-ikz)\]with \(k = 2\pi/\lambda\) and \(L_p^{|l|}\) the associated Laguerre polynomial.
- Parameters:
r (array_like) – Polar transverse coordinates (broadcastable);
ris the radial distance from the axis,phithe azimuthal angle.phi (array_like) – Polar transverse coordinates (broadcastable);
ris the radial distance from the axis,phithe azimuthal angle.z (float) – Axial position.
beam (GaussianBeam) – The underlying fundamental-mode beam.
l (int) – Azimuthal (orbital) mode index.
p (int) – Radial mode index.
- Returns:
ndarray – Complex field amplitude, broadcast shape of
randphi.
Examples
>>> import numpy as np >>> beam = GaussianBeam(wavelength=1.0, w0=1.0, z0=0.0) >>> u00 = laguerre_gaussian_mode(0.0, 0.0, 0.0, beam, 0, 0) >>> round(float(abs(u00)), 6) 1.0
- physicskit.optics.m2_beam_waist(z, wavelength, w0, M2=1.0)[source]#
Beam radius of a non-ideal (\(M^2 > 1\)) real laser beam, waist fixed at \(z=0\).
\[w(z) = w_0 \sqrt{1 + \left(\frac{z}{z_{R,\text{eff}}}\right)^2}, \qquad z_{R,\text{eff}} = \frac{\pi w_0^2}{M^2 \lambda}\]A beam-quality factor \(M^2 \ge 1\) (equal to 1 for an ideal diffraction-limited Gaussian beam) reduces the effective Rayleigh range, so a real beam of the same waist diverges faster than the ideal case.
- Parameters:
- Returns:
float or ndarray – Beam radius
w(z).
Examples
>>> round(float(m2_beam_waist(0.0, wavelength=0.5e-3, w0=0.1, M2=2.0)), 6) 0.1
- physicskit.optics.multilayer_response(n_layers, d_layers, wavelength, n_incident=1.0, n_substrate=1.5, angle=0.0, polarization='s')[source]#
Reflection and transmission of a stack of thin layers by the characteristic-matrix method.
- Parameters:
n_layers (
ArrayLike) – Refractive index of each layer, from the incident side; complex values \(n + i\kappa\) with \(\kappa > 0\) describe absorption (time dependence \(e^{-i\omega t}\)).d_layers (
ArrayLike) – Physical thickness of each layer, in the units ofwavelength.wavelength (
ArrayLike) – Vacuum wavelength(s).n_incident (
complex) – Index of the incident medium.n_substrate (
complex) – Index of the exit medium.angle (
float) – Angle of incidence in radians.polarization (
str)
- Return type:
- Returns:
dict – Complex amplitude coefficients
"r","t"and the power reflectance"R"and transmittance"T", each shaped likewavelength.
Examples
Bare glass reflects \(((1 - 1.5)/(1 + 1.5))^2 = 4\%\); a quarter-wave film of index \(\sqrt{1.5}\) removes it at the design wavelength:
>>> round(float(multilayer_response([], [], 550.0)["R"]), 4) 0.04 >>> n_ar = np.sqrt(1.5) >>> float(multilayer_response([n_ar], [550.0 / (4 * n_ar)], 550.0)["R"]) < 1e-12 True
- physicskit.optics.plot_beam_envelope(beam, z_range, n_points=200, ax=None)[source]#
Plot a Gaussian beam’s \(\pm w(z)\) waist envelope over a propagation range.
- Parameters:
beam (physicskit.optics.gaussian.GaussianBeam) – The beam to plot.
z_range (tuple(float, float)) –
(z_min, z_max)propagation-axis range to plot over.n_points (int, default=200) – Number of sampled points.
ax (matplotlib.axes.Axes, optional) – Axes to draw into; a new figure is created if omitted.
- Returns:
fig (matplotlib.figure.Figure)
ax (matplotlib.axes.Axes)
Examples
>>> from physicskit.optics.gaussian import GaussianBeam >>> beam = GaussianBeam(wavelength=0.5e-3, w0=0.1, z0=0.0) >>> fig, ax = plot_beam_envelope(beam, z_range=(-10, 10)) >>> isinstance(fig, plt.Figure) True >>> bool(ax.lines[0].get_ydata().min() >= 0) True
- physicskit.optics.plot_diffraction_pattern(U, dx, ax=None, log_scale=False)[source]#
Plot the intensity pattern of a complex diffracted field as a 2D heatmap.
- Parameters:
U (ndarray of shape (Ny, Nx)) – Complex field amplitude (e.g. from
physicskit.optics.wave.angular_spectrum_propagate()).dx (float) – Grid spacing, used to set physical axis extents.
ax (matplotlib.axes.Axes, optional) – Axes to draw into; a new figure is created if omitted.
log_scale (bool, default=False) – If True, plot
log10(intensity + eps)to reveal faint diffraction fringes alongside the bright central peak.
- Returns:
fig (matplotlib.figure.Figure)
ax (matplotlib.axes.Axes)
Examples
>>> from physicskit.optics.wave import circular_aperture >>> U = circular_aperture((64, 64), dx=1e-3, radius=5e-3).astype(complex) >>> fig, ax = plot_diffraction_pattern(U, dx=1e-3) >>> isinstance(fig, plt.Figure) True
- physicskit.optics.plot_ray_trace(system, y0, theta0, ax=None)[source]#
Plot a ray’s height through each element of an
OpticalSystem.- Parameters:
system (physicskit.optics.ray.OpticalSystem) – The optical system to trace the ray through.
y0 (float) – Initial ray height.
theta0 (float) – Initial ray angle.
ax (matplotlib.axes.Axes, optional) – Axes to draw into; a new figure is created if omitted.
- Returns:
fig (matplotlib.figure.Figure)
ax (matplotlib.axes.Axes)
Examples
>>> from physicskit.optics.ray import OpticalElement, OpticalSystem, free_space, thin_lens >>> system = OpticalSystem([ ... OpticalElement(free_space(1.0), name="d1", length=1.0), ... OpticalElement(thin_lens(1.0), name="lens"), ... OpticalElement(free_space(1.0), name="d2", length=1.0), ... ]) >>> fig, ax = plot_ray_trace(system, y0=0.5, theta0=0.0) >>> isinstance(fig, plt.Figure) True >>> len(ax.lines[0].get_xdata()) 4
- physicskit.optics.propagate_q(q, M)[source]#
Propagate a complex beam parameter through a paraxial optical system.
Applies the same bilinear transformation used for ray-transfer (ABCD) matrices,
\[q_{\text{out}} = \frac{A q + B}{C q + D},\]with \(A, B, C, D\) the entries of
Min the conventionM = [[A, B], [C, D]].- Parameters:
q (complex) – Beam parameter before the system.
M (ndarray of shape (2, 2)) – Ray-transfer (ABCD) matrix of the optical system.
- Returns:
complex – Beam parameter
q_outafter the system.
Examples
>>> import numpy as np >>> free_space = np.array([[1.0, 2.0], [0.0, 1.0]]) >>> propagate_q(1j, free_space) (2+1j)
- physicskit.optics.q_to_beam_params(q, wavelength)[source]#
Extract the beam radius and radius of curvature from a complex beam parameter.
Inverts
\[\frac{1}{q} = \frac{1}{R} - i\,\frac{\lambda}{\pi w^2}.\]At the beam waist \(\mathrm{Re}(1/q) = 0\), so
Rwould formally diverge; this is handled explicitly andnp.infis returned there instead of raising or silently producingnan.- Parameters:
- Returns:
w (float) – Beam radius (\(1/e^2\) intensity radius).
R (float) – Radius of curvature of the wavefront;
np.infat the waist.
Examples
>>> import numpy as np >>> zR = 3.0 >>> w, R = q_to_beam_params(1j * zR, wavelength=0.5e-3) >>> round(float(w), 6), R (0.021851, inf)
- physicskit.optics.quarter_wave_band_gap(n_high, n_low)[source]#
Relative width \(\Delta\omega/\omega_0\) of the first gap of a quarter-wave stack.
\[\frac{\Delta\omega}{\omega_0} = \frac4\pi\arcsin\frac{n_H - n_L}{n_H + n_L}\](Yariv and Yeh, Optical Waves in Crystals, 1984, sec. 6.3).
Examples
>>> round(quarter_wave_band_gap(2.3, 1.38), 4) 0.3217
- physicskit.optics.quarter_wave_stack(n_high, n_low, n_pairs, design_wavelength, cavity=False)[source]#
Layer indices and thicknesses of a quarter-wave (Bragg) mirror, or of a Fabry-Pérot cavity.
- Parameters:
n_high (
float) – Indices of the two materials; the stack starts withn_high.n_low (
float) – Indices of the two materials; the stack starts withn_high.n_pairs (
int) – Number of high/low pairs.design_wavelength (
float) – Wavelength at which every layer is a quarter wave thick.cavity (
bool) – If True, return two such mirrors separated by a half-wave (\(\lambda/2n_{\text{low}}\)) spacer of the low index: a Fabry-Pérot cavity with a resonance atdesign_wavelength.
- Return type:
- Returns:
n_layers, d_layers (ndarray)
Examples
>>> n, d = quarter_wave_stack(2.3, 1.38, 2, 600.0) >>> n.real.tolist(), (n.real * d).tolist() ([2.3, 1.38, 2.3, 1.38], [150.0, 150.0, 150.0, 150.0])
- physicskit.optics.shg_coupled_amplitudes(z, delta_k=0.0, kappa=1.0, A0=1.0, qpm_period=None)[source]#
Integrate the SHG coupled-amplitude equations, with optional quasi-phase matching.
- Parameters:
z (
ArrayLike) – Increasing output positions, starting at 0.delta_k (
float) – Phase mismatch \(\Delta k\).kappa (
float) – Nonlinear coupling.A0 (
complex) – Fundamental amplitude at \(z = 0\); the harmonic starts at 0.qpm_period (
float|None) – If given, \(\kappa\) changes sign every half period (periodically poled crystal); first-order QPM uses \(2\pi/\Delta k\).
- Return type:
- Returns:
dict – Complex
"A1"and"A2"atz.
Examples
>>> out = shg_coupled_amplitudes(np.linspace(0, 2, 5)) >>> bool(np.allclose(np.abs(out["A2"]) ** 2, np.tanh(np.linspace(0, 2, 5)) ** 2, atol=1e-6)) True
- physicskit.optics.shg_phase_matched_efficiency(z, kappa=1.0, A0=1.0)[source]#
Conversion efficiency \(|A_2|^2/|A_0|^2 = \tanh^2(\kappa A_0 z)\) with pump depletion.
- Parameters:
- Return type:
- Returns:
ndarray
Examples
>>> round(float(shg_phase_matched_efficiency(10.0)), 6) 1.0
- physicskit.optics.shg_undepleted_power(L, delta_k, kappa=1.0, A1=1.0)[source]#
Second-harmonic power for an undepleted pump.
\[|A_2(L)|^2 = \kappa^2 |A_1|^4 L^2 \operatorname{sinc}^2(\Delta k L / 2) = \frac{4\kappa^2|A_1|^4}{\Delta k^2}\sin^2\frac{\Delta k L}{2}.\]- Parameters:
- Return type:
- Returns:
ndarray
Examples
>>> round(float(shg_undepleted_power(np.pi, delta_k=1.0)), 12) # one coherence length: 4/dk^2 4.0
- physicskit.optics.single_slit_aperture(shape, dx, width)[source]#
A single slit of the given
widthalong x, open along the full height in y.- Parameters:
- Returns:
ndarray of shape (Ny, Nx) – Real-valued array of 1.0 (open) and 0.0 (blocked).
Examples
>>> ap = single_slit_aperture((3, 7), dx=1.0, width=2.5) >>> ap[0].tolist() [0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0]
- physicskit.optics.spherical_mirror(R)[source]#
ABCD matrix for reflection from a spherical mirror of radius of curvature
R.\[\begin{split}M = \begin{pmatrix} 1 & 0 \\ -2/R & 1 \end{pmatrix}\end{split}\]- Parameters:
R (float) – Radius of curvature, positive for a concave mirror as seen by the incoming ray (i.e. a focusing mirror).
- Returns:
ndarray of shape (2, 2)
Examples
>>> spherical_mirror(2.0) array([[ 1., 0.], [-1., 1.]])
- physicskit.optics.squeezed_state(xi, alpha=0.0, cutoff=30)[source]#
The displaced squeezed vacuum state, using the squeeze-then-displace convention.
Constructs
\[\lvert\xi, \alpha\rangle = \hat D(\alpha)\, \hat S(\xi)\, \lvert 0\rangle, \qquad \hat S(\xi) = \exp\!\left[\frac{\xi^* \hat a^2 - \xi \hat a^{\dagger 2}}{2}\right], \qquad \hat D(\alpha) = \exp\!\left(\alpha \hat a^\dagger - \alpha^* \hat a\right)\]i.e. the vacuum is squeezed first and then displaced. (The opposite order, displace-then-squeeze, gives a different, also physically valid, state – squeeze-then-displace is the convention adopted here and is the more common one in the literature.) The ladder operators are built on the same truncated Fock basis, so this is only accurate while the squeezed/displaced amplitude is small relative to
cutoff.- Parameters:
- Returns:
ndarray of shape (cutoff,) – Complex, L2-normalized state vector.
Examples
>>> psi = squeezed_state(0.0, alpha=0.0, cutoff=6) >>> psi array([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j])
- physicskit.optics.thick_lens(R1, R2, t, n, n_ext=1.0)[source]#
ABCD matrix for a thick lens: two curved interfaces separated by thickness
t.Composes, in the order light passes through them, refraction into the lens at the first surface, propagation across the lens body, and refraction back out at the second surface:
\[\begin{split}M = M_{R_2}\,M_t\,M_{R_1}, \qquad M_{R_1} = \text{curved\_interface}(R_1, n_{\text{ext}}, n), \\ M_t = \text{free\_space}(t) \ \text{(in medium } n\text{)}, \qquad M_{R_2} = \text{curved\_interface}(R_2, n, n_{\text{ext}}).\end{split}\]- Parameters:
R1 (float) – Radius of curvature of the first (entrance) surface.
R2 (float) – Radius of curvature of the second (exit) surface.
t (float) – Center thickness of the lens.
n (float) – Refractive index of the lens material.
n_ext (float, default=1.0) – Refractive index of the surrounding medium (air, by default).
- Returns:
ndarray of shape (2, 2)
Examples
A symmetric biconvex lens in air; the resulting matrix has unit determinant, as any lossless ABCD system must:
>>> M = thick_lens(R1=0.1, R2=-0.1, t=0.01, n=1.5) >>> round(float(np.linalg.det(M)), 8) 1.0
- physicskit.optics.thin_lens(f)[source]#
ABCD matrix for a thin lens of focal length
f.\[\begin{split}M = \begin{pmatrix} 1 & 0 \\ -1/f & 1 \end{pmatrix}\end{split}\]- Parameters:
f (float) – Focal length (positive for a converging lens, negative for diverging).
- Returns:
ndarray of shape (2, 2)
Examples
>>> thin_lens(0.5) array([[ 1., 0.], [-2., 1.]])
- physicskit.optics.type_i_phase_matching_angle(n_o_w, n_o_2w, n_e_2w)[source]#
Type-I (ooe) phase-matching angle of a negative uniaxial crystal.
Solves \(n_e^{2\omega}(\theta) = n_o^{\omega}\):
\[\sin^2\theta_m = \frac{(n_o^{\omega})^{-2} - (n_o^{2\omega})^{-2}} {(n_e^{2\omega})^{-2} - (n_o^{2\omega})^{-2}}.\]- Parameters:
- Return type:
- Returns:
float – \(\theta_m\) in radians.
- Raises:
ValueError – If the birefringence cannot compensate the dispersion.
Examples
KDP doubling 1064 nm (Sellmeier indices from Zernike, JOSA 54, 1215 (1964)):
>>> round(float(np.degrees(type_i_phase_matching_angle(1.4942, 1.5129, 1.4709))), 1) 41.3
- physicskit.optics.wigner_negativity(W, x_grid, p_grid)[source]#
Wigner negativity: the integrated negative volume of a Wigner function.
\[N_W = \iint \max(0, -W(x,p))\; dx\, dp\]A standard non-classicality diagnostic: zero for classical/Gaussian states (coherent, thermal, squeezed vacuum), strictly positive for genuinely non-classical states such as Fock states with \(n \ge 1\).
- Parameters:
W (ndarray of shape (Nx, Np)) – Wigner function, e.g. from
compute_wigner_function().x_grid (ndarray of shape (Nx,)) – Grid of \(x\) quadrature values.
p_grid (ndarray of shape (Np,)) – Grid of \(p\) quadrature values.
- Returns:
float – The (non-negative) integrated negative volume.
Examples
>>> import numpy as np >>> vac = fock_state(0, 8) >>> xg = np.linspace(-5, 5, 61) >>> pg = np.linspace(-5, 5, 61) >>> W = compute_wigner_function(vac, xg, pg) >>> wigner_negativity(W, xg, pg) 0.0
Geometric ray optics: paraxial ray transfer (ABCD) matrices and optical systems.
In the paraxial approximation, a light ray at a given plane along the optical axis is fully described by two numbers – its height above the axis, \(y\), and the angle it makes with the axis, \(\theta\) (in radians, small-angle/paraxial regime) – collected into a state vector \((y, \theta)^T\). Every simple optical element (propagation through free space, refraction at an interface, a thin or thick lens, a curved mirror, a graded-index medium, …) acts on this vector as a linear map, a \(2\times 2\) “ABCD” matrix:
This formalism – developed piecemeal through the 19th century (Gauss’s
theory of optical systems) and formalized for laser resonator design by
Kogelnik and Li in the 1960s – reduces the analysis of an arbitrarily long
chain of lenses, mirrors, and gaps to ordinary matrix multiplication: the
matrix of a compound system is just the product of the matrices of its
elements, applied in the order light encounters them (rightmost first).
The same matrices reappear in physicskit.optics.gaussian to
propagate the complex beam parameter of a Gaussian laser beam, and their
trace controls whether a laser cavity is a stable resonator.
- class physicskit.optics.ray.OpticalElement(matrix, name='', length=0.0)[source]#
Bases:
objectA single named optical element wrapping one ABCD matrix.
- Parameters:
matrix (array_like, shape (2, 2)) – The element’s ray transfer matrix, e.g. from
thin_lens()orfree_space().name (str, default="") – Human-readable label (e.g.
"f=50mm lens").length (float, default=0.0) – Physical length occupied by this element along the optical axis (zero for a “thin” element such as a lens or mirror).
Examples
>>> elem = OpticalElement(thin_lens(0.05), name="focusing lens", length=0.0) >>> elem.name 'focusing lens'
- class physicskit.optics.ray.OpticalSystem(elements)[source]#
Bases:
objectAn ordered sequence of
OpticalElementforming a compound optical system.- Parameters:
elements (list of OpticalElement) – The elements in the order light passes through them:
elements[0]is hit first.
Examples
A single thin lens followed by propagation over its focal length focuses any parallel ray bundle back to the axis:
>>> f = 0.1 >>> sys = OpticalSystem([ ... OpticalElement(thin_lens(f), name="lens"), ... OpticalElement(free_space(f), name="propagate to focus"), ... ]) >>> trajectory = sys.trace_ray(y0=0.01, theta0=0.0) >>> abs(float(trajectory[-1, 0])) < 1e-12 True
- is_stable()[source]#
Whether the system satisfies the resonator stability condition \(|A+D| \le 2\).
- Returns:
bool
- property stability_parameter#
Resonator stability parameter \((A+D)/2\) of the system matrix.
- Returns:
float
- system_matrix()[source]#
Total ABCD matrix of the system, \(M = M_n \cdots M_2 M_1\).
elements[0]is applied first (it is the rightmost factor), so it acts on the incoming ray state before any later element.- Returns:
ndarray of shape (2, 2)
- physicskit.optics.ray.cavity_round_trip_matrix(elements)[source]#
Round-trip ABCD matrix of a laser cavity, given its elements in traversal order.
Equivalent to
OpticalSystem(elements).system_matrix(); provided as a standalone function for cavities analyzed without constructing a fullOpticalSystem.- Parameters:
elements (list of OpticalElement) – The elements encountered over one full round trip, in order.
- Returns:
ndarray of shape (2, 2)
See also
cavity_stabilityTest the resulting matrix for resonator stability.
Examples
>>> M = cavity_round_trip_matrix([ ... OpticalElement(spherical_mirror(2.0), name="M1"), ... OpticalElement(free_space(1.0), name="gap"), ... OpticalElement(spherical_mirror(2.0), name="M2"), ... OpticalElement(free_space(1.0), name="gap"), ... ]) >>> cavity_stability(M) True
- physicskit.optics.ray.cavity_stability(M)[source]#
Resonator stability test \(|A+D| \le 2\) (equivalently \(|\operatorname{tr} M| \le 2\)).
A laser cavity with round-trip matrix
Msupports stable, non-diverging paraxial ray bundles if and only if this holds.- Parameters:
M (array_like, shape (2, 2)) – A round-trip ABCD matrix, e.g. from
cavity_round_trip_matrix()orOpticalSystem.system_matrix().- Returns:
bool
Examples
>>> cavity_stability(np.array([[1.0, 0.0], [0.0, 1.0]])) True >>> cavity_stability(np.array([[3.0, 0.0], [0.0, 3.0]])) False
- physicskit.optics.ray.curved_interface(R, n1, n2)[source]#
ABCD matrix for refraction at a spherical interface of radius
R, indexn1ton2.\[\begin{split}M = \begin{pmatrix} 1 & 0 \\ (n_1-n_2)/(R n_2) & n_1/n_2 \end{pmatrix}\end{split}\]- Parameters:
- Returns:
ndarray of shape (2, 2)
See also
flat_interfaceThe \(R \to \infty\) limit.
Examples
>>> np.allclose(curved_interface(1.0e12, 1.0, 1.5), flat_interface(1.0, 1.5), atol=1e-6) True
- physicskit.optics.ray.flat_interface(n1, n2)[source]#
ABCD matrix for refraction at a flat interface, index
n1ton2.\[\begin{split}M = \begin{pmatrix} 1 & 0 \\ 0 & n_1/n_2 \end{pmatrix}\end{split}\]- Parameters:
- Returns:
ndarray of shape (2, 2)
Examples
>>> flat_interface(1.0, 1.5) array([[1. , 0. ], [0. , 0.66666667]])
- physicskit.optics.ray.free_space(d)[source]#
ABCD matrix for propagation through a distance
dof free space (or any homogeneous medium).\[\begin{split}M = \begin{pmatrix} 1 & d \\ 0 & 1 \end{pmatrix}\end{split}\]A ray’s height changes in proportion to its angle and the distance traveled; its angle is unchanged.
- Parameters:
d (float) – Propagation distance.
- Returns:
ndarray of shape (2, 2)
Examples
>>> free_space(2.0) array([[1., 2.], [0., 1.]])
- physicskit.optics.ray.grin_medium(n0, n2_coeff, d)[source]#
ABCD matrix for a graded-index (GRIN) medium of length
d.For the standard quadratic radial index profile
\[n(r) = n_0\left(1 - \frac{n_2 r^2}{2}\right),\]paraxial rays oscillate sinusoidally about the axis, giving
\[A = D = \cos(\sqrt{n_2}\,d), \qquad B = \frac{\sin(\sqrt{n_2}\,d)}{n_0\sqrt{n_2}}, \qquad C = -n_0\sqrt{n_2}\,\sin(\sqrt{n_2}\,d).\]Input and output ray angles are measured outside the rod, in a medium of index 1: this is the inside-the-rod solution (\(A=D=\cos\), \(B=\sin/\sqrt{n_2}\), \(C=-\sqrt{n_2}\sin\)) sandwiched between the flat entrance and exit faces,
flat_interface(n0, 1) @ M_inside @ flat_interface(1, n0), which is where the \(n_0\) factors come from. Asn2_coeff\(\to 0\), \(A=D\to 1\), \(C\to 0\), and \(B \to d/n_0\) – the familiar reduced thickness of a homogeneous slab of index \(n_0\) in air, equal tofree_space()only for \(n_0 = 1\).- Parameters:
- Returns:
ndarray of shape (2, 2)
Examples
A tiny gradient barely distinguishable from a homogeneous medium of index 1 reduces to plain free-space propagation:
>>> np.allclose(grin_medium(n0=1.0, n2_coeff=1e-8, d=2.0), free_space(2.0), atol=1e-4) True
- physicskit.optics.ray.spherical_mirror(R)[source]#
ABCD matrix for reflection from a spherical mirror of radius of curvature
R.\[\begin{split}M = \begin{pmatrix} 1 & 0 \\ -2/R & 1 \end{pmatrix}\end{split}\]- Parameters:
R (float) – Radius of curvature, positive for a concave mirror as seen by the incoming ray (i.e. a focusing mirror).
- Returns:
ndarray of shape (2, 2)
Examples
>>> spherical_mirror(2.0) array([[ 1., 0.], [-1., 1.]])
- physicskit.optics.ray.thick_lens(R1, R2, t, n, n_ext=1.0)[source]#
ABCD matrix for a thick lens: two curved interfaces separated by thickness
t.Composes, in the order light passes through them, refraction into the lens at the first surface, propagation across the lens body, and refraction back out at the second surface:
\[\begin{split}M = M_{R_2}\,M_t\,M_{R_1}, \qquad M_{R_1} = \text{curved\_interface}(R_1, n_{\text{ext}}, n), \\ M_t = \text{free\_space}(t) \ \text{(in medium } n\text{)}, \qquad M_{R_2} = \text{curved\_interface}(R_2, n, n_{\text{ext}}).\end{split}\]- Parameters:
R1 (float) – Radius of curvature of the first (entrance) surface.
R2 (float) – Radius of curvature of the second (exit) surface.
t (float) – Center thickness of the lens.
n (float) – Refractive index of the lens material.
n_ext (float, default=1.0) – Refractive index of the surrounding medium (air, by default).
- Returns:
ndarray of shape (2, 2)
Examples
A symmetric biconvex lens in air; the resulting matrix has unit determinant, as any lossless ABCD system must:
>>> M = thick_lens(R1=0.1, R2=-0.1, t=0.01, n=1.5) >>> round(float(np.linalg.det(M)), 8) 1.0
- physicskit.optics.ray.thin_lens(f)[source]#
ABCD matrix for a thin lens of focal length
f.\[\begin{split}M = \begin{pmatrix} 1 & 0 \\ -1/f & 1 \end{pmatrix}\end{split}\]- Parameters:
f (float) – Focal length (positive for a converging lens, negative for diverging).
- Returns:
ndarray of shape (2, 2)
Examples
>>> thin_lens(0.5) array([[ 1., 0.], [-2., 1.]])
Scalar wave optics: FFT-based Fresnel, Fraunhofer, and angular-spectrum diffraction.
Treats light as a scalar complex field \(U(x,y)\) obeying the Helmholtz equation, and propagates it between parallel planes using the three workhorse methods of Fourier optics (Goodman, Introduction to Fourier Optics):
angular_spectrum_propagate()– the exact scalar-diffraction solution, decomposing the field into plane waves (its 2D Fourier transform), advancing each by its own propagation phase \(e^{ik_z z}\), and re-synthesizing. Valid at any distance, including deep into the near field, as long as the grid resolves the field’s spatial frequencies.fresnel_diffraction()– the paraxial (parabolic-wave) near-field approximation, computable as a single Fourier transform of the aperture times a quadratic phase.fraunhofer_diffraction()– the further far-field approximation valid once \(z\) is large enough that the quadratic phase across the aperture itself is negligible; the diffraction pattern becomes simply the (scaled, phase-prefactored) Fourier transform of the aperture, the basis for classic single- and double-slit interference patterns.
All propagation routines expect a field sampled on a uniform square-pixel
grid of spacing dx (same length units as wavelength), and treat
array index (Ny//2, Nx//2) as the on-axis origin.
- physicskit.optics.wave.angular_spectrum_propagate(U0, wavelength, z, dx)[source]#
Exact scalar diffraction propagation via the angular spectrum method.
Decomposes the input field into plane-wave components (its 2D Fourier transform), advances each by its own longitudinal propagation phase, and re-synthesizes:
\[\begin{split}U(x,y;z) = \mathcal{F}^{-1}\!\left[ \mathcal{F}[U_0](f_x,f_y)\; e^{ik_z z} \right], \qquad k_z = \begin{cases} \sqrt{k^2 - k_x^2 - k_y^2} & k_x^2+k_y^2 \le k^2 \quad \text{(propagating)}\\ i\sqrt{k_x^2+k_y^2 - k^2} & k_x^2+k_y^2 > k^2 \quad \text{(evanescent)} \end{cases}\end{split}\]with \(k=2\pi/\lambda\), \(k_x = 2\pi f_x\), \(k_y = 2\pi f_y\). Evanescent orders get a purely imaginary \(k_z\), so \(e^{ik_z z}\) decays exponentially rather than producing
nan/inf. Unlikefresnel_diffraction()andfraunhofer_diffraction(), this is not a paraxial approximation and the output remains on the same grid (shape and spacingdx) as the input.- Parameters:
- Returns:
ndarray of shape (Ny, Nx), complex – The propagated field, on the same grid as the input.
Examples
>>> ap = circular_aperture((64, 64), dx=0.01, radius=0.05) >>> U = angular_spectrum_propagate(ap, wavelength=0.5e-3, z=0.05, dx=0.01) >>> U.shape (64, 64) >>> bool(np.isclose(intensity(U).sum(), intensity(ap).sum(), rtol=0.02)) True
- physicskit.optics.wave.circular_aperture(shape, dx, radius)[source]#
A circular (disk) aperture, transmittance 1 inside, 0 outside.
- Parameters:
- Returns:
ndarray of shape (Ny, Nx) – Real-valued array of 1.0 (open) and 0.0 (blocked).
Examples
>>> ap = circular_aperture((5, 5), dx=1.0, radius=1.5) >>> float(ap[2, 2]) 1.0 >>> float(ap[0, 0]) 0.0
- physicskit.optics.wave.double_slit_aperture(shape, dx, width, separation)[source]#
Two parallel slits of the given
width, centers separated byseparation.- Parameters:
- Returns:
ndarray of shape (Ny, Nx) – Real-valued array of 1.0 (open) and 0.0 (blocked).
Examples
>>> ap = double_slit_aperture((3, 11), dx=1.0, width=1.5, separation=6.0) >>> ap[0].tolist() [0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0]
- physicskit.optics.wave.field_grid(shape, dx)[source]#
Centered physical-coordinate grid for an array of the given
shape.- Parameters:
- Returns:
X, Y (ndarray of shape (Ny, Nx)) – Cartesian coordinates, with the origin at index
(Ny//2, Nx//2).
Examples
>>> X, Y = field_grid((4, 4), dx=1.0) >>> X[0].tolist() [-2.0, -1.0, 0.0, 1.0]
- physicskit.optics.wave.fraunhofer_diffraction(aperture, wavelength, z, dx)[source]#
Far-field (Fraunhofer) diffraction pattern of
apertureat distancez.Uses the standard single-Fourier-transform Fraunhofer formula (Goodman convention),
\[U(x',y') = \frac{e^{ikz}}{i\lambda z} e^{i\frac{k}{2z}(x'^2+y'^2)} \iint U_0(x,y)\, e^{-i\frac{2\pi}{\lambda z}(x x' + y y')}\,dx\,dy,\]with \(k = 2\pi/\lambda\). The double integral is exactly a 2D Fourier transform of
apertureevaluated at spatial frequency \((f_x, f_y) = (x'/(\lambda z), y'/(\lambda z))\), computed here with a single FFT (the discrete sum is converted to a continuum integral by thedx**2pixel-area factor). The output array lives on its own natural grid of spacing \(\lambda z / (N\,dx)\), not the input grid.- Parameters:
- Returns:
ndarray of shape (Ny, Nx), complex – The far-field complex amplitude.
See also
fresnel_diffractionThe near-field approximation this is a further limit of.
Examples
>>> ap = circular_aperture((64, 64), dx=0.01, radius=0.05) >>> U = fraunhofer_diffraction(ap, wavelength=0.5e-3, z=2.0, dx=0.01) >>> U.shape (64, 64) >>> bool(intensity(U)[32, 32] == intensity(U).max()) True
- physicskit.optics.wave.fresnel_diffraction(aperture, wavelength, z, dx)[source]#
Near-field (Fresnel) diffraction pattern of
apertureat distancez.The single-Fourier-transform (“one-step”) Fresnel propagation method: multiply the input field by the Fresnel quadratic phase, Fourier transform, then multiply by the corresponding output quadratic phase and prefactor,
\[U(x',y') = \frac{e^{ikz}}{i\lambda z} e^{i\frac{k}{2z}(x'^2+y'^2)}\, \mathcal{F}\!\left[U_0(x,y)\, e^{i\frac{k}{2z}(x^2+y^2)}\right]_{f_x=x'/(\lambda z),\ f_y=y'/(\lambda z)},\]the paraxial approximation to exact scalar diffraction. As with
fraunhofer_diffraction(), the output lives on its own natural grid of spacing \(\lambda z/(N\,dx)\).- Parameters:
- Returns:
ndarray of shape (Ny, Nx), complex – The propagated complex amplitude.
See also
angular_spectrum_propagateThe exact (non-paraxial) alternative, on the same input grid.
Examples
>>> ap = circular_aperture((64, 64), dx=0.01, radius=0.05) >>> U = fresnel_diffraction(ap, wavelength=0.5e-3, z=5.0, dx=0.01) >>> U.shape (64, 64)
- physicskit.optics.wave.intensity(U)[source]#
Optical intensity \(|U|^2\) of a complex field.
- Parameters:
U (ndarray) – Complex (or real) field amplitude.
- Returns:
ndarray – Real-valued intensity, same shape as
U.
Examples
>>> intensity(np.array([3.0 + 4.0j])) array([25.])
- physicskit.optics.wave.single_slit_aperture(shape, dx, width)[source]#
A single slit of the given
widthalong x, open along the full height in y.- Parameters:
- Returns:
ndarray of shape (Ny, Nx) – Real-valued array of 1.0 (open) and 0.0 (blocked).
Examples
>>> ap = single_slit_aperture((3, 7), dx=1.0, width=2.5) >>> ap[0].tolist() [0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0]
Gaussian beam propagation: the complex beam parameter, ABCD transformation, and higher-order modes.
A paraxial, monochromatic beam with a Gaussian transverse intensity profile is completely described, at any position \(z\) along its propagation axis, by a single complex number – the beam parameter \(q(z)\), introduced by Kogelnik & Li (Appl. Opt. 5, 1550, 1966) – through
where \(w(z)\) is the \(1/e^2\) intensity radius and \(R(z)\) the radius of curvature of the wavefronts. Propagation through any paraxial optical system described by a ray-transfer (ABCD) matrix updates \(q\) by the same bilinear (Mobius) transformation that acts on ray-tracing matrices, which is what makes the complex-\(q\) formalism so useful: free-space propagation, lenses, mirrors, and interfaces all become 2x2 matrix multiplications, exactly as in ordinary ray optics. This module also covers the free-space eigenmodes of the paraxial wave equation built on top of the fundamental Gaussian – the Hermite-Gaussian and Laguerre-Gaussian mode families – and the empirical \(M^2\) beam-quality factor used to describe real, non-diffraction-limited laser beams.
The ABCD matrix convention used throughout (shared with
physicskit.optics.ray) is M = [[A, B], [C, D]] acting on a ray
state [y, theta] as state_out = M @ state_in; e.g. free space of
length d is [[1, d], [0, 1]] and a thin lens of focal length f
is [[1, 0], [-1/f, 1]].
- class physicskit.optics.gaussian.GaussianBeam(wavelength, w0, z0=0.0)[source]#
Bases:
objectA fundamental (\(\mathrm{TEM}_{00}\)) Gaussian laser beam.
- Parameters:
- property divergence_angle#
Far-field half-angle divergence, \(\theta = \lambda/(\pi w_0)\).
- gouy_phase(z)[source]#
Gouy phase at axial position
z, \(\zeta(z) = \arctan[(z-z_0)/z_R]\).- Parameters:
z (float or array_like) – Axial position(s).
- Returns:
float or ndarray
- q_parameter(z)[source]#
Complex beam parameter at axial position
z, \(q(z) = (z-z_0) + i z_R\).- Parameters:
z (float) – Axial position.
- Returns:
complex
- radius_of_curvature(z)[source]#
Wavefront radius of curvature at axial position
z.\[R(z) = (z - z_0)\left[1 + \left(\frac{z_R}{z - z_0}\right)^2\right]\]with \(R(z_0) = \infty\) (the wavefront is flat at the waist), handled explicitly rather than relying on the indeterminate-form arithmetic of the formula above.
- Parameters:
z (float or array_like) – Axial position(s).
- Returns:
float or ndarray
- property rayleigh_range#
Rayleigh range, \(z_R = \pi w_0^2/\lambda\).
- physicskit.optics.gaussian.hermite_gaussian_mode(x, y, z, beam, m, n)[source]#
Hermite-Gaussian \(\mathrm{TEM}_{mn}\) mode amplitude.
\[u_{mn}(x, y, z) = \frac{w_0}{w(z)}\, H_m\!\left(\frac{\sqrt2\,x}{w(z)}\right) H_n\!\left(\frac{\sqrt2\,y}{w(z)}\right) \exp\!\left[-\frac{x^2+y^2}{w(z)^2}\right] \exp\!\left[-\frac{ik(x^2+y^2)}{2R(z)}\right] \exp\!\left[i(m+n+1)\zeta(z)\right] \exp(-ikz)\]with \(k = 2\pi/\lambda\). \(m=n=0\) reduces to the fundamental Gaussian mode carried by
beam.- Parameters:
x (array_like) – Transverse coordinates (broadcastable).
y (array_like) – Transverse coordinates (broadcastable).
z (float) – Axial position.
beam (GaussianBeam) – The underlying fundamental-mode beam (sets \(w_0\), \(z_0\), \(\lambda\)).
m (int) – Transverse mode indices along \(x\) and \(y\).
n (int) – Transverse mode indices along \(x\) and \(y\).
- Returns:
ndarray – Complex field amplitude, broadcast shape of
xandy.
Examples
>>> import numpy as np >>> beam = GaussianBeam(wavelength=1.0, w0=1.0, z0=0.0) >>> u00 = hermite_gaussian_mode(0.0, 0.0, 0.0, beam, 0, 0) >>> round(float(abs(u00)), 6) 1.0
- physicskit.optics.gaussian.laguerre_gaussian_mode(r, phi, z, beam, l, p)[source]#
Laguerre-Gaussian \(\mathrm{LG}_p^l\) mode amplitude.
\[u_{lp}(r, \phi, z) = \frac{w_0}{w(z)} \left(\frac{r\sqrt2}{w(z)}\right)^{|l|} L_p^{|l|}\!\left(\frac{2r^2}{w(z)^2}\right) \exp\!\left[-\frac{r^2}{w(z)^2}\right] \exp\!\left[-\frac{ikr^2}{2R(z)}\right] \exp(il\phi) \exp\!\left[i(|l|+2p+1)\zeta(z)\right] \exp(-ikz)\]with \(k = 2\pi/\lambda\) and \(L_p^{|l|}\) the associated Laguerre polynomial.
- Parameters:
r (array_like) – Polar transverse coordinates (broadcastable);
ris the radial distance from the axis,phithe azimuthal angle.phi (array_like) – Polar transverse coordinates (broadcastable);
ris the radial distance from the axis,phithe azimuthal angle.z (float) – Axial position.
beam (GaussianBeam) – The underlying fundamental-mode beam.
l (int) – Azimuthal (orbital) mode index.
p (int) – Radial mode index.
- Returns:
ndarray – Complex field amplitude, broadcast shape of
randphi.
Examples
>>> import numpy as np >>> beam = GaussianBeam(wavelength=1.0, w0=1.0, z0=0.0) >>> u00 = laguerre_gaussian_mode(0.0, 0.0, 0.0, beam, 0, 0) >>> round(float(abs(u00)), 6) 1.0
- physicskit.optics.gaussian.m2_beam_waist(z, wavelength, w0, M2=1.0)[source]#
Beam radius of a non-ideal (\(M^2 > 1\)) real laser beam, waist fixed at \(z=0\).
\[w(z) = w_0 \sqrt{1 + \left(\frac{z}{z_{R,\text{eff}}}\right)^2}, \qquad z_{R,\text{eff}} = \frac{\pi w_0^2}{M^2 \lambda}\]A beam-quality factor \(M^2 \ge 1\) (equal to 1 for an ideal diffraction-limited Gaussian beam) reduces the effective Rayleigh range, so a real beam of the same waist diverges faster than the ideal case.
- Parameters:
- Returns:
float or ndarray – Beam radius
w(z).
Examples
>>> round(float(m2_beam_waist(0.0, wavelength=0.5e-3, w0=0.1, M2=2.0)), 6) 0.1
- physicskit.optics.gaussian.propagate_q(q, M)[source]#
Propagate a complex beam parameter through a paraxial optical system.
Applies the same bilinear transformation used for ray-transfer (ABCD) matrices,
\[q_{\text{out}} = \frac{A q + B}{C q + D},\]with \(A, B, C, D\) the entries of
Min the conventionM = [[A, B], [C, D]].- Parameters:
q (complex) – Beam parameter before the system.
M (ndarray of shape (2, 2)) – Ray-transfer (ABCD) matrix of the optical system.
- Returns:
complex – Beam parameter
q_outafter the system.
Examples
>>> import numpy as np >>> free_space = np.array([[1.0, 2.0], [0.0, 1.0]]) >>> propagate_q(1j, free_space) (2+1j)
- physicskit.optics.gaussian.q_to_beam_params(q, wavelength)[source]#
Extract the beam radius and radius of curvature from a complex beam parameter.
Inverts
\[\frac{1}{q} = \frac{1}{R} - i\,\frac{\lambda}{\pi w^2}.\]At the beam waist \(\mathrm{Re}(1/q) = 0\), so
Rwould formally diverge; this is handled explicitly andnp.infis returned there instead of raising or silently producingnan.- Parameters:
- Returns:
w (float) – Beam radius (\(1/e^2\) intensity radius).
R (float) – Radius of curvature of the wavefront;
np.infat the waist.
Examples
>>> import numpy as np >>> zR = 3.0 >>> w, R = q_to_beam_params(1j * zR, wavelength=0.5e-3) >>> round(float(w), 6), R (0.021851, inf)
Quantum optics in the truncated Fock basis: states, Wigner functions, and Jaynes-Cummings dynamics.
Light is quantized in terms of the harmonic-oscillator (photon-number, or
Fock) states \(\lvert n\rangle\) of a single cavity mode. This module
works throughout in a Fock basis truncated to a finite dimension
cutoff – large enough that the state’s amplitude on the highest
retained level is negligible – and provides:
constructors for the number, coherent, and squeezed states (built from the ladder operators of
physicskit.quantum.core.operators);the Wigner quasi-probability distribution, computed from its closed-form matrix elements in the Fock basis (Cahill & Glauber, Phys. Rev. 177, 1857, 1969), using the dimensionless quadratures
\[\hat x = \frac{\hat a + \hat a^\dagger}{\sqrt2}, \qquad \hat p = \frac{\hat a - \hat a^\dagger}{i\sqrt2};\]the Jaynes-Cummings model (Jaynes & Cummings, Proc. IEEE 51, 89, 1963), the fully quantum treatment of a two-level atom coupled to a single cavity mode in the rotating-wave approximation, famous for predicting vacuum Rabi oscillations and collapse-and-revival – effects with no semiclassical analogue.
- class physicskit.optics.quantum_optics.JaynesCummingsModel(omega_c, omega_a, g, cutoff=10)[source]#
Bases:
objectThe Jaynes-Cummings model: a two-level atom coupled to a single quantized cavity mode.
In the rotating-wave approximation, the Hamiltonian is
\[\hat H = \omega_c\, \hat a^\dagger \hat a \otimes \hat I + \frac{\omega_a}{2}\, \hat I \otimes \hat\sigma_z + g\left(\hat a \otimes \hat\sigma_+ + \hat a^\dagger \otimes \hat\sigma_-\right)\]with the cavity (dimension
cutoff) as the left tensor factor and the atom (dimension 2, basis \(\{\lvert e\rangle, \lvert g\rangle\}\), so \(\hat\sigma_z = \mathrm{diag}(+1,-1)\), \(\hat\sigma_+ = \lvert e\rangle\langle g\rvert\), \(\hat\sigma_- = \lvert g\rangle\langle e\rvert\)) as the right factor, i.e.H = kron(H_cav, H_atom)terms throughout. A combined basis state \(\lvert n, e/g\rangle\) therefore sits at flat index2*n(excited) or2*n+1(ground) of the2*cutoff-dimensional Hilbert space.- Parameters:
- evolve(psi0, t_array)[source]#
Time-evolve a state under the Jaynes-Cummings Hamiltonian.
Diagonalizes \(\hat H\) once and reconstructs \(\lvert\psi(t)\rangle = e^{-i\hat H t}\lvert\psi_0\rangle\) at every requested time from the eigendecomposition, which is much faster than exponentiating \(\hat H\) separately for each
t.- Parameters:
psi0 (ndarray of shape (2*cutoff,)) – Initial state.
t_array (ndarray of shape (T,)) – Times at which to evaluate the evolved state.
- Returns:
ndarray of shape (T, 2*cutoff) – Complex states \(\lvert\psi(t)\rangle\) at each requested time.
- excited_state_population(t_array, n_photons=0)[source]#
Atomic excited-state population \(P_e(t)\) starting from \(\lvert e, n\rangle\).
Starts from the initial state \(\lvert e, n_\text{photons}\rangle\) (atom excited,
n_photonsphotons in the cavity) and returns\[P_e(t) = \sum_n \left\lvert \langle e, n \rvert \psi(t)\rangle \right\rvert^2.\]On resonance (\(\omega_c = \omega_a\)) with
n_photons=0, this reduces to the textbook vacuum Rabi formula \(P_e(t) = \cos^2(gt)\).- Parameters:
- Returns:
ndarray of shape (T,) – Real-valued excited-state population at each time.
Examples
>>> import numpy as np >>> jc = JaynesCummingsModel(omega_c=1.0, omega_a=1.0, g=0.5, cutoff=10) >>> t = np.array([0.0, np.pi / 2 / 0.5]) >>> Pe = jc.excited_state_population(t, n_photons=0) >>> np.round(Pe, 6) array([1., 0.])
- hamiltonian()[source]#
The Jaynes-Cummings Hamiltonian as a dense matrix.
- Returns:
ndarray of shape (2*cutoff, 2*cutoff) – The Hamiltonian \(\hat H\), in the
cavity (x) atombasis ordering documented on the class.
Examples
>>> import numpy as np >>> jc = JaynesCummingsModel(omega_c=1.0, omega_a=1.0, g=0.1, cutoff=3) >>> H = jc.hamiltonian() >>> H.shape (6, 6) >>> bool(np.allclose(H, H.conj().T)) True
- physicskit.optics.quantum_optics.coherent_state(alpha, cutoff)[source]#
The coherent state \(\lvert\alpha\rangle\), truncated to a finite Fock basis.
\[\lvert\alpha\rangle = e^{-\lvert\alpha\rvert^2/2} \sum_{n=0}^{\infty} \frac{\alpha^n}{\sqrt{n!}}\,\lvert n\rangle\]The sum is truncated to
cutoffterms and the result renormalized, so it is a good approximation only while \(\lvert\alpha\rvert^2 \ll\)cutoff(i.e. the mean photon number is well within the truncated space). Amplitudes are built iteratively in log-space viascipy.special.gammaln()to avoid overflow for largecutoff.- Parameters:
- Returns:
ndarray of shape (cutoff,) – Complex, L2-normalized state vector.
Examples
>>> psi = coherent_state(0.0, 8) >>> psi array([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j])
- physicskit.optics.quantum_optics.compute_wigner_function(state_vector, x_grid, p_grid)[source]#
Wigner quasi-probability distribution of a Fock-basis state.
Computed from the density matrix \(\rho = \lvert\psi\rangle\langle\psi\rvert\) and the closed-form Fock-basis Wigner matrix elements,
\[W(x,p) = \sum_{n,m} \rho_{nm}\, K_{nm}(x,p),\]with \(K_{nm}\) built from associated Laguerre polynomials of \(2(x^2+p^2)\) (see
scipy.special.eval_genlaguerre()). Uses dimensionless quadratures \(x = (a+a^\dagger)/\sqrt2\), \(p = (a-a^\dagger)/(i\sqrt2)\), for which \(\iint W(x,p)\,dx\,dp = 1\) for any normalized state.- Parameters:
state_vector (ndarray of shape (cutoff,)) – Fock-basis ket.
x_grid (ndarray of shape (Nx,)) – Grid of \(x\) quadrature values.
p_grid (ndarray of shape (Np,)) – Grid of \(p\) quadrature values.
- Returns:
ndarray of shape (Nx, Np) – Real-valued Wigner function \(W(x,p)\).
Examples
>>> import numpy as np >>> vac = fock_state(0, 8) >>> xg = np.linspace(-4, 4, 41) >>> pg = np.linspace(-4, 4, 41) >>> W = compute_wigner_function(vac, xg, pg) >>> W.shape (41, 41) >>> round(float(W[20, 20] * np.pi), 6) 1.0
- physicskit.optics.quantum_optics.fock_state(n, cutoff)[source]#
The number (Fock) state \(\lvert n\rangle\).
- Parameters:
- Returns:
ndarray of shape (cutoff,) – Complex state vector, all zeros except a
1at indexn.- Raises:
ValueError – If
nis negative orn >= cutoff.
Examples
>>> fock_state(1, 4) array([0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j])
- physicskit.optics.quantum_optics.squeezed_state(xi, alpha=0.0, cutoff=30)[source]#
The displaced squeezed vacuum state, using the squeeze-then-displace convention.
Constructs
\[\lvert\xi, \alpha\rangle = \hat D(\alpha)\, \hat S(\xi)\, \lvert 0\rangle, \qquad \hat S(\xi) = \exp\!\left[\frac{\xi^* \hat a^2 - \xi \hat a^{\dagger 2}}{2}\right], \qquad \hat D(\alpha) = \exp\!\left(\alpha \hat a^\dagger - \alpha^* \hat a\right)\]i.e. the vacuum is squeezed first and then displaced. (The opposite order, displace-then-squeeze, gives a different, also physically valid, state – squeeze-then-displace is the convention adopted here and is the more common one in the literature.) The ladder operators are built on the same truncated Fock basis, so this is only accurate while the squeezed/displaced amplitude is small relative to
cutoff.- Parameters:
- Returns:
ndarray of shape (cutoff,) – Complex, L2-normalized state vector.
Examples
>>> psi = squeezed_state(0.0, alpha=0.0, cutoff=6) >>> psi array([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j])
- physicskit.optics.quantum_optics.wigner_negativity(W, x_grid, p_grid)[source]#
Wigner negativity: the integrated negative volume of a Wigner function.
\[N_W = \iint \max(0, -W(x,p))\; dx\, dp\]A standard non-classicality diagnostic: zero for classical/Gaussian states (coherent, thermal, squeezed vacuum), strictly positive for genuinely non-classical states such as Fock states with \(n \ge 1\).
- Parameters:
W (ndarray of shape (Nx, Np)) – Wigner function, e.g. from
compute_wigner_function().x_grid (ndarray of shape (Nx,)) – Grid of \(x\) quadrature values.
p_grid (ndarray of shape (Np,)) – Grid of \(p\) quadrature values.
- Returns:
float – The (non-negative) integrated negative volume.
Examples
>>> import numpy as np >>> vac = fock_state(0, 8) >>> xg = np.linspace(-5, 5, 61) >>> pg = np.linspace(-5, 5, 61) >>> W = compute_wigner_function(vac, xg, pg) >>> wigner_negativity(W, xg, pg) 0.0
Interference in layered media: thin films, Fabry-Pérot cavities and 1D photonic crystals.
A plane wave crossing a stack of homogeneous layers is described exactly by the characteristic matrix of each layer (Abelès, Ann. Phys. (Paris) 5, 596 (1950); Born and Wolf, Principles of Optics, sec. 1.6),
with tilted admittance \(\eta_j = n_j\cos\theta_j\) (s polarization) or \(n_j/\cos\theta_j\) (p). The product of the layer matrices gives the reflection and transmission of the whole stack. Three classic results follow:
a single quarter-wave film of index \(\sqrt{n_0 n_s}\) cancels reflection at one wavelength (antireflection coating);
two mirrors of reflectance \(R\) facing each other transmit the Airy function \(T = 1/(1 + F\sin^2(\delta/2))\), \(F = 4R/(1-R)^2\) (Fabry and Pérot, 1899);
a periodic stack has Bloch modes with \(\cos(K\Lambda) = \tfrac12\operatorname{tr} M_{\text{period}}\), and where \(|\tfrac12\operatorname{tr} M| > 1\) light cannot propagate: a photonic band gap (Rayleigh 1887; Yablonovitch 1987).
- physicskit.optics.thin_films.airy_transmission(delta, R)[source]#
Fabry-Pérot (Airy) transmission of two lossless mirrors of reflectance \(R\).
\[T = \frac{1}{1 + F\sin^2(\delta/2)}, \qquad \delta = \frac{4\pi n L\cos\theta}{\lambda},\]where \(\delta\) is the round-trip phase.
- Parameters:
- Return type:
- Returns:
ndarray
Examples
>>> float(airy_transmission(0.0, 0.99)) # every resonance transmits fully 1.0
- physicskit.optics.thin_films.bloch_wavenumber(n_layers, d_layers, wavelength)[source]#
Bloch wavenumber \(K\) of an infinite periodic stack at normal incidence.
For one period of layers, \(\cos(K\Lambda) = \tfrac12 \operatorname{tr} M_{\text{period}}\); for two layers this is
\[\cos(K\Lambda) = \cos\delta_1\cos\delta_2 - \tfrac12\left(\frac{n_1}{n_2} + \frac{n_2}{n_1}\right)\sin\delta_1\sin\delta_2.\]- Parameters:
- Return type:
- Returns:
ndarray of complex – \(K\Lambda\), with real part in \([0, \pi]\) and a nonzero imaginary part (the decay per period) inside band gaps.
Examples
>>> KL = bloch_wavenumber([1.0, 1.0], [0.25, 0.25], 1.0) # uniform medium: K = 2 pi n / lambda >>> round(float(KL.real), 6) == round(np.pi, 6) True
- physicskit.optics.thin_films.fabry_perot_free_spectral_range(length, n=1.0, wavelength=None)[source]#
Free spectral range \(c/2nL\) in frequency, or \(\lambda^2/2nL\) in wavelength.
- physicskit.optics.thin_films.finesse(R)[source]#
Reflectance finesse \(\mathcal F = \pi\sqrt R/(1 - R)\): free spectral range over linewidth.
Examples
>>> round(float(finesse(0.9)), 2) 29.8
- physicskit.optics.thin_films.multilayer_response(n_layers, d_layers, wavelength, n_incident=1.0, n_substrate=1.5, angle=0.0, polarization='s')[source]#
Reflection and transmission of a stack of thin layers by the characteristic-matrix method.
- Parameters:
n_layers (
ArrayLike) – Refractive index of each layer, from the incident side; complex values \(n + i\kappa\) with \(\kappa > 0\) describe absorption (time dependence \(e^{-i\omega t}\)).d_layers (
ArrayLike) – Physical thickness of each layer, in the units ofwavelength.wavelength (
ArrayLike) – Vacuum wavelength(s).n_incident (
complex) – Index of the incident medium.n_substrate (
complex) – Index of the exit medium.angle (
float) – Angle of incidence in radians.polarization (
str)
- Return type:
- Returns:
dict – Complex amplitude coefficients
"r","t"and the power reflectance"R"and transmittance"T", each shaped likewavelength.
Examples
Bare glass reflects \(((1 - 1.5)/(1 + 1.5))^2 = 4\%\); a quarter-wave film of index \(\sqrt{1.5}\) removes it at the design wavelength:
>>> round(float(multilayer_response([], [], 550.0)["R"]), 4) 0.04 >>> n_ar = np.sqrt(1.5) >>> float(multilayer_response([n_ar], [550.0 / (4 * n_ar)], 550.0)["R"]) < 1e-12 True
- physicskit.optics.thin_films.quarter_wave_band_gap(n_high, n_low)[source]#
Relative width \(\Delta\omega/\omega_0\) of the first gap of a quarter-wave stack.
\[\frac{\Delta\omega}{\omega_0} = \frac4\pi\arcsin\frac{n_H - n_L}{n_H + n_L}\](Yariv and Yeh, Optical Waves in Crystals, 1984, sec. 6.3).
Examples
>>> round(quarter_wave_band_gap(2.3, 1.38), 4) 0.3217
- physicskit.optics.thin_films.quarter_wave_stack(n_high, n_low, n_pairs, design_wavelength, cavity=False)[source]#
Layer indices and thicknesses of a quarter-wave (Bragg) mirror, or of a Fabry-Pérot cavity.
- Parameters:
n_high (
float) – Indices of the two materials; the stack starts withn_high.n_low (
float) – Indices of the two materials; the stack starts withn_high.n_pairs (
int) – Number of high/low pairs.design_wavelength (
float) – Wavelength at which every layer is a quarter wave thick.cavity (
bool) – If True, return two such mirrors separated by a half-wave (\(\lambda/2n_{\text{low}}\)) spacer of the low index: a Fabry-Pérot cavity with a resonance atdesign_wavelength.
- Return type:
- Returns:
n_layers, d_layers (ndarray)
Examples
>>> n, d = quarter_wave_stack(2.3, 1.38, 2, 600.0) >>> n.real.tolist(), (n.real * d).tolist() ([2.3, 1.38, 2.3, 1.38], [150.0, 150.0, 150.0, 150.0])
Second-harmonic generation and phase matching.
In a medium with a second-order susceptibility \(\chi^{(2)}\), a field at frequency \(\omega\) drives a polarization at \(2\omega\) (Franken et al., Phys. Rev. Lett. 7, 118 (1961)). For collinear plane waves with slowly varying amplitudes \(A_1\) (fundamental) and \(A_2\) (second harmonic), normalized so that \(|A_1|^2 + |A_2|^2\) is the conserved power, the coupled-amplitude equations are (Armstrong, Bloembergen, Ducuing and Pershan, Phys. Rev. 127, 1918 (1962); Boyd, Nonlinear Optics, ch. 2)
Three regimes are solved in closed form:
undepleted pump, \(|A_2(L)|^2 = \kappa^2|A_1|^4 L^2 \operatorname{sinc}^2(\Delta k L/2)\), which oscillates with the coherence length \(L_c = \pi/\Delta k\);
perfect phase matching with pump depletion, \(|A_2|^2 = |A_0|^2\tanh^2(\kappa A_0 z)\);
quasi-phase matching, where the sign of \(\kappa\) is flipped every \(L_c\) and the harmonic grows linearly at \(2/\pi\) of the phase-matched rate.
Birefringent phase matching uses the extraordinary index \(n_e(\theta)\) to make \(n_e^{2\omega}(\theta) = n_o^{\omega}\) (Giordmaine 1962; Maker et al. 1962).
- physicskit.optics.nonlinear.extraordinary_index(theta, n_o, n_e)[source]#
Index of the extraordinary wave at angle \(\theta\) to the optic axis of a uniaxial crystal.
\[\frac{1}{n_e(\theta)^2} = \frac{\cos^2\theta}{n_o^2} + \frac{\sin^2\theta}{n_e^2}\]
- physicskit.optics.nonlinear.shg_coupled_amplitudes(z, delta_k=0.0, kappa=1.0, A0=1.0, qpm_period=None)[source]#
Integrate the SHG coupled-amplitude equations, with optional quasi-phase matching.
- Parameters:
z (
ArrayLike) – Increasing output positions, starting at 0.delta_k (
float) – Phase mismatch \(\Delta k\).kappa (
float) – Nonlinear coupling.A0 (
complex) – Fundamental amplitude at \(z = 0\); the harmonic starts at 0.qpm_period (
float|None) – If given, \(\kappa\) changes sign every half period (periodically poled crystal); first-order QPM uses \(2\pi/\Delta k\).
- Return type:
- Returns:
dict – Complex
"A1"and"A2"atz.
Examples
>>> out = shg_coupled_amplitudes(np.linspace(0, 2, 5)) >>> bool(np.allclose(np.abs(out["A2"]) ** 2, np.tanh(np.linspace(0, 2, 5)) ** 2, atol=1e-6)) True
- physicskit.optics.nonlinear.shg_phase_matched_efficiency(z, kappa=1.0, A0=1.0)[source]#
Conversion efficiency \(|A_2|^2/|A_0|^2 = \tanh^2(\kappa A_0 z)\) with pump depletion.
- Parameters:
- Return type:
- Returns:
ndarray
Examples
>>> round(float(shg_phase_matched_efficiency(10.0)), 6) 1.0
- physicskit.optics.nonlinear.shg_undepleted_power(L, delta_k, kappa=1.0, A1=1.0)[source]#
Second-harmonic power for an undepleted pump.
\[|A_2(L)|^2 = \kappa^2 |A_1|^4 L^2 \operatorname{sinc}^2(\Delta k L / 2) = \frac{4\kappa^2|A_1|^4}{\Delta k^2}\sin^2\frac{\Delta k L}{2}.\]- Parameters:
- Return type:
- Returns:
ndarray
Examples
>>> round(float(shg_undepleted_power(np.pi, delta_k=1.0)), 12) # one coherence length: 4/dk^2 4.0
- physicskit.optics.nonlinear.type_i_phase_matching_angle(n_o_w, n_o_2w, n_e_2w)[source]#
Type-I (ooe) phase-matching angle of a negative uniaxial crystal.
Solves \(n_e^{2\omega}(\theta) = n_o^{\omega}\):
\[\sin^2\theta_m = \frac{(n_o^{\omega})^{-2} - (n_o^{2\omega})^{-2}} {(n_e^{2\omega})^{-2} - (n_o^{2\omega})^{-2}}.\]- Parameters:
- Return type:
- Returns:
float – \(\theta_m\) in radians.
- Raises:
ValueError – If the birefringence cannot compensate the dispersion.
Examples
KDP doubling 1064 nm (Sellmeier indices from Zernike, JOSA 54, 1215 (1964)):
>>> round(float(np.degrees(type_i_phase_matching_angle(1.4942, 1.5129, 1.4709))), 1) 41.3
Plotting helpers: ray traces, Gaussian beam envelopes, diffraction patterns, Wigner surfaces.
Matplotlib is used for 2D plots (ray traces through an optical system, beam
waist envelopes, diffraction-pattern heatmaps), and Plotly for the
interactive 3D Wigner phase-space surface. Every function returns its figure
object rather than calling show(), so it composes cleanly into larger
figures or headless pipelines.
- physicskit.optics.visualizers.animate_diffraction_propagation(aperture, wavelength, z_values, dx, log_scale=False, interval=100, ax=None)[source]#
Animate the diffraction pattern developing as propagation distance
zincreases.Steps
physicskit.optics.wave.angular_spectrum_propagate()over each distance inz_values(treatingzas the animation’s “time” axis – the standard way to visualize the Fresnel-to-Fraunhofer development of a diffraction pattern), and animates the resulting intensity as an imshow heatmap: a double-slit aperture’s near-field wavefronts visibly evolve into the far-field interference fringes aszgrows.- Parameters:
aperture (ndarray of shape (Ny, Nx)) – Input field immediately after the aperture (e.g. from
physicskit.optics.wave.double_slit_aperture()).wavelength (float) – Wavelength.
z_values (ndarray) – Sequence of propagation distances to sweep over.
dx (float) – Grid spacing (same for input and output, since
angular_spectrum_propagate()stays on the same grid).log_scale (bool, default=False) – If True, plot
log10(intensity + eps)to reveal faint fringes.interval (int, default=100) – Delay between frames, in milliseconds.
ax (matplotlib.axes.Axes, optional) – Axes to draw into; a new figure is created if omitted.
- Returns:
matplotlib.animation.FuncAnimation
Examples
>>> import numpy as np >>> from physicskit.optics.wave import double_slit_aperture >>> ap = double_slit_aperture((64, 64), dx=1e-3, width=2e-3, separation=1e-2).astype(complex) >>> z_values = np.linspace(0.01, 2.0, 5) >>> anim = animate_diffraction_propagation(ap, wavelength=0.5e-3, z_values=z_values, dx=1e-3) >>> isinstance(anim, FuncAnimation) True
- physicskit.optics.visualizers.interactive_wigner_surface(W, x_grid, p_grid, title=None)[source]#
An interactive 3D Plotly surface plot of a Wigner quasi-probability distribution.
- Parameters:
W (ndarray of shape (Nx, Np)) – Wigner function, e.g. from
physicskit.optics.quantum_optics.compute_wigner_function().x_grid (ndarray of shape (Nx,)) – Grid of x quadrature values.
p_grid (ndarray of shape (Np,)) – Grid of p quadrature values.
title (str, optional) – Plot title.
- Returns:
plotly.graph_objects.Figure
Examples
>>> import numpy as np >>> from physicskit.optics.quantum_optics import coherent_state, compute_wigner_function >>> x = np.linspace(-4, 4, 40) >>> p = np.linspace(-4, 4, 40) >>> W = compute_wigner_function(coherent_state(1.0, 20), x, p) >>> fig = interactive_wigner_surface(W, x, p) >>> isinstance(fig, go.Figure) True
- physicskit.optics.visualizers.plot_beam_envelope(beam, z_range, n_points=200, ax=None)[source]#
Plot a Gaussian beam’s \(\pm w(z)\) waist envelope over a propagation range.
- Parameters:
beam (physicskit.optics.gaussian.GaussianBeam) – The beam to plot.
z_range (tuple(float, float)) –
(z_min, z_max)propagation-axis range to plot over.n_points (int, default=200) – Number of sampled points.
ax (matplotlib.axes.Axes, optional) – Axes to draw into; a new figure is created if omitted.
- Returns:
fig (matplotlib.figure.Figure)
ax (matplotlib.axes.Axes)
Examples
>>> from physicskit.optics.gaussian import GaussianBeam >>> beam = GaussianBeam(wavelength=0.5e-3, w0=0.1, z0=0.0) >>> fig, ax = plot_beam_envelope(beam, z_range=(-10, 10)) >>> isinstance(fig, plt.Figure) True >>> bool(ax.lines[0].get_ydata().min() >= 0) True
- physicskit.optics.visualizers.plot_diffraction_pattern(U, dx, ax=None, log_scale=False)[source]#
Plot the intensity pattern of a complex diffracted field as a 2D heatmap.
- Parameters:
U (ndarray of shape (Ny, Nx)) – Complex field amplitude (e.g. from
physicskit.optics.wave.angular_spectrum_propagate()).dx (float) – Grid spacing, used to set physical axis extents.
ax (matplotlib.axes.Axes, optional) – Axes to draw into; a new figure is created if omitted.
log_scale (bool, default=False) – If True, plot
log10(intensity + eps)to reveal faint diffraction fringes alongside the bright central peak.
- Returns:
fig (matplotlib.figure.Figure)
ax (matplotlib.axes.Axes)
Examples
>>> from physicskit.optics.wave import circular_aperture >>> U = circular_aperture((64, 64), dx=1e-3, radius=5e-3).astype(complex) >>> fig, ax = plot_diffraction_pattern(U, dx=1e-3) >>> isinstance(fig, plt.Figure) True
- physicskit.optics.visualizers.plot_ray_trace(system, y0, theta0, ax=None)[source]#
Plot a ray’s height through each element of an
OpticalSystem.- Parameters:
system (physicskit.optics.ray.OpticalSystem) – The optical system to trace the ray through.
y0 (float) – Initial ray height.
theta0 (float) – Initial ray angle.
ax (matplotlib.axes.Axes, optional) – Axes to draw into; a new figure is created if omitted.
- Returns:
fig (matplotlib.figure.Figure)
ax (matplotlib.axes.Axes)
Examples
>>> from physicskit.optics.ray import OpticalElement, OpticalSystem, free_space, thin_lens >>> system = OpticalSystem([ ... OpticalElement(free_space(1.0), name="d1", length=1.0), ... OpticalElement(thin_lens(1.0), name="lens"), ... OpticalElement(free_space(1.0), name="d2", length=1.0), ... ]) >>> fig, ax = plot_ray_trace(system, y0=0.5, theta0=0.0) >>> isinstance(fig, plt.Figure) True >>> len(ax.lines[0].get_xdata()) 4