def edge_rays_bundle(num_rays, center, direction, radius, ang_range, flux=None, radius_in=0.): radius = float(radius) radius_in = float(radius_in) a = edge_rays_directions(num_rays, ang_range) # Rotate to a frame in which <direction> is Z: perp_rot = rotation_to_z(direction) directions = N.sum(perp_rot[...,None] * a[None,...], axis=1) # Locations: # See [1] xi1 = random.uniform(size=num_rays) thetas = random.uniform(high=2.*N.pi, size=num_rays) rs = N.sqrt(radius_in**2.+xi1*(radius**2.-radius_in**2.)) xs = rs * N.cos(thetas) ys = rs * N.sin(thetas) # Rotate locations to the plane defined by <direction>: vertices_local = N.vstack((xs, ys, N.zeros(num_rays))) vertices_global = N.dot(perp_rot, vertices_local) rayb = RayBundle(vertices=vertices_global + center, directions=directions) if flux != None: rayb.set_energy(N.pi*(radius**2.-radius_in**2.)/num_rays*flux*N.ones(num_rays)) return rayb
class TestHomogenizer(unittest.TestCase): def setUp(self): """A homogenizer transforms a bundle correctly""" hmg = rect_homogenizer(5., 3., 10., 0.9) self.engine = TracerEngine(hmg) self.bund = RayBundle() # 4 rays starting somewhat above (+z) the homogenizer pos = N.zeros((3,4)) pos[2] = N.r_[11, 11, 11, 11] self.bund.set_vertices(pos) # One ray going to each wall: dir = N.c_[[1, 0, -1], [-1, 0, -1], [0, 1, -1], [0, -1, -1]]/N.sqrt(2) self.bund.set_directions(dir) # Laborious setup details: self.bund.set_energy(N.ones(4)*4.) self.bund.set_ref_index(N.ones(4)) def test_first_hits(self): """Test bundle enters homogenizer correctly""" v, d = self.engine.ray_tracer(self.bund, 1, 0.05) out_dirs = N.c_[[-1, 0, -1], [1, 0, -1], [0, -1, -1], [0, 1, -1]]/N.sqrt(2) N.testing.assert_array_almost_equal(d, out_dirs) out_hits = N.c_[ [2.5, 0, 8.5], [-2.5, 0, 8.5], [0, 1.5, 9.5], [0, -1.5, 9.5]] N.testing.assert_array_almost_equal(v, out_hits)
def test_up_down(self): """Rays coming from below are absorbed, from above reflected""" going_down = N.c_[[1, 1, -1], [-1, 1, -1], [-1, -1, -1], [1, -1, -1]] / N.sqrt(3) going_up = going_down.copy() going_up[2] = 1 / N.sqrt(3) pos_up = N.c_[[0,0,1], [1,-1,1], [1,1,1], [-1,1,1]] pos_down = pos_up.copy() pos_down[2] = -1 bund = RayBundle() bund.set_directions(N.hstack((going_down, going_up))) bund.set_vertices(N.hstack((pos_up, pos_down))) bund.set_energy(N.tile(100, 8)) bund.set_ref_index(N.tile(1, 8)) gm = FlatGeometryManager() prm = gm.find_intersections(N.eye(4), bund) absref = optics_callables.AbsorberReflector(0.) selector = N.arange(8) gm.select_rays(selector) outg = absref(gm, bund, selector) e = outg.get_energy() N.testing.assert_array_equal(e[:4], 100) N.testing.assert_array_equal(e[4:], 0)
def oblique_solar_rect_bundle(num_rays, center, source_direction, rays_direction, x, y, ang_range, flux=None, procs=1): a = pillbox_sunshape_directions(num_rays, ang_range) # Rotate to a frame in which <direction> is Z: perp_rot = rotation_to_z(rays_direction) directions = N.sum(perp_rot[..., None] * a[None, ...], axis=1) xs = random.uniform(low=-x / 2., high=x / 2., size=num_rays) ys = random.uniform(low=-y / 2., high=y / 2., size=num_rays) if (source_direction == N.array([0, 0, -1])).all(): xs, ys = ys, xs # Rotate locations to the plane defined by <direction>: vertices_local = N.vstack((ys, xs, N.zeros(num_rays))) perp_rot = rotation_to_z(source_direction) vertices_global = N.dot(perp_rot, vertices_local) rayb = RayBundle(vertices=vertices_global + center, directions=directions) if flux != None: cosangle = 2. * N.sin( N.sqrt(N.sum((rays_direction - source_direction)**2)) / 2.) rayb.set_energy(x * y / num_rays * flux * N.ones(num_rays) * N.cos(cosangle)) else: rayb.set_energy(N.ones(num_rays) / float(num_rays) / procs) return rayb
class TestHomogenizer(unittest.TestCase): def setUp(self): """A homogenizer transforms a bundle correctly""" hmg = rect_homogenizer(5., 3., 10., 0.9) self.engine = TracerEngine(hmg) self.bund = RayBundle() # 4 rays starting somewhat above (+z) the homogenizer pos = N.zeros((3, 4)) pos[2] = N.r_[11, 11, 11, 11] self.bund.set_vertices(pos) # One ray going to each wall: dir = N.c_[[1, 0, -1], [-1, 0, -1], [0, 1, -1], [0, -1, -1]] / N.sqrt(2) self.bund.set_directions(dir) # Laborious setup details: self.bund.set_energy(N.ones(4) * 4.) self.bund.set_ref_index(N.ones(4)) def test_first_hits(self): """Test bundle enters homogenizer correctly""" v, d = self.engine.ray_tracer(self.bund, 1, 0.05) out_dirs = N.c_[[-1, 0, -1], [1, 0, -1], [0, -1, -1], [0, 1, -1]] / N.sqrt(2) N.testing.assert_array_almost_equal(d, out_dirs) out_hits = N.c_[[2.5, 0, 8.5], [-2.5, 0, 8.5], [0, 1.5, 9.5], [0, -1.5, 9.5]] N.testing.assert_array_almost_equal(v, out_hits)
def edge_rays_bundle(num_rays, center, direction, radius, ang_range, flux=None, radius_in=0.): radius = float(radius) radius_in = float(radius_in) a = edge_rays_directions(num_rays, ang_range) # Rotate to a frame in which <direction> is Z: perp_rot = rotation_to_z(direction) directions = N.sum(perp_rot[..., None] * a[None, ...], axis=1) # Locations: # See [1] xi1 = random.uniform(size=num_rays) thetas = random.uniform(high=2. * N.pi, size=num_rays) rs = N.sqrt(radius_in**2. + xi1 * (radius**2. - radius_in**2.)) xs = rs * N.cos(thetas) ys = rs * N.sin(thetas) # Rotate locations to the plane defined by <direction>: vertices_local = N.vstack((xs, ys, N.zeros(num_rays))) vertices_global = N.dot(perp_rot, vertices_local) rayb = RayBundle(vertices=vertices_global + center, directions=directions) if flux != None: rayb.set_energy(N.pi * (radius**2. - radius_in**2.) / num_rays * flux * N.ones(num_rays)) return rayb
class TestTraceProtocol6(unittest.TestCase): """ Tests a spherical surface """ def setUp(self): surface1 = Surface(HemisphereGM(2.), opt.perfect_mirror, rotation=general_axis_rotation(N.r_[1,0,0], N.pi/2.)) surface2 = Surface(HemisphereGM(2.), opt.perfect_mirror, location=N.array([0,-2,0]), rotation=general_axis_rotation(N.r_[1,0,0], -N.pi/2.)) self._bund = RayBundle() self._bund.set_directions(N.c_[[0,1,0]]) self._bund.set_vertices(N.c_[[0,-1,0]]) self._bund.set_energy(N.r_[[1]]) self._bund.set_ref_index(N.r_[[1]]) assembly = Assembly() object1 = AssembledObject() object2 = AssembledObject() object1.add_surface(surface1) object2.add_surface(surface2) assembly.add_object(object1) assembly.add_object(object2) self.engine = TracerEngine(assembly) def test_ray_tracers1(self): params = self.engine.ray_tracer(self._bund, 1, .05)[0] correct_params = N.c_[[0,2,0]] N.testing.assert_array_almost_equal(params,correct_params)
def rect_ray_bundle(num_rays, center, direction, x, y, sunshape, ang_rang, flux=None, BUIE=None): ''' generate a rectancular ray bundle for different sunshape options Arguments: num_rays - number of rays to generate center - a column 3-array with the 3D coordinate of the ray bundle's center direction - a 1D 3-array with the unit average direction vector for the bundle. x - width of the rectangular ray bundle y - height of the rectangular ray bundle sunshape - str, 'pillbox', 'Gaussian','Buie' or 'collimated' ang_rang - the angular width of the solar disk (pillbox), sigma (gaussian) or CSR (Buie sunshape) flux - if not None, the ray bundle's energy is set such that each ray has an equal amount of energy, and the total energy is flux*pi*radius**2 Returns: A RayBundle object with the above characteristics set. ''' if sunshape == 'pillbox': a = pillbox_sunshape_directions(num_rays, ang_rang) elif sunshape == 'gaussian': a = gaussian_sunshape_directions(num_rays, ang_rang) elif sunshape == 'buie': a = buie_sunshape_directions(num_rays, BUIE.sample, BUIE.FI) elif sunshape == 'collimated': a = collimated_directions(num_rays) # making sure the rect bundle can cover the whole region of interest x *= 1.2 y *= 1.2 # Rotate to a frame in which <direction> is Z: perp_rot = rotation_to_z(direction) directions = N.sum(perp_rot[..., None] * a[None, ...], axis=1) # Locations: # See [1] xs = N.random.uniform(low=-x / 2., high=x / 2., size=num_rays) ys = N.random.uniform(low=-y / 2., high=y / 2., size=num_rays) # Rotate locations to the plane defined by <direction>: vertices_local = N.vstack((xs, ys, N.zeros(num_rays))) vertices_global = N.dot(perp_rot, vertices_local) rayb = RayBundle(vertices=vertices_global + center, directions=directions) if flux != None: rayb.set_energy(x * y / num_rays * flux * N.ones(num_rays)) return rayb
class TestObjectBuilding1(unittest.TestCase): """Tests an object composed of sphere surfaces""" def setUp(self): self.assembly = Assembly() surface1 = Surface(HemisphereGM(3.), optics_callables.perfect_mirror, location=N.array([0,0,-1.]), rotation=general_axis_rotation(N.r_[1,0,0], N.pi)) surface2 = Surface(HemisphereGM(3.), optics_callables.perfect_mirror, location=N.array([0,0,1.])) self.object = AssembledObject() self.object.add_surface(surface1) self.object.add_surface(surface2) self.assembly.add_object(self.object) dir = N.c_[[0,0,1.],[0,0,1.]] position = N.c_[[0,0,-3.],[0,0,-1.]] self._bund = RayBundle(position, dir, energy=N.ones(2)) def test_object(self): """Tests that the assembly heirarchy works at a basic level""" self.engine = TracerEngine(self.assembly) inters = self.engine.ray_tracer(self._bund,1,.05)[0] correct_inters = N.c_[[0,0,2],[0,0,-2]] N.testing.assert_array_almost_equal(inters, correct_inters) def test_translation(self): """Tests an assembly that has been translated""" trans = N.array([[1,0,0,0],[0,1,0,0],[0,0,1,1],[0,0,0,1]]) self.assembly.transform_children(trans) self.engine = TracerEngine(self.assembly) params = self.engine.ray_tracer(self._bund,1,.05)[0] correct_params = N.c_[[0,0,3],[0,0,-1]] N.testing.assert_array_almost_equal(params, correct_params) def test_rotation_and_translation(self): """Tests an assembly that has been translated and rotated""" self._bund = RayBundle() self._bund.set_vertices(N.c_[[0,-5,1],[0,5,1]]) self._bund.set_directions(N.c_[[0,1,0],[0,1,0]]) self._bund.set_energy(N.r_[[1,1]]) self._bund.set_ref_index(N.r_[[1,1]]) trans = generate_transform(N.r_[[1,0,0]], N.pi/2, N.c_[[0,0,1]]) self.assembly.transform_children(trans) self.engine = TracerEngine(self.assembly) params = self.engine.ray_tracer(self._bund,1,.05)[0] correct_params = N.c_[[0,-2,1]] N.testing.assert_array_almost_equal(params, correct_params)
def triangular_bundle(num_rays, A, AB, AC, direction, ang_range=N.pi / 2., flux=None, procs=1): """ Triangular ray-casting surface anchored on the point A. Arguments: - num_rays: the number of rays - A: The first summit of the triangle and its anchor point. - AB and AC the vertices of the sides of the triangle in its plane of reference. - direction: The direction at which the source is pointing - ang_range: the angular range of the rays emitted by the source Returns: - A ray bundle object for tracing """ # Triangle ray vertices: # Declare random numbers: r1 = N.vstack(N.random.uniform(size=num_rays)) r2 = N.vstack(N.random.uniform(size=num_rays)) # Define points in a local referential where A is at [0,0] on a z=0 plane. sqrtr1 = N.sqrt(r1) Plocs = sqrtr1 * (1. - r2) * AB + r2 * sqrtr1 * AC # Triangle point picking vertices_local = N.array([Plocs[:, 0], Plocs[:, 1], N.zeros(num_rays)]) # Bring everything back to the global referential: rot = rotation_to_z(direction) vertices_global = N.dot(rot, vertices_local) + N.vstack(A) # Local referential directions: a = pillbox_sunshape_directions(num_rays, ang_range) # Rotate to a frame in which <direction> is Z: directions = N.sum(rot[..., None] * a[None, ...], axis=1) rayb = RayBundle() rayb.set_vertices(vertices_global) rayb.set_directions(directions) l1 = N.sqrt(N.sum(AB**2)) l2 = N.sqrt(N.sum(AC**2)) l3 = N.sqrt(N.sum((-AB + AC)**2)) s = (l1 + l2 + l3) / 2. area = N.sqrt(s * (s - l1) * (s - l2) * (s - l3)) if flux != None: rayb.set_energy(N.ones(num_rays) * flux * area / float(num_rays)) else: rayb.set_energy(N.ones(num_rays) / float(num_rays) / procs) return rayb
def pillbox_rect_bundle(num_rays, center, direction, x, y, ang_rang, flux=None): ''' generate a rectancular ray bundle for pillbox sunshape Arguments: num_rays - number of rays to generate center - a column 3-array with the 3D coordinate of the ray bundle's center direction - a 1D 3-array with the unit average direction vector for the bundle. x - width of the rectangular ray bundle y - height of the rectangular ray bundle ang_rang - the angular width of the solar disk flux - if not None, the ray bundle's energy is set such that each ray has an equal amount of energy, and the total energy is flux*pi*radius**2 Returns: A RayBundle object with the above characteristics set. ''' a = pillbox_sunshape_directions(num_rays, ang_rang) x *= 1.2 y *= 1.2 # Rotate to a frame in which <direction> is Z: perp_rot = rotation_to_z(direction) directions = N.sum(perp_rot[..., None] * a[None, ...], axis=1) # Locations: # See [1] xs = N.random.uniform(low=-x / 2., high=x / 2., size=num_rays) ys = N.random.uniform(low=-y / 2., high=y / 2., size=num_rays) #if (direction == N.array([0,0,-1])).all(): # xs, ys = ys, xs # Rotate locations to the plane defined by <direction>: vertices_local = N.vstack((xs, ys, N.zeros(num_rays))) vertices_global = N.dot(perp_rot, vertices_local) #dirct=N.vstack((-N.ones(num_rays)*direction[0],-N.ones(num_rays)*direction[1],-N.ones(num_rays)*direction[2])) #vertices_global=vertices_local+dirct*100. rayb = RayBundle(vertices=vertices_global + center, directions=directions) if flux != None: rayb.set_energy(x * y / num_rays * flux * N.ones(num_rays)) return rayb
def pillbox_effective_rays(num_rays, X, Y, Z, hits, direction, A_source, ang_rang, DNI): ''' Generate a ray bundle according to Buie et al.: "Sunshape distributions for terrestrial simulations." Solar Energy 74 (2003) 113-122 (DOI: 10.1016/S0038-092X(03)00125-7). *the ray source just cover each individule mirror Arguments: num_rays - number of rays over one heliostat, therefore total num of rays is num_rays*num_helios direction - (1,3)direction of the normal to the source disc. vertices - (3,n) array vertices of each ray energy - float - energy of each ray , W DNI - Direct normal irradiantion W/m2 Returns: A raybundle object with the above characteristics set. ''' total_rays = num_rays * N.sum(hits) a = pillbox_sunshape_directions(total_rays, ang_rang) # Rotate to a frame in which <direction> is Z: perp_rot = rotation_to_z(direction) directions = N.sum(perp_rot[..., None] * a[None, ...], axis=1) Xs = N.array([]) Ys = N.array([]) Zs = N.array([]) #X,Y=N.meshgrid(X,Y) for i in xrange(len(hits)): for j in xrange(len(hits[i])): if hits[i, j] == 1: xs = N.random.uniform(low=X[i], high=X[i + 1], size=num_rays) ys = N.random.uniform(low=Y[j], high=Y[j + 1], size=num_rays) zs = Z * N.ones(num_rays) Xs = N.append(Xs, xs) Ys = N.append(Ys, ys) Zs = N.append(Zs, zs) vertices = N.vstack((Xs, Ys, Zs)) energy = N.ones(total_rays) * DNI * A_source / float(total_rays) rayb = RayBundle(vertices=vertices, directions=directions) if DNI != None: rayb.set_energy(energy) print total_rays return rayb
def solar_disk_bundle(num_rays, center, direction, radius, ang_range, flux=None, radius_in=0., angular_span=[0.,2.*N.pi], procs=1): """ Generates a ray bundle emanating from a disk, with each surface element of the disk having the same ray density. The rays all point at directions uniformly distributed between a given angle range from a given direction. Setting of the bundle's energy is left to the caller. Arguments: num_rays - number of rays to generate. center - a column 3-array with the 3D coordinate of the disk's center direction - a 1D 3-array with the unit average direction vector for the bundle. radius - of the disk. ang_range - in radians, the maximum deviation from <direction>. flux - if not None, the ray bundle's energy is set such that each ray has an equal amount of energy, and the total energy is flux*pi*radius**2 radius_in - Inner radius if the disc is pierced angular_span - wedge of the disc to consider Returns: A RayBundle object with the above characteristics set. """ # FIXME why should 'center' be a column vector... that's just annoying. radius = float(radius) radius_in = float(radius_in) a = pillbox_sunshape_directions(num_rays, ang_range) # Rotate to a frame in which <direction> is Z: perp_rot = rotation_to_z(direction) directions = N.sum(perp_rot[...,None] * a[None,...], axis=1) # Locations: # See [1] xi1 = random.uniform(size=num_rays) thetas = random.uniform(low=angular_span[0], high=angular_span[1], size=num_rays) rs = N.sqrt(radius_in**2.+xi1*(radius**2.-radius_in**2.)) xs = rs * N.cos(thetas) ys = rs * N.sin(thetas) # Rotate locations to the plane defined by <direction>: vertices_local = N.vstack((xs, ys, N.zeros(num_rays))) vertices_global = N.dot(perp_rot, vertices_local) rayb = RayBundle(vertices=vertices_global + center, directions=directions) if flux != None: rayb.set_energy(N.pi*(radius**2.-radius_in**2.)/num_rays*flux*N.ones(num_rays)) else: rayb.set_energy(N.ones(num_rays)/num_rays/procs) return rayb
def test_paraxial_ray(self): """A paraxial ray in reflected correctly""" bund = RayBundle() bund.set_vertices(N.c_[[0.01, 0., 2.]]) bund.set_directions(N.c_[[0., 0., -1.]]) bund.set_energy(N.r_[100.]) bund.set_ref_index(N.r_[1]) self.engine.ray_tracer(bund, 15, 10.) non_degenerate = self.engine.tree[-1].get_energy() > 10 v = self.engine.tree[-1].get_vertices()[:,non_degenerate] d = self.engine.tree[-1].get_directions()[:,non_degenerate] # Not high equality demanded, because of spherical aberration. N.testing.assert_array_almost_equal(v, N.c_[[-0.01, 0., 1.5]], 2) N.testing.assert_array_almost_equal(d, N.c_[[0., 0., 1.]], 2)
def test_paraxial_ray(self): """A paraxial ray in reflected correctly""" bund = RayBundle() bund.set_vertices(N.c_[[0.01, 0., 2.]]) bund.set_directions(N.c_[[0., 0., -1.]]) bund.set_energy(N.r_[100.]) bund.set_ref_index(N.r_[1]) self.engine.ray_tracer(bund, 15, 10.) non_degenerate = self.engine.tree[-1].get_energy() > 10 v = self.engine.tree[-1].get_vertices()[:, non_degenerate] d = self.engine.tree[-1].get_directions()[:, non_degenerate] # Not high equality demanded, because of spherical aberration. N.testing.assert_array_almost_equal(v, N.c_[[-0.01, 0., 1.5]], 2) N.testing.assert_array_almost_equal(d, N.c_[[0., 0., 1.]], 2)
def triangular_bundle(num_rays, A, AB, AC, direction, ang_range=N.pi/2., flux=None, procs=1): """ Triangular ray-casting surface anchored on the point A. Arguments: - num_rays: the number of rays - A: The first summit of the triangle and its anchor point. - AB and AC the vertices of the sides of the triangle in its plane of reference. - direction: The direction at which the source is pointing - ang_range: the angular range of the rays emitted by the source Returns: - A ray bundle object for tracing """ # Triangle ray vertices: # Declare random numbers: r1 = N.vstack(N.random.uniform(size=num_rays)) r2 = N.vstack(N.random.uniform(size=num_rays)) # Define points in a local referential where A is at [0,0] on a z=0 plane. sqrtr1 = N.sqrt(r1) Plocs = sqrtr1*(1.-r2)*AB+r2*sqrtr1*AC # Triangle point picking vertices_local = N.array([Plocs[:,0], Plocs[:,1], N.zeros(num_rays)]) # Bring everything back to the global referential: rot = rotation_to_z(direction) vertices_global = N.dot(rot, vertices_local)+N.vstack(A) # Local referential directions: a = pillbox_sunshape_directions(num_rays, ang_range) # Rotate to a frame in which <direction> is Z: directions = N.sum(rot[...,None] * a[None,...], axis=1) rayb = RayBundle() rayb.set_vertices(vertices_global) rayb.set_directions(directions) l1 = N.sqrt(N.sum(AB**2)) l2 = N.sqrt(N.sum(AC**2)) l3 = N.sqrt(N.sum((-AB+AC)**2)) s = (l1+l2+l3)/2. area = N.sqrt(s*(s-l1)*(s-l2)*(s-l3)) if flux != None: rayb.set_energy(N.ones(num_rays)*flux*area/float(num_rays)) else: rayb.set_energy(N.ones(num_rays)/float(num_rays)/procs) return rayb
class TestRectOneSided(unittest.TestCase): def setUp(self): self.mirror = rect_one_sided_mirror(1.5, 1.5, 0.9) pos = N.zeros((3, 8)) pos[0] = N.tile(N.r_[0, 0.5, 2, -2], 2) pos[2] = N.repeat(N.r_[1, -1], 4) dir = N.zeros((3, 8)) dir[2] = N.repeat(N.r_[-1, 1], 4) self.bund = RayBundle() self.bund.set_vertices(pos) self.bund.set_directions(dir) self.bund.set_energy(N.ones(8) * 1000) self.bund.set_ref_index(N.ones(8)) def test_regular(self): """One-sided plate without rotation""" e = TracerEngine(Assembly(objects=[self.mirror])) e.ray_tracer(self.bund, 1, 0.05) outg = e.tree[-1] correct_verts = N.zeros((3, 2)) correct_verts[0] = N.r_[0, 0.5] N.testing.assert_array_equal( outg.get_vertices()[:, outg.get_energy() > 0], correct_verts) N.testing.assert_array_almost_equal(outg.get_energy(), N.r_[100., 100., 0, 0]) def test_rotated(self): """One-sided plate with rotation""" rot = sp.roty(N.pi / 4.) self.mirror.set_transform(rot) e = TracerEngine(Assembly(objects=[self.mirror])) e.ray_tracer(self.bund, 1, 0.05) outg = e.tree[-1] correct_verts = N.array([[0., 0.5], [0., 0.], [0., -0.5]]) N.testing.assert_array_almost_equal( outg.get_vertices()[:, outg.get_energy() > 0], correct_verts) N.testing.assert_array_almost_equal(outg.get_energy(), N.r_[100., 100., 0, 0])
def single_ray_source(position, direction, flux=None): ''' Establishes a single ray source originating from a definned point on a defined exact direction for the purpose of testing single ray behviours. Arguments: position - column 3-array with the ray's starting position. direction - a 1D 3-array with the unit average direction vector for the bundle. flux - if not None, the energy transported by the ray. Returns: A Raybundle object with the corresponding characteristics. ''' directions = N.tile(direction[:, None], 1) directions /= N.sqrt(N.sum(directions**2, axis=0)) singray = RayBundle(vertices=position, directions=directions) singray.set_energy(flux * N.ones(1)) return singray
def single_ray_source(position, direction, flux=None): ''' Establishes a single ray source originating from a definned point on a defined exact direction for the purpose of testing single ray behviours. Arguments: position - column 3-array with the ray's starting position. direction - a 1D 3-array with the unit average direction vector for the bundle. flux - if not None, the energy transported by the ray. Returns: A Raybundle object with the corresponding characteristics. ''' directions = N.tile(direction[:,None],1) directions /= N.sqrt(N.sum(directions**2, axis=0)) singray = RayBundle(vertices = position, directions = directions) singray.set_energy(flux*N.ones(1)) return singray
class TestRectOneSided(unittest.TestCase): def setUp(self): self.mirror = rect_one_sided_mirror(1.5, 1.5, 0.9) pos = N.zeros((3,8)) pos[0] = N.tile(N.r_[0, 0.5, 2, -2], 2) pos[2] = N.repeat(N.r_[1, -1], 4) dir = N.zeros((3,8)) dir[2] = N.repeat(N.r_[-1, 1], 4) self.bund = RayBundle() self.bund.set_vertices(pos) self.bund.set_directions(dir) self.bund.set_energy(N.ones(8)*1000) self.bund.set_ref_index(N.ones(8)) def test_regular(self): """One-sided plate without rotation""" e = TracerEngine(Assembly(objects=[self.mirror])) e.ray_tracer(self.bund, 1, 0.05) outg = e.tree[-1] correct_verts = N.zeros((3,2)) correct_verts[0] = N.r_[0, 0.5] N.testing.assert_array_equal( outg.get_vertices()[:,outg.get_energy() > 0], correct_verts) N.testing.assert_array_almost_equal( outg.get_energy(), N.r_[100., 100., 0, 0]) def test_rotated(self): """One-sided plate with rotation""" rot = sp.roty(N.pi/4.) self.mirror.set_transform(rot) e = TracerEngine(Assembly(objects=[self.mirror])) e.ray_tracer(self.bund, 1, 0.05) outg = e.tree[-1] correct_verts = N.array([[0., 0.5], [0., 0.], [0., -0.5]]) N.testing.assert_array_almost_equal( outg.get_vertices()[:,outg.get_energy() > 0], correct_verts) N.testing.assert_array_almost_equal( outg.get_energy(), N.r_[100., 100., 0, 0])
def solar_rect_bundle(num_rays, center, direction, x, y, ang_range, flux=None): a = pillbox_sunshape_directions(num_rays, ang_range) # Rotate to a frame in which <direction> is Z: perp_rot = rotation_to_z(direction) directions = N.sum(perp_rot[..., None] * a[None, ...], axis=1) xs = random.uniform(low=-x / 2., high=x / 2., size=num_rays) ys = random.uniform(low=-y / 2., high=y / 2., size=num_rays) if (direction == N.array([0, 0, -1])).all(): xs, ys = ys, xs # Rotate locations to the plane defined by <direction>: vertices_local = N.vstack((xs, ys, N.zeros(num_rays))) vertices_global = N.dot(perp_rot, vertices_local) rayb = RayBundle(vertices=vertices_global + center, directions=directions) if flux != None: rayb.set_energy(x * y / num_rays * flux * N.ones(num_rays)) return rayb
def solar_rect_bundle(num_rays, center, direction, x, y, ang_range, flux=None): a = pillbox_sunshape_directions(num_rays, ang_range) # Rotate to a frame in which <direction> is Z: perp_rot = rotation_to_z(direction) directions = N.sum(perp_rot[...,None] * a[None,...], axis=1) xs = random.uniform(low=-x/2., high=x/2., size=num_rays) ys = random.uniform(low=-y/2., high=y/2., size=num_rays) if (direction == N.array([0,0,-1])).all(): xs, ys = ys, xs # Rotate locations to the plane defined by <direction>: vertices_local = N.vstack((ys, xs, N.zeros(num_rays))) vertices_global = N.dot(perp_rot, vertices_local) rayb = RayBundle(vertices=vertices_global + center, directions=directions) if flux != None: rayb.set_energy(x*y/num_rays*flux*N.ones(num_rays)) return rayb
class TestTraceProtocol6(unittest.TestCase): """ Tests a spherical surface """ def setUp(self): surface1 = Surface(HemisphereGM(2.), opt.perfect_mirror, rotation=general_axis_rotation( N.r_[1, 0, 0], N.pi / 2.)) surface2 = Surface(HemisphereGM(2.), opt.perfect_mirror, location=N.array([0, -2, 0]), rotation=general_axis_rotation( N.r_[1, 0, 0], -N.pi / 2.)) self._bund = RayBundle() self._bund.set_directions(N.c_[[0, 1, 0]]) self._bund.set_vertices(N.c_[[0, -1, 0]]) self._bund.set_energy(N.r_[[1]]) self._bund.set_ref_index(N.r_[[1]]) assembly = Assembly() object1 = AssembledObject() object2 = AssembledObject() object1.add_surface(surface1) object2.add_surface(surface2) assembly.add_object(object1) assembly.add_object(object2) self.engine = TracerEngine(assembly) def test_ray_tracers1(self): params = self.engine.ray_tracer(self._bund, 1, .05)[0] correct_params = N.c_[[0, 2, 0]] N.testing.assert_array_almost_equal(params, correct_params)
def oblique_solar_rect_bundle(num_rays, center, source_direction, rays_direction, x, y, ang_range, flux=None, procs=1): a = pillbox_sunshape_directions(num_rays, ang_range) # Rotate to a frame in which <direction> is Z: perp_rot = rotation_to_z(rays_direction) directions = N.sum(perp_rot[...,None] * a[None,...], axis=1) xs = random.uniform(low=-x/2., high=x/2., size=num_rays) ys = random.uniform(low=-y/2., high=y/2., size=num_rays) if (source_direction == N.array([0,0,-1])).all(): xs, ys = ys, xs # Rotate locations to the plane defined by <direction>: vertices_local = N.vstack((ys, xs, N.zeros(num_rays))) perp_rot = rotation_to_z(source_direction) vertices_global = N.dot(perp_rot, vertices_local) rayb = RayBundle(vertices=vertices_global + center, directions=directions) if flux != None: cosangle = 2.*N.sin(N.sqrt(N.sum((rays_direction-source_direction)**2))/2.) rayb.set_energy(x*y/num_rays*flux*N.ones(num_rays)*N.cos(cosangle)) else: rayb.set_energy(N.ones(num_rays)/float(num_rays)/procs) return rayb
class TestObjectBuilding1(unittest.TestCase): """Tests an object composed of sphere surfaces""" def setUp(self): self.assembly = Assembly() surface1 = Surface(HemisphereGM(3.), optics_callables.perfect_mirror, location=N.array([0, 0, -1.]), rotation=general_axis_rotation(N.r_[1, 0, 0], N.pi)) surface2 = Surface(HemisphereGM(3.), optics_callables.perfect_mirror, location=N.array([0, 0, 1.])) self.object = AssembledObject() self.object.add_surface(surface1) self.object.add_surface(surface2) self.assembly.add_object(self.object) dir = N.c_[[0, 0, 1.], [0, 0, 1.]] position = N.c_[[0, 0, -3.], [0, 0, -1.]] self._bund = RayBundle(position, dir, energy=N.ones(2)) def test_object(self): """Tests that the assembly heirarchy works at a basic level""" self.engine = TracerEngine(self.assembly) inters = self.engine.ray_tracer(self._bund, 1, .05)[0] correct_inters = N.c_[[0, 0, 2], [0, 0, -2]] N.testing.assert_array_almost_equal(inters, correct_inters) def test_translation(self): """Tests an assembly that has been translated""" trans = N.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 1], [0, 0, 0, 1]]) self.assembly.transform_children(trans) self.engine = TracerEngine(self.assembly) params = self.engine.ray_tracer(self._bund, 1, .05)[0] correct_params = N.c_[[0, 0, 3], [0, 0, -1]] N.testing.assert_array_almost_equal(params, correct_params) def test_rotation_and_translation(self): """Tests an assembly that has been translated and rotated""" self._bund = RayBundle() self._bund.set_vertices(N.c_[[0, -5, 1], [0, 5, 1]]) self._bund.set_directions(N.c_[[0, 1, 0], [0, 1, 0]]) self._bund.set_energy(N.r_[[1, 1]]) self._bund.set_ref_index(N.r_[[1, 1]]) trans = generate_transform(N.r_[[1, 0, 0]], N.pi / 2, N.c_[[0, 0, 1]]) self.assembly.transform_children(trans) self.engine = TracerEngine(self.assembly) params = self.engine.ray_tracer(self._bund, 1, .05)[0] correct_params = N.c_[[0, -2, 1]] N.testing.assert_array_almost_equal(params, correct_params)
def solar_disk_bundle(num_rays, center, direction, radius, ang_range, flux=None, radius_in=0., angular_span=[0., 2. * N.pi], x_cut=None, procs=1, rays_direction=None): """ Generates a ray bundle emanating from a disk, with each surface element of the disk having the same ray density. The rays all point at directions uniformly distributed between a given angle range from a given direction. Setting of the bundle's energy is left to the caller. Arguments: num_rays - number of rays to generate. center - a column 3-array with the 3D coordinate of the disk's center direction - a 1D 3-array with the unit average direction vector for the bundle. radius - of the disk. ang_range - in radians, the maximum deviation from <direction>. flux - if not None, the ray bundle's energy is set such that each ray has an equal amount of energy, and the total energy is flux*pi*radius**2 radius_in - Inner radius if the disc is pierced angular_span - wedge of the disc to consider Returns: A RayBundle object with the above characteristics set. """ radius = float(radius) radius_in = float(radius_in) a = pillbox_sunshape_directions(num_rays, ang_range) # Rotate to a frame in which <direction> is Z: if rays_direction == None: rays_direction = direction perp_rot = rotation_to_z(rays_direction) directions = N.sum(perp_rot[..., None] * a[None, ...], axis=1) # Locations: # See [1] xi1 = random.uniform(size=num_rays) thetas = random.uniform(low=angular_span[0], high=angular_span[1], size=num_rays) rs = N.sqrt(radius_in**2. + xi1 * (radius**2. - radius_in**2.)) xs = rs * N.cos(thetas) ys = rs * N.sin(thetas) # Rotate locations to the plane defined by <direction>: vertices_local = N.vstack((xs, ys, N.zeros(num_rays))) if x_cut != None: vertices_local = vertices_local[:, xs < x_cut] missing_rays = num_rays - vertices_local.shape[1] while missing_rays > 0: xi1 = random.uniform(size=2 * missing_rays) thetas = random.uniform(low=angular_span[0], high=angular_span[1], size=2 * missing_rays) rs = N.sqrt(radius_in**2. + xi1 * (radius**2. - radius_in**2.)) xs = rs * N.cos(thetas) ys = rs * N.sin(thetas) vertices_local = N.concatenate( (vertices_local, N.vstack( (xs, ys, N.zeros(2 * missing_rays)))), axis=1) vertices_local = vertices_local[:, vertices_local[0] < x_cut] missing_rays = num_rays - vertices_local.shape[1] if missing_rays < 0: vertices_local = vertices_local[:, :num_rays] vertices_global = N.dot(perp_rot, vertices_local) rayb = RayBundle(vertices=vertices_global + center, directions=directions) if flux != None: cosangle = 2. * N.sin( N.sqrt(N.sum((rays_direction - direction)**2)) / 2.) rayb.set_energy(N.pi * (radius**2. - radius_in**2.) / num_rays * flux * N.ones(num_rays) * N.cos(cosangle)) else: rayb.set_energy(N.ones(num_rays) / float(num_rays) / procs) return rayb
def triangular_bundle(num_rays, A, B, C, direction=None, ang_range=N.pi / 2., flux=None, procs=1): """ Triangular ray-casting surface. A, B and C are 3D coordinates of the vertices. Right hand rule determines the normal vector direction. Arguments: - num_rays: the number of rays - A: The first summit of the triangle and its anchor point. - AB and AC the vertices of the sides of the triangle in its plane of reference. - direction: The direction around which rays are escaping the source. If None, the direction is the normal. - ang_range: the angular range of the rays emitted by the source Returns: - A ray bundle object for tracing """ # Triangle ray vertices: # Declare random numbers: r1 = N.vstack(N.random.uniform(size=num_rays)) r2 = N.vstack(N.random.uniform(size=num_rays)) AB = B - A AC = C - A sqrtr1 = N.sqrt(r1) vertices = (A + sqrtr1 * (1. - r2) * AB + r2 * sqrtr1 * AC).T # Triangle point picking # Local referential directions: a = pillbox_sunshape_directions(num_rays, ang_range) # Normal vector: normal = N.cross(AB, AC) normal = normal / N.sqrt(N.sum(normal**2)) if direction is None: direction = normal # Rotate to a frame in which <direction> is direction: rot = rotation_to_z(direction) directions = N.sum(rot[..., None] * a[None, ...], axis=1) rayb = RayBundle() rayb.set_vertices(vertices) rayb.set_directions(directions) # Heron's formula for triangle surface area l1 = N.sqrt(N.sum(AB**2)) l2 = N.sqrt(N.sum(AC**2)) l3 = N.sqrt(N.sum((-AB + AC)**2)) s = (l1 + l2 + l3) / 2. area = N.sqrt(s * (s - l1) * (s - l2) * (s - l3)) if flux != None: cosangle = 2. * N.arcsin(0.5 * N.sqrt(N.sum((direction - normal)**2))) rayb.set_energy(area / num_rays * flux * N.ones(num_rays) * N.cos(cosangle)) else: rayb.set_energy(N.ones(num_rays) / float(num_rays) / procs) return rayb