class TowerScene(): """ Creates a scene of the heliostats, tower and receiver """ # recobj is an assembled receiver object # surf_ls is a list of all surfaces used in the receiver # crit_ls is a list of all surfaces to be viewed in a histogram # heliostat is a csv file of coordinates (Example: sandia_hstat_coordinates.csv) # dx,dy,dz are x,y,z offsets from the origin (default dz is 6.1 metres) # rx,ry,rz are rotations about the x,y,z axes in radians (default 0) def __init__(self,rec_obj,surf_ls,crit_ls,heliostat,sun_az = 0.,sun_elev = 34.9,\ dx = 0., dy = 0., dz = 6.1, rx = 0, ry = 0, rz = 0): self.sun_az = sun_az self.sun_elev = sun_elev self.rec_obj = rec_obj self.surf_ls = surf_ls self.crit_ls = crit_ls # add offset properties self.dx = dx self.dy = dy self.dz = dz # add rotation properties self.rx = rx self.ry = ry self.rz = rz # add the heliostat coordinates self.pos = N.loadtxt(heliostat, delimiter=",") self.pos *= 0.1 # generate the entire plant now self.gen_plant() # creates an attribute which shows number of rays used, start at zero self.no_of_rays = 0 self.helio_hits = 0 def gen_rays(self): sun_vec = solar_vector(self.sun_az*degree, self.sun_elev*degree) rpos = (self.pos + sun_vec).T direct = N.tile(-sun_vec, (self.pos.shape[0], 1)).T rays = RayBundle(rpos, direct, energy=N.ones(self.pos.shape[0])) return rays def gen_plant(self): """Generates the entire plant""" # set heliostat field characteristics: 0.52m*0.52m, abs = 0, aim_h =61 self.field = HeliostatField(self.pos, 6.09e-1, 6.09e-1, 0, 6.1, 1e-3) # generates a transformation matrix of the receiver rec_trans for rotations rx_M = N.matrix(rotx(self.rx)) ry_M = N.matrix(rotx(self.ry)) rz_M = N.matrix(rotx(self.rz)) rec_trans = N.array((rx_M)*(ry_M)*(rz_M)) # applies translations to the rotation matrix to get the final transformation rec_trans[0,3] = self.dx rec_trans[1,3] = self.dy rec_trans[2,3] = self.dz # applies the transformation to the receiver object self.rec_obj.set_transform(rec_trans) # combines all objects into a single plant self.plant = Assembly(objects = [self.rec_obj], subassemblies=[self.field]) def aim_field(self): """Aims the field to the sun?""" self.field.aim_to_sun(self.sun_az*degree, self.sun_elev*degree) def trace(self, rph, iters = 10000, minE = 1e-9, render = False): """Commences raytracing using (rph) number of rays per heliostat, for a maximum of (iters) iterations, discarding rays with energy less than (minE). If render is True, a 3D scene will be displayed which would need to be closed to proceed.""" # Get the solar vector using azimuth and elevation sun_vec = solar_vector(self.sun_az*degree, self.sun_elev*degree) # Calculate number of rays used. Rays per heliostat * number of heliostats. num_rays = rph*len(self.field.get_heliostats()) self.no_of_rays += num_rays # Generates the ray bundle rot_sun = rotation_to_z(-sun_vec) direct = N.dot(rot_sun, pillbox_sunshape_directions(num_rays, 0.00465)) xy = N.random.uniform(low=-0.25, high=0.25, size=(2, num_rays)) base_pos = N.tile(self.pos, (rph, 1)).T #Check if its is rph or num_rays base_pos += N.dot(rot_sun[:,:2], xy) base_pos -= direct rays = RayBundle(base_pos, direct, energy=N.ones(num_rays)) # Perform the raytracing e = TracerEngine(self.plant) e.ray_tracer(rays, iters, minE, tree=True) e.minener = minE rays_in = sum(e.tree._bunds[0].get_energy()) self.helio_hits = sum(e.tree._bunds[1].get_energy()) # Optional rendering if render == True: trace_scene = Renderer(e) trace_scene.show_rays() def hist_comb(self, no_of_bins=100): """Returns a combined histogram of all critical surfaces and relevant data""" # H is the histogram array # boundlist is a list of plate boundaries given in x coordinates # extent is a list of [xmin,xmax,ymin,ymax] values # binarea is the area of each bin. Used to estimate flux concentration # Define empty elements X_offset = 0 # Used to shift values to the right for each subsequent surface all_X = [] # List of all x-coordinates all_Y = [] # List of all y-coordinates all_E = [] # List of all energy values boundlist = [0] # List of plate boundaries, starts with x=0 #print("length here"+str(len((self.plant.get_local_objects()[0]).get_surfaces()))) #for plate in self.crit_ls: #For each surface within the list of critical surfs crit_length = len(self.crit_ls) count = 0 while count < crit_length: # count is one less than crit_length for indexing convention surface = (self.plant.get_local_objects()[0]).get_surfaces()[count] # returns all coordinates where a hit occured and its energy absorbed energy, pts = surface.get_optics_manager().get_all_hits() corners = surface.mesh(1) #corners is an array of all corners of the plate # BLC is bottom left corner "origin" of the histogram plot # BRC is the bottom right corner "x-axis" used for vector u # TLC is the top right corner "y-axis" used for vector v BLC = N.array([corners[0][1][1],corners[1][1][1],corners[2][1][1]]) BRC = N.array([corners[0][0][1],corners[1][0][1],corners[2][0][1]]) TLC = N.array([corners[0][1][0],corners[1][1][0],corners[2][1][0]]) # Get vectors u and v in array form of array([x,y,z]) u = BRC - BLC v = TLC - BLC # Get width(magnitude of u) and height(magnitude of v) in float form w = (sum(u**2))**0.5 h = (sum(v**2))**0.5 # Get unit vectors of u and v in form of array([x,y,z]) u_hat = u/w v_hat = v/h # Local x-position determined using dot product of each point with direction # Returns a list of local x and y coordinates origin = N.array([[BLC[0]],[BLC[1]],[BLC[2]]]) local_X = list((N.array(N.matrix(u_hat)*N.matrix(pts-origin))+X_offset)[0]) #local_Y = list((N.array(N.matrix(v_hat)*N.matrix(pts-origin)))[0]) local_Y = list((((N.array(N.matrix(v_hat)*N.matrix(pts-origin)))[0])*-1)+h) # Adds to the lists all_X += local_X all_Y += local_Y all_E += list(energy) X_offset += w boundlist.append(X_offset) count += 1 # Now time to build a histogram rngy = h rngx = X_offset bins = [no_of_bins,int(no_of_bins*X_offset)] H,ybins,xbins = N.histogram2d(all_Y,all_X,bins,range=([0,rngy],[0,rngx]), weights=all_E) extent = [xbins[0],xbins[-1],ybins[0],ybins[-1]] binarea = (float(h)/no_of_bins)*(float(X_offset)/int(no_of_bins*X_offset)) return H, boundlist, extent, binarea def energies(self): """Returns the total number of hits on the heliostats, receiver and the total energy absorbed""" totalenergy = 0.0 totalhits = 0 heliohits = self.helio_hits #length = 0 #for surface in self.plant.get_local_objects()[0].get_surfaces(): for surface in (self.plant.get_local_objects()[0]).get_surfaces(): energy, pts = surface.get_optics_manager().get_all_hits() absorp = surface._opt._opt._abs #length += len(energy) #plt.plot(range(0,len(energy)),energy,'ro') #plt.show() totalenergy += sum(energy) totalhits += sum(energy == absorp) #print("Length is"+str(length)) return totalenergy, totalhits, heliohits
class TowerScene(TracerScene): # Location of the sun: sun_az = t_api.Range(0, 180, 90, label="Sun azimuth") sun_elev = t_api.Range(0, 90, 45, label="Sun elevation") # Heliostat placement distance: radial_res = t_api.Float(1., label="Radial distance") ang_res = t_api.Float(N.pi/8, lable="Angular distance") # Flux map figure: fmap = t_api.Instance(Figure) fmap_btn = t_api.Button(label="Update flux map") def __init__(self): self.gen_plant() TracerScene.__init__(self, self.plant, self.gen_rays()) self.aim_field() self.set_background((0., 0.5, 1.)) def gen_rays(self): sun_vec = solar_vector(self.sun_az*degree, self.sun_elev*degree) rpos = (self.pos + sun_vec).T direct = N.tile(-sun_vec, (self.pos.shape[0], 1)).T rays = RayBundle(rpos, direct, energy=N.ones(self.pos.shape[0])) return rays def gen_plant(self): xy = radial_stagger(-N.pi/4, N.pi/4 + 0.0001, self.ang_res, 5, 20, self.radial_res) self.pos = N.hstack((xy, N.zeros((xy.shape[0], 1)))) self.field = HeliostatField(self.pos, 0.5, 0.5, 0, 10) self.rec, recobj = one_sided_receiver(1., 1.) rec_trans = roty(N.pi/2) rec_trans[2,3] = 10 recobj.set_transform(rec_trans) self.plant = Assembly(objects=[recobj], subassemblies=[self.field]) @t_api.on_trait_change('sun_az, sun_elev') def aim_field(self): self.clear_scene() rays = self.gen_rays() self.field.aim_to_sun(self.sun_az*degree, self.sun_elev*degree) self.set_assembly(self.plant) # Q&D example. self.set_source(rays) @t_api.on_trait_change('radial_res, ang_res') def replace_plant(self): self.gen_plant() self.aim_field() @t_api.on_trait_change('_scene.activated') def initialize_camere(self): self._scene.mlab.view(0, -90) self._scene.mlab.roll(90) def _fmap_btn_fired(self): """Generate a flux map using much more rays than drawn""" # Generate a large ray bundle using a radial stagger much denser # than the field. sun_vec = solar_vector(self.sun_az*degree, self.sun_elev*degree) hstat_rays = 1000 num_rays = hstat_rays*len(self.field.get_heliostats()) rot_sun = rotation_to_z(-sun_vec) direct = N.dot(rot_sun, pillbox_sunshape_directions(num_rays, 0.00465)) xy = N.random.uniform(low=-0.25, high=0.25, size=(2, num_rays)) base_pos = N.tile(self.pos, (hstat_rays, 1)).T base_pos += N.dot(rot_sun[:,:2], xy) base_pos -= direct rays = RayBundle(base_pos, direct, energy=N.ones(num_rays)) # Perform the trace: self.rec.get_optics_manager().reset() e = TracerEngine(self.plant) e.ray_tracer(rays, 1000, 0.05) # Show a histogram of hits: energy, pts = self.rec.get_optics_manager().get_all_hits() x, y = self.rec.global_to_local(pts)[:2] rngx = 0.5 rngy = 0.5 bins = 50 H, xbins, ybins = N.histogram2d(x, y, bins, \ range=([-rngx,rngx], [-rngy,rngy]), weights=energy) self.fmap.axes[0].images=[] self.fmap.axes[0].imshow(H, aspect='auto') wx.CallAfter(self.fmap.canvas.draw) def _fmap_default(self): figure = Figure() figure.add_axes([0.05, 0.04, 0.9, 0.92]) return figure # Parameters of the form that is shown to the user: view = tui.View(tui.HGroup(tui.VGroup( TracerScene.scene_view_item(500, 500), tui.HGroup('-', 'sun_az', 'sun_elev'), tui.HGroup('radial_res', 'ang_res'), tui.Item('fmap_btn', show_label=False)), tui.Item('fmap', show_label=False, editor=MPLFigureEditor())))
class TowerScene(): # Location of the sun: sun_az = 80. sun_elev = 45. # Heliostat placement distance: radial_res = 1. ang_res = N.pi/8 def __init__(self): self.gen_plant() def gen_rays(self): sun_vec = solar_vector(self.sun_az*degree, self.sun_elev*degree) rpos = (self.pos + sun_vec).T direct = N.tile(-sun_vec, (self.pos.shape[0], 1)).T rays = RayBundle(rpos, direct, energy=N.ones(self.pos.shape[0])) return rays def gen_plant(self): xy = radial_stagger(-N.pi/4, N.pi/4 + 0.0001, self.ang_res, 5., 20., self.radial_res) self.pos = N.hstack((xy, N.zeros((xy.shape[0], 1)))) self.field = HeliostatField(self.pos, 0.5, 0.5, 0, 10) self.rec, recobj = one_sided_receiver(1., 1.) rec_trans = roty(N.pi/2) rec_trans[2,3] = 10 recobj.set_transform(rec_trans) self.plant = Assembly(objects=[recobj], subassemblies=[self.field]) def aim_field(self): self.field.aim_to_sun(self.sun_az*degree, self.sun_elev*degree) def trace(self): """Generate a flux map using much more rays than drawn""" # Generate a large ray bundle using a radial stagger much denser # than the field. sun_vec = solar_vector(self.sun_az*degree, self.sun_elev*degree) hstat_rays = 20 num_rays = hstat_rays*len(self.field.get_heliostats()) rot_sun = rotation_to_z(-sun_vec) direct = N.dot(rot_sun, pillbox_sunshape_directions(num_rays, 0.00465)) xy = N.random.uniform(low=-0.25, high=0.25, size=(2, num_rays)) base_pos = N.tile(self.pos, (hstat_rays, 1)).T base_pos += N.dot(rot_sun[:,:2], xy) base_pos -= direct rays = RayBundle(base_pos, direct, energy=N.ones(num_rays)) # Perform the trace: e = TracerEngine(self.plant) e.ray_tracer(rays, 100, 0.05, tree=True) e.minener = 1e-5 # Render: trace_scene = Renderer(e) trace_scene.show_rays()
class TowerScene(): # Location of the sun: sun_az = 80. sun_elev = 45. # Heliostat placement distance: radial_res = 1. ang_res = N.pi / 8 def __init__(self): self.gen_plant() def gen_rays(self): sun_vec = solar_vector(self.sun_az * degree, self.sun_elev * degree) rpos = (self.pos + sun_vec).T direct = N.tile(-sun_vec, (self.pos.shape[0], 1)).T rays = RayBundle(rpos, direct, energy=N.ones(self.pos.shape[0])) return rays def gen_plant(self): xy = radial_stagger(-N.pi / 4, N.pi / 4 + 0.0001, self.ang_res, 5., 20., self.radial_res) self.pos = N.hstack((xy, N.zeros((xy.shape[0], 1)))) self.field = HeliostatField(self.pos, 0.5, 0.5, 0, 10) self.rec, recobj = one_sided_receiver(1., 1.) rec_trans = roty(N.pi / 2) rec_trans[2, 3] = 10 recobj.set_transform(rec_trans) self.plant = Assembly(objects=[recobj], subassemblies=[self.field]) def aim_field(self): self.field.aim_to_sun(self.sun_az * degree, self.sun_elev * degree) def trace(self): """Generate a flux map using much more rays than drawn""" # Generate a large ray bundle using a radial stagger much denser # than the field. sun_vec = solar_vector(self.sun_az * degree, self.sun_elev * degree) hstat_rays = 20 num_rays = hstat_rays * len(self.field.get_heliostats()) rot_sun = rotation_to_z(-sun_vec) direct = N.dot(rot_sun, pillbox_sunshape_directions(num_rays, 0.00465)) xy = N.random.uniform(low=-0.25, high=0.25, size=(2, num_rays)) base_pos = N.tile(self.pos, (hstat_rays, 1)).T base_pos += N.dot(rot_sun[:, :2], xy) base_pos -= direct rays = RayBundle(base_pos, direct, energy=N.ones(num_rays)) # Perform the trace: e = TracerEngine(self.plant) e.ray_tracer(rays, 100, 0.05, tree=True) e.minener = 1e-5 # Render: trace_scene = Renderer(e) trace_scene.show_rays()
class TowerScene(): # Location of the sun: sun_az = 80. # degrees from positive X-axis. azimuth sun_elev = 45. # degrees from XY-plane def __init__(self): self.gen_plant() def gen_rays(self): sun_vec = solar_vector(self.sun_az*degree, self.sun_elev*degree) #notice here the angles are positive. Hmmm? The sun rays are pointing up? rpos = (self.pos + sun_vec).T #where does self.pos come from? And T? direct = N.tile(-sun_vec, (self.pos.shape[0], 1)).T rays = RayBundle(rpos, direct, energy=N.ones(self.pos.shape[0])) return rays def gen_plant(self): # import custom coordinate file self.pos = N.loadtxt("sandia_hstat_coordinates.csv", delimiter=',') self.pos *= 0.1 # set heliostat field characteristics: 0.52m*0.52m, abs = 0, aim_h = 61 self.field = HeliostatField(self.pos, 6.09e-1, 6.09e-1, 0, 6.1,1e-3) self.reclist, recobj = one_sided_receiver(1.0, 1.0, 0.8) rec_trans = rotx(N.pi/-2) # originally N.pi/2, changed to minus rotx(N.pi/-2) print(recobj) rec_trans[2,3] = 6.1 # height of the tower original 6.1 recobj.set_transform(rec_trans) self.plant = Assembly(objects=[recobj], subassemblies=[self.field]) def aim_field(self): self.field.aim_to_sun(self.sun_az*degree, self.sun_elev*degree) def trace(self): """Generate a flux map using much more rays than drawn""" # Generate a large ray bundle using a radial stagger much denser # than the field. sun_vec = solar_vector(self.sun_az*degree, self.sun_elev*degree) #hstat_rays hstat_rays = 1000 num_rays = hstat_rays*len(self.field.get_heliostats()) rot_sun = rotation_to_z(-sun_vec) direct = N.dot(rot_sun, pillbox_sunshape_directions(num_rays, 0.00465)) xy = N.random.uniform(low=-0.25, high=0.25, size=(2, num_rays)) base_pos = N.tile(self.pos, (hstat_rays, 1)).T base_pos += N.dot(rot_sun[:,:2], xy) base_pos -= direct rays = RayBundle(base_pos, direct, energy=N.ones(num_rays)) # Perform the trace: e = TracerEngine(self.plant) e.ray_tracer(rays, 100, 0.05, tree=True) e.minener = 1e-6 # default 1e-5 # Render: #trace_scene = Renderer(e) #trace_scene.show_rays() # Initialise a histogram of hits: energy, pts = self.reclist.get_optics_manager().get_all_hits() x, y = self.reclist.global_to_local(pts)[:2] rngx = 0.55 #0.5 rngy = 0.55 #0.5 bins = 100 #50 H, xbins, ybins = N.histogram2d(x, y, bins, \ range=([-rngx,rngx], [-rngy,rngy]), weights=energy) #print(H, xbins, ybins) total=N.sum(H) print(total) extent = [ybins[0], ybins[-1], xbins[-1], xbins[0]] plt.imshow(H, extent=extent, interpolation='nearest') plt.colorbar() plt.title("front") plt.show()