Skip to content

Variable Lens Sequence

A configurable system with multiple lenses. We define three types of lenses:

  • A plano convex lens
  • A symmetric biconvex lens
  • A convex plano lens, the same shape as the first lens, just reversed.

The number of lenses of each type are input parameters of the script, and the complete sequence is:

  • X plano convex lenses
  • Y biconvex convex lenses
  • Z convex plano lenses
python
import torch
import torch.nn as nn
import torch.optim as optim
import torchlensmaker as tlm

import math
import itertools
import numpy as np

### DESIGN ###

# Design parameters
square_size = 30
lens_diameter = math.sqrt(2)*square_size
focal_length = 35.

# Number of each lens type
nplano = 2
nbiconvex = 2
nrplano = 1

# Mechanical sizes
lens_min_thickness = 1.2
lens_spacing = 3.

### MODEL ###

# Parametric surfaces 
surface_plano = tlm.Parabola(lens_diameter, tlm.parameter(-0.002))
surface_biconvex = tlm.Parabola(lens_diameter, tlm.parameter(0.002))

# Lenses
lenses_plano = [tlm.lenses.semiplanar_front(
    surface_plano,
    tlm.OuterGap(lens_min_thickness),
    material="BK7",
) for i in range(nplano)]

lenses_biconvex = [tlm.lenses.symmetric_singlet(
    surface_biconvex,
    tlm.OuterGap(lens_min_thickness),
    material="BK7",
) for i in range(nbiconvex)]

lenses_rplano = [tlm.lenses.semiplanar_rear(
    surface_plano,
    tlm.OuterGap(lens_min_thickness),
    material="BK7",
    scale=-1,
) for i in range(nrplano)]

optics = tlm.Sequential(
    tlm.PointSourceAtInfinity(beam_diameter=0.9*lens_diameter),
    tlm.Gap(10.),
    
    *itertools.chain.from_iterable([[tlm.Gap(lens_spacing), lens] for lens in lenses_plano]),
    *itertools.chain.from_iterable([[tlm.Gap(lens_spacing), lens] for lens in lenses_biconvex]),
    *itertools.chain.from_iterable([[tlm.Gap(lens_spacing), lens] for lens in lenses_rplano]),
    
    tlm.Gap(focal_length),
    tlm.FocalPoint(),
)

# print(optics)

print("Lens design")
print("Square size", square_size)
print("Lens diameter", lens_diameter)
print("Configuration", nplano, nbiconvex, nrplano)
print("lens_min_thickness", lens_min_thickness)
print("lens_spacing", lens_spacing)

tlm.show(optics, dim=2)
tlm.show(optics, dim=3)
Lens design
Square size 30
Lens diameter 42.42640687119285
Configuration 2 2 1
lens_min_thickness 1.2
lens_spacing 3.0
python
def regu_equalparam(_):
    a1 = surface_plano.sag_function.A
    a2 = surface_biconvex.sag_function.A
    return 500*torch.pow((500*a1)**2 - (500*a2)**2, 2)
    #params = torch.cat([param.view(-1) for param in optics.parameters()])
    #return torch.pow(torch.diff(1000*torch.abs(params)).sum(), 2)

def regu_equalthickness(_):
    t0 = lenses_plano[0].inner_thickness()
    t1 = lenses_biconvex[0].inner_thickness()
    return 100*torch.pow(t0 - t1, 2)

optics.set_sampling2d(pupil=10)

tlm.optimize(
    optics,
    optimizer = optim.Adam(optics.parameters(), lr=1e-4),
    dim = 2,
    num_iter = 100,
    regularization = regu_equalthickness
).plot()

def print_lens(lens_name, lens):
    # TODO thickness at a specific radial distance
    print(lens_name)
    inner = lens.inner_thickness().item()
    outer = lens.outer_thickness().item()
    print(f"    inner: {inner:.3f} outer: {outer:.3f}")
    
    a1 = dict(lens.sequence[0].surface.named_parameters())
    a2 = dict(lens.sequence[-1].surface.named_parameters())
    print("    surface1", [p.tolist() for p in a1.values()])
    print("    surface2", [p.tolist() for p in a2.values()])

print_lens("Plano-convex", lenses_plano[0])
print_lens("Bi-convex", lenses_biconvex[0])
print_lens("Reverse plano-convex", lenses_rplano[0])
[  1/100] L= 84.72070 | grad norm= 180033.4531
[  6/100] L= 10.01243 | grad norm= 48591.4883
[ 11/100] L= 8.91529 | grad norm= 45465.1211
[ 16/100] L= 13.30397 | grad norm= 62370.8359
[ 21/100] L= 5.41949 | grad norm= 25825.1973
[ 26/100] L= 4.48006 | grad norm= 15841.8789
[ 31/100] L= 5.78247 | grad norm= 28091.5137
[ 36/100] L= 4.03027 | grad norm= 11640.4307
[ 41/100] L= 3.77295 | grad norm= 9781.5957
[ 46/100] L= 3.94824 | grad norm= 14469.2266
[ 51/100] L= 3.43071 | grad norm= 4727.0225
[ 56/100] L= 3.40025 | grad norm= 5938.4189
[ 61/100] L= 3.27935 | grad norm= 5355.4429
[ 66/100] L= 3.09737 | grad norm= 2780.3262
[ 71/100] L= 3.02034 | grad norm= 5084.4707
[ 76/100] L= 2.87297 | grad norm= 2988.1594
[ 81/100] L= 2.75835 | grad norm= 2515.1323
[ 86/100] L= 2.63210 | grad norm= 2374.2886
[ 91/100] L= 2.50121 | grad norm= 2642.3618
[ 96/100] L= 2.37238 | grad norm= 2887.8804
[100/100] L= 2.26273 | grad norm= 2455.7751

png

Plano-convex
    inner: 2.714 outer: -678.432
    surface1 []
    surface2 [-0.0033636821899563074]
Bi-convex
    inner: 2.716 outer: -679.337
    surface1 [0.0016840794123709202]
    surface2 [0.0016840794123709202]
Reverse plano-convex
    inner: 2.714 outer: -678.432
    surface1 [-0.0033636821899563074]
    surface2 []
python
tlm.show2d(optics)
tlm.show3d(optics)