Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,14 @@ device = "cuda" if torch.cuda.is_available() else "cpu"
torchoptics.set_default_spacing(10e-6)
torchoptics.set_default_wavelength(700e-9)

shape = 1000
f = 200e-3
shape = 500 # Number of grid points in each dimension
f = 50e-3 # Lens focal length (m)

input_field = Field(checkerboard(shape, tile_length=400e-6, num_tiles=15)).to(device)
input_field = Field(checkerboard(shape, tile_length=200e-6, num_tiles=15)).to(device)

system = System(
Lens(shape, f, z=1 * f),
AmplitudeModulator(1 - circle(shape, radius=5e-4), z=2 * f),
AmplitudeModulator(1 - circle(shape, radius=200e-6), z=2 * f),
Lens(shape, f, z=3 * f),
).to(device)

Expand Down
Binary file modified docs/source/_static/4f_system.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 10 additions & 10 deletions docs/source/quickstart/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ Let's create a field from a circular aperture:
.. plot::
:context: close-figs

shape = 1000 # 1000×1000 grid (10 mm × 10 mm physical extent)
field = Field(circle(shape, radius=2e-3))
shape = 500 # 500×500 grid (5 mm × 5 mm physical extent)
field = Field(circle(shape, radius=1e-3))
field.visualize(title="Circular Aperture (z = 0)")

Use :meth:`~torchoptics.Field.propagate_to_z` to propagate a field through free space. As light
Expand All @@ -73,13 +73,13 @@ travels, it diffracts, producing characteristic patterns at different distances:
:context: close-figs

# Near-field (Fresnel) diffraction
field.propagate_to_z(0.5).visualize(title="z = 0.5 m (Fresnel region)")
field.propagate_to_z(0.2).visualize(title="z = 0.2 m (Fresnel region)")

.. plot::
:context: close-figs

# Far-field (Fraunhofer) diffraction: the Airy pattern
field.propagate_to_z(10.0).visualize(title="z = 10.0 m (Fraunhofer region)")
field.propagate_to_z(2.0).visualize(title="z = 2.0 m (Fraunhofer region)")

Close to the aperture (the Fresnel region), diffraction produces fringes near the edges. Far away
(the Fraunhofer region), the wavefront converges to the `Airy pattern
Expand Down Expand Up @@ -107,18 +107,18 @@ where :math:`r = \sqrt{x^2 + y^2}`, :math:`R` is the aperture radius (half the l
physical extent), :math:`\lambda` is the wavelength, and :math:`f` is the focal length.

Calling an element on a field (``lens(field)``) applies this transformation. Let's focus a Gaussian
beam with a 400 mm lens:
beam with a 200 mm lens:

.. plot::
:context: close-figs

gaussian_beam = Field(gaussian(shape, waist_radius=3e-3))
gaussian_beam = Field(gaussian(shape, waist_radius=1e-3))
gaussian_beam.visualize(title="Gaussian Beam (z = 0)")

.. plot::
:context: close-figs

f = 1 # Focal length: 1 m
f = 200e-3 # Focal length: 200 mm
lens = Lens(shape, f, z=0)

focused = lens(gaussian_beam).propagate_to_z(f)
Expand All @@ -144,15 +144,15 @@ blocks low spatial frequencies, extracting edges from a checkerboard:
.. plot::
:context: close-figs

input_field = Field(checkerboard(shape, tile_length=400e-6, num_tiles=15))
input_field = Field(checkerboard(shape, tile_length=200e-6, num_tiles=15))
input_field.visualize(title="Input Field", vmax=1)

.. plot::
:context: close-figs

# High-pass filter at the Fourier plane (z = 2f)
f = 200e-3 # Focal length: 200 mm
filter_mask = 1 - circle(shape, radius=500e-6)
f = 50e-3 # Focal length: 50 mm
filter_mask = 1 - circle(shape, radius=200e-6)
aperture = AmplitudeModulator(filter_mask, z=2 * f)

aperture.visualize(title="High-Pass Filter at Fourier Plane")
Expand Down
13 changes: 7 additions & 6 deletions docs/source/user-guide/fields.rst
Original file line number Diff line number Diff line change
Expand Up @@ -152,16 +152,17 @@ The overlap integral between two fields is:

.. math::

\langle \psi_1 | \psi_2 \rangle = \sum_{i,j} \psi_1^*(i,j) \, \psi_2(i,j) \, \Delta A
\eta = \sum_{i,j} \psi_1(i,j) \, \psi_2^*(i,j) \, \Delta A

:meth:`~torchoptics.Field.inner` returns this as a complex scalar. Taking the squared magnitude
gives the **mode overlap** :math:`|\langle\psi_1|\psi_2\rangle|^2`: a value in :math:`[0, 1]`
when both fields are normalized to unit power (by Cauchy–Schwarz), and a natural fidelity metric
for inverse design (see :doc:`inverse_design`):
:meth:`~torchoptics.Field.inner` returns this as a complex scalar, where :math:`\psi_1` is
``self`` and :math:`\psi_2` is the argument. Taking the squared magnitude gives the
**mode overlap** :math:`|\eta|^2`: a value in :math:`[0, 1]` when both fields are normalized
to unit power (by Cauchy–Schwarz), and a natural fidelity metric for inverse design
(see :doc:`inverse_design`):

.. code-block:: python

overlap = field_a.inner(field_b).abs().square() # in [0, 1] if both normalized
overlap = field_a.inner(field_b).abs().square() # |η|² in [0, 1] if both normalized
loss = 1 - overlap

Both fields must share the same geometry (``shape``, ``spacing``, ``offset``, and ``z``).
Expand Down
7 changes: 4 additions & 3 deletions docs/source/user-guide/propagation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ a critical distance :math:`z_c` and comparing it to the propagation distance

z_c = \frac{2 \, |x_\text{max}| \, \Delta}{\lambda}

where :math:`|x_\text{max}|` is the maximum coordinate magnitude across both planes
(half the physical field width), :math:`\Delta` is the spacing, and :math:`\lambda` is the
wavelength.
where :math:`|x_\text{max}|` denotes, for each transverse dimension (x and y), the maximum
coordinate separation along that dimension between any grid point in the input plane and
any grid point in the propagation plane; :math:`\Delta` is the grid spacing in that
dimension, and :math:`\lambda` is the wavelength.

- :math:`|\Delta z| < z_c` in at least one dimension → **ASM** (short-distance regime).
- Otherwise → **DIM** (long-distance regime).
Expand Down
9 changes: 5 additions & 4 deletions docs/source/user-guide/spatial_coherence.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,11 @@ and Gaussian coherence:
\Gamma(x_1, y_1, x_2, y_2) = \sqrt{I(x_1,y_1)\,I(x_2,y_2)} \cdot
\exp\!\left(-\frac{|\mathbf{r}_1-\mathbf{r}_2|^2}{2\sigma_c^2}\right)

where :math:`I(x,y) = \exp(-2r^2/w^2)` is the Gaussian intensity with waist :math:`w`, and
:math:`\sigma_c` is the **coherence width**: the length scale over which the field remains
correlated. A small :math:`\sigma_c` (much less than :math:`w`) gives a low-coherence source
like an LED; a large :math:`\sigma_c` approaches a coherent Gaussian beam:
where :math:`I(x,y) = \frac{2}{\pi w^2}\exp(-2r^2/w^2)` is the Gaussian intensity with waist
:math:`w` (normalized to unit power), and :math:`\sigma_c` is the **coherence width**: the
length scale over which the field remains correlated. A small :math:`\sigma_c` (much less than
:math:`w`) gives a low-coherence source like an LED; a large :math:`\sigma_c` approaches a
coherent Gaussian beam:

.. plot::
:context: reset
Expand Down
10 changes: 5 additions & 5 deletions examples/optical_systems/4f_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
plane enables spatial filtering: low-pass filters smooth the image, while high-pass
filters extract edges and fine detail.

The four focal planes have distinct physical meanings:
The five key planes have distinct physical meanings:

- :math:`z = 0` — Input plane: the object field
- :math:`z = f` — Back focal plane of lens 1: spatial frequency spectrum begins forming
Expand Down Expand Up @@ -39,12 +39,12 @@
# A checkerboard input field tests both low- and high-frequency response,
# since a checkerboard contains spatial frequencies near its fundamental tile period.

shape = 1000 # Grid size (number of points per dimension)
shape = 500 # Grid size (number of points per dimension)
spacing = 10e-6 # Grid spacing (m)
wavelength = 700e-9 # Wavelength (m)
focal_length = 200e-3 # Lens focal length (m)
focal_length = 50e-3 # Lens focal length (m)

tile_length = 400e-6 # Checkerboard tile size (m)
tile_length = 200e-6 # Checkerboard tile size (m)
num_tiles = 15 # Number of tiles in each dimension

device = "cuda" if torch.cuda.is_available() else "cpu"
Expand Down Expand Up @@ -90,7 +90,7 @@
# frequencies, leaving only the low-frequency content. The result is a blurred
# version of the input; edges are smoothed and fine detail is lost.

radius = 500e-6 # Low-pass filter radius in the Fourier plane (m)
radius = 200e-6 # Low-pass filter radius in the Fourier plane (m)
low_pass_profile = circle(shape, radius)

low_pass_4f_system = System(
Expand Down
2 changes: 1 addition & 1 deletion examples/optimization/training_petal_beam.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
# Target Field: Eight-Petal Beam
# ------------------------------
# The target is an interference pattern formed by the superposition of two Laguerre-Gaussian
# modes :math:`\mathrm{LG}_{0}^{4}` and :math:`\mathrm{LG}_{0}^{-4}`, producing an
# modes :math:`\mathrm{LG}_{0}^{+4}` and :math:`\mathrm{LG}_{0}^{-4}`, producing an
# eight-petal intensity distribution.

petal_profile = laguerre_gaussian(shape, p=0, l=4, waist_radius=waist_radius)
Expand Down
Loading