Aberrations

Chromatic Aberrations

Analyze longitudinal and lateral color effects across the visible spectrum.

IntermediateAberrationsNumPy backend8 min read

Introduction

This tutorial demonstrates how to assess chromatic aberrations in Optiland. We will first investigate a singlet with poor color correction, and then a well-corrected achromatic doublet.

Core concepts used

LchC()
Computes the first-order longitudinal chromatic aberration contributions for each surface; these can be summed to find the total axial color.
draw(wavelengths=[...])
Renders the system with multiple specific wavelengths simultaneously to visualize color separation.
analysis.RayFan(lens)
Plots aberrations for multiple wavelengths on a single set of axes to show the "spread" of color.

Step-by-step build

1

Import numpy, analysis, and optic

python
import numpy as np

from optiland import analysis, optic
2

Define the high-dispersion N-SF6 Singlet class

Let's define a simple singlet using a material with high dispersion:

python
class Singlet(optic.Optic):
 """Simple Singlet"""

 def __init__(self):
     super().__init__()

     # add surfaces
     self.add_surface(index=0, radius=np.inf, thickness=np.inf)
     self.add_surface(
         index=1,
         thickness=0.5,
         radius=32.2526,
         is_stop=True,
         material="N-SF6",
     )
     self.add_surface(index=2, thickness=19.8532, radius=-31.9756)
     self.add_surface(index=3)

     # add aperture
     self.set_aperture(aperture_type="EPD", value=3.4)

     # add field
     self.set_field_type(field_type="angle")
     self.add_field(y=0.0)

     # add wavelength
     self.add_wavelength(value=0.48613270)
     self.add_wavelength(value=0.58756180, is_primary=True)
     self.add_wavelength(value=0.65627250)
3

Draw the singlet with multiple wavelengths

python
singlet = Singlet()
singlet.draw(
 wavelengths=[0.48613270, 0.587561806, 0.65627250],
 figsize=(16, 4),
 num_rays=3,
)
Step
4

Compute first-order longitudinal axial color for the singlet

As can be seen in the visualization, the wavelengths of light focus at different points on the optical axis (zooming in can also help to see this more clearly). To quantify this, let's compute the first-order longitudinal axial color:

python
print(f"First-order Longitudinal Color: {np.sum(singlet.aberrations.LchC()):.3f}")
5

Plot the ray fan aberrations for the singlet

This can also be seen in ray aberration plots. The slope of the tranverse errors at the Px=0P_x=0 and Py=0P_y=0 locations vary with wavelength. This indicates that the defocus differs as a function of wavelength.

python
fan = analysis.RayFan(singlet)
fan.view()
Step
6

Define the achromatic N-BAK1/SF2 Doublet class

Now, let's define an achromatic doublet, which improves the chromatic aberration performance in comparison to our simple singlet:

python
class Doublet(optic.Optic):
 """Achromatic Doublet

 Milton Laikin, Lens Design, 4th ed., CRC Press, 2007, p. 45
 """

 def __init__(self):
     super().__init__()

     # add surfaces
     self.add_surface(index=0, radius=np.inf, thickness=np.inf)
     self.add_surface(
         index=1,
         radius=12.38401,
         thickness=0.4340,
         is_stop=True,
         material="N-BAK1",
     )
     self.add_surface(
         index=2,
         radius=-7.94140,
         thickness=0.3210,
         material=("SF2", "schott"),
     )
     self.add_surface(index=3, radius=-48.44396, thickness=19.6059)
     self.add_surface(index=4)

     # add aperture
     self.set_aperture(aperture_type="EPD", value=3.4)

     # add field
     self.set_field_type(field_type="angle")
     self.add_field(y=0)

     # add wavelength
     self.add_wavelength(value=0.48613270)
     self.add_wavelength(value=0.58756180, is_primary=True)
     self.add_wavelength(value=0.65627250)
7

Draw the doublet with multiple wavelengths

python
doublet = Doublet()
doublet.draw(
 wavelengths=[0.48613270, 0.587561806, 0.65627250],
 figsize=(16, 4),
 num_rays=3,
)
Step
8

Compute first-order longitudinal axial color for the doublet

We can already see that the various wavelengths appear to focus at a similar location. Let's confirm that the longitudinal chromatic aberration has indeed been reduced by computing this directly and generating the tranverse ray aberrations plots.

python
print(f"First-order Longitudinal Color: {np.sum(doublet.aberrations.LchC()):.3f}")
9

Plot the ray fan aberrations for the doublet

This value was -0.789 for the singlet and is now -0.015 for the doublet, which is a significant improvement. Now let's look at the tranverse ray aberration plots:

python
fan = analysis.RayFan(doublet)
fan.view()
Step
Show full code listing
python
import numpy as np

from optiland import analysis, optic

class Singlet(optic.Optic):
  """Simple Singlet"""

  def __init__(self):
      super().__init__()

      # add surfaces
      self.add_surface(index=0, radius=np.inf, thickness=np.inf)
      self.add_surface(
          index=1,
          thickness=0.5,
          radius=32.2526,
          is_stop=True,
          material="N-SF6",
      )
      self.add_surface(index=2, thickness=19.8532, radius=-31.9756)
      self.add_surface(index=3)

      # add aperture
      self.set_aperture(aperture_type="EPD", value=3.4)

      # add field
      self.set_field_type(field_type="angle")
      self.add_field(y=0.0)

      # add wavelength
      self.add_wavelength(value=0.48613270)
      self.add_wavelength(value=0.58756180, is_primary=True)
      self.add_wavelength(value=0.65627250)

singlet = Singlet()
singlet.draw(
  wavelengths=[0.48613270, 0.587561806, 0.65627250],
  figsize=(16, 4),
  num_rays=3,
)

print(f"First-order Longitudinal Color: {np.sum(singlet.aberrations.LchC()):.3f}")

fan = analysis.RayFan(singlet)
fan.view()

class Doublet(optic.Optic):
  """Achromatic Doublet

  Milton Laikin, Lens Design, 4th ed., CRC Press, 2007, p. 45
  """

  def __init__(self):
      super().__init__()

      # add surfaces
      self.add_surface(index=0, radius=np.inf, thickness=np.inf)
      self.add_surface(
          index=1,
          radius=12.38401,
          thickness=0.4340,
          is_stop=True,
          material="N-BAK1",
      )
      self.add_surface(
          index=2,
          radius=-7.94140,
          thickness=0.3210,
          material=("SF2", "schott"),
      )
      self.add_surface(index=3, radius=-48.44396, thickness=19.6059)
      self.add_surface(index=4)

      # add aperture
      self.set_aperture(aperture_type="EPD", value=3.4)

      # add field
      self.set_field_type(field_type="angle")
      self.add_field(y=0)

      # add wavelength
      self.add_wavelength(value=0.48613270)
      self.add_wavelength(value=0.58756180, is_primary=True)
      self.add_wavelength(value=0.65627250)

doublet = Doublet()
doublet.draw(
  wavelengths=[0.48613270, 0.587561806, 0.65627250],
  figsize=(16, 4),
  num_rays=3,
)

print(f"First-order Longitudinal Color: {np.sum(doublet.aberrations.LchC()):.3f}")

fan = analysis.RayFan(doublet)
fan.view()

Conclusions

Note that the y-axis scale of this plot is significantly smaller than that of the singlet. We can confirm that chromatic aberrations have been significantly reduced.

Next tutorials