Optiland Cheat Sheet

Optiland is a Python library for designing, analyzing, and optimizing optical systems. This guide covers the fundamental concepts to get you started.

Core Concepts

At its heart, Optiland revolves around a few key components:

  • Optic Object: This is the main container for your entire optical system. It holds all the surfaces, aperture definitions, field points, and wavelength information.

    • Example: my_system = optic.Optic()

  • SurfaceGroup (optiland.surfaces.SurfaceGroup): Manages the collection of surfaces within an Optic object.

  • Surfaces (Surface): These represent the individual optical surfaces (lenses, mirrors, image planes, etc.). Each surface has a geometry, material properties on either side, and can optionally have coatings, physical apertures, or be designated as the system’s aperture stop.

    • Object Surface (ObjectSurface): The first surface in your system, representing the object being imaged. It can be at a finite distance or at infinity.

    • Image Surface (ImageSurface): The final surface where the image is formed.

    • Standard Surface (StandardGeometry): Spherical or conic surfaces.

    • Aspheric & Freeform Surfaces: Optiland supports various complex geometries like EvenAsphere, OddAsphere, PolynomialGeometry, ChebyshevPolynomialGeometry, ZernikePolynomialGeometry, and more…

    • Paraxial Surface (ParaxialSurface): A thin lens approximation defined by its focal length, useful for initial layouts.

  • Materials (Material): Define the optical properties of the media between surfaces, primarily the refractive index (n) and optionally the extinction coefficient (k) as a function of wavelength.

    • Optiland can use data from the refractiveindex.info database (MaterialFile) or allow you to define ideal materials (IdealMaterial) or materials based on index (nd) and Abbe number (Vd) (AbbeMaterial). The AbbeMaterial supports both a legacy polynomial model and a new, more accurate Buchdahl model (recommended).

  • Geometries (BaseGeometry): These define the mathematical shape of a surface (e.g., plane, sphere, asphere).

  • Aperture (BaseSystemAperture): Defines the system’s limiting aperture. This can be specified as Entrance Pupil Diameter (EPD), Image Space F-number (imageFNO), Object Space Numerical Aperture (objectNA), or Float by Stop Size (float_by_stop_size).

  • Fields (Field, FieldGroup): Define the points in the object plane that are being imaged. Can be specified by angle or object height. Vignetting can also be applied.

  • Wavelengths (Wavelength, WavelengthGroup): Specify the wavelengths of light used for analysis, including a primary wavelength. All wavelengths are internally converted to microns (µm).

  • Coordinate Systems (CoordinateSystem): Each surface has its own local coordinate system (LCS) defined by its position (x, y, z) and rotation (rx, ry, rz) relative to a reference system.

  • Apodization (BaseApodization): Defines the intensity distribution within the pupil. This is uniform (UniformApodization) by default, but other options include Gaussian (GaussianApodization).

Numerical Backend

  • Optiland can use NumPy (default) or PyTorch.

  • PyTorch allows for automatic differentiation.
    • Switch with:

      import optiland.backend as be
      be.set_backend("torch")  # or "numpy"
      

Basic Workflow: Defining an Optical System

  1. Import Optiland:

    from optiland import optic
    from optiland import materials # if using specific materials
    import optiland.backend as be  # backend for numerical operations - either numpy or torch
    
  2. Create an Optic Instance:

    my_lens = optic.Optic(name="My Cooke Triplet")
    
  3. Add Surfaces (add_surface): Surfaces are added sequentially. * The first surface (index 0) is typically the object surface.

    my_lens.surfaces.add(index=0, radius=np.inf, thickness=np.inf) # Object at infinity
    
    • Add optical surfaces with their properties:

      my_lens.surfaces.add(index=1, radius=22.01359, thickness=3.25896, material="SK16")
      my_lens.surfaces.add(index=2, radius=-435.76044, thickness=6.00755) # Air gap by default
      my_lens.surfaces.add(index=3, radius=-22.21328, thickness=0.99997, material=("F2", "schott"), is_stop=True) # Stop surface
      # ... more surfaces ...
      
    • The last surface is the image plane.

      my_lens.surfaces.add(index=N) # N is the index after the last optical surface
      
  4. Set System Aperture (set_aperture):

    my_lens.set_aperture(aperture_type="EPD", value=10.0)  # Entrance Pupil Diameter of 10 mm
    # Or: my_lens.set_aperture(aperture_type="imageFNO", value=5.0)
    # Or: my_lens.set_aperture(aperture_type="float_by_stop_size", value=7.6), specifies diameter of the stop surface
    
  5. Define Field of View (set_field_type, add_field):

    my_lens.fields.set_type(field_type="angle") # Field specified by angle
    my_lens.fields.add(y=0.0)  # On-axis field
    my_lens.fields.add(y=14.0) # Off-axis field at 14 degrees
    my_lens.fields.add(y=20.0)
    # Or for object height:
    # my_lens.fields.set_type(field_type="object_height")
    # my_lens.fields.add(y=10.0) # Object height of 10 mm
    
  6. Define Wavelengths (add_wavelength):

    my_lens.wavelengths.add(value=0.4861) # F-line (blue) in µm
    my_lens.wavelengths.add(value=0.5876, is_primary=True) # d-line (yellow), primary
    my_lens.wavelengths.add(value=0.6563) # C-line (red)
    
  7. (Optional) Image Plane Solve (image_solve): Moves the image surface to the paraxial focus.

    my_lens.image_solve()
    

Coordinate System & Sign Conventions

Understanding Optiland’s coordinate system and sign conventions is crucial:

  • Global Coordinate System (GCS): A fixed reference frame.

  • Local Coordinate System (LCS): Each surface has its own LCS.

  • Light Propagation: From left to right, along the positive z-axis.

  • Surface Vertex: Surface 1 typically at GCS origin (z=0). Others at their LCS origin.

  • Thickness: Axial separation to the next surface. Positive means to the right.

  • Radius of Curvature (R):
    • Positive R: Center of curvature to the right (convex to left).

    • Negative R: Center of curvature to the left (concave to left).

    • Infinite R: Planar surface.

  • Tilts and Decenters: The rotation matrix (of the global CS) is given by R = Rz @ Ry @ Rx.

  • Ray Parameters:
    • Height (y): Positive above the optical axis.

    • Slope (u - paraxial): Positive if traveling upwards.

    • Direction Cosines (L, M, N - real): Components of the unit vector.

  • Angles: Positive clockwise.

Ray Tracing

Optiland uses normalized coordinates for both the field and pupil to define rays in a general, system-independent way:

  • Field Coordinates (Hx, Hy): Define the ray’s starting field position. (0, 0) corresponds to the optical axis, and (±1, ±1) spans the full normalized field of view.

  • Pupil Coordinates (Px, Py): Define the ray’s position in the entrance pupil. (0, 0) corresponds to the chief ray, and (±1, ±1) spans the full normalized entrance pupil.

Optiland can trace both paraxial and real rays.

  • Paraxial Rays:

    • For first-order calculations. Access through optic.paraxial.

    • Example:

      heights, slopes = lens.paraxial.trace(Hy, Py)
      
  • Real Rays:

    • For detailed analysis, including aberrations.

    • Example: Trace a bundle of rays

      optic.trace(Hx, Hy, wavelength, num_rays, distribution)
      
    • Example: Trace a specific ray, defined by the normalized field and pupil coordinates.

      optic.trace_generic(Hx, Hy, Px, Py, wavelength)
      
  • Advanced Ray Tracing (RealRays, surface_group.trace): For more control, create a RealRays object and trace using optic.surfaces.trace(rays).

    • Example:

      from optiland.rays import RealRays
      import optiland.backend as be
      # Assume 'my_lens' is an existing Optic object
      # Create a grid of rays at z=0 (e.g., entrance pupil plane)
      x_coords = be.linspace(-5.0, 5.0, 3) # Adjust range based on EPD
      y_coords = be.linspace(-5.0, 5.0, 3)
      X, Y = be.meshgrid(x_coords, y_coords)
      # Create a collimated ray bundle (traveling along +z)
      x_in = X.reshape(-1)
      y_in = Y.reshape(-1)
      z_in = be.zeros_like(x_in)
      L_in = be.zeros_like(x_in)
      M_in = be.zeros_like(x_in)
      N_in = be.ones_like(x_in)
      intensity = be.ones_like(x_in)
      # Create the RealRays object
      primary_wl = my_lens.wavelengths.primary_wavelength.value
      rays_in = RealRays(x=x_in, y=y_in, z=z_in,
                         L=L_in, M=M_in, N=N_in,
                         wavelength=primary_wl, intensity=intensity)
      # Trace the manually created rays
      rays_out = my_lens.surfaces.trace(rays_in)
      # Get x, y coordinates at the image plane (last surface)
      x_image = my_lens.surfaces.x[-1,:]
      y_image = my_lens.surfaces.y[-1,:]
      
  • Ray Distributions (distribution.py): Specify pupil distribution (e.g., 'hexapolar', 'uniform', 'random').

Analysis Tools

Optiland offers a suite of tools to evaluate performance:

  • Aberrations: Seidel & chromatic. (my_lens.aberrations.seidels())

  • SpotDiagram: Geometric ray spread.

  • RayFan: Transverse ray aberrations.

  • OPD: Wavefront errors.

  • MTF: Image contrast vs. frequency.

  • PSF: Point spread function.

  • FieldCurvature, Distortion: Field performance.

  • (Many classes have a ``.view()`` method for plotting).

See the Example Gallery for a full overview of available analysis tools and their usage.

Visualization

  • 2D Layout (optic.draw()):

    my_lens.draw(num_rays=5, distribution='line_y')
    
  • 3D Layout (optic.draw3D()):

    my_lens.draw3D(num_rays=24, distribution='ring')
    
  • Lens Data Table (optic.info()): Prints surface data in a tabular format, resembling the commonly found Lens Data Editor (LDE).

    my_lens.info()
    

GUI Keyboard Shortcuts

  • Ctrl+K: Open the VS Code-style Command Palette for quick access to actions and tools.

  • 1 / 2: Load layout presets 1 and 2, respectively.

Advanced Features (Brief Overview)

  • Coatings (coatings.py): Model anti-reflection or reflective coatings (SimpleCoating, FresnelCoating).

  • Thin Films (thin_film/*): Define and optimize multilayer thin-film stacks, including tolerancing and Needle Synthesis.

  • Polarization (polarized_rays.py, jones.py): Trace polarized light and apply Jones calculus for polarizing elements.

  • Pickups (pickup.py): Link a parameter of one surface to another (e.g., make radius of S2 = -radius of S1).

  • Solves (solves): Automatically adjust parameters to meet certain conditions (e.g., QuickFocusSolve adjusts image plane for best focus).

  • Optimization (optimization/*): Define merit functions with operands and variables to optimize system designs.

  • Tolerancing (tolerancing/*): Analyze the impact of manufacturing errors using sensitivity analysis and Monte Carlo simulations.

This cheat sheet should provide a solid starting point. Happy designing! ✨