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)