def point_source_p(origin=(0.,0.,0.),direction=(0.,0.,0),span=pi/8,num_rays=(10,10),wavelength=0.58929, label=""): """Point source, with a polar beam distribution This function creates a point source, where the rays are organized in a polar grid. Parameters: *origin* Tuple with the coordinates of the central ray origin *direction* Tuple with the rotation of the beam arround the XYZ axes. *span* Tuple angular size of the ray pencil. *num_rays* Tuple (nr,na) containing the number of rays used to create the beam *label* String used to identify the ray source """ ret_val=[Ray(pos=(0,0,0),dir=(0,0,1),wavelength=wavelength, label=label).ch_coord_sys_inv(origin,direction)] nr,nt=num_rays for r_ in range(1,nr): r=span*float(r_)/nr temp_ray=Ray(pos=(0,0,0),dir=(0,0,1),wavelength=wavelength, label=label).ch_coord_sys_inv((0,0,0),(r,0,0)) for t in range(nt): tz=2*pi*t/nt temp_ray1=temp_ray.ch_coord_sys_inv((0,0,0),(0,0,tz)) ret_val.append(temp_ray1.ch_coord_sys_inv(origin,direction)) return ret_val
def point_source_r(origin=(0.,0.,0.),direction=(0.,0.,0),span=pi/8,num_rays=100,wavelength=0.58929, label=""): """Point source, with a ranrom beam distribution This function creates a point source, where the rays are organized in a random grid. Parameters: *origin* Tuple with the coordinates of the central ray origin *direction* Tuple with the rotation of the beam arround the XYZ axes. *span* Tuple angular size of the ray pencil. *num_rays* Number of rays used to create the beam *label* String used to identify the ray source """ ret_val=[] for n_ in range(num_rays): rx=normal(0,span) ry=normal(0,span) temp_ray=Ray(pos=(0,0,0),dir=(0,0,1),wavelength=wavelength, label=label).ch_coord_sys_inv((0,0,0),(rx,ry,0)) ret_val.append(temp_ray.ch_coord_sys_inv(origin,direction)) return ret_val
def test_intersection(): # TODO: choose better example # Intersecting expected_intersection_point = (0, 0, 0) ray1 = Ray(pos=expected_intersection_point, dir=(0, 0.2, 1)) ray2 = Ray(pos=expected_intersection_point, dir=(1, 2, 1)) intersection_point, real_ = calc.intersection(ray1, ray2) np.testing.assert_almost_equal(intersection_point, expected_intersection_point) assert real_ == True # Not intersecting ray1 = Ray(pos=(0, 0, 0), dir=(0, 0.2, 1)) ray2 = Ray(pos=(1, 0, 0), dir=(0, 0.2, 1)) intersection_point, real_ = calc.intersection(ray1, ray2) np.testing.assert_almost_equal(intersection_point, [np.nan, np.nan, np.nan]) assert real_ == False
def test_paraxial_location(): # image_location, real_ = paraxial_location(opsys, opaxis): lens1 = library.Edmund.get("45179") # f=200 r= 25 optical_axis = Ray(pos=(0, 0, -10000), dir=(0, 0, 1), wavelength=0.55) ccd = CCD(size=(10, 10)) s = System( complist=[ (lens1, (0, 0, 100), (0, np.pi, 0)), (ccd, (0, 0, 320.053), (0, 0, 0)), ], n=1, ) PB = parallel_beam_c( origin=(0, 0, 50), direction=(0, 0, 0), size=(15, 15), num_rays=(15, 15), wavelength=0.55, ) s.ray_add(PB) s.propagate() image_location, real_ = calc.paraxial_location(s, optical_axis) np.testing.assert_almost_equal(image_location, [-5.59109334e-16, 0, 3.07249900e02]) assert real_ == False
def parallel_beam_p(origin=(0., 0., 0.), direction=(0., 0., 0), radius=0.5, num_rays=(5, 10), wavelength=0.58929, label=""): """Polar grid parallel beam This function creates a parallel beam, where the rays are organized in a polar grid. Parameters: *origin* Tuple with the coordinates of the central ray origin *direction* Tuple with the rotation of the beam arround the XYZ axes. *r* Beam radious *num_rays* Tuple (nr,na) containing the number of rays used to create the beam. *label* String used to identify the ray source """ ret_val = [ Ray(pos=(0, 0, 0), dir=(0, 0, 1), wavelength=wavelength, label=label).ch_coord_sys_inv(origin, direction) ] nr, nt = num_rays for r_ in range(1, nr): r = radius * float(r_) / (nr - 1) for t in range(nt): x_ = r * sin(2 * pi * t / nt) y_ = r * cos(2 * pi * t / nt) ret_val.append( Ray(pos=(x_, y_, 0), dir=(0, 0, 1), wavelength=wavelength, label=label).ch_coord_sys_inv(origin, direction)) return ret_val
def point_source_p(origin=(0., 0., 0.), direction=(0., 0., 0), span=pi / 8, num_rays=(10, 10), wavelength=0.58929, label=""): """Point source, with a polar beam distribution This function creates a point source, where the rays are organized in a polar grid. Parameters: *origin* Tuple with the coordinates of the central ray origin *direction* Tuple with the rotation of the beam arround the XYZ axes. *span* Tuple angular size of the ray pencil. *num_rays* Tuple (nr,na) containing the number of rays used to create the beam *label* String used to identify the ray source """ ret_val = [ Ray(pos=(0, 0, 0), dir=(0, 0, 1), wavelength=wavelength, label=label).ch_coord_sys_inv(origin, direction) ] nr, nt = num_rays for r_ in range(1, nr): r = span * float(r_) / nr temp_ray = Ray(pos=(0, 0, 0), dir=(0, 0, 1), wavelength=wavelength, label=label).ch_coord_sys_inv((0, 0, 0), (r, 0, 0)) for t in range(nt): tz = 2 * pi * t / nt temp_ray1 = temp_ray.ch_coord_sys_inv((0, 0, 0), (0, 0, tz)) ret_val.append(temp_ray1.ch_coord_sys_inv(origin, direction)) return ret_val
def test_nearest_points(): # Real closest point ray1 = Ray(pos=(0, 0, 0), dir=(1, 1, 0)) ray2 = Ray(pos=(1, 0, 1), dir=(0, 1, 0)) closest_point_on_ray1, closest_point_on_ray2, distance, real_ = calc.nearest_points( ray1, ray2) np.testing.assert_almost_equal(closest_point_on_ray1, [1.0, 1.0, 0.0]) np.testing.assert_almost_equal(closest_point_on_ray2, [1.0, 1.0, 1.0]) assert distance == 1 assert real_ is True # Virtual closest point ray1 = Ray(pos=(0, 0, 0), dir=(1, 1, 0)) ray2 = Ray(pos=(1, 10, 1), dir=(0, 1, 0)) closest_point_on_ray1, closest_point_on_ray2, distance, real_ = calc.nearest_points( ray1, ray2) np.testing.assert_almost_equal(closest_point_on_ray1, [1.0, 1.0, 0.0]) np.testing.assert_almost_equal(closest_point_on_ray2, [1.0, 1.0, 1.0]) assert distance == 1 assert real_ is False
def pyoptools_repr(self, obj): wl = obj.wl.getValueAs("µm").Value pla = obj.getGlobalPlacement() X, Y, Z = pla.Base r_vec = pla.Rotation.multVec(FreeCAD.Base.Vector(0, 0, 1)) return [ Ray(pos=(X, Y, Z), dir=(r_vec.x, r_vec.y, r_vec.z), wavelength=wl) ]
def point_source_c(origin=(0.,0.,0.),direction=(0.,0.,0),span=(pi/8,pi/8)\ ,num_rays=(10,10),wavelength=0.58929, label=""): """Point source, with a cartesian beam distribution This function creates a point source, where the rays are organized in a cartesian grid. Parameters: *origin* Tuple with the coordinates of the central ray origin *direction* Tuple with the rotation of the beam arround the XYZ axes. *span* Tuple angular size of the ray pencil. *num_rays* Tuple (nx,ny) containing the number of rays used to create the beam. *label* String used to identify the ray source """ ret_val = [] nx, ny = num_rays dx, dy = span for ix in range(nx): for iy in range(ny): if nx != 1: tx = -dx / 2. + dx * ix / (nx - 1) else: tx = 0. if ny != 1: ty = -dy / 2. + dy * iy / (ny - 1) else: ty = 0. temp_ray = Ray(pos=(0, 0, 0), dir=(0, 0, 1), wavelength=wavelength, label=label).ch_coord_sys_inv((0, 0, 0), (tx, ty, 0)) ret_val.append(temp_ray.ch_coord_sys_inv(origin, direction)) return ret_val
def point_source_c(origin=(0.,0.,0.),direction=(0.,0.,0),span=(pi/8,pi/8)\ ,num_rays=(10,10),wavelength=0.58929, label=""): """Point source, with a cartesian beam distribution This function creates a point source, where the rays are organized in a cartesian grid. Parameters: *origin* Tuple with the coordinates of the central ray origin *direction* Tuple with the rotation of the beam arround the XYZ axes. *span* Tuple angular size of the ray pencil. *num_rays* Tuple (nx,ny) containing the number of rays used to create the beam. *label* String used to identify the ray source """ ret_val=[] nx,ny=num_rays dx,dy=span for ix in range(nx): for iy in range(ny): if nx!=1: tx=-dx/2.+dx*ix/(nx-1) else: tx=0. if ny!=1: ty=-dy/2.+dy*iy/(ny-1) else: ty=0. temp_ray=Ray(pos=(0,0,0), dir=(0,0,1), wavelength=wavelength, label=label ).ch_coord_sys_inv((0,0,0),(tx,ty,0)) ret_val.append(temp_ray.ch_coord_sys_inv(origin,direction)) return ret_val
def point_source_r(origin=(0., 0., 0.), direction=(0., 0., 0), span=pi / 8, num_rays=100, wavelength=0.58929, label=""): """Point source, with a ranrom beam distribution This function creates a point source, where the rays are organized in a random grid. Parameters: *origin* Tuple with the coordinates of the central ray origin *direction* Tuple with the rotation of the beam arround the XYZ axes. *span* Tuple angular size of the ray pencil. *num_rays* Number of rays used to create the beam *label* String used to identify the ray source """ ret_val = [] for n_ in range(num_rays): rx = normal(0, span) ry = normal(0, span) temp_ray = Ray(pos=(0, 0, 0), dir=(0, 0, 1), wavelength=wavelength, label=label).ch_coord_sys_inv((0, 0, 0), (rx, ry, 0)) ret_val.append(temp_ray.ch_coord_sys_inv(origin, direction)) return ret_val
def parallel_beam_c(origin=(0., 0., 0.), direction=(0., 0., 0.), size=(1., 1.), num_rays=(10, 10), wavelength=0.58929, label=""): """Cartesian grid parallel beam This function creates a parallel beam, where the rays are organized in a cartesian grid. Parameters: *origin* Tuple with the coordinates of the central ray origin *direction* Tuple with the rotation of the beam arround the XYZ axes. *size* Tuple with the beam's width and the height. *num_rays* Tuple (nx,ny) containing the number of rays used to create the beam. *label* String used to identify the ray source """ ret_val = [] nx, ny = num_rays dx, dy = size # note modify this to use traits dx = float(dx) dy = float(dy) for ix in range(nx): for iy in range(ny): x = -dx / 2. + dx * ix / (nx - 1) y = -dy / 2. + dy * iy / (ny - 1) ret_val.append( Ray(pos=(x, y, 0), dir=(0, 0, 1), wavelength=wavelength, label=label).ch_coord_sys_inv(origin, direction)) return ret_val
def test_find_ppp(): # find_ppp(opsys, opaxis) lens1 = library.Edmund.get("45179") # f=200 r= 25 optical_axis = Ray(pos=(0, 0, -10000), dir=(0, 0, 1), wavelength=0.55) ccd = CCD(size=(10, 10)) s = System( complist=[ (lens1, (0, 0, 100), (0, np.pi, 0)), (ccd, (0, 0, 320.053), (0, 0, 0)), ], n=1, ) PB = parallel_beam_c( origin=(0, 0, 50), direction=(0, 0, 0), size=(15, 15), num_rays=(15, 15), wavelength=0.55, ) s.ray_add(PB) s.propagate() result = calc.find_ppp(s, optical_axis) np.testing.assert_almost_equal(result, [0.0, 0.0, 104.5670357])
def test_intersection(): # Intersecting exactly expected_intersection_point = (3, 1, 1) d1 = np.array((0.1, 0.2, 0.3)) d2 = np.array((1, 0.5, 0)) t1 = 2 t2 = 3 ray1 = Ray(pos=expected_intersection_point - t1 * d1, dir=d1) ray2 = Ray(pos=expected_intersection_point - t2 * d2, dir=d2) intersection_point, real_ = calc.intersection(ray1, ray2) np.testing.assert_almost_equal(intersection_point, expected_intersection_point) assert real_ is True # Approximately intersecting ray1 = Ray( pos=np.array((-1.18414888e-15, 0.0, 5.603)), dir=np.array((-5.22664407e-18, 0.0, 1.0)), ) ray2 = Ray( pos=np.array((0.01068222, 0.0, 5.60299917)), dir=np.array((-5.56560819e-05, 0.0, 9.99999998e-01)), ) intersection_point, real_ = calc.intersection(ray1, ray2) expected_intersection_point = np.array((0.0, 0.0, 1.97535672e02)) np.testing.assert_almost_equal(intersection_point, expected_intersection_point) assert real_ is True # Not intersecting ray1 = Ray(pos=(0, 0, 0), dir=(0, 0.2, 1)) ray2 = Ray(pos=(1, 0, 0), dir=(0, 0.2, 1)) intersection_point, real_ = calc.intersection(ray1, ray2) np.testing.assert_almost_equal(intersection_point, [np.nan, np.nan, np.nan]) assert real_ is False
def chief_ray_search(opsys, ccds, o=(0., 0., 0.), rt=(0., 0., 0.), er=0.1, w=pi / 2., maxiter=1000, wavelength=.58929): ''' This function uses a random search algorithm to find the chief_ray for a given optical system and object point. **Algorithm description:** The algorithm starts using a given ray, propagating it in the optical system, and finding the intersection point of this test ray and the aperture plane. The distance from this point and the optical axis is recorded. Using a gaussian random generator, two rotation angles are calculated, to generate a new test ray that is propagated in the optical system, and its distance to the optical axis is found at the aperture plane. If this distance is less than the distance found for the previous ray, this ray is taken as the new *chief ray* candidate, and the algorithm is repeated until the number of iterations reaches *maxiter*, or until the distance is less than *er*. the *rt* parameter gives the rotations made to a ray originating in *o*, and propagating in the *Z* direction, to find the first test ray. A detector object *ccds* should be placed at the aperture plane. It is used to find the point where the ray intersects the aperture. To increase the convergense speed of the algorithm, it is better to make sure that the first test ray intersects the detector. **Parameters:** ========== ====================================================== opsys Optical system that will be used to find the chief ray ccds Detector placed in the aperture plane. Should be centred in the optical axis o Tuple, list or numpy array indicating the coordinates of the object point used to find the chief ray rt Tuple with the rotations made to a ray propagating in the z direction to obtain the first test ray er Maximum acceptable distance between the ray and the center of the aperture w Gaussian width in radians wavelength Wavelength of the ray used to find the principal ray given in micrometers (.58929 by default). ========== ====================================================== **Return Value:** Chief ray found. (Ray instance) .. todo:: Implement a function similar to this one, using a minimization algorithm ''' #log.info("Entering chief_ray_search function") test_ray = Ray(wavelength=wavelength) opsys.clear_ray_list() btx, bty, btz = rt #btz is not used ntry = 0 nt = 0 #Check the initial test ray retray = test_ray.ch_coord_sys_inv(o, (btx, bty, 0)) #log.info("Calculating test_ray") opsys.clear_ray_list() opsys.reset() opsys.ray_add(retray) opsys.propagate() try: x, y, z = ccds.hit_list[0][0] dist = sqrt(square(x) + square(y)) except: dist = inf p_dist = dist while (p_dist > er) and (ntry < maxiter): ntry = ntry + 1 nt = nt + 1 rx = normal(btx, w) ry = normal(bty, w) tray = test_ray.ch_coord_sys_inv(o, (rx, ry, 0)) opsys.clear_ray_list() opsys.reset() opsys.ray_add(tray) opsys.propagate() try: x, y, z = ccds.hit_list[0][0] dist = sqrt(square(x) + square(y)) except: #log.info("CCD not hitted by ray") dist = inf if p_dist > dist: #Select this ray as new generator ray btx = rx bty = ry p_dist = dist nt = 0 retray = tray #log.info("distance to aperture center="+str(dist)) if (nt > 10) and p_dist < inf: nt = 0 w = w / 2 #limit the minimum value of w if w < .0000001: w = .0000001 # print p_dist,ntry return retray
def chief_ray_search(opsys,ccds,o=(0.,0.,0.),rt=(0.,0.,0.),er=0.1,w=pi/2.,maxiter=1000,wavelength=.58929): ''' This function uses a random search algorithm to find the chief_ray for a given optical system and object point. **Algorithm description:** The algorithm starts using a given ray, propagating it in the optical system, and finding the intersection point of this test ray and the aperture plane. The distance from this point and the optical axis is recorded. Using a gaussian random generator, two rotation angles are calculated, to generate a new test ray that is propagated in the optical system, and its distance to the optical axis is found at the aperture plane. If this distance is less than the distance found for the previous ray, this ray is taken as the new *chief ray* candidate, and the algorithm is repeated until the number of iterations reaches *maxiter*, or until the distance is less than *er*. the *rt* parameter gives the rotations made to a ray originating in *o*, and propagating in the *Z* direction, to find the first test ray. A detector object *ccds* should be placed at the aperture plane. It is used to find the point where the ray intersects the aperture. To increase the convergense speed of the algorithm, it is better to make sure that the first test ray intersects the detector. **Parameters:** ========== ====================================================== opsys Optical system that will be used to find the chief ray ccds Detector placed in the aperture plane. Should be centred in the optical axis o Tuple, list or numpy array indicating the coordinates of the object point used to find the chief ray rt Tuple with the rotations made to a ray propagating in the z direction to obtain the first test ray er Maximum acceptable distance between the ray and the center of the aperture w Gaussian width in radians wavelength Wavelength of the ray used to find the principal ray given in micrometers (.58929 by default). ========== ====================================================== **Return Value:** Chief ray found. (Ray instance) .. todo:: Implement a function similar to this one, using a minimization algorithm ''' #log.info("Entering chief_ray_search function") test_ray=Ray(wavelength=wavelength) opsys.clear_ray_list() btx,bty,btz=rt #btz is not used ntry=0 nt=0 #Check the initial test ray retray=test_ray.ch_coord_sys_inv(o,(btx,bty,0)) #log.info("Calculating test_ray") opsys.clear_ray_list() opsys.reset() opsys.ray_add(retray) opsys.propagate() try: x,y,z=ccds.hit_list[0][0] dist=sqrt(square(x)+square(y)) except: dist=inf p_dist=dist while (p_dist> er)and (ntry<maxiter): ntry=ntry+1 nt=nt+1 rx=normal(btx,w) ry=normal(bty,w) tray=test_ray.ch_coord_sys_inv(o,(rx,ry,0)) opsys.clear_ray_list() opsys.reset() opsys.ray_add(tray) opsys.propagate() try: x,y,z=ccds.hit_list[0][0] dist=sqrt(square(x)+square(y)) except: #log.info("CCD not hitted by ray") dist=inf if p_dist>dist: #Select this ray as new generator ray btx=rx bty=ry p_dist=dist nt=0 retray=tray #log.info("distance to aperture center="+str(dist)) if (nt>10)and p_dist<inf: nt=0 w=w/2 #limit the minimum value of w if w<.0000001: w=.0000001 # print p_dist,ntry return retray