pyoptics.raywave¶
Conversion between ray ensembles and sampled wave fields.
Geometrical optics describes a wave field by its rays: the eikonal S (the optical path, OPL) gives the phase k_0*S, the rays are the normals of its wavefronts, and the transport equation div(I grad S) = 0 says that the power in a tube of rays is conserved. This module goes back and forth between the two descriptions.
Rays to waves¶
Ray field summation (rays_to_field()). Rays launched on a structured grid (e.g. a grid of pupil coordinates) are traced to a plane; the grid of their intersections is cut into triangles, each a small tube of rays. A triangle carries the power of its launch triangle, so its irradiance in the plane is the launch irradiance times the ratio of the launch and target areas (the inverse Jacobian of the ray map, Born and Wolf ch. 3.1). Inside the triangle the phase is interpolated from the eikonal S and its gradient n*d_perp at the three rays (the mean of the linear interpolation and of the tangent planes, which is exact for quadratic wavefronts), the field of the triangle is
u = sqrt(I_launch*|A_launch/A_target|)*exp(i*k_0*S)*exp(-i*pi*m/2),
and the fields of all triangles that cover a sample are added. Where the ray map folds (behind a caustic), several sheets of triangles cover the same point and interfere; m, the Maslov (KMAH) index, counts the caustics that the tube has crossed: the number of negative eigenvalues of the 2 x 2 affine map from the launch triangle to the target triangle (1 at a line focus, 2 at a point focus: the Gouy phase -pi). This is the classical asymptotic field. It is exact far from caustics, where the field is locally a sum of plane waves with slowly varying amplitude, and it diverges on the caustic itself (A_target -> 0), where the rays touch and geometrical optics breaks down; in the shadow of a caustic it gives zero instead of the evanescent Airy tail (Kravtsov and Orlov 1993).
Gaussian beamlets (GaussianBeamlets). The standard remedy is to give every ray a finite width: the field in a launch plane is written as a sum of Gaussian beamlets centred on the rays, each with the local tilt and curvature of the wavefront (Červený, Popov and Pšenčík 1982; Arnaud 1985; the Gaussian beam decomposition of lens design programs). A frame of Gaussians exp(-r**2/w**2) on a grid of spacing Delta adds up to pi*w**2/Delta**2 (to better than 1e-8 for w >= 1.5*Delta), so the beamlet of a sample with complex amplitude A and the area Delta**2 has the amplitude A*Delta**2/(pi*w**2) and
u_j(x) = a_j*exp(i*(phi_j + k_j.xi + xi.Gamma_j.xi/2)), xi = x - x_j,
Gamma_j = H_j + 2i/w_j**2,
with the transverse wave vector k_j and the Hessian H_j of the phase. In a homogeneous medium the angular spectrum of every beamlet is propagated in closed form with k_z expanded to second order about k_j (k_z ~ k_z0 + g.dk + dk.G.dk/2, any tilt):
u_j(x, z) = a_j/sqrt(det(I - z*Gamma_j*G))
*exp(i*(phi_j + z*k_z0 + k_j.xi + eta.Gamma_z.eta/2)),
eta = xi + z*g = xi - z*k_j/k_z0, Gamma_z = Gamma_j*(I - z*G*Gamma_j)^-1.
The centre moves along the ray, the width and curvature follow the complex q parameter, and the square root (taken on the continuous branch: the eigenvalues of Gamma*G have negative imaginary parts) gives the Gouy phase. The beamlets stay finite at foci and caustics, so their sum is the field everywhere, as long as the beamlets are narrow compared to the scale on which the wavefront curvature changes and wide compared to the wavelength. Beamlets can be launched from rays (GaussianBeamlets.from_rays(), curvature from neighbouring rays) or from a sampled field (GaussianBeamlets.from_field(), also from a QuadraticPhaseField of pyoptics.analytic_phase, whose analytic phase supplies tilt and curvature).
GaussianBeamlets.trace() carries the beamlets through the surfaces of a raytrace.System (Gaussian beam decomposition through real optics). Every beamlet is followed by its base ray and eight parabasal rays (centre and direction shifted in x and y), traced exactly. Their central differences in the output plane give the 4 x 4 ray transfer matrix M = [[A, B], [C, D]] of the neighbourhood of the base ray in the canonical coordinates (x, k_perp), which is symplectic, and the beamlet follows the complex ABCD law (a Gaussian beamlet is the family of complex rays k_perp = Gamma*xi, Arnaud 1985)
Gamma' = (C + D Gamma)(A + B Gamma)^-1, a' = a/sqrt(det(A + B Gamma)),
with the phase k_0*OPL of the base ray. det(A + B Gamma) never vanishes, but its argument grows by pi through every line focus: the square root is taken on the branch that follows the beamlet (its Gouy phase), accumulated in closed form along every straight segment of the base ray. Beamlets whose rays are vignetted are dropped, so an aperture inside a system is resolved to the beamlet size; for a sharp cut, sum the beamlets in the aperture plane and decompose the field again with from_field().
Waves to rays¶
Phase gradient (field_to_rays()). The time averaged energy flux of a scalar
field, the Poynting vector of the paraxial vector field, is proportional to
Im(conj(u)*grad u) = |u|**2 grad(phase): the local ray direction is the
phase gradient, k_perp = grad(phase), completed with
k_z = sqrt(n**2 k_0**2 - |k_perp|**2), each sample becomes a ray with the
power |u|**2 dx dy and the optical path phase/k_0. The gradient is computed
from the phase differences of neighbouring samples (no unwrapping needed for
the directions). The result can be traced with raytrace.System.trace(). It
is the right picture where the field is locally a single plane wave; inside
a caustic, where several sheets overlap, the local phase gradient is an
average direction weighted with the interference pattern and need not
describe any ray.
Local spectra and Wigner functions (local_spectrum_rays(), wigner()). Phase space distributions assign several directions to one point. The Wigner function
W(x, k) = int u(x + s/2) conj(u(x - s/2)) exp(-i*k*s) ds
(here in 1D, for a section of a field) has the intensity and the spectrum as marginals, is concentrated on the Lagrangian manifold of the ray family, k = dS/dx, which folds at caustics, and oscillates (negative values) between the sheets (Bastiaans 1979; Alonso 2011). Its smoothed, positive version is the spectrogram (Husimi function): the spectrum of the field in a Gaussian window. local_spectrum_rays() samples it in 2D: every window centre and every spectral component above a threshold is a ray, with the power of that component. It resolves crossing ray sheets at the price of the uncertainty relation (position to the window size, direction to 2*pi/window), and the rays carry no phase (an incoherent ray set).
Helpers: straight_rays() moves rays to another plane of a homogeneous medium, ray_irradiance() bins ray powers into an irradiance map.
Conventions as in pyoptics.propagators and pyoptics.raytrace: fields vary as
exp(i(k.r - omega*t)), the phase of the field is +k_0*OPL (vacuum
wavenumber times the optical path, which includes the refractive index),
the light travels in +z, ray directions are unit vectors. Fields are
arrays of shape (Ny, Nx) on linear coordinates x, y. |u|**2 is the
irradiance on the plane (power per area of the plane, not per area normal to
the ray).
References¶
M. Born and E. Wolf, “Principles of Optics”, 7th ed., Cambridge University Press (1999), ch. 3.
Yu. A. Kravtsov and Yu. I. Orlov, “Caustics, Catastrophes and Wave Fields”, Springer (1993).
V. P. Maslov and M. V. Fedoriuk, “Semi-Classical Approximation in Quantum Mechanics”, Reidel (1981).
V. Červený, M. M. Popov and I. Pšenčík, “Computation of wave fields in inhomogeneous media - Gaussian beam approach”, Geophys. J. R. Astr. Soc. 70, 109-128 (1982).
J. A. Arnaud, “Representation of Gaussian beams by complex rays”, Appl. Opt. 24, 538-543 (1985).
A. W. Greynolds, “Propagation of generally astigmatic Gaussian beams along skew ray paths”, Proc. SPIE 560, 33-50 (1985).
M. J. Bastiaans, “Wigner distribution function and its application to first-order optics”, J. Opt. Soc. Am. 69, 1710-1716 (1979).
M. A. Alonso, “Wigner functions in optics: describing beams as ray bundles and pulses as particle ensembles”, Adv. Opt. Photon. 3, 272-365 (2011).
G. W. Forbes and M. A. Alonso, “Using rays better. I. Theory for smoothly varying media”, J. Opt. Soc. Am. A 18, 1132-1145 (2001).
Classes
Rays from a field: start points and unit directions, shape (N, 3), optical path (N,) (NaN if the rays carry no phase) and power (N,). |
|
A field written as a sum of Gaussian beamlets, propagated in closed form. |
Functions
Rays along the local phase gradient (energy flux) of a field. |
|
Rays from the local spectra of a field (sampled spectrogram / Husimi function). |
|
Irradiance of a ray ensemble: the powers binned on a grid, per area. |
|
Sampled field from rays on a structured grid (ray field summation). |
|
Move rays along straight lines to the plane z of a homogeneous medium. |
|
Wigner distribution of a 1D field (e.g. a section of a 2D field). |
- class pyoptics.raywave.FieldRays(points, directions, opl, power)¶
Bases:
tupleRays from a field: start points and unit directions, shape (N, 3), optical path (N,) (NaN if the rays carry no phase) and power (N,). Has the attributes of raytrace.Rays, so it can be passed to System.trace() directly.
- directions¶
Alias for field number 1
- opl¶
Alias for field number 2
- points¶
Alias for field number 0
- power¶
Alias for field number 3
- pyoptics.raywave.straight_rays(points, directions, opl, z, n=1.0)[source]¶
Move rays along straight lines to the plane z of a homogeneous medium.
- Parameters:
- points, directionsarrays, shape (…, 3)
Positions and unit directions.
- oplarray, shape (…)
Optical path at the positions.
- znumber
z of the target plane.
- nnumber, optional
Refractive index of the medium.
- Returns:
- points, oplarrays
Intersections with the plane and the optical path there (the path is negative for rays that travel backwards to reach the plane).
- pyoptics.raywave.ray_irradiance(points, power, x_edges, y_edges)[source]¶
Irradiance of a ray ensemble: the powers binned on a grid, per area.
- Parameters:
- pointsarray, shape (…, 2) or (…, 3)
Ray positions in the plane (x, y; z is ignored).
- powerarray or number
Power of each ray.
- x_edges, y_edgesarrays
Bin edges.
- Returns:
- Earray, shape (len(y_edges) - 1, len(x_edges) - 1)
Power per area of the bins (the geometrical intensity).
- pyoptics.raywave.rays_to_field(x, y, points, directions, opl, wavelength, launch=None, irradiance=1.0, n=1.0, gouy=True, max_pairs=4000000)[source]¶
Sampled field from rays on a structured grid (ray field summation).
Every cell of the ray grid is split into two triangles (tubes of rays). A triangle carries the power of its launch triangle, its irradiance in the target plane is the launch irradiance times the area ratio, its phase is interpolated from the eikonal and the ray directions of its corners, and the fields of all triangles covering a sample are added (several sheets behind a caustic). See the module docstring.
- Parameters:
- x, yarrays
Linear, equidistant coordinates of the output grid.
- pointsarray, shape (Nv, Nu, 2) or (Nv, Nu, 3)
Ray intersections with the target plane, on the structured launch grid (neighbouring rays are neighbours in the array). NaN for lost rays; their triangles are skipped.
- directionsarray, shape (Nv, Nu, 3)
Unit ray directions at the target plane.
- oplarray, shape (Nv, Nu)
Optical paths at the target plane.
- wavelengthnumber
Vacuum wavelength.
- launcharray, shape (Nv, Nu, 2), optional
Positions of the rays in the launch plane (e.g. entrance pupil coordinates in length units, or the start points). Default: the array indices (u, v), so that irradiance is the power per grid cell.
- irradiancenumber or array (Nv, Nu), optional
Irradiance at the launch positions (power per launch area).
- nnumber, optional
Refractive index at the target plane.
- gouybool, optional
Include the phase -pi/2 per crossed caustic (Maslov index).
- max_pairsint, optional
Triangle-sample pairs evaluated at once (memory).
- Returns:
- uarray, shape (len(y), len(x))
Complex field with
|u|**2the geometrical irradiance and phase k_0*OPL.
- class pyoptics.raywave.GaussianBeamlets(centres, k_perp, phase, amplitude, Gamma, wavelength, n=1.0, z0=0.0)[source]¶
Bases:
objectA field written as a sum of Gaussian beamlets, propagated in closed form.
Beamlet j in the launch plane z0
u_j = a_j*exp(i*(phase_j + k_j.xi + xi.Gamma_j.xi/2)), xi = (x, y) - centre_j,
with the transverse wave vector k_j and the complex symmetric matrix Gamma_j = H_j + 2i/w_j**2 (H: Hessian of the wavefront phase, w: 1/e amplitude radius). field() evaluates the sum in any plane z of the homogeneous medium (see the module docstring). Usually built with from_rays() or from_field().
- Parameters:
- centresarray, shape (N, 2)
Positions (x, y) in the launch plane.
- k_perparray, shape (N, 2)
Transverse wave vectors (k_x, k_y),
|k_perp| < n*k_0.- phasearray, shape (N,)
Phase at the centres.
- amplitudearray, shape (N,)
Complex amplitudes a_j.
- Gammaarray, shape (N, 2, 2)
Complex curvature matrices (positive definite imaginary part).
- wavelengthnumber
Vacuum wavelength.
- nnumber, optional
Refractive index of the medium.
- z0number, optional
z of the launch plane.
- property k¶
Wavenumber in the medium.
- classmethod from_field(field, x, y, wavelength, n=1.0, step=4, overlap=1.5, phase=None, threshold=0.0001, z0=0.0)[source]¶
Beamlets on a sub grid of a sampled field.
- Parameters:
- fieldarray (Ny, Nx) or analytic_phase.QuadraticPhaseField
The field. For a QuadraticPhaseField, tilt and curvature of the analytic phase are taken exactly and only the smooth amplitude is differentiated (x, y and wavelength are then taken from it and the arguments ignored).
- x, yarrays
Linear coordinates.
- wavelengthnumber
Vacuum wavelength.
- nnumber, optional
Refractive index.
- stepint, optional
Beamlet spacing in samples.
- overlapnumber, optional
Beamlet radius w over the spacing (>= 1.5 for a flat frame).
- phasearray, optional
Unwrapped phase of the field; default: unwrapped by integrating the wrapped phase differences.
- thresholdnumber, optional
Drop beamlets with
|u|**2below threshold times the maximum.- z0number, optional
z of the field.
- classmethod from_rays(points, directions, opl, wavelength, launch=None, irradiance=1.0, n=1.0, overlap=1.5)[source]¶
One beamlet per ray of a structured ray grid in a plane.
The wavefront curvature at every ray comes from the directions of its neighbours (H = dk_perp/dx, finite differences on the grid), the beamlet radius from the local ray spacing, w = overlap*sqrt(area), the amplitude from the irradiance, carried from the launch plane like in rays_to_field().
- Parameters:
- pointsarray, shape (Nv, Nu, 3)
Ray intersections with the launch plane of the beamlets (all with the same z).
- directionsarray, shape (Nv, Nu, 3)
Unit directions.
- oplarray, shape (Nv, Nu)
Optical paths.
- wavelengthnumber
Vacuum wavelength.
- launch, irradianceoptional
As in rays_to_field(): positions and irradiance of the rays where they were launched (default: the irradiance in the beamlet plane is the power per grid cell over the cell area, i.e. the given irradiance is the power per cell).
- nnumber, optional
Refractive index of the medium.
- overlapnumber, optional
Beamlet radius over the ray spacing.
- propagated(z)[source]¶
Centres, widths and curvatures in the plane z (see field()).
- Returns:
- centresarray (N, 2)
Where the beamlet axes cross the plane.
- Gamma_zarray (N, 2, 2)
Complex curvature matrices there.
- factorarray (N,)
Complex amplitude factor a_j/sqrt(det(I - (z - z0) Gamma G)) times the phase exp(i*(phase_j + (z - z0)*k**2/k_z)) on the axis.
- field(x, y, z=None, max_pairs=20000000)[source]¶
The sum of the beamlets at the points (x, y) of the plane z.
- Parameters:
- x, yarrays
Coordinates, broadcast against each other (e.g. a meshgrid, or a line).
- znumber, optional
Plane (default: the launch plane z0).
- max_pairsint, optional
Beamlet-point pairs evaluated at once (memory).
- Returns:
- ucomplex array of the broadcast shape of x and y
- trace(system, z=None, clip=True, delta=0.001, details=False)[source]¶
Carry the beamlets through the surfaces of a raytrace.System.
Every beamlet is followed by its base ray and eight parabasal rays (its centre shifted by +-delta*w in x and y, its transverse wave vector by +-delta*2/w in k_x and k_y), all traced exactly through the system. In the output plane, central differences of the parabasal rays give the 4 x 4 ray transfer matrix M = [[A, B], [C, D]] of the neighbourhood of the base ray, in the canonical coordinates (x, y, k_x, k_y) of the launch plane and of the output plane, and the beamlet follows the complex ABCD law (Arnaud 1985)
Gamma' = (C + D Gamma)(A + B Gamma)^-1, a' = a/sqrt(det(A + B Gamma)), phase' = k_0*OPL of the base ray.
The square root is taken on the branch that follows the beamlet through the system (its Gouy phase): the argument of det(A + B Gamma) is accumulated in planes normal to the base ray, in closed form along every straight segment and by the (small) change of the projection at every surface. A beamlet is dropped if one of its rays is lost (vignetting with clip=True, total internal reflection, missed surfaces). Mirrors fold the light out of +z; the output plane must lie in a medium where the light travels in +z.
- Parameters:
- systemraytrace.System
The system, with the same wavelength; the beamlets lie in its object space (index system.n[0]), in front of the first surface.
- znumber, optional
Output plane (global z) behind the last surface; default: the image plane of the system.
- clipbool, optional
Drop beamlets that hit a semi diameter.
- deltanumber, optional
Size of the parabasal offsets relative to the beamlet.
- detailsbool, optional
Also return the transfer matrices and Gouy phases.
- Returns:
- beamletsGaussianBeamlets
In the plane z, in the medium behind the last surface.
- Marray (N, 4, 4)
Only with details=True: transfer matrices of the kept beamlets.
- gouyarray (N,)
Only with details=True: -arg(det(A + B Gamma))/2, unwrapped (for a round beamlet through a focus: -pi/2 per line focus).
- keeparray (N,) of bool
Only with details=True: which input beamlets were kept.
- pyoptics.raywave.field_to_rays(field, x=None, y=None, wavelength=None, n=1.0, z=0.0, step=1, threshold=0.001, phase=None, structured=False)[source]¶
Rays along the local phase gradient (energy flux) of a field.
Every sample (of a sub grid with the given step) with
|u|**2above threshold times the maximum (and so are its four neighbours) becomes a ray at (x, y, z) with the direction (k_x, k_y, k_z)/(n*k_0), k_perp = grad(phase), the power|u|**2times the area per ray and the optical path phase/k_0.- Parameters:
- fieldarray (Ny, Nx) or analytic_phase.QuadraticPhaseField
Field. For a QuadraticPhaseField the analytic phase is differentiated exactly (x, y, wavelength and n are taken from it).
- x, yarrays
Linear coordinates.
- wavelengthnumber
Vacuum wavelength.
- nnumber, optional
Refractive index.
- znumber, optional
z of the plane (for the start points).
- stepint, optional
Take every step-th sample along x and y.
- thresholdnumber, optional
Relative intensity below which no rays are made.
- phasearray, optional
Unwrapped phase; default: unwrapped from the field (for the optical path; the directions never need unwrapping).
- structuredbool, optional
If True, keep the grid: the arrays have the shape (Ny’, Nx’, …) of the (sub sampled) grid, with NaN for the dropped samples, so that the rays (also after System.trace() and a reshape) can be turned into a field again with rays_to_field() (launch: the start points, irradiance:
|u|**2).
- Returns:
- FieldRays
Can be traced with raytrace.System.trace(). Samples whose gradient exceeds n*k_0 (evanescent) are dropped.
- pyoptics.raywave.local_spectrum_rays(field, x, y, wavelength, n=1.0, z=0.0, window=32, step=None, threshold=0.01, pad=2, jitter=None)[source]¶
Rays from the local spectra of a field (sampled spectrogram / Husimi function).
The field is cut into Gaussian windowed patches (1/e**2 intensity radius window/4 samples) centred every step samples. Every component of the (zero padded) patch spectrum with a power above threshold times the largest one becomes a ray at the patch centre with the direction of its wave vector and the power of that component, scaled such that all rays together carry the power of the field. Several ray directions per point: crossing sheets of a caustic are resolved. The rays carry no phase (opl NaN).
- Parameters:
- fieldarray (Ny, Nx)
Sampled field.
- x, yarrays
Linear coordinates.
- wavelengthnumber
Vacuum wavelength.
- nnumber, optional
Refractive index.
- znumber, optional
z of the plane.
- windowint, optional
Patch size in samples (even).
- stepint, optional
Distance of the patch centres in samples (default window//4).
- thresholdnumber, optional
Relative power below which spectral components are dropped.
- padint, optional
Zero padding factor of the patch FFT (finer direction sampling).
- jitternumpy.random.Generator or int, optional
If given (a generator or a seed), every ray is moved to a random point of its phase space cell (uniformly within the step in x and y and the spectral bin in k_x and k_y). Without, the rays sit on a lattice of positions and directions, which shows as moire patterns when the rays are binned in other planes.
- Returns:
- FieldRays
The power is normalized such that its sum equals
sum(|u|**2)*dx*dy.
- pyoptics.raywave.wigner(u, x, upsample=2)[source]¶
Wigner distribution of a 1D field (e.g. a section of a 2D field).
W(x, k) = int u(x + s/2) conj(u(x - s/2)) exp(-i*k*s) ds, evaluated on the sample positions x and the wavenumbers k (the field is interpolated by Fourier zero padding first, by the factor upsample, so that the wavenumbers cover the full band of the input sampling without aliasing).
Its marginals are the intensity,
int W dk/(2*pi) = |u(x)|**2, and the power spectrum; it is real and can be negative (interference between ray sheets).- Parameters:
- uarray (N,)
Field.
- xarray (N,)
Linear coordinates.
- upsampleint, optional
Interpolation factor (>= 2).
- Returns:
- karray (N*upsample,)
Wavenumbers, ascending, from -pi/dx to pi/dx.
- Warray (len(k), N)
Wigner distribution, W[i, j] at (x[j], k[i]).