def setWithRays(self, pencil, plane): """ Set PSF from RayPencil in a specifed OpticalPlane, note this work for curved planes, such as optics.surface.SphericalOpticalPlane as well as the normal flat planes. :param pencil: The RayPencil (note only valid rays are considered) :type pencil: RayPencil :param plane: The OpticalPlane, of if float OpticalPlane as (0,0,plane) :type plane: OpticalPlane or float or None This is th main used method to calcaute a psf in a plane. """ if isinstance(plane, (float, int)) or isinstance(plane, Vector3d): plane = OpticalPlane(plane) # Form the moments moments = FixedMoments() # Initialse for r in pencil: if r: pt = r.pointInPlane(plane) moments.addPoint(pt) self.wavelength = r.wavelength # Get the poisting of the centre of the Psf from the moments. self.set(plane.getSourcePoint(moments.centroid())) # Get the other parameters from the moments. self.major, self.minor, self.alpha = moments.ellipse() return self
def draw(self, plane, drawpsf=True): """ Draw the spot disagram to the current active MatPlotLib as circles with the centre given my the wavelelength of the first ray. :param plane: the plane where the diagram is located. :param drawpsf: draw the geometric psf on the disgram, (default = True) """ # Make an optical plane if needed if isinstance(plane, float) or isinstance(plane, Vector3d): plane = OpticalPlane(plane) xData = [] # X and Y point locations yData = [] for r in self.raypencil: if r: pt = r.pointInPlane(plane) # Find the point in the plane xData.append(pt.x) yData.append(pt.y) # Get the colour of the ray as a hex string col = WavelengthColour(self.raypencil[0].wavelength) # Scatter plot to the current figure axis('equal') scatter(xData, yData, color=col, marker='o') if drawpsf: # Make a psf if required and plot it psf = Psf().setWithRays(self.raypencil, plane) psf.draw()
def optimalArea(self, pencil, plane): """ Method to find the optimal area PSF from a raypencil starting as the guess locaion plane. This uses a simple itterative search bit provided that a reasonable guess is given for the inituial plane it converes quickly. The initial guess would normally be the paraxial image plane. :param pencil: the pencil or rays :param plane: the guess at the optimal plane, typically the paraxial plane. :return: the optimal psf Note the rays in the pencil are NOT changed. """ if isinstance(plane, float) or isinstance(plane, Vector3d): plane = OpticalPlane(plane) # set with initial condition tio local psf. psf = self.setWithRays(pencil, plane) area = psf.area() wave = pencil[0].wavelength / 1000 # Wavelengh in mm delta = -0.25 # Initial plane movement (usually it optmimal is short) deltaReduced = True # reversal flag # Loop looking for best while True: pl = plane.incrementSurface(delta) # Increment plane by delta self = Psf().setWithRays(pencil, pl) # Make new psf na = self.area() if na < area: # Accept plane = pl # Accept plane area = na deltaReduced = False if abs(delta) < wave: break else: # Dont accept if deltaReduced: # Delta not reduced last time delta = -delta deltaReduced = False else: delta *= 0.25 # Reduce delta deltaReduced = True # # Found best PSF, in self, just return return self
def draw(self, delta=0.0, drawpsf=True): """ Draw the diagram :param delta: displacement of plane from reference point (Default = 0.0) :type delta: float :param drawpsf: draw the geometric psf (Default = True) """ # Calcualte location of the plane self.plane = OpticalPlane(self.pt.z + delta) SpotDiagram.draw(self, self.plane, drawpsf) # Use underlying draw
def setWithRay(self,ray, plane, refpt = None): """ Set the value in a specifed plane using a ray and optional reference point. This is the main method used to calcuuate wavefront abberations in a plane wrt to a specificied refererence point. :param ray: The intensity ray :param plane: The flat plan in which to form the wavepoint :param refpt: The reference point. This assumed to be the image point. """ if isinstance(plane,float): # If plane as float, make a plane plane = OpticalPlane(plane) self.wavelength = ray.wavelength self.set(ray.pointInPlane(plane)) # set self to point in plane. distance = plane.getDistance(ray.position,ray.director) # get distance from ray to plane. if refpt != None: # Deal with reference point ref = refpt - plane.getPoint() # Ref point relative to centre of plane. xc = self.x - ref.x # Position relative to ref yc = self.y - ref.y c = 1.0/ref.z # Curvature u = ray.director # direction of ray # # Distance to reference sphere (same code as in QuadricSurface) f = c*(xc*xc + yc*yc) g = u.z - c*(xc*u.x + yc*u.y) a = g*g - c*f if a < 0: raise ValueError("analysis.WavePoint: ray misses reference sphere") else: distance += f/(g + math.sqrt(a)) # set total pathlength, pathleng of ray + pathlength to plane self.pathlength = ray.pathlength + distance*ray.refractiveindex.getValue(ray.wavelength) return self
def getWavePointSet(self,source,wave = None,nrays = 10,refopt = 1): """ Get the WavePointSet for collimated beam in the exitpupil of the lens :param source: source of input beeam, either SourcePoint, Unit3d or angle. :type source: SourcePoint, Unit3d, Angle or float :param wave: analysis wavelength :type wave: float :param nrays: number of raays across input aperture, (Default = 10) :type nrays: int :param refopt: reference point option, 0 = paraxial, 1 = centre of PSF in image plane, 2 = optimal area PSF :type refopt: int """ # Sort out angle if isinstance(source,SourcePoint): u = Vector3d(source) elif isinstance(source,float) or isinstance(source,int): u = Unit3d(Angle(source)) else: u = Unit3d(source) # Make pencil with array and record path, and propagate through lens pencil = RayPencil().addBeam(self.lens,u,"array",nrays=nrays,wavelength=wave,path=True) pencil *= self.lens # Get image point of object self.refpt = self.lens.imagePoint(u,self.design) # Paraxial point location pt = self.lens.getPoint() self.ip = OpticalPlane(Vector3d(pt.x,pt.y,self.refpt.z)) if refopt == 0: # got what we need aready None elif refopt == 1: self.refpt = Psf().setWithRays(pencil,self.ip) # Centre of PSF in image plane elif refopt == 2: self.refpt = Psf().optimalArea(pencil,self.ip) # Optimal area PSF, not in image plane else: print("Illegal ref type") ep = self.lens.exitPupil(self.design) # Exit pupil of lens # Form the wavefront wf = WavePointSet(ep.getRadius()).setWithRays(pencil,ep,self.refpt) return wf
def setWithRays(self,pencil,plane,refpt = None): """ Create a set of wavepoint from a pencil, in a specified plane with an optional reference point :param pencil: The raypencil containing a list of arrays :param plane: the plane of the wavepoints. :param refpt: The reference point, may be None. """ if isinstance(plane,float): # If plane as float, make a plane plane = OpticalPlane(plane) self.plane = plane # Record plane #if hasattr(plane, "maxRadius"): # self.maxRadius = plane.maxRadius # Set to plane maxradius if defined for r in pencil: if r: # Only take valid rays. self.add(WavePoint().setWithRay(r,plane,refpt)) return self
def main(): # Get lens from database lens = DataBaseLens() # Get angle of beam and wavelnegth angle = getFloat("Angle in degrees", 0.0, 0.0, 15.0) u = Unit3d(Angle().setDegrees(angle)) # Angle as unit vectr w = getFloat("Wavelength", Default) # Make two ray pencils, one for spot diagram and one for display (vertical only) pencil = RayPencil().addBeam(lens, u, "array", wavelength=w) vpencil = RayPencil().addBeam(lens, u, "vl", wavelength=w).addMonitor(RayPath()) bf = lens.backFocalPlane() # Propagate through lens to back focal plane pencil *= lens vpencil *= lens vpencil *= bf # Get optimal area psf and create a SpotDiagram sd = SpotDiagram(pencil) # Go round loop plotting the sopt diagram as various zplane positions while True: zp = getFloat("Zplane", bf.getPoint().z) plane = OpticalPlane(zp) plt.subplot(2, 1, 1) lens.draw() vpencil.draw() plt.axis("equal") plt.title("Lens " + lens.title) plt.subplot(2, 1, 2) sd.draw(plane, True) plt.title("Spot diagram") plt.show(block=True)
def drawAberrationPlot(self,angle,wavelength = None ,colour=["r","g","b"],legend = "lower left"): """ Form and draw the plots at specified angle :param angle: the ray angle :type angle: float or Angle or Unit3d :param colour: line colours is three elemnts list, Default = ["r","g","b"]) :param legend: location of ledgend, (Default = "lower left") :type legend: str """ if isinstance(angle,float): u = Unit3d(Angle(angle)) # direction of beam else: u = Unit3d(angle) angle = Angle(u).theta wavelength = getDefaultWavelength(wavelength) ref = self.lens.imagePoint(u,self.design) # Get image point at design wavelength ip = OpticalPlane(ref) # Image plane nrays = 50 ca = self.lens.entranceAperture() # Make list for plots mvals = [] # Meridional svalsx = [] # Sagittal x svalsy = [] # Sagittal y # Start of loop to make rays rscan = np.linspace(-ca.maxRadius,ca.maxRadius,nrays + 1) for r in rscan: # Make two rays mray = IntensityRay(ca.point + Vector3d(0.0, r, 0.0), u, wavelength) sray = IntensityRay(ca.point + Vector3d(r, 0.0, 0.0), u, wavelength) # Add to pencil and propagate both back to clear lens pencil = RayPencil(mray,sray).propagate(-ca.maxRadius) # propagate through lens to image surafce pencil *= self.lens pencil *= ip # If rays valid (so not blocked), abberations to list if mray: mpos = mray.position - ref mvals.append(mpos.y) else: mvals.append(float("nan")) if sray: spos = sray.position - ref svalsx.append(spos.x) svalsy.append(spos.y) else: svalsx.append(float("nan")) svalsy.append(float("nan")) # plots with suitable labels to the current axis. plt.plot(rscan,mvals, color = colour[0], label="Meridional") plt.plot(rscan,svalsx,color = colour[1], label="Sagittal x") plt.plot(rscan,svalsy,color = colour[2], label="Sagittal y") plt.title("{0:s}: a: {1:4.2f} w: {2:4.2f} d: {3:4.2f}".\ format(self.lens.title,math.degrees(angle),wavelength,\ self.design)) plt.legend(loc=legend,fontsize="small") plt.grid()