Work on the second design a lot; write my own version of the raytracer to try to speed it up a bit; currently ~30x faster

This commit is contained in:
Kelvin Ly 2022-03-12 19:14:59 -05:00
parent e75d541417
commit 9ceab36b29
4 changed files with 2057 additions and 146 deletions

701
faster raytrace.ipynb Normal file
View File

@ -0,0 +1,701 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 5,
"id": "aef7b0a6",
"metadata": {},
"outputs": [],
"source": [
"# here's an attempt to speed up raytracing so it takes a reasonable amount of\n",
"# time to optimize systems\n",
"# mainly by leveraging numpy to run all the calculations in a tighter for\n",
"# loop than what Python can do\n",
"\n",
"# first let's copy the system from before to test against\n",
"\n",
"from rayoptics.environment import *\n",
"\n",
"opm = OpticalModel()\n",
"sm = opm['seq_model']\n",
"osp = opm['optical_spec']\n",
"pm = opm['parax_model']\n",
"\n",
"osp['pupil'] = PupilSpec(osp, key=['object', 'pupil'], value=16)\n",
"osp['fov'] = FieldSpec(osp, key=['object', 'angle'], value=5, flds=[0., 0.707, 1.], is_relative=True)\n",
"osp['wvls'] = WvlSpec([('F', 0.5), (587.5618, 1.0), ('C', 0.5)], ref_wl=1)\n",
"\n",
"opm.radius_mode = True\n",
"\n",
"sm.gaps[0].thi=1e10\n",
"\n",
"def calc_curvature(n, fl):\n",
" return (n-1)*fl\n",
"\n",
"def achromatic(f, v0, v1):\n",
" return (v0-v1)*f/v0, -v1*f/(v0-v1)\n",
"\n",
"def calc_curvatures(ns, fls):\n",
" return tuple((n-1)*f for n, f in zip(ns, fls))\n",
"\n",
"n_bk7 = 1.5168\n",
"n_lasf9 = 1.85025\n",
"n_f2 = 1.62005\n",
"\n",
"v_bk7 = 64.17\n",
"v_lasf9 = 32.16\n",
"v_f2 = 36.43\n",
"# try for chaining a 3x telescope setup with a second 3x telescope setup\n",
"f0 = 150\n",
"f0_0, f0_1 = achromatic(f0, v_bk7, v_f2)\n",
"\n",
"f1 = 60\n",
"f1_0, f1_1 = achromatic(f1, v_bk7, v_f2)\n",
"\n",
"r0_0, r0_1, r1_0, r1_1 = calc_curvatures((n_bk7, n_f2, n_bk7, n_f2), (f0_0, f0_1, f1_0, f1_1))\n",
"\n",
"sm.add_surface([r1_0, 4, 'N-BK7', 'Schott', 16])\n",
"sm.add_surface([1e9, 2, 'N-F2', 'Schott', 16])\n",
"sm.add_surface([-r1_1, 30])\n",
"\n",
"opm.update_model()"
]
},
{
"cell_type": "code",
"execution_count": 146,
"id": "5c6bebd1",
"metadata": {},
"outputs": [],
"source": [
"# now we trace all the functions needed for raytracing until we get to the algorithm,\n",
"# having them transfer arrays of values to evaluate instead of a single point\n",
"\n",
"import rayoptics.raytr.trace as raytr_trace\n",
"from rayoptics.optical.model_constants import Intfc, Gap, Tfrm, Indx, Zdir\n",
"from rayoptics.elem.profiles import Spherical\n",
"from rayoptics.elem.surface import Surface\n",
"\n",
"from rayoptics.raytr.traceerror import TraceError, TraceMissedSurfaceError, TraceTIRError, TraceEvanescentRayError\n",
"\n",
"def super_trace_grid(sm, fi, wl=None, num_rays=21,\n",
" append_if_none=True, **kwargs):\n",
" \"\"\" fct is applied to the raw grid and returned as a grid \"\"\"\n",
" osp = sm.opt_model.optical_spec\n",
" wvls = osp.spectral_region\n",
" wvl = sm.central_wavelength()\n",
" wv_list = wvls.wavelengths if wl is None else [wvl]\n",
" fld = osp.field_of_view.fields[fi]\n",
" foc = osp.defocus.get_focus()\n",
"\n",
" # make sure this is imported\n",
" rs_pkg, cr_pkg = raytr_trace.setup_pupil_coords(sm.opt_model,\n",
" fld, wvl, foc)\n",
" fld.chief_ray = cr_pkg\n",
" fld.ref_sphere = rs_pkg\n",
"\n",
" grids = []\n",
" grid_start = np.array([-1., -1.])\n",
" grid_stop = np.array([1., 1.])\n",
" grid_def = [grid_start, grid_stop, num_rays]\n",
" results = None\n",
" for wi, wvl in enumerate(wv_list):\n",
" result = np.expand_dims(rly_trace_grid(sm.opt_model, grid_def, fld, wvl, foc,\n",
" **kwargs), axis=0)\n",
" if results is None:\n",
" results = result\n",
" else:\n",
" results = np.concatenate((results, result), axis=0)\n",
" rc = wvls.render_colors\n",
" return results, rc\n",
"\n",
"# this generates an array of valid points from the grid\n",
"def rly_trace_grid(opt_model, grid_rng, fld, wvl, foc, **kwargs): # from trace_grid\n",
" start = np.array(grid_rng[0])\n",
" stop = grid_rng[1]\n",
" num = grid_rng[2]\n",
" step = np.array((stop - start)/(num - 1))\n",
" grid = []\n",
" \n",
" valid_points = None\n",
" \n",
" for x in np.linspace(start[0], stop[0], num, dtype=np.float64):\n",
" ys = np.linspace(start[1], stop[1], num, dtype=np.float64)\n",
" valid_ys = ys[x**2 + ys**2 <= 1.0]\n",
" points = np.zeros((valid_ys.shape[0], 2))\n",
" points[:, 0] = x\n",
" points[:, 1] = valid_ys\n",
" if valid_points is None:\n",
" valid_points = points\n",
" else:\n",
" valid_points = np.concatenate((valid_points, points))\n",
" return rly_rly_trace(opt_model, valid_points, fld, wvl, **kwargs)\n",
"\n",
"def fld_apply_vignetting(fld, pupils):\n",
" ret = np.copy(pupils)\n",
" if fld.vlx != 0.0:\n",
" ret[pupils[:, 0] < 0.0, 0] *= (1. - fld.vlx)\n",
" if fld.vux != 0.0:\n",
" ret[pupils[:, 0] > 0.0, 0] *= (1. - fld.vux)\n",
" if fld.vly != 0.0:\n",
" ret[pupils[:, 1] < 0.0, 1] *= (1. - fld.vly)\n",
" if fld.vuy != 0.0:\n",
" ret[pupils[:, 1] > 0.0, 1] *= (1. - fld.vuy)\n",
" return ret\n",
"\n",
"def norm(v):\n",
" return np.sqrt(np.sum(v*v, axis=1))\n",
" \n",
"# some final data conditioning before the good part\n",
"def rly_rly_trace(opt_model, pupils, fld, wvl, **kwargs): # from trace_base\n",
" vig_pupils = fld_apply_vignetting(fld, pupils)\n",
" osp = opt_model.optical_spec\n",
" fod = osp.parax_data.fod\n",
" eprad = fod.enp_radius\n",
" aim_pt = np.array([0., 0.])\n",
" if hasattr(fld, 'aim_pt') and fld.aim_pt is not None:\n",
" aim_pt = np.array(fld.aim_pt)\n",
" pt1 = np.zeros((pupils.shape[0], 3))\n",
" pt1[:,0:2] = eprad*vig_pupils+aim_pt\n",
" pt1[:, 2] = fod.obj_dist+fod.enp_dist\n",
" pt0 = osp.obj_coords(fld)\n",
" dir0 = pt1 - pt0\n",
" length = norm(dir0)\n",
" dir0 = dir0/np.expand_dims(length, axis=1)\n",
" return rly_rly_rly_trace(opt_model.seq_model, pt0, dir0, wvl, **kwargs)\n",
"\n",
"def rly_rly_rly_trace(seq_model, pt0, dir0, wvl, eps=1.0e-12, **kwargs): # from raytr.trace and raytr.trace_raw\n",
" path = [v for v in seq_model.path(wvl)]\n",
" \n",
" #kwargs['first_surf'] = kwargs.get('first_surf', 1)\n",
" #kwargs['last_surf'] = kwargs.get('last_surf',\n",
" # seq_model.get_num_surfaces()-2)\n",
" \n",
" rays = np.zeros((dir0.shape[0], len(path), 10))\n",
" # rays[nray][path][px, py, pz, dx, dy, dz, nx, ny, nz, dist]\n",
"\n",
" #first_surf = kwargs.get('first_surf', 0)\n",
" #last_surf = kwargs.get('last_surf', None)\n",
" first_surf = kwargs.get('first_surf', 1)\n",
" last_surf = kwargs.get('last_surf', seq_model.get_num_surfaces()-2)\n",
" \n",
" # trace object surface\n",
" obj = path[0]\n",
" pt0 = np.expand_dims(pt0, axis=0)\n",
"\n",
" srf_obj = obj[Intfc]\n",
" dst_b4, pt_obj = itfc_intersect(srf_obj, pt0, dir0, z_dir=obj[Zdir])\n",
"\n",
" before = obj\n",
" before_pt = pt_obj\n",
" before_dir = dir0\n",
" before_normal = itfc_normal(srf_obj, before_pt)\n",
" tfrm_from_before = before[Tfrm]\n",
" z_dir_before = before[Zdir]\n",
"\n",
" # loop remaining surfaces in path\n",
" for surf, after in enumerate(path[1:]):\n",
" try:\n",
" #np.tensordot(t, pts, (0, 1)).T seems to work for mass matrix multiplication\n",
" rt, t = tfrm_from_before\n",
" #b4_pt, b4_dir = rt.dot(before_pt - t), rt.dot(before_dir)\n",
" b4_pt = np.tensordot(rt, before_pt - t, (0, 1)).T\n",
" b4_dir = np.tensordot(rt, before_dir, (0, 1)).T\n",
"\n",
" #pp_dst = -b4_pt.dot(b4_dir)\n",
" pp_dst = -np.expand_dims(np.sum(b4_pt*b4_dir, axis=1), axis=1)\n",
" pp_pt_before = b4_pt + pp_dst*b4_dir\n",
"\n",
" ifc = after[Intfc]\n",
" z_dir_after = after[Zdir]\n",
"\n",
" # intersect ray with profile\n",
" pp_dst_intrsct, inc_pt = itfc_intersect(ifc, pp_pt_before, b4_dir,\n",
" eps=eps, z_dir=z_dir_before)\n",
" dst_b4 = pp_dst[:, 0] + pp_dst_intrsct\n",
" rays[:, surf, 0:3] = before_pt\n",
" rays[:, surf, 3:6] = before_dir\n",
" rays[:, surf, 6:9] = before_normal\n",
" rays[:, surf, 9] = dst_b4\n",
"\n",
" normal = itfc_normal(ifc, inc_pt)\n",
"\n",
"\n",
" '''\n",
" # if the interface has a phase element, process that first\n",
" if hasattr(ifc, 'phase_element'):\n",
" doe_dir, phs = phase(ifc, inc_pt, b4_dir, normal, z_dir_before,\n",
" wvl, before[Indx], after[Indx])\n",
" # the output of the phase element becomes the input for the\n",
" # refraction/reflection calculation\n",
" b4_dir = doe_dir\n",
" op_delta += phs\n",
" '''\n",
"\n",
" # refract or reflect ray at interface\n",
" if ifc.interact_mode == 'reflect':\n",
" after_dir = reflect(b4_dir, normal)\n",
" elif ifc.interact_mode == 'transmit':\n",
" after_dir = bend(b4_dir, normal, before[Indx], after[Indx])\n",
" elif ifc.interact_mode == 'dummy':\n",
" after_dir = b4_dir\n",
" else: # no action, input becomes output\n",
" after_dir = b4_dir\n",
"\n",
" # Per `Hopkins, 1981 <https://dx.doi.org/10.1080/713820605>`_, the\n",
" # propagation direction is given by the direction cosines of the\n",
" # ray and therefore doesn't require the use of a negated\n",
" # refractive index following a reflection. Thus we use the\n",
" # (positive) refractive indices from the seq_model.rndx array.\n",
"\n",
" before_pt = inc_pt\n",
" before_normal = normal\n",
" before_dir = after_dir\n",
" z_dir_before = z_dir_after\n",
" before = after\n",
" tfrm_from_before = before[Tfrm]\n",
"\n",
" except TraceMissedSurfaceError as ray_miss:\n",
" ray_miss.surf = surf+1\n",
" ray_miss.ifc = ifc\n",
" ray_miss.prev_tfrm = before[Tfrm]\n",
" ray_miss.ray_pkg = rays, wvl\n",
" raise ray_miss\n",
"\n",
" except TraceTIRError as ray_tir:\n",
" ray_tir.surf = surf+1\n",
" ray_tir.ifc = ifc\n",
" ray_tir.int_pt = inc_pt\n",
" ray_tir.ray_pkg = rays, wvl\n",
" raise ray_tir\n",
"\n",
" except TraceEvanescentRayError as ray_evn:\n",
" ray_evn.surf = surf+1\n",
" ray_evn.ifc = ifc\n",
" ray_evn.int_pt = inc_pt\n",
" ray_evn.ray_pkg = rays, wvl\n",
" raise ray_evn\n",
"\n",
" # lifted from the loop since it'll no longer hit the\n",
" # StopIteration exception\n",
"\n",
" if len(path) > 1:\n",
" rays[:, surf + 1, 0:3] = inc_pt\n",
" rays[:, surf + 1, 3:6] = after_dir\n",
" rays[:, surf + 1, 6:9] = normal\n",
" rays[:, surf + 1, 9] = 0\n",
" return rays\n",
" \n",
"def itfc_intersect(ifc, p, d, eps=1e-12, z_dir=1):\n",
" if isinstance(ifc, Surface):\n",
" if isinstance(ifc.profile, Spherical):\n",
" # copied from elem.profiles\n",
" ax2 = ifc.profile.cv\n",
" cx2 = ifc.profile.cv * np.sum(p*p, axis=1) - 2*p[:,2]\n",
" b = ifc.profile.cv * np.sum(d*p, axis=1) - d[:,2]\n",
" \n",
" discr = b*b-ax2*cx2\n",
" # Use z_dir to pick correct root\n",
" if np.any(discr < 0):\n",
" raise TraceMissedSurfaceError\n",
"\n",
" s = cx2/(z_dir*np.sqrt(discr) - b)\n",
"\n",
" p1 = p + np.expand_dims(s, axis=1)*d\n",
" return s, p1\n",
" else:\n",
" raise RuntimeError(\"intersection not implemented for profile {}\".format(ifc.profile))\n",
" else:\n",
" raise RuntimeError(\"intersection not implemented for {}\".format(ifc))\n",
"\n",
"def itfc_normal(ifc, pts):\n",
" if isinstance(ifc, Surface):\n",
" if isinstance(ifc.profile, Spherical):\n",
" # copied from elem.profiles\n",
" return np.stack([-ifc.profile.cv*pts[:,0], -ifc.profile.cv*pts[:,1], 1.0-ifc.profile.cv*pts[:,2]],\n",
" axis=-1)\n",
" else:\n",
" raise RuntimeError(\"intersection not implemented for profile {}\".format(ifc.profile))\n",
" else:\n",
" raise RuntimeError(\"intersection not implemented for {}\".format(ifc))\n",
" \n",
"#copied from raytrace.reflect\n",
"def reflect(d_in, normals):\n",
" normal_len = norm(normals)\n",
" cosI = np.sum(d_in * normals, axis=1)/normal_len\n",
" d_out = d_in - 2.0*cosI*normals\n",
" return d_out\n",
"\n",
"#copied from raytrace.bend\n",
"def bend(d_in, normal, n_in, n_out):\n",
" try:\n",
" normal_len = norm(normal)\n",
" cosI = np.sum(d_in * normal, axis=1)/normal_len\n",
" sinI_sqr = 1.0 - cosI*cosI\n",
" n_cosIp = np.copysign(np.sqrt(n_out*n_out - n_in*n_in*sinI_sqr), cosI)\n",
" alpha = np.expand_dims(n_cosIp - n_in*cosI, axis=1)\n",
" d_out = (n_in*d_in + alpha*normal)/n_out\n",
" return d_out\n",
" except ValueError:\n",
" raise TraceTIRError(d_in, normal, n_in, n_out)"
]
},
{
"cell_type": "code",
"execution_count": 147,
"id": "887c1b0d",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(array([0.44231141, 0.4014741 , 0.38252552]),\n",
" array([0.44778724, 0.40678566, 0.38775669]))"
]
},
"execution_count": 147,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"super_rays, _ = super_trace_grid(sm, 0)\n",
"#print(super_rays[:,:,-1])\n",
"\n",
"def ray_err(sm, rays, fi):\n",
" osp = sm.opt_model.optical_spec\n",
" fld = osp.field_of_view.fields[fi]\n",
" foc = osp.defocus.get_focus()\n",
" \n",
" image_pt = fld.ref_sphere[0]\n",
" dist = np.expand_dims(foc/rays[:,:,-1,5], axis=-1)\n",
" defocused_pts = rays[:,:,-1,0:3] + dist*rays[:,:,-1,3:6]\n",
" t_abr = defocused_pts - image_pt\n",
" return np.sqrt(np.sum(t_abr*t_abr, axis=2))\n",
"\n",
"def dump_dist(p, wi, ray_pkg, fld, wvl, foc):\n",
" if ray_pkg is not None:\n",
" image_pt = fld.ref_sphere[0]\n",
" ray = ray_pkg[mc.ray]\n",
" dist = foc / ray[-1][mc.d][2]\n",
" defocused_pt = ray[-1][mc.p] + dist*ray[-1][mc.d]\n",
" t_abr = defocused_pt - image_pt\n",
" return np.sqrt(np.sum(t_abr*t_abr))\n",
" \n",
"def spot_rms(sm, fld_idx=0, num_rays=21):\n",
" return np.sqrt(np.mean(np.square(sm.trace_grid(dump_dist, fld_idx, form='list', num_rays=21, append_if_none=False)[0]), axis=1))\n",
"\n",
"def spot_rms2(sm, rays=None, fld_idx=0, num_rays=21):\n",
" if rays is None:\n",
" rays, _ = super_trace_grid(sm, fld_idx, num_rays=21)\n",
" return np.sqrt(np.mean(np.square(ray_err(sm, rays, fld_idx)), axis=1))\n",
"\n",
"spot_rms(sm), spot_rms2(sm)"
]
},
{
"cell_type": "code",
"execution_count": 163,
"id": "b98b3263",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(array([[[ 8.43971916e-01, 3.75098629e-01, 0.00000000e+00],\n",
" [ 2.36775522e-01, 1.05233565e-01, 9.65848461e-01]],\n",
" \n",
" [[ 7.60036422e-01, 2.53345474e-01, 0.00000000e+00],\n",
" [ 2.33696527e-01, 7.78988423e-02, 9.69184040e-01]],\n",
" \n",
" [[ 7.02253533e-01, 1.56056341e-01, 0.00000000e+00],\n",
" [ 2.31559445e-01, 5.14576544e-02, 9.71458869e-01]],\n",
" \n",
" [[ 6.68405774e-01, 7.42673082e-02, 0.00000000e+00],\n",
" [ 2.30301018e-01, 2.55890020e-02, 9.72782938e-01]],\n",
" \n",
" [[ 6.57255825e-01, 1.01347299e-16, 0.00000000e+00],\n",
" [ 2.29885412e-01, 3.54477885e-17, 9.73217703e-01]],\n",
" \n",
" [[ 6.68405774e-01, -7.42673082e-02, 0.00000000e+00],\n",
" [ 2.30301018e-01, -2.55890020e-02, 9.72782938e-01]],\n",
" \n",
" [[ 7.02253533e-01, -1.56056341e-01, 0.00000000e+00],\n",
" [ 2.31559445e-01, -5.14576544e-02, 9.71458869e-01]],\n",
" \n",
" [[ 7.60036422e-01, -2.53345474e-01, 0.00000000e+00],\n",
" [ 2.33696527e-01, -7.78988423e-02, 9.69184040e-01]],\n",
" \n",
" [[ 8.43971916e-01, -3.75098629e-01, 0.00000000e+00],\n",
" [ 2.36775522e-01, -1.05233565e-01, 9.65848461e-01]],\n",
" \n",
" [[ 6.65190535e-01, 4.15744084e-01, 0.00000000e+00],\n",
" [ 2.07346720e-01, 1.29591700e-01, 9.69645981e-01]]]),\n",
" array([[ 0.97899133, 0. , 0. , 0.26458622, 0. ,\n",
" 0.96436203, -0. , -0. , 1. , 0. ],\n",
" [ 0.84397192, 0.37509863, 0. , 0.23677552, 0.10523357,\n",
" 0.96584846, -0. , -0. , 1. , 0. ],\n",
" [ 0.76003642, 0.25334547, 0. , 0.23369653, 0.07789884,\n",
" 0.96918404, -0. , -0. , 1. , 0. ],\n",
" [ 0.70225353, 0.15605634, 0. , 0.23155944, 0.05145765,\n",
" 0.97145887, -0. , -0. , 1. , 0. ],\n",
" [ 0.66840577, 0.07426731, 0. , 0.23030102, 0.025589 ,\n",
" 0.97278294, -0. , -0. , 1. , 0. ],\n",
" [ 0.65725583, 0. , 0. , 0.22988541, 0. ,\n",
" 0.9732177 , -0. , -0. , 1. , 0. ],\n",
" [ 0.66840577, -0.07426731, 0. , 0.23030102, -0.025589 ,\n",
" 0.97278294, -0. , 0. , 1. , 0. ],\n",
" [ 0.70225353, -0.15605634, 0. , 0.23155944, -0.05145765,\n",
" 0.97145887, -0. , 0. , 1. , 0. ],\n",
" [ 0.76003642, -0.25334547, 0. , 0.23369653, -0.07789884,\n",
" 0.96918404, -0. , 0. , 1. , 0. ],\n",
" [ 0.84397192, -0.37509863, 0. , 0.23677552, -0.10523357,\n",
" 0.96584846, -0. , 0. , 1. , 0. ]]))"
]
},
"execution_count": 163,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# uhh why are they different?\n",
"# let's check the rays themselves to see what they look like\n",
"\n",
"def dump_rays(p, wi, ray_pkg, fld, wvl, foc):\n",
" if ray_pkg is not None:\n",
" ray = ray_pkg[mc.ray]\n",
" return [ray[-1][mc.p], ray[-1][mc.d]]\n",
"\n",
"sm.trace_grid(dump_rays, 0, form='list', append_if_none=False)[0][0][0:10], super_rays[0,0:10,-1]"
]
},
{
"cell_type": "code",
"execution_count": 164,
"id": "d2723737",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(311, (313, 10))"
]
},
"execution_count": 164,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# they look really similar...\n",
"# let's make sure they're the same size\n",
"ref_rays, _ = sm.trace_grid(dump_rays, 0, form='list', append_if_none=False)\n",
"len(ref_rays[0]), super_rays[0,:,-1].shape"
]
},
{
"cell_type": "code",
"execution_count": 165,
"id": "7ba8bcbc",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[[-0.00000000e+00, -0.00000000e+00, 0.00000000e+00],\n",
" [-7.20000000e-10, -3.20000000e-10, 1.00000000e+00]],\n",
"\n",
" [[-0.00000000e+00, -0.00000000e+00, 0.00000000e+00],\n",
" [-7.20000000e-10, -2.40000000e-10, 1.00000000e+00]],\n",
"\n",
" [[-0.00000000e+00, -0.00000000e+00, 0.00000000e+00],\n",
" [-7.20000000e-10, -1.60000000e-10, 1.00000000e+00]],\n",
"\n",
" ...,\n",
"\n",
" [[ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00],\n",
" [ 7.20000000e-10, 2.40000000e-10, 1.00000000e+00]],\n",
"\n",
" [[ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00],\n",
" [ 7.20000000e-10, 3.20000000e-10, 1.00000000e+00]],\n",
"\n",
" [[ 0.00000000e+00, -0.00000000e+00, 0.00000000e+00],\n",
" [ 8.00000000e-10, -1.11022302e-25, 1.00000000e+00]]])"
]
},
"execution_count": 165,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# so the original has five more points than super_rays...\n",
"# but which ones?\n",
"\n",
"def dump_start(p, wi, ray_pkg, fld, wvl, foc):\n",
" if ray_pkg is not None:\n",
" image_pt = fld.ref_sphere[0]\n",
" ray = ray_pkg[mc.ray]\n",
" #v = ray[-1][mc.d][0:2] / ray[-1][mc.d][2]\n",
" return [ray[0][mc.p], ray[0][mc.d]]\n",
" \n",
"sm.trace_grid(dump_start, 0, form='list', append_if_none=False)[0][0]"
]
},
{
"cell_type": "code",
"execution_count": 166,
"id": "25c4bcc4",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[-0.0e+00, 0.0e+00, 0.0e+00, -8.0e-10, 0.0e+00, 1.0e+00],\n",
" [-0.0e+00, -0.0e+00, 0.0e+00, -7.2e-10, -3.2e-10, 1.0e+00],\n",
" [-0.0e+00, -0.0e+00, 0.0e+00, -7.2e-10, -2.4e-10, 1.0e+00],\n",
" ...,\n",
" [ 0.0e+00, 0.0e+00, 0.0e+00, 7.2e-10, 2.4e-10, 1.0e+00],\n",
" [ 0.0e+00, 0.0e+00, 0.0e+00, 7.2e-10, 3.2e-10, 1.0e+00],\n",
" [ 0.0e+00, 0.0e+00, 0.0e+00, 8.0e-10, 0.0e+00, 1.0e+00]])"
]
},
"execution_count": 166,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"super_rays[0,:,0,0:6]"
]
},
{
"cell_type": "code",
"execution_count": 167,
"id": "ff8a27fc",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(array([0.00557354, 0.0097539 , 0.01172143]),\n",
" array([0.00558912, 0.00978306, 0.01175699]))"
]
},
"execution_count": 167,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# look at that, the last point's different\n",
"# it looks like it'd be pointed closer to the right edge of the screen than the other ones\n",
"\n",
"# let's reduce the objective distance and see if the discrepancy disappears\n",
"\n",
"sm.gaps[0].thi=1e9\n",
"opm.update_model()\n",
"\n",
"spot_rms(sm), spot_rms2(sm)"
]
},
{
"cell_type": "code",
"execution_count": 168,
"id": "1ac36295",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(array([0.44231141, 0.4014741 , 0.38252552]),\n",
" array([0.44778724, 0.40678566, 0.38775669]),\n",
" array([0.44231141, 0.4014741 , 0.38252552]),\n",
" array([0.44778724, 0.40678566, 0.38775669]))"
]
},
"execution_count": 168,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# ugh let's see if it persists with different grid sizes\n",
"\n",
"sm.gaps[0].thi=1e10\n",
"opm.update_model()\n",
"\n",
"spot_rms(sm, num_rays=41), spot_rms2(sm, num_rays=41), spot_rms(sm, num_rays=61), spot_rms2(sm, num_rays=61)"
]
},
{
"cell_type": "code",
"execution_count": 172,
"id": "705bc2ed",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(2195380700.2, 65261865.4, 33.63956403550794)"
]
},
"execution_count": 172,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# the difference is fairly small, let's see if the speedup is worth trading some accuracy\n",
"\n",
"import time\n",
"ref_start = time.perf_counter_ns()\n",
"for _ in range(100):\n",
" sm.trace_grid(dump_rays, 0, form='list', append_if_none=False)\n",
"ref_end = time.perf_counter_ns()\n",
"\n",
"fast_start = time.perf_counter_ns()\n",
"for _ in range(100):\n",
" super_trace_grid(sm, 0)\n",
"fast_end = time.perf_counter_ns()\n",
"\n",
"ref_elapsed = (ref_end - ref_start)/10.\n",
"fast_elapsed = (fast_end - fast_start)/10.\n",
"\n",
"ref_elapsed, fast_elapsed, ref_elapsed/fast_elapsed"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "aacb52a3",
"metadata": {},
"outputs": [],
"source": [
"# ~30x speedup is probably worth a <1% error, let's just go with it"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

618
spyglass-blindopt.ipynb Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long