def __init__(self, pos, dirn, wavelength=None, intensity=1.0, index=AirIndex()): """ Constructor for to set parameters """ if isinstance(pos, ParaxialRay): Ray.__init__(self, pos.wavelength, pos.intensity, pos.refractiveindex) self.position = Vector3d(0.0, pos.h, pos.z) self.director = Unit3d(Angle(pos.u)) else: if isinstance(pos, SourcePoint): intensity = pos.spectrum Ray.__init__(self, wavelength, intensity, index) # Set wavelnegth intensity and index in super self.position = Vector3d( pos ) # Make localcopy of Position and Director (since they get updated) self.director = Unit3d(dirn) self.pathlength = None # Set opl to none (not calculated)
def imagePoint(self, op): """ Method to calcualte three-dimensional image of a point in object space in global coordinates using geometric optics. op be in range of formats: - float, Angle, Unit3d: assumes an infinite object. - Vector3d: finite object at specified vector location. :param op: Position of object point :type op: Vector3d or Unit3d or Angle or float :return: Position the image point as vector.Vector3d This is """ if isinstance(op, (float, int, Unit3d, Angle)): # Infitite Object op = Unit3d().parseAngle(op) # Sort out angle p = Vector3d(0, 0, self.backNodalPoint()) return Vector3d(p + op * (self.backFocalLength() / op.z)) elif isinstance(op, Vector3d): # Finite object u = self.frontPrincipalPlane() - op.z v = u / (self.backPower() * u - 1) ip = self.backPrincipalPlane() + v # Image location mag = -v / u return Vector3d(mag * op.x, mag * op.y, ip) else: raise TypeError( "matrix.ParaxialGroup.pointImage: called with unknown type {0:s}" .format(str(op)))
def __init__(self, pt=0.0, radius=1.0): if isinstance(pt, (float, int)): self.point = Vector3d(0.0, 0.0, pt) else: self.point = Vector3d(pt) self.maxRadius = float(radius)
def __init__(self, pos, s=1.0): """ Create a 3d source point """ if isinstance(pos, (float, int)): Vector3d.__init__(self, 0, 0, pos) else: Vector3d.__init__(self, pos) if isinstance(s, Spectrum): # Add spectrum if given self.spectrum = s else: self.spectrum = Spectrum(s) # Set constant spectrum
def getVector3d(prompt, default=None, maxabs=None): """ Read a Vector3d from the terminal with checking. Format from terminal may be 'x,y,z' OR '[x,y,z]', also each componet will be evaluated. :param prompt: the prompt to be displayed :type prompt: str :param default: the default value (may be None) :type default: Vector3d :param maxabs: abs max value of the Vector3d (defaults to None) :type maxabs: float :return: the set Vector3d Its also Tries to evaluate any sensible string as a Vector3d. """ if maxabs == None: maxabs = float("Inf") while True: val = __getInput(prompt, default) try: if isinstance(val, str): # Its a string val = eval(val) # Eval list vec = Vector3d(val) if abs(vec) <= maxabs: return vec # Success else: logger.error("Abs value of '{0:s}' greater than '{1:6.3e}'".\ format(repr(vec), maxabs)) except (ValueError,NameError,ZeroDivisionError,SyntaxError): logger.error("Conversion of '{0:s}' to Vector3d failed.".format(str(val)))
def main(): lens = DataBaseLens("Tessar-F6.3") lens.setIris(0.7) u = Unit3d().parseAngle("5") pencil = RayPencil().addBeam(lens, u, "array", path=True) pencil *= lens ep = lens.exitPupil() t.tprint(repr(ep)) rpt = lens.imagePoint(u) rpt += Vector3d(0, 0, 0.2) t.tprint("Image point is ", repr(rpt)) ws = WavePointSet(ep.getRadius()).setWithRays(pencil, ep, refpt=rpt) zw = ws.fitZernike(4) t.tprint(repr(zw)) t.tprint("Error is : ", str(ws.zerr)) # Show the fitting error for each component zw.plot() ws.plot() inter = Interferometer(zw) inter.draw(0.0, 0.0) plt.show()
def __init__(self, pt=Vector3d(), xpixel=256, ypixel=None, xsize=200, ysize=None, wave=TriColour): """ Form the OpticalImage with either blank array of nmpy image array """ if isinstance(pt, ImagePlane): # Deal with ImagePlane ImagePlane.__init__(self, pt.point, pt.xsize, pt.ysize) else: if ysize == None: ysize = xsize ImagePlane.__init__(self, pt, xsize, ysize) # Set underying ImagePlane if isinstance(xpixel, int): if ypixel == None: ypixel = xpixel self.image = np.zeros((xpixel, ypixel, 3)) # Make array of zeros. else: self.image = xpixel # assume numpy array given self.xpixel, self.ypixel, c = self.image.shape # set xpixel and ypixel from image data self.wavelengths = wave
def __init__(self,lens, design = None): """ Constructor """ self.lens = lens self.design = getDesignWavelength(design) self.refpt = Vector3d() self.ip = self.lens.backFocalPlane(self.design) # Make back focal plane to proagate to
def getPoint(self): """ Method to get the group point, used by other optics classes. :return: get the group point Vector3d For compatibility for OpticalGroup. """ return Vector3d(0.0, 0.0, self.input_plane)
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 main(): a = Vector3d(1,2,3) b = a.set([4,5,6]) print(repr(b)) u = Unit3d().parseAngle("35","71") print(repr(u)) v = getUnit3d("Direction") print(repr(v))
def __init__(self, pos=0.0, intensity=1.0, major=1.0, minor=None, alpha=0.0, wavelength=None): """ Constructor """ if isinstance(pos, float): Vector3d.__init__(self, [0.0, 0.0, pos]) else: Vector3d.__init__(self, pos) self.intensity = 1.0 self.major = major if minor == None: self.minor = self.major else: self.minor = minor self.alpha = alpha self.wavelength = getDefaultWavelength(wavelength)
def setPoint(self,pt = 0.0): """ Method to set the surface reference point in a consistent way for all surfaces. The specified point can be copy from Surface, Vector3d, list, truple, Vector2d or float/int. :param pt: surface point can be Surface, Vector3d, 3 component list/truple, Vector2d with z = 0, float giving (0,0,z), (Default = 0.0) :type z_or_v: Vector3d, list[], Vector2d or float :return: self """ if isinstance(pt,Surface): self.point = pt.point.copy() elif isinstance(pt,(Vector3d,list,tuple)): self.point = Vector3d(pt) elif isinstance(pt,Vector2d): self.point = Vector3d(pt.x,pt.y,0.0) elif isinstance(pt,(float,int)): self.point = Vector3d(0.0,0.0,pt) else: raise TypeError("surface.Surface.setPoint: called with unknown type") return self
def getSurfaceInteraction(self,ray): """ Method to get get surface interaction information for a ray. :param ray: the Ray :type ray: poptics.ray.Ray :return: SurfaceInteration with reference point and other parameters invalid This is abstract surface, so only type, surface point and refarctive index will be valid. """ pt = self.getPoint() return SurfaceInteraction(self.type,pt,float("nan"),\ Vector3d().setInvalid(),Blocked,self.refractiveindex)
def setKnife(self,knife = 0.0, theta = 0.0, shift = 0.0): """ Set the knife parameters :param knife: distance of knife edge from axis (Default = 0.0) :type knife: float :param theta: angle of knife edge wrt to y-axis in radians (Default = 0.0) :type theta: float :param shift: distance along the z axis from the aperture position (Default = 0.0) :type shift: float """ self.knife = float(knife) self.theta = float(theta) self.shift = Vector3d(0.0,0.0,shift) # Hold shift as vector return self
def incrementPoint(self,delta): """ Move the reference point by specifed amount. Distance moved can be a Vector3d OR float where the movement will be along the z-axis. :param delta: distance moved :type delta: Vector3d or float :return: self """ if isinstance(delta,(float,int)): self.point += Vector3d(0.0,0.0,float(delta)) else: self.point += delta return self
def getGradient(self, ray): """ Method to calcualte the Gradient as specifed ray position """ p = ray.position n = self.index.getValue(ray) x = p.x - self.point.x y = p.y - self.point.y rsqr = x * x + y * y dx = 2 * n * x * self.coef[1] dy = 2 * n * y * self.coef[1] """ for i in range(2,len(self.coef)): r = math.pow(rsqr,i-1) dx += 2*x*n*i*coef[i]*r dy += 2*y*n*i*coef[i]*r """ return Vector3d(dx, dy, 0.0)
def main(): # Form two apertures both 20mm with Iris closed to 0.5 ratio ca = CircularAperture(50, 20) iris = IrisAperture(80, 20, 0.5) # source for the rays at (0,10,-50) in global coordinates source = SourcePoint(Vector3d(0.0, 10, -50)) # Form a pencil is the circular aperture as specified angle of 0.45 microns # and add a RayPath to ech ray pencil = RayPencil().addBeam(ca, source, wavelength=0.65).addMonitor(RayPath()) # Propgate throgh the the both aperture and another 30 mm to make it visible pencil *= ca pencil *= iris pencil += 30 # Make a diagram ca.draw() iris.draw() pencil.draw() plt.axis("equal") plt.show()
def __str__(self): """ Implement str """ return Vector3d.__str__(self) + " s: " + str(self.spectrum)
def getPoint(self): """ Method to get the input point. used by orher optics classes """ return Vector3d(0.0, 0.0, self.getInputPlane())
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()
def addBeam(self, ca, source, key="vl", nrays=10, wavelength=None, intensity=1.0, index=AirIndex(), path=False): """ Method to add a beam if intensity rays being either Collimated or Source Beam. The beam will fill the given circular aperture and will either come from a single SourcePoint or at a specified angle. :param ca: circular aperture to filled (any object with maxRadius attribute) :type ca: optics.surface.CircularAperture :param source: source or rays, either a SourcePoint or angle. :type source: SourcePoint or Vectore3d or Unit3d or Angle or float :param key: method of fill, allowed keys as "vl", "hl" and "array",(default is "vl") :type key: str :param nrays: number or rays across radius, (default = 10) :type nrays: int :param wavelenth: the wavelength, (default = Default) :type wavelength: float :param intensity: the ray intensity, (default = 1.0) only used for Collimated beam; for SourceBeam picked up from SourcePoint :type intensity: float :param index: the refratcive index, (Default = AirIndex()) :type index: RefractiveIndex :param path: record pathlength, (default = False) is pathlength of each ray recorded :type path: bool :return: self """ # Sort out aperture to fill. if not hasattr(ca, "maxRadius"): ca = ca.entranceAperture() pt = ca.getPoint() # Reference point radius = ca.maxRadius if isinstance(source, SourcePoint): # Rays from a source s = Vector3d(source) intensity = source.getIntensity(wavelength) else: s = Vector3d().setInvalid( ) # Set s unvalid (will be used for testing) u = Unit3d().parseAngle(source) rscan = linspace(-radius, radius, 2 * nrays + 1) # Point across radius (make sure one in centre) if key.startswith("ar"): xscan = rscan yscan = rscan elif key.startswith("vl"): xscan = [0.0] yscan = rscan elif key.startswith("hl"): xscan = rscan yscan = [0.0] else: print("Error") # Scan through making the rays for y in yscan: for x in xscan: if x * x + y * y <= radius * radius: # Ignore if outside radius of aperture p = Vector3d( pt.x + x, pt.y + y, pt.z) # Point in aperture in global coordinates if s: # From source u = Unit3d(p - s) ray = IntensityRay(s, u, wavelength, intensity, index) # Make source ray else: # Collimated beam dist = float(radius + x * u.x + y * u.y) p -= dist * u # Propagate point to make it look nicer ray = IntensityRay(p, u, wavelength, intensity, index) # Make collimated if path: ray.pathlength = 0.0 self.append(ray) # Append to self return self
def __str__(self): """ Implement str """ return " {0:s} i: {1:7.4f} major: {2:7.4f} minor: {3:7.4f} alpha: {4:7.4f}".\ format(Vector3d.__str__(self),self.intensity,self.major,self.minor,self.alpha)