k1lib.kop module
This module is for optics simulation. This is exposed automatically with:
from k1lib.imports import *
kop.Rays.parallel(...) # exposed
A comprehensive introduction is available here: https://mlexps.com/optics/1-kop-intro/
- class k1lib.kop.Drawable[source]
Bases:
object
Base class to do common drawing routines
- img() PIL.Image.Image [source]
Returns PIL image
- class k1lib.kop.RandomPoints(data)[source]
Bases:
Drawable
- class k1lib.kop.Drawables(drawables: List[Drawable], config=None)[source]
Bases:
Drawable
- class k1lib.kop.Rays(data, prevRays: Optional[Rays] = None, ogSurface: Optional[Surface] = None)[source]
Bases:
Drawable
- __init__(data, prevRays: Optional[Rays] = None, ogSurface: Optional[Surface] = None)[source]
Represents a bunch of rays, stored in a numpy array so that all operations are fast
data format: array of shape (N, 6) - 0) x coordinate of starting point - 1) y coordinate of starting point - 2) angle counter clockwise from positive x axis - 3) length (inf for forever) - 4) wavelength in nm - 5) #transforms - 6) power in Watts, defaulted to 1W - 7) intersected?: used for outgoing rays exiting out of a surface. If surface._cast(prevRay) shows that it does intersect, then add that to the outgoing rays
The #transforms is a little complicated: - Original ray is 0, then each time a glass/mirror surface does something interesting,
the outgoing ray is incremented by 1. If it doesn’t touch the optic element, then it keeps the same number as before
- Parameters
prevRays – a reference to the previous Rays object, in order to limit the length of all previous rays!
ogSurface – the surface that generates this Rays
- static parallel(x=0, y=0, theta=0, N=10, height=10, color=700, power=1)[source]
Creates parallel rays starting from a particular point with a particular angle
- Parameters
theta – angle from positive x axis counterclockwise
N – how many rays to create total?
height – the height of the parallel rays if it were to have no angle
- static parallelToBounds(x=0, y=0, bounds=None, N=10, color=700, power=1, coverage=0.9, angleOffset=0)[source]
Creates parallel rays starting from a particular point to the center of some bounds. The bounds should have the format (xmin, ymin, xmax, ymax). :class:`Surface`s, :class:`OpticElement`s all have the .bounds() method, so you can use them
See also:
parallel()
- Parameters
coverage – number from 0 to 1. 1 meaning the parallel lines should cover the entire (rotated) height of the bounds, 0.5 means it covers only half of the height, and so on. Think of this as %height
- static pointToBounds(x=0, y=0, bounds=None, N=10, color=700, power=1, coverage=0.9, angleOffset=0)[source]
Creates rays starting from a particular point to the bounds.
See also:
parallelToBounds()
- Parameters
coverage – instead of %height like
parallelToBounds()
, this is the anglular coverage, 1 for covering the entire object.angleOffset – offset to the angle from the starting point to the center of the bounds
- hitbox(spread=2, maxWH=400, mode='simple', intersectOnly=True) PIL.Image.Image [source]
Visualizes the Ray’s starting point, with colors and whatnot. Example:
r = Rays.parallel(theta=pi/8) r.hitbox()
- Parameters
spread – will color this much nearby pixels
maxWH – size of the returned image. Either width or height will be around this big, whichever is bigger
mode – several plotting modes, each with its own performance and accuracy characteristics
intersectOnly – if True (default), will only plot the hitbox of the rays that actually hits the Surface that generates the ray and not some previous surface
- k1lib.kop.sellmeier(wavelengths: ndarray, glassParam)[source]
Runs the sellmeier equation to get the index of refraction for multiple wavelengths for a particular glass material
- class k1lib.kop.Surface(gp1=None, gp2=None, mode='glass', capture=False, angleStd=0, opticElement=None)[source]
Bases:
Drawable
- __init__(gp1=None, gp2=None, mode='glass', capture=False, angleStd=0, opticElement=None)[source]
Represents a geometry, like line, arc, or parabola. Rays can interact with a surface, resulting in new Rays. An optic element can have multiple of these surfaces and react differently to incoming rays.
The convension is that gp1 is the environment’s glassParam, while gp2 is the Surface’s glassParam. A surface needs both in order to calculate the deflection angles.
These modes are available:
glass: transparent (or semi-transparent) surface, bends incoming rays
mirror: perfectly reflective (or allow some percentage of light to pass through)
sensor: absorbes all incoming rays, can retrieve the image that’s absorbed
opaque: reflects incoming rays in a random direction outward
- Parameters
gp1 – glass params 1, usually this is of the environment (air)
gp2 – glass param 2, usually this is of the object (borosilicate glass, sapphire, water)
mode – explained above
capture – if True, captures all rays that comes in through .cast() function. Captures will stay the same across copy.copy() instances to save perf! The captures will be available at
.captures
angleStd – if specified, when casting outgoing rays, will add this much standard deviation in the rays’ angle, useful to quickly simulate a bumpy piece of glass
opticElement – the OpticElement that this Surface is a part of
- class k1lib.kop.LineSurface(x1, y1, x2, y2, **kwargs)[source]
Bases:
Surface
- class k1lib.kop.ArcSurface(x, y, r, startAngle, endAngle, **kwargs)[source]
Bases:
Surface
- static from2Points(x1, y1, x2, y2, r, **kwargs) ArcSurface [source]
Creates an
ArcSurface
from 2 points and a radius. Example:kop.ArcSurface.from2Points(1, 2, 3, 4, 10)
- k1lib.kop.polySolver(polys)[source]
Expected to take in a (N,F) array of N different polynomials with order F-1 and return 2 arrays:
intersected (N,) bool array: whether there’re any roots
beta (N,) float array: the root of each polynomial that’s >0 and is the minimum out of all roots
Right now, only supports vectorized solvers with order 2 polynomials. Any higher and it falls back to using un-vectorized np.roots(), or other methods like it. This is separated out so that it can be swapped out for a better implementation
Potential candidates for >2 order polynomials: - scipy: fsolve, root, newton, brentq
- class k1lib.kop.PolySurface(x=0, y=0, angle=0, scale=1, xmin=-10, xmax=10, coeff=(0, 0, 0.1), solver=<function polySolver>, **kwargs)[source]
Bases:
Surface
- __init__(x=0, y=0, angle=0, scale=1, xmin=-10, xmax=10, coeff=(0, 0, 0.1), solver=<function polySolver>, **kwargs)[source]
Surface defined using a polynomial.
Let’s say there’s a parabola that you’d like to input into the simulation, say
x^2/10
. There are several dials and knobs that you can change to position your Surface right where you want it.There are 2 frame of reference I’ll be using. First is world frame, where all your elements sits in, and second is poly frame, where your polynomial coefficients will define (x, y) in.
In the poly frame, the surface is defined as all x points in (xmin, xmax), together with all y points calculated straight from your coefficients. So if
coeff = [1, 2, 3]
, then the equation will bepy = f(px) = 3px^2 + 2px + 1
. Now pairs of (px, py) points in poly frame is obtained.Then (px, py) will go through several transformations to get to the world frame: - Rotated by
angle
radians counterclockwise - Scaled byscale
- Translated by(x, y)
Then to calculate reflections and whatnot, internally, all rays will be translated from world to poly frame, then the intersection points are solved and then results will be translated from poly back to world frame.
- Parameters
x – translation applied to surface
angle – angle to rotate the surface by
scale – scaling factor to scale the surface by
xmin – to define the domain of the given polynomial in poly frame
coeff – polynomial coefficients
- class k1lib.kop.RaysPath(rayss: List[Rays])[source]
Bases:
Drawable
- class k1lib.kop.Polygon(points=None, **kw)[source]
Bases:
OpticElement
- class k1lib.kop.Lens(x=50, y=0, R1=300, R2=300, thickness=3, height=20, angle=0, surface='arc', **kw)[source]
Bases:
OpticElement
- __init__(x=50, y=0, R1=300, R2=300, thickness=3, height=20, angle=0, surface='arc', **kw)[source]
Represents a Lens with center at (x, y), and with 2 radiuses, R1 for left and R2 for right.
- Parameters
R1 – radius of the left side, works for both arc and parabola mode
angle – angle offset of the Lens
surface – either “arc” or “parabola”, which will make a lens of that geometry