pyoptics.tilted

Propagation onto tilted planes.

The field u(x, y) is given in the source plane z = 0. The target plane passes through the point r0 (usually (0, 0, z0)) and is spanned by the columns of a rotation matrix R: its local axes x^, y^ and its normal n^ are

x^ = R[:, 0],  y^ = R[:, 1],  n^ = R[:, 2]

in source coordinates, and the target point with local coordinates (x^, y^) lies at r = r0 + x^*R[:, 0] + y^*R[:, 1]. geometry.R_y(theta) tilts the plane about the y axis, its normal towards +x.

Every plane wave exp(i*k.r) of the source spectrum reaches the target plane as exp(i*k.r0)*exp(i*(k^_x*x^ + k^_y*y^)) with the rotated wave vector k^ = R^T k. The field on the target plane is thus again a superposition of plane waves in its own coordinates, but on a non-uniform frequency grid. Two methods evaluate it:

  • “exact”: the non-uniform plane wave sum, evaluated directly. For a rotation about the x or y axis, k^_y (or k^_x) depends on one source frequency only, and the sum separates into one matrix product per row of the spectrum (O(N³)). For a general rotation, the type 3 non-uniform FFT of finufft is used if it is installed, otherwise a direct sum (O(N⁴), only sensible for small N). No interpolation, no Jacobian.

  • “rotation” (Matsushima, Schimmel and Wyrowski, JOSA A 20, 1755 (2003)): the target spectrum is sampled on a uniform k^ grid, each k^ is mapped back to k = R k^, the (zero padded) source spectrum is interpolated there and multiplied by the Jacobian |d(k_x, k_y)/d(k^_x, k^_y)| = k_z/k^_z, followed by one inverse FFT. Fast (O(N² log N)); the accuracy is set by the interpolation (cubic by default) and improves with padding. The result is periodic in the output window: the field must decay inside it, otherwise it wraps around (the “exact” sum does not).

The Jacobian is not optional: without it, the field on the tilted plane is off by a factor of about cos(theta) (an error of 1 - cos(theta), 50 % at 60°).

Carrier. A beam travelling along z arrives on a plane tilted by theta with the transverse wavenumber k^_x = -k*sin(theta): its phase ramps by one wavelength per lambda/sin(theta). With remove_carrier=True (default) this mean tilt (the power weighted mean of k^_x, k^_y over the spectrum) is removed and returned as k_carrier, the field on the plane being

u_true(x^, y^) = u_t(x^, y^)*exp(i*(k_carrier[0]*x^ + k_carrier[1]*y^)).

k_carrier_in tells the routines about a carrier that was removed from an input field in the same way, e.g. for the way back.

Further notes:

  • Only plane waves that cross the target plane in the direction of its normal (k^_z > 0) are kept; these are all waves if the source spectrum contains no angles beyond 90° - theta. The result is thus a valid field for propagation onwards from the tilted plane. Evanescent waves of the source are dropped.

  • The footprint of a beam stretches by 1/cos(theta) along x^, and the target plane spans z0 -/+ (L/2)*sin(theta) along z. Choose the output window (x_out, y_out) and the padding accordingly. A ValueError is raised if the plane crosses the source plane inside the window.

  • The target plane may also lie behind the source plane (all points z < 0): the forward travelling waves are then propagated backwards. This is the way back from a tilted plane, see reverse.

  • |u_t|² integrated over the tilted plane is not conserved (a tilted detector catches the beam on a larger area); the power flux through the plane, power_flux(), is.

Functions:

  • tilted_plane(): scalar field on a tilted plane (or from a tilted plane to a parallel one)

  • vectorial_tilted_plane(): the same for [E_x, E_y], returning the field components in the plane’s own axes

  • rotate_angular_spectrum(): low level building block of the “rotation” method

  • tilted_beam_widths(): ISO 11146 beam widths on an inclined plane (detector)

  • power_flux(): power through a plane from the angular spectrum

  • rotation_from_angles(): R for tilts about x and y

Convention: fields vary as exp(i(k.r - omega*t)), see pyoptics.propagators.

References

K. Matsushima, H. Schimmel and F. Wyrowski, “Fast calculation method for optical diffraction on tilted planes by use of the angular spectrum of plane waves”, J. Opt. Soc. Am. A 20, 1755-1762 (2003).

Functions

power_flux

Power flux through a plane, computed from the angular spectrum.

rotate_angular_spectrum

Express an angular spectrum in a rotated frame.

rotation_from_angles

Rotation matrix R = R_x(theta_x) R_y(theta_y).

tilted_beam_widths

ISO 11146 beam widths of a beam on an inclined plane (e.g. a tilted detector).

tilted_plane

Propagate a scalar field from the plane z = 0 onto a tilted plane.

vectorial_tilted_plane

Propagate a vector field from the plane z = 0 onto a tilted plane.

pyoptics.tilted.tilted_plane(field, x, y, z0, wavelength, n=1.0, R=None, angles=None, method=None, x_out=None, y_out=None, remove_carrier=True, pad=2, kind='cubic', k_carrier_in=(0.0, 0.0), reverse=False)[source]

Propagate a scalar field from the plane z = 0 onto a tilted plane.

See the module docstring for the geometry, the methods and the carrier.

Parameters:
fieldarray

Field of shape (Ny, Nx) in the source plane.

x, yarrays

Linear or meshed coordinates of the source plane, centered (x = 0 at index N//2).

z0number or array

Distance of the target plane’s origin (0, 0, z0), or its position (x0, y0, z0).

wavelengthnumber

Vacuum wavelength.

nnumber, optional

Refractive index (real) of the medium.

Rarray, optional

Rotation matrix whose columns are the target axes x^, y^ and the normal, e.g. geometry.R_y(theta). Default: no rotation.

anglestuple, optional

(theta_x, theta_y) instead of R, see rotation_from_angles().

methodstr, optional

“exact” or “rotation”. By default “exact” for N <= 256 (input and output) if the rotation is about the x or y axis or finufft is installed (or for N <= 64), and “rotation” otherwise. “exact” takes about a second for N = 256 (with pad=2), “rotation” a few hundredths; with cubic interpolation they agree to about 1E-6 if the field fits into the output window.

x_out, y_outarrays, optional

Coordinates in the target plane (linear). Default: the source coordinates (same N and dx). “exact” accepts any points, “rotation” needs equidistant ones.

remove_carrierboolean, optional

If True (default), remove the mean phase tilt, see the module docstring.

padnumber, optional

Zero padding factor of the source field. Padding refines the sampling of the source spectrum: it improves the interpolation of “rotation” and pushes the periodic copies of the source (both methods) away.

kindstr, optional

Interpolation of the source spectrum for “rotation”: “cubic” (default, error about 1E-6 to 1E-8 relative to the peak) or “linear” (about 1E-3 without padding, decreasing with pad**2).

k_carrier_intuple, optional

Carrier (k_x, k_y) removed from the input field, which is field*exp(i*(k_x*x + k_y*y)) in truth.

reverseboolean, optional

If True, the source plane is the tilted one: the field is given on a plane rotated by R (or angles), and the target is the plane z = z0 (or through the point z0) of the unrotated frame, parallel to its x-y plane, with x_out, y_out along its x and y. This is the transformation with R^-1.

Returns:
u_tarray

Field on the target plane, shape (len(y_out), len(x_out)), with the carrier removed if remove_carrier.

x_t, y_tarrays

Linear coordinates in the target plane.

k_carrierarray

The removed carrier (k^_x, k^_y), zeros if remove_carrier is False.

pyoptics.tilted.vectorial_tilted_plane(field, x, y, z0, wavelength, n=1.0, R=None, angles=None, method=None, x_out=None, y_out=None, remove_carrier=True, pad=2, kind='cubic', k_carrier_in=(0.0, 0.0), reverse=False)[source]

Propagate a vector field from the plane z = 0 onto a tilted plane.

E_z of the source follows from div(E) = 0 (as in propagators.longitudinal_component(), a given E_z is ignored). The three Cartesian components are propagated onto the target plane like scalar fields, and are then expressed in the plane’s own axes: the local components are Ê = R^T E, i.e. Ê_x^ = E.x^, Ê_y^ = E.y^ and Ê_n = E.n^ (the component normal to the plane).

Parameters:
fieldarray

Vector field [E_x, E_y] or [E_x, E_y, E_z] of shape (Ny, Nx, 2) or (Ny, Nx, 3), components along the source axes.

x, y, z0, wavelength, n, R, angles, method, x_out, y_out, remove_carrier, pad, kind, k_carrier_in, reverse

See tilted_plane().

Returns:
E_tarray

Field [Ê_x^, Ê_y^, Ê_n] on the target plane, shape (len(y_out), len(x_out), 3).

x_t, y_tarrays
k_carrierarray
pyoptics.tilted.rotate_angular_spectrum(A, kx, ky, k, R, kx_out=None, ky_out=None, kind='cubic', jacobian=True)[source]

Express an angular spectrum in a rotated frame.

A(k_x, k_y) are the amplitudes of the plane waves exp(i*k.r) (k_z > 0) of a field, sampled on the uniform grid kx, ky. The same field, written in the frame whose axes are the columns of R (same origin), has the angular spectrum

Â(k^_x, k^_y) = A(k_x, k_y)*k_z/k^_z,  k = R k^,

k_z/k^_z being the Jacobian |d(k_x, k_y)/d(k^_x, k^_y)|. A is interpolated at k = R k^ (“linear” or “cubic”, outside of the grid it is zero). Waves with k^_z <= 0 or k_z <= 0 are set to zero.

A is treated as a spectral density. For discrete spectra (FT() samples) multiply the result by (dk^_x*dk^_y)/(dk_x*dk_y) if the frequency spacings differ.

Parameters:
Aarray

Spectrum of shape (len(ky), len(kx)).

kx, kyarrays

Linear, equidistant wavenumbers of A.

knumber

Wavenumber in the medium.

Rarray

Rotation matrix.

kx_out, ky_outarrays, optional

Wavenumbers k^_x, k^_y of the output grid. Default: kx, ky.

kindstr, optional

“cubic” (default) or “linear” interpolation.

jacobianboolean, optional

Apply the Jacobian (default). jacobian=False is wrong and only meant to demonstrate the error it causes.

Returns:
A_rotarray

Spectrum of shape (len(ky_out), len(kx_out)).

pyoptics.tilted.tilted_beam_widths(field, x, y, z0, wavelength, n=1.0, iterate=True, window=3.0, **kwargs)[source]

ISO 11146 beam widths of a beam on an inclined plane (e.g. a tilted detector).

The field is propagated with tilted_plane() and beam_quality.beam_widths() is evaluated with the intensity |u_t|² in the plane’s own coordinates. For a collimated beam on a plane tilted by theta about y, d_x grows as d/cos(theta).

Parameters:
field, x, y, z0, wavelength, n

See tilted_plane().

iterate, windowoptional

See beam_quality.beam_widths().

kwargs

Further arguments of tilted_plane() (R, angles, x_out, …).

Returns:
widthsbeam_quality.BeamWidths

Widths, azimuth and centroid in the plane’s coordinates.

u_t, x_t, y_t

Field and coordinates on the plane, see tilted_plane().

pyoptics.tilted.power_flux(field, x, y, wavelength, n=1.0, k_carrier=(0.0, 0.0))[source]

Power flux through a plane, computed from the angular spectrum.

P = sum over the propagating plane waves of |A|² k_z/k (in the units of the integral of |u|²; for a paraxial field P equals the integral of |u|² over the plane). Unlike the integral of |u|², P is the same on every plane the field crosses, tilted or not.

Parameters:
fieldarray
x, yarrays

Linear or meshed, equidistant coordinates.

wavelengthnumber
nnumber, optional
k_carriertuple, optional

Carrier removed from the field (see tilted_plane()).

Returns:
Pnumber
pyoptics.tilted.rotation_from_angles(theta_x=0.0, theta_y=0.0)[source]

Rotation matrix R = R_x(theta_x) R_y(theta_y).

The plane is first tilted by theta_y about the y axis (normal towards +x), then by theta_x about the x axis (normal towards -y).

Parameters:
theta_x, theta_ynumbers

Angles in radians.

Returns:
Rarray

3x3 rotation matrix.