Pickups

Pickups define relationships between the components of a lens. For instance, in a singlet lens, the radii of curvature can be constrained to be equal in magnitude but opposite in sign.

[1]:
import numpy as np

from optiland import optic, optimization

Define a starting lens:

[2]:
lens = optic.Optic()

# add surfaces
lens.surfaces.add(index=0, radius=np.inf, thickness=np.inf)
lens.surfaces.add(index=1, radius=50, thickness=3, material="SK16", is_stop=True)
lens.surfaces.add(index=2, radius=-50, thickness=30)
lens.surfaces.add(index=3)

# set aperture
lens.set_aperture(aperture_type="EPD", value=10)

# set fields
lens.fields.set_type(field_type="angle")
lens.fields.add(y=0)

# set wavelengths
lens.wavelengths.add(value=0.48)
lens.wavelengths.add(value=0.55, is_primary=True)
lens.wavelengths.add(value=0.65)

lens.draw()
../../_images/gallery_optimization_pickups_4_0.png

Define pickups:

[ ]:
lens.pickups.add(
    source_surface_idx=1,
    attr_type="radius",
    target_surface_idx=2,
    scale=-1,
    offset=0,
)

Note that pickups can also act on generic surface attributes using the string parameter ttr_type. When doing so, you can use [i] to stand in for the dynamic surface index that is resolved at runtime so the target does not accidentally overwrite the source attribute if you provide a hardcoded index.

As an example, if you wanted to pickup the geometry coefficients, we can provide [i] as the index for the generic path:

python lens.pickups.add(     source_surface_idx=1,     attr_type='surface_group.surfaces[i].geometry.coefficients',     target_surface_idx=2 )

Note: We omit executing this here since our current lens geometry does not have coefficients.

Define optimization problem:

[4]:
problem = optimization.OptimizationProblem()

Add operands (targets for optimization):

[ ]:
"""
Add a wavefront error operand for all wavelengths.

Use Gaussian quadrature distribution for the rays (see distribution documentation for
more information).
"""

for wave in lens.wavelengths.get_wavelengths():
    input_data = {
        "optic": lens,
        "Hx": 0,
        "Hy": 0,
        "num_rays": 3,
        "wavelength": wave,
        "distribution": "gaussian_quad",
    }
    problem.add_operand(
        operand_type="OPD_difference",
        target=0,
        weight=1,
        input_data=input_data,
    )

Define variables - let first radius of curvature vary (the second surface will match this value, but with opposite sign):

[6]:
problem.add_variable(lens, "radius", surface_number=1)

Check initial merit function value and system properties:

[7]:
problem.info()
╒════╤════════════════════════╤═══════════════════╕
│    │   Merit Function Value │   Improvement (%) │
╞════╪════════════════════════╪═══════════════════╡
│  0 │                3879.67 │                 0 │
╘════╧════════════════════════╧═══════════════════╛
╒════╤════════════════╤══════════╤══════════╤═════════╤═════════╤════════════════════╕
│    │ Operand Type   │   Target │   Weight │   Value │   Delta │   Contribution (%) │
╞════╪════════════════╪══════════╪══════════╪═════════╪═════════╪════════════════════╡
│  0 │ OPD difference │        0 │        1 │ 40.0587 │ 40.0587 │            41.3618 │
│  1 │ OPD difference │        0 │        1 │ 36.0072 │ 36.0072 │            33.4184 │
│  2 │ OPD difference │        0 │        1 │ 31.2801 │ 31.2801 │            25.2198 │
╘════╧════════════════╧══════════╧══════════╧═════════╧═════════╧════════════════════╛
╒════╤═════════════════╤═══════════╤═════════╤══════════════╤══════════════╕
│    │ Variable Type   │   Surface │   Value │ Min. Bound   │ Max. Bound   │
╞════╪═════════════════╪═══════════╪═════════╪══════════════╪══════════════╡
│  0 │ radius          │         1 │      50 │              │              │
╘════╧═════════════════╧═══════════╧═════════╧══════════════╧══════════════╛

Define optimizer:

[8]:
optimizer = optimization.OptimizerGeneric(problem)

Run optimization:

[9]:
optimizer.optimize()
[9]:
  message: CONVERGENCE: REL_REDUCTION_OF_F_<=_FACTR*EPSMCH
  success: True
   status: 0
      fun: 7.40021876171174
        x: [-6.131e-01]
      nit: 9
      jac: [ 2.496e+02]
     nfev: 58
     njev: 29
 hess_inv: <1x1 LbfgsInvHessProduct with dtype=float64>

Print merit function value and system properties after optimization:

[10]:
problem.info()
╒════╤════════════════════════╤═══════════════════╕
│    │   Merit Function Value │   Improvement (%) │
╞════╪════════════════════════╪═══════════════════╡
│  0 │                7.40022 │           99.8093 │
╘════╧════════════════════════╧═══════════════════╛
╒════╤════════════════╤══════════╤══════════╤══════════╤══════════╤════════════════════╕
│    │ Operand Type   │   Target │   Weight │    Value │    Delta │   Contribution (%) │
╞════╪════════════════╪══════════╪══════════╪══════════╪══════════╪════════════════════╡
│  0 │ OPD difference │        0 │        1 │ 1.81071  │ 1.81071  │            44.3049 │
│  1 │ OPD difference │        0 │        1 │ 0.899126 │ 0.899126 │            10.9244 │
│  2 │ OPD difference │        0 │        1 │ 1.8202   │ 1.8202   │            44.7707 │
╘════╧════════════════╧══════════╧══════════╧══════════╧══════════╧════════════════════╛
╒════╤═════════════════╤═══════════╤═════════╤══════════════╤══════════════╕
│    │ Variable Type   │   Surface │   Value │ Min. Bound   │ Max. Bound   │
╞════╪═════════════════╪═══════════╪═════════╪══════════════╪══════════════╡
│  0 │ radius          │         1 │ 38.6889 │              │              │
╘════╧═════════════════╧═══════════╧═════════╧══════════════╧══════════════╛

Draw final lens:

[11]:
lens.draw(num_rays=5)
../../_images/gallery_optimization_pickups_23_0.png

Confirm that the radii of curvature are equal and opposite:

[12]:
lens.info()
╒════╤═════════════════╤══════════╤═════════════╤════════════╤═════════╤═════════════════╕
│    │ Type            │   Radius │   Thickness │ Material   │   Conic │   Semi-aperture │
╞════╪═════════════════╪══════════╪═════════════╪════════════╪═════════╪═════════════════╡
│  0 │ Planar          │ inf      │         inf │ Air        │       0 │        5        │
│  1 │ Stop - Standard │  38.6889 │           3 │ SK16       │       0 │        5        │
│  2 │ Standard        │ -38.6889 │          30 │ Air        │       0 │        4.85123  │
│  3 │ Planar          │ inf      │         nan │ Air        │       0 │        0.095248 │
╘════╧═════════════════╧══════════╧═════════════╧════════════╧═════════╧═════════════════╛