API Cheat Sheet

Copy-paste snippets for the 20 most common Optiland tasks. New to these concepts? See the Glossary first.


1. Install and import

pip install optiland
from optiland import optic
import optiland.backend as be

2. Load a sample lens

from optiland.samples.objectives import CookeTriplet, ReverseTelephoto

lens = CookeTriplet()
lens.info()   # print surface table

3. Build a simple singlet from scratch

from optiland import optic

lens = optic.Optic()
lens.surfaces.add(index=0, radius=float("inf"), thickness=float("inf"))  # object at infinity
lens.surfaces.add(index=1, radius=50.0, thickness=5.0, material="N-BK7", is_stop=True)
lens.surfaces.add(index=2, radius=-50.0, thickness=45.0)
lens.surfaces.add(index=3)  # image plane
lens.set_aperture(aperture_type="EPD", value=10.0)
lens.fields.set_type("angle")
lens.fields.add(y=0.0)
lens.wavelengths.add(value=0.5876, is_primary=True)
lens.updater.image_solve()

4. Add a surface

# Insert a surface at index 2 with radius, thickness, and material
lens.surfaces.add(index=2, radius=-435.76, thickness=6.0, material=("F2", "schott"))

5. Set aperture, field, and wavelength

lens.set_aperture(aperture_type="EPD", value=10.0)
# alternatives: "imageFNO", "objectNA", "float_by_stop_size"

lens.fields.set_type("angle")   # or "object_height"
lens.fields.add(y=0.0)
lens.fields.add(y=14.0)
lens.fields.add(y=20.0)

lens.wavelengths.add(value=0.4861)                    # F-line
lens.wavelengths.add(value=0.5876, is_primary=True)   # d-line
lens.wavelengths.add(value=0.6563)                    # C-line

6. Switch backend (NumPy ↔ PyTorch)

import optiland.backend as be

be.set_backend("torch")    # enable PyTorch (autograd, GPU)
be.set_backend("numpy")    # revert to NumPy (default)

# GPU and precision (PyTorch only)
be.set_device("cuda")
be.set_precision("float64")

7. Draw the lens (2D)

lens.draw(num_rays=5, distribution="line_y")

8. Draw the lens (3D)

lens.draw3D(num_rays=24, distribution="ring")

9. Trace rays manually

# Trace a distribution of rays for a given field and wavelength
rays = lens.trace(Hx=0, Hy=0, wavelength=0.5876, num_rays=64, distribution="hexapolar")
print(rays.x, rays.y)   # image-plane x, y coordinates

# Trace a single ray defined by normalized field + pupil coordinates
ray = lens.trace_generic(Hx=0, Hy=1, Px=0, Py=0, wavelength=0.5876)

10. Spot diagram

from optiland.analysis import SpotDiagram

spot = SpotDiagram(lens)
spot.view()

11. Ray fan plot

from optiland.analysis import RayFan

fan = RayFan(lens)
fan.view()

12. Wavefront / Zernike decomposition

from optiland.wavefront import Wavefront, ZernikeOPD

wf = Wavefront(lens, field=(0, 0), wavelength="primary")
wf.view()

zfit = ZernikeOPD(lens, field=(0, 0), wavelength="primary", num_terms=37)
zfit.view()

13. PSF and MTF

from optiland.psf import FFTPSF
from optiland.mtf import FFTMTF

psf = FFTPSF(lens, field=(0, 0), wavelength="primary")
psf.view()

mtf = FFTMTF(lens)
mtf.view()

14. Paraxial properties (EFL, f/#, pupil positions)

print("EFL:", lens.paraxial.f2())
print("f/#:", lens.paraxial.FNO())
print("EPD:", lens.paraxial.EPD())
print("EPL:", lens.paraxial.EPL())   # entrance pupil location, relative to surface 1
print("XPL:", lens.paraxial.XPL())   # exit pupil location, relative to the image surface
print("Magnification:", lens.paraxial.magnification())

Note

EPL() is measured relative to the first physical surface (surface 1), matching the convention of XPL() (relative to the image surface) and the other first-order quantities. If you need the entrance pupil as a global z coordinate — e.g. to compare against object or surface positions — use lens.paraxial.entrance_pupil_z().


15. Define an optimization variable

from optiland.optimization import OptimizationProblem

problem = OptimizationProblem()
problem.add_variable(lens, "radius", surface_number=1)
problem.add_variable(lens, "thickness", surface_number=1)

16. Define an operand

input_data = {"optic": lens}
problem.add_operand(operand_type="f2", target=50.0, weight=1, input_data=input_data)
problem.add_operand(operand_type="rms_spot_size", target=0.0, weight=1,
                    input_data={"optic": lens, "field_index": 1, "wavelength_index": 0,
                                "distribution": "hexapolar", "num_rays": 100})

17. Run local optimization

from optiland.optimization import LeastSquares

optimizer = LeastSquares(problem)
result = optimizer.optimize()
print(result)

18. Run global optimization

from optiland.optimization import DualAnnealing

optimizer = DualAnnealing(problem)
result = optimizer.optimize()
print(result)

19. Save / load system (JSON)

from optiland.fileio import save_optiland_file, load_optiland_file

save_optiland_file(lens, "my_lens.json")
lens2 = load_optiland_file("my_lens.json")

20. Generate a prescription report

from optiland.prescription import Prescription

p = Prescription(lens)
p.view()                         # Rich console output
p.save("prescription.txt")       # plain text
p.save("prescription.pdf")       # PDF (requires reportlab)