In [ ]:
import batoid
import os
import yaml
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import interact
import ipywidgets as widgets
%matplotlib inline

In [ ]:
D = 0.1
m0 = batoid.ConstMedium(1.000277)  # air
w, n = np.genfromtxt(os.path.join(batoid.datadir, "media", "silica_dispersion.txt")).T
w *= 1e-6
m1 = batoid.TableMedium(batoid.Table(w, n, 'linear'))
wavelength = 500e-9
n0 = m0.getN(wavelength)
n1 = m1.getN(wavelength)
R1 = 0.5  # convex entrance surface
R2 = -0.5  # convex exit surface
d = 0.01
# Lens maker equation:
f = 1./((n1-n0)*(1./R1 - 1./R2 + (n1-n0)*d/R1/R2))

In [ ]:
def makeSystem(focus):
    systemStr = """
    opticalSystem:
      type: CompoundOptic
      inMedium: 1.000277
      items:
        -
          type: RefractiveInterface
          surface:
            type: Sphere
            R: {R1}
          obscuration:
            type: ClearCircle
            radius: {Do2}
          inMedium: 1.000277
          outMedium: &silica
            type: SellmeierMedium
            B1: 0.6961663
            B2: 0.4079426
            B3: 0.8974794
            C1: 0.00467914825849
            C2: 0.013512063073959999
            C3: 97.93400253792099
          name: L1
        -
          type: RefractiveInterface
          surface:
            type: Sphere
            R: {R2}
          obscuration:
            type: ClearCircle
            radius: {Do2}
          coordSys:
            z: {d}
          inMedium: *silica
          outMedium: 1.000277
          name: L2
        -
          type: Detector
          surface:
            type: Plane
          obscuration:
            type: ClearCircle
            radius: {Do2}
          coordSys:
            z: {f}
          inMedium: 1.000277          
          name: D
    """.format(**dict(R1=R1, R2=R2, Do2=D/2, d=d, f=f+focus))
    config = yaml.safe_load(systemStr)
    system = batoid.parse.parse_optic(config['opticalSystem'])
    return system

In [ ]:
@interact(theta_x=widgets.FloatSlider(min=-5,max=5,step=1,value=1.1, continuous_update=False),
          theta_y=widgets.FloatSlider(min=-5,max=5,step=1,value=2.2, continuous_update=False),
          wavelength=widgets.FloatSlider(min=0.4,max=0.7,step=0.05,value=0.5, continuous_update=False),
          focus=widgets.FloatSlider(min=-20, max=20, step=1,value=0.0, continuous_update=False))
def spot(theta_x, theta_y, wavelength, focus):
    """Display a spot diagram for a Newtonian telescope.

    @param theta_x  Field angle in degrees
    @param theta_y  Field angle in degrees
    @param wavelength in microns
    @param focus    Defocus distance in mm
    """
    system = makeSystem(focus*1e-3)
    dirCos = batoid.utils.fieldToDirCos(np.deg2rad(theta_x), np.deg2rad(theta_y))
    # Flip the z-cosine since we constructed our telescope to point down
    # instead of up, and hence need the initial rays to point up instead
    # of down.
    dirCos = dirCos[0:2]+(-dirCos[2],)
    rays = batoid.RayVector.asPolar(
        backDist=1.0, nrad=16, naz=64, wavelength=wavelength*1e-6,
        dirCos=dirCos,
        outer=D/2*0.999, medium=batoid.ConstMedium(1.000277)
    )
    
    system.trace(rays)
    rays.trimVignetted()
    plt.scatter(1e3*(rays.x-np.mean(rays.x)), 1e3*(rays.y-np.mean(rays.y)), s=1)
    plt.xlim(-1, 1)
    plt.ylim(-1, 1)
    plt.xlabel("mm")
    plt.ylabel("mm")
    plt.title(r"$\theta_x = {:4.2f}\,,\theta_y = {:4.2f}\,,\lambda = {:4g}\,, f={:4.2f}$".format(theta_x, theta_y, wavelength, focus))
    plt.axes().set_aspect('equal')

In [ ]: