Lens System Design | Exact Ray Tracing | Multi fields | Polychromatic | Operands and Optimization | Sensitivity and Monte Carlo Tolerance | Coatings and Polarization
Introduction and Motivation:
Commercial optical design packages like Zemax are powerful, but they are closed boxes: you set up a system, press optimize, and trust the numbers. In addition they are limited access. I wanted to understand what happens between the prescription and the spot diagram — so I built my own optical design engine from scratch in Python, implementing every step from a ray striking a surface to a fully analyzed, optimized, and toleranced lens system.
The engine traces real, thick lenses using exact vector Snell refraction — no paraxial approximation — across field and wavelength, on general surfaces (spherical, conic, aspheric, cylindrical, flat). On top of that ray-tracing core it builds the analyses an optical designer actually uses: spot diagrams, ray and wavefront (OPD) fans, Zernike decomposition, field curvature, distortion, physical-optics PSF and MTF. It then closes the design loop with a damped-least-squares optimizer and a manufacturing-tolerancing suite, so a system can be designed, optimized, and checked for real-world yield entirely within the tool.
A guiding theme runs through the project: the connection between first-order (paraxial) optics and exact geometrical ray tracing. First-order theory tells you where an image should form and how large it should be; exact ray tracing tells you how far short of that ideal a real design falls, and why. Building the engine meant implementing both and watching them agree in the limit — which is also the clearest way to learn where the approximations break.
Physics & Computational Architecture:
At its core the engine represents an optical system as an ordered list of surfaces, each carrying a radius of curvature, a thickness (the axial gap to the next surface), a glass, a clear aperture, and a shape — spherical, conic, even-aspheric, cylindrical, or flat. This prescription is the single source of truth: everything downstream, from the paraxial focal length to the final MTF, is computed by tracing light through it.
Tracing is sequential and fully three-dimensional. A ray is a position and a unit direction vector in space; a bundle of rays is launched from the object (or across the entrance pupil) and propagated surface by surface. At each surface the engine finds the ray–surface intersection by solving for where the ray meets the surface sag — a Newton iteration for curved and aspheric shapes, closed-form for planes — then applies exact vector Snell refraction using the surface normal at the intersection point. There is no paraxial small-angle approximation anywhere in the path: the full vector form of Snell’s law is evaluated, total internal reflection is detected and flagged, and rays that miss the clear aperture are marked as vignetted rather than silently dropped. The result is an honest geometric trace, valid at any field angle and aperture.
Because real glass disperses, every trace is polychromatic: the refractive index at each surface is evaluated per wavelength from the glass’s dispersion data, so chromatic effects fall out of the same machinery that produces the monochromatic spot. Alongside the geometric path, the engine accumulates optical path length — physical distance weighted by refractive index — along every ray, including the final image-space medium. That accumulated OPL, referenced to a common focus, is the wavefront, and it is what feeds the OPD maps, the Zernike decomposition, and the diffraction-based PSF and MTF.
A separate coordinate-transformation layer lets any element be tilted or decentered and then returned to the axis, which is what makes alignment tolerancing possible. And running in parallel to the exact trace is a first-order (paraxial) engine — a y–nu marginal-ray trace and the equivalent ABCD ray-transfer matrices — that provides the focal length, pupil locations, f-number, and magnification. Keeping both engines side by side is deliberate: the paraxial layer predicts the ideal, the exact trace measures the real, and the gap between them is precisely the aberration content the rest of the tool analyzes.