Glass Expert Optimization Example

This notebook demonstrates how to use the GlassExpert optimizer in Optiland to optimize an optical system with both continuous (radii, thicknesses) and categorical (glasses) variables.

A description of GlassExpert’s architecture can be found in the documentation (Developer’s Guide: Categorical Optimization with Glass Expert)

[1]:
import optiland.backend as be
from optiland import optic, optimization
from optiland.materials import glasses_selection

1. Define the Optical System

We’ll use a simple Cooke Triplet as the starting point for our optimization.

[2]:
class CookeTripletStartPoint(optic.Optic):
    def __init__(self):
        super().__init__()
        self.surfaces.add(index=0, radius=be.inf, thickness=be.inf)  # Object surface
        self.surfaces.add(index=1, radius=25.0, thickness=2.5, material="N-BK7")
        self.surfaces.add(index=2, radius=-150.0, thickness=7.5)
        self.surfaces.add(
            index=3, radius=-25.0, thickness=1.5, material="N-F2", is_stop=True
        )  # Stop surface
        self.surfaces.add(index=4, radius=25.0, thickness=5.0)
        self.surfaces.add(index=5, radius=150.0, thickness=2.5, material="N-BK7")
        self.surfaces.add(index=6, radius=-25.0, thickness=40.0)
        self.surfaces.add(index=7)  # Image surface

        self.set_aperture(aperture_type="EPD", value=10)
        self.fields.set_type(field_type="angle")
        self.fields.add(y=0)
        self.fields.add(y=5)
        self.fields.add(y=10)

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


lens_system = CookeTripletStartPoint()

# Display initial lens
lens_system.draw(title="Initial Cooke Triplet")
lens_system.info()
╒════╤═════════════════╤═══════════╤══════════╤═════════════╤════════════╤═════════╤═════════════════╕
│    │ Type            │ Comment   │   Radius │   Thickness │ Material   │   Conic │   Semi-aperture │
╞════╪═════════════════╪═══════════╪══════════╪═════════════╪════════════╪═════════╪═════════════════╡
│  0 │ Planar          │           │      inf │       inf   │ Air        │       0 │         7.04289 │
│  1 │ Standard        │           │       25 │         2.5 │ N-BK7      │       0 │         7.04289 │
│  2 │ Standard        │           │     -150 │         7.5 │ Air        │       0 │         6.5123  │
│  3 │ Stop - Standard │           │      -25 │         1.5 │ N-F2       │       0 │         3.92965 │
│  4 │ Standard        │           │       25 │         5   │ Air        │       0 │         4.11651 │
│  5 │ Standard        │           │      150 │         2.5 │ N-BK7      │       0 │         5.63609 │
│  6 │ Standard        │           │      -25 │        40   │ Air        │       0 │         6.105   │
│  7 │ Planar          │           │      inf │       nan   │ Air        │       0 │        12.4368  │
╘════╧═════════════════╧═══════════╧══════════╧═════════════╧════════════╧═════════╧═════════════════╛
../../_images/gallery_optimization_glass_expert_example_3_1.png

2. Define the Optimization Problem

We’ll set up operands to minimize RMS spot size and maintain a target focal length. Variables will include radii, thicknesses, and the materials of the lenses.

[3]:
problem = optimization.OptimizationProblem()
target_focal_length = 50.0

# Operand: Effective Focal Length (EFL)
problem.add_operand(
    input_data={"optic": lens_system},
    operand_type="f2",
    target=target_focal_length,
    weight=1.0,
)

# Operands: RMS Spot Size for each field
for Hx, Hy in lens_system.fields.get_field_coords():
    problem.add_operand(
        operand_type="rms_spot_size",
        target=0.0,
        weight=10.0,
        input_data={
            "optic": lens_system,
            "surface_number": -1,  # Image surface
            "Hx": Hx,
            "Hy": Hy,
            "num_rays": 16,  # Rays on one axis for 'uniform' distribution
            "wavelength": lens_system.primary_wavelength,
            "distribution": "uniform",
        },
    )

# Variables: Radii
problem.add_variable(lens_system, "radius", surface_number=1, min_val=10, max_val=100)
problem.add_variable(lens_system, "radius", surface_number=2, min_val=-200, max_val=-20)
problem.add_variable(lens_system, "radius", surface_number=3, min_val=-100, max_val=-10)
problem.add_variable(lens_system, "radius", surface_number=4, min_val=10, max_val=100)
problem.add_variable(lens_system, "radius", surface_number=5, min_val=20, max_val=200)
problem.add_variable(lens_system, "radius", surface_number=6, min_val=-100, max_val=-10)

# Variables: Thicknesses (air and glass)
problem.add_variable(
    lens_system, "thickness", surface_number=1, min_val=1, max_val=5
)  # Lens 1 thickness
problem.add_variable(
    lens_system, "thickness", surface_number=2, min_val=1, max_val=15
)  # Air space 1
problem.add_variable(
    lens_system, "thickness", surface_number=3, min_val=1, max_val=5
)  # Lens 2 thickness
problem.add_variable(
    lens_system, "thickness", surface_number=4, min_val=1, max_val=15
)  # Air space 2
problem.add_variable(
    lens_system, "thickness", surface_number=5, min_val=1, max_val=5
)  # Lens 3 thickness
problem.add_variable(
    lens_system, "thickness", surface_number=6, min_val=30, max_val=60
)  # Back focal length (approx)

# Variables: Materials (using Glass Expert)
available_glasses = glasses_selection(
    lambda_min=0.4, lambda_max=0.7, catalogs=["schott", "ohara_common"]
)
problem.add_variable(
    lens_system, "material", surface_number=1, glass_selection=available_glasses
)
problem.add_variable(
    lens_system, "material", surface_number=3, glass_selection=available_glasses
)
problem.add_variable(
    lens_system, "material", surface_number=5, glass_selection=available_glasses
)

problem.info()
╒════╤════════════════════════╤═══════════════════╕
│    │   Merit Function Value │   Improvement (%) │
╞════╪════════════════════════╪═══════════════════╡
│  0 │                14065.2 │                 0 │
╘════╧════════════════════════╧═══════════════════╛
╒════╤════════════════╤══════════╤══════════════╤══════════════╤══════════╤═════════╤═════════╤════════════════╕
│    │ Operand Type   │   Target │ Min. Bound   │ Max. Bound   │   Weight │   Value │   Delta │   Contrib. [%] │
╞════╪════════════════╪══════════╪══════════════╪══════════════╪══════════╪═════════╪═════════╪════════════════╡
│  0 │ f2             │       50 │              │              │        1 │ 161.994 │ 111.994 │          89.18 │
│  1 │ rms spot size  │        0 │              │              │       10 │   2.249 │   2.249 │           3.59 │
│  2 │ rms spot size  │        0 │              │              │       10 │   2.251 │   2.251 │           3.6  │
│  3 │ rms spot size  │        0 │              │              │       10 │   2.259 │   2.259 │           3.63 │
╘════╧════════════════╧══════════╧══════════════╧══════════════╧══════════╧═════════╧═════════╧════════════════╛
╒════╤═════════════════╤═══════════╤════════════════════╤══════════════╤══════════════╕
│    │ Variable Type   │   Surface │ Value              │   Min. Bound │   Max. Bound │
╞════╪═════════════════╪═══════════╪════════════════════╪══════════════╪══════════════╡
│  0 │ radius          │         1 │ 25.0               │           10 │          100 │
│  1 │ radius          │         2 │ -150.0             │         -200 │          -20 │
│  2 │ radius          │         3 │ -25.0              │         -100 │          -10 │
│  3 │ radius          │         4 │ 25.0               │           10 │          100 │
│  4 │ radius          │         5 │ 150.0              │           20 │          200 │
│  5 │ radius          │         6 │ -25.0              │         -100 │          -10 │
│  6 │ thickness       │         1 │ 2.5                │            1 │            5 │
│  7 │ thickness       │         2 │ 7.5                │            1 │           15 │
│  8 │ thickness       │         3 │ 1.5000000000000002 │            1 │            5 │
│  9 │ thickness       │         4 │ 5.0                │            1 │           15 │
│ 10 │ thickness       │         5 │ 2.5                │            1 │            5 │
│ 11 │ thickness       │         6 │ 40.0               │           30 │           60 │
│ 12 │ material        │         1 │ N-BK7              │          nan │          nan │
│ 13 │ material        │         3 │ N-F2               │          nan │          nan │
│ 14 │ material        │         5 │ N-BK7              │          nan │          nan │
╘════╧═════════════════╧═══════════╧════════════════════╧══════════════╧══════════════╛

3. Run the Glass Expert Optimization

[4]:
optimizer = optimization.GlassExpert(problem)

# Draw the starting lens
lens_system.draw(title="Starting lens")
print(f"Initial error function value: {problem.initial_value:.1f}")

# Run optimization
res = optimizer.run(
    num_neighbours=6,
    maxiter=100,
    tol=1e-6,
    verbose=True,
    plot_glass_map=False,
)

# Display the optimized lens
lens_system.draw(title="Optimized lens")
lens_system.info()
Initial error function value: 14065.2
Initial glasses combination: ['N-BK7', 'N-F2', 'N-BK7']

----------------------------------------------------------------------
Global exploration

Selecting Material, Surface 1:
        Trying N-SF11   as Material, Surface 1. Error function value: 3.69e-03
        Trying BOROFLOAT33 as Material, Surface 1. Error function value: 0.21
        Trying N-SK4    as Material, Surface 1. Error function value: 5.03e-03
        Trying N-KZFS4  as Material, Surface 1. Error function value: 7.40e-03
        Trying N-PK52A  as Material, Surface 1. Error function value: 0.19
        Trying N-SF2    as Material, Surface 1. Error function value: 4.47e-03
        -> Selected N-SF11 as Material, Surface 1.
        New combination: ['N-SF11', 'N-F2', 'N-BK7']
        Best error function value: 3.69e-03 (-100%).

Selecting Material, Surface 3:
        Trying N-SF11   as Material, Surface 3. Error function value: 4.53e-03
        Trying BOROFLOAT33 as Material, Surface 3. Error function value: 4.20e-03
        Trying N-SK4    as Material, Surface 3. Error function value: 3.96e-03
        Trying N-KZFS4  as Material, Surface 3. Error function value: 3.91e-03
        Trying N-PK52A  as Material, Surface 3. Error function value: 4.42e-03
        Trying N-SF2    as Material, Surface 3. Error function value: 3.67e-03
        -> Selected N-SF2 as Material, Surface 3.
        New combination: ['N-SF11', 'N-SF2', 'N-BK7']
        Best error function value: 3.67e-03 (-1%).

Selecting Material, Surface 5:
        Trying N-SF11   as Material, Surface 5. Error function value: 3.35e-03
        Trying BOROFLOAT33 as Material, Surface 5. Error function value: 4.25e-03
        Trying N-SK4    as Material, Surface 5. Error function value: 2.78e-03
        Trying N-KZFS4  as Material, Surface 5. Error function value: 2.55e-03
        Trying N-PK52A  as Material, Surface 5. Error function value: 3.92e-03
        Trying N-SF2    as Material, Surface 5. Error function value: 3.06e-03
        -> Selected N-KZFS4 as Material, Surface 5.
        New combination: ['N-SF11', 'N-SF2', 'N-KZFS4']
        Best error function value: 2.55e-03 (-31%).


----------------------------------------------------------------------
Local exploration

Selecting Material, Surface 1:
        Trying SF11     as Material, Surface 1. Error function value: 2.54e-03
        Trying SFL6     as Material, Surface 1. Error function value: 3.99e-03
        Trying N-SF6    as Material, Surface 1. Error function value: 3.99e-03
        Trying N-SF6HT  as Material, Surface 1. Error function value: 3.99e-03
        Trying N-SF6HTultra as Material, Surface 1. Error function value: 3.99e-03
        Trying SF56A    as Material, Surface 1. Error function value: 2.54e-03
        -> Selected SF56A as Material, Surface 1.
        New combination: ['SF56A', 'N-SF2', 'N-KZFS4']
        Best error function value: 2.54e-03 (-0%).

Selecting Material, Surface 3:
        Trying SF2      as Material, Surface 3. Error function value: 2.54e-03
        Trying N-SF19   as Material, Surface 3. Error function value: 2.59e-03
        Trying N-KZFS8  as Material, Surface 3. Error function value: 4.07e-03
        Trying N-LAF7   as Material, Surface 3. Error function value: 4.45e-03
        Trying LAFN7    as Material, Surface 3. Error function value: 4.45e-03
        Trying N-LASF45 as Material, Surface 3. Error function value: 3.18e-03
        No better glass found, keeping N-SF2.
        Combination: ['SF56A', 'N-SF2', 'N-KZFS4']
        Best error function value: 2.54e-03 (0%).

Selecting Material, Surface 5:
        Trying N-KZFS4HT as Material, Surface 5. Error function value: 2.54e-03
        Trying KZFSN4   as Material, Surface 5. Error function value: 2.54e-03
        Trying N-LAF2   as Material, Surface 5. Error function value: 1.85e-03
        Trying N-BAF51  as Material, Surface 5. Error function value: 2.12e-03
        Trying N-LAF33  as Material, Surface 5. Error function value: 1.91e-03
        Trying N-BAF4   as Material, Surface 5. Error function value: 3.49e-03
        -> Selected N-LAF2 as Material, Surface 5.
        New combination: ['SF56A', 'N-SF2', 'N-LAF2']
        Best error function value: 1.85e-03 (-27%).

╒════╤═════════════════╤═══════════╤═══════════╤═════════════╤════════════╤═════════╤═════════════════╕
│    │ Type            │ Comment   │    Radius │   Thickness │ Material   │   Conic │   Semi-aperture │
╞════╪═════════════════╪═══════════╪═══════════╪═════════════╪════════════╪═════════╪═════════════════╡
│  0 │ Planar          │           │  inf      │   inf       │ Air        │       0 │         7.30434 │
│  1 │ Standard        │           │   33.3728 │     3.85319 │ SF56A      │       0 │         7.30434 │
│  2 │ Standard        │           │ -158.569  │     7.54508 │ Air        │       0 │         6.55284 │
│  3 │ Stop - Standard │           │  -21.4607 │     4.11158 │ N-SF2      │       0 │         3.68193 │
│  4 │ Standard        │           │   24.3137 │     5.5196  │ Air        │       0 │         4.20475 │
│  5 │ Standard        │           │  155.658  │     3.1396  │ N-LAF2     │       0 │         5.97945 │
│  6 │ Standard        │           │  -20.5099 │    40.5119  │ Air        │       0 │         6.50684 │
│  7 │ Planar          │           │  inf      │   nan       │ Air        │       0 │         8.81285 │
╘════╧═════════════════╧═══════════╧═══════════╧═════════════╧════════════╧═════════╧═════════════════╛
../../_images/gallery_optimization_glass_expert_example_7_1.png
../../_images/gallery_optimization_glass_expert_example_7_2.png