def gen_plant(self, width=6.1, height=6.1, absorptivity=0.04, aim_height=60., sigma_xy=1e-3, rec_w=11., rec_h=11.): self.pos[:, 1] = self.pos[:, 1] - 4. # correction for the true position of the plate on the tower. self.width = width self.height = height self.absorptivity = absorptivity self.field = HeliostatField(self.pos, width, height, absorptivity, aim_height, sigma_xy) self.rec_w = rec_w self.rec_h = rec_h rec, recobj = one_sided_receiver(self.rec_w, self.rec_h) rec_trans = rotx(N.pi / -2) rec_trans[2, 3] = self.field._th # Evaluating just the receiver recobj.set_transform(rec_trans) self.plant = Assembly(objects=[recobj], subassemblies=[self.field])
class TestHeliostatField(unittest.TestCase): def setUp(self): spread = N.r_[50:101:10] self.pos = N.zeros((2*len(spread), 3)) self.pos[:len(spread), 0] = spread self.pos[len(spread):, 1] = spread self.pos[:,2] = 4.5 self.field = HeliostatField(self.pos, 8., 8., 0., 90.) s2 = N.sqrt(2)/2 self.sunvec = N.r_[-s2, 0, s2] # Noon, winterish. ray_pos = (self.pos + self.sunvec).T ray_dir = N.tile(-self.sunvec, (self.pos.shape[0], 1)).T self.rays = RayBundle(ray_pos, ray_dir, energy=N.ones(self.pos.shape[0])) def test_secure_position(self): """Heliostats at default position absorb the sunlight""" e = TracerEngine(self.field) e.ray_tracer(self.rays, 1, 0.05) N.testing.assert_array_equal(e.tree[-1].get_energy(), 0) def test_aim(self): """Aiming heliostats works""" elev = N.pi/4 az = N.pi/2 self.field.aim_to_sun(az, elev) e = TracerEngine(self.field) v, d = e.ray_tracer(self.rays, 1, 0.05) N.testing.assert_array_almost_equal(d[1, :self.pos.shape[0]/2], 0) N.testing.assert_array_almost_equal(d[0, self.pos.shape[0]/2:], 0) N.testing.assert_array_almost_equal(abs(d[2]*(v[0] + v[1])/(d[0] + d[1])), 85.5)
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 setUp(self): spread = N.r_[50:101:10] self.pos = N.zeros((2 * len(spread), 3)) self.pos[:len(spread), 0] = spread self.pos[len(spread):, 1] = spread self.pos[:, 2] = 4.5 self.field = HeliostatField(self.pos, 8., 8., 0., 90.) s2 = N.sqrt(2) / 2 self.sunvec = N.r_[-s2, 0, s2] # Noon, winterish. ray_pos = (self.pos + self.sunvec).T ray_dir = N.tile(-self.sunvec, (self.pos.shape[0], 1)).T self.rays = RayBundle(ray_pos, ray_dir, energy=N.ones(self.pos.shape[0]))
def gen_plant(self): # set heliostat field characteristics: 6.09m*6.09m, abs = 0, aim_h = 61 self.pos[:,1] = self.pos[:,1]-4. # correct6ion for the true position of the plate on the tower. self.field = HeliostatField(self.pos, 6.09, 6.09, absorptivity=0.04, aim_height=60, sigma_xy=1e-3, option=None) self.rec_w = 11 self.rec_h = 11 self.rec, recobj = one_sided_receiver(self.rec_w, self.rec_h) rec_trans = rotx(N.pi/-2) rec_trans[2,3] = self.field._th #================= ground_rec = False #================= if ground_rec: # Evaluating missed rays in the field along with receiver radius = 1.10 * math.sqrt((self.x_dist/2)**2 + (self.y_dist/2)**2) self.ground_rec, ground_recobj = one_sided_receiver(3*radius, 3*radius) ground_rec_trans = rotz(0) ground_rec_trans[0,3] = self.field_centre[0] ground_rec_trans[1,3] = self.field_centre[1] recobj.set_transform(rec_trans) ground_recobj.set_transform(ground_rec_trans) self.plant = Assembly(objects=[recobj, ground_recobj], subassemblies=[self.field]) else: # Evaluating just the receiver recobj.set_transform(rec_trans) self.plant = Assembly(objects=[recobj], subassemblies=[self.field])
def gen_plant(self): # set heliostat field characteristics: 6.09m*6.09m, abs = 0, aim_h = 61 self.pos[:, 1] = self.pos[:, 1] - 4. # correct6ion for the true position of the plate on the tower. self.field = HeliostatField(self.pos, 6.09, 6.09, absorptivity=0.04, aim_height=60, sigma_xy=1e-3, option=None) self.rec_w = 11 self.rec_h = 11 self.rec, recobj = one_sided_receiver(self.rec_w, self.rec_h) rec_trans = rotx(N.pi / -2) rec_trans[2, 3] = self.field._th #================= ground_rec = False #================= if ground_rec: # Evaluating missed rays in the field along with receiver radius = 1.10 * math.sqrt((self.x_dist / 2)**2 + (self.y_dist / 2)**2) self.ground_rec, ground_recobj = one_sided_receiver( 3 * radius, 3 * radius) ground_rec_trans = rotz(0) ground_rec_trans[0, 3] = self.field_centre[0] ground_rec_trans[1, 3] = self.field_centre[1] recobj.set_transform(rec_trans) ground_recobj.set_transform(ground_rec_trans) self.plant = Assembly(objects=[recobj, ground_recobj], subassemblies=[self.field]) else: # Evaluating just the receiver recobj.set_transform(rec_trans) self.plant = Assembly(objects=[recobj], subassemblies=[self.field])
class TestHeliostatField(unittest.TestCase): def setUp(self): spread = N.r_[50:101:10] self.pos = N.zeros((2 * len(spread), 3)) self.pos[:len(spread), 0] = spread self.pos[len(spread):, 1] = spread self.pos[:, 2] = 4.5 self.field = HeliostatField(self.pos, 8., 8., 0., 90.) s2 = N.sqrt(2) / 2 self.sunvec = N.r_[-s2, 0, s2] # Noon, winterish. ray_pos = (self.pos + self.sunvec).T ray_dir = N.tile(-self.sunvec, (self.pos.shape[0], 1)).T self.rays = RayBundle(ray_pos, ray_dir, energy=N.ones(self.pos.shape[0])) def test_secure_position(self): """Heliostats at default position absorb the sunlight""" e = TracerEngine(self.field) e.ray_tracer(self.rays, 1, 0.05) N.testing.assert_array_equal(e.tree[-1].get_energy(), 0) def test_aim(self): """Aiming heliostats works""" elev = N.pi / 4 az = N.pi / 2 self.field.aim_to_sun(az, elev) e = TracerEngine(self.field) v, d = e.ray_tracer(self.rays, 1, 0.05) N.testing.assert_array_almost_equal(d[1, :self.pos.shape[0] / 2], 0) N.testing.assert_array_almost_equal(d[0, self.pos.shape[0] / 2:], 0) N.testing.assert_array_almost_equal( abs(d[2] * (v[0] + v[1]) / (d[0] + d[1])), 85.5)
def setUp(self): spread = N.r_[50:101:10] self.pos = N.zeros((2*len(spread), 3)) self.pos[:len(spread), 0] = spread self.pos[len(spread):, 1] = spread self.pos[:,2] = 4.5 self.field = HeliostatField(self.pos, 8., 8., 0., 90.) s2 = N.sqrt(2)/2 self.sunvec = N.r_[-s2, 0, s2] # Noon, winterish. ray_pos = (self.pos + self.sunvec).T ray_dir = N.tile(-self.sunvec, (self.pos.shape[0], 1)).T self.rays = RayBundle(ray_pos, ray_dir, energy=N.ones(self.pos.shape[0]))
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 gen_plant(self, width=6.1, height=6.1, absorptivity=0.04, aim_height=60., sigma_xy=1e-3, rec_w=11., rec_h=11.): self.pos[:,1] = self.pos[:,1]-4. # correction for the true position of the plate on the tower. self.width = width self.height = height self.absorptivity = absorptivity self.field = HeliostatField(self.pos, width, height, absorptivity, aim_height, sigma_xy) self.rec_w = rec_w self.rec_h = rec_h rec, recobj = one_sided_receiver(self.rec_w, self.rec_h) rec_trans = rotx(N.pi/-2) rec_trans[2,3] = self.field._th # Evaluating just the receiver recobj.set_transform(rec_trans) self.plant = Assembly(objects=[recobj], subassemblies=[self.field])
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])
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()
class TowerScene(): # Location of the sun: sun_az = 0 sun_elev = 34.96 sun_vec = solar_vector(sun_az*degree, sun_elev*degree) hstat_normals = N.zeros((218,3)) # import custom coordinate file pos = N.loadtxt("/home/charles/Documents/Tracer/examples/sandia_hstat_coordinates.csv", delimiter=',') # Field-based calculations for source size parameters #=================================================== t_pos = pos.T xc_min = t_pos[0][N.argmin(t_pos[0])] xc_max = t_pos[0][N.argmax(t_pos[0])] yc_min = t_pos[1][N.argmin(t_pos[1])] yc_max = t_pos[1][N.argmax(t_pos[1])] x_dist = xc_max - xc_min y_dist = yc_max - yc_min xc_cent = (xc_min + xc_max) / 2 yc_cent = (yc_min + yc_max) / 2 field_centre = N.r_[xc_cent, yc_cent, 0] #=================================================== def __init__(self): self.gen_plant() def gen_rays(self, num_rays, flux=1000.): #======================== individual_source = True #======================== if individual_source: # Pillbox source on a per-heliostat basis radius = 1.20 * math.sqrt(2 * 3.405**2) direction = N.array(-self.sun_vec) ray_list = [] num_surfs = self.pos.shape[0] for i in xrange(num_surfs): centre = N.c_[50 * self.sun_vec + self.pos[i]] rayb = solar_disk_bundle(num_rays/num_surfs, centre, direction, radius, 4.65e-3, flux) ray_list.append(rayb) rays = concatenate_rays(ray_list) del ray_list else: # Large pillbox sunshape source disc source covering entire field area: radius = 1.10 * math.sqrt((self.x_dist/2)**2 + (self.y_dist/2)**2) self.source_area = N.pi * radius**2 centre = N.c_[300*self.sun_vec + self.field_centre] direction = N.array(-self.sun_vec) rays = solar_disk_bundle(num_rays, centre, direction, radius, 4.65e-3, flux) return rays def gen_plant(self): # set heliostat field characteristics: 6.09m*6.09m, abs = 0, aim_h = 61 self.pos[:,1] = self.pos[:,1]-4. # correct6ion for the true position of the plate on the tower. self.field = HeliostatField(self.pos, 6.09, 6.09, absorptivity=0.04, aim_height=60, sigma_xy=1e-3, option=None) self.rec_w = 11 self.rec_h = 11 self.rec, recobj = one_sided_receiver(self.rec_w, self.rec_h) rec_trans = rotx(N.pi/-2) rec_trans[2,3] = self.field._th #================= ground_rec = False #================= if ground_rec: # Evaluating missed rays in the field along with receiver radius = 1.10 * math.sqrt((self.x_dist/2)**2 + (self.y_dist/2)**2) self.ground_rec, ground_recobj = one_sided_receiver(3*radius, 3*radius) ground_rec_trans = rotz(0) ground_rec_trans[0,3] = self.field_centre[0] ground_rec_trans[1,3] = self.field_centre[1] recobj.set_transform(rec_trans) ground_recobj.set_transform(ground_rec_trans) self.plant = Assembly(objects=[recobj, ground_recobj], subassemblies=[self.field]) else: # Evaluating just the receiver recobj.set_transform(rec_trans) self.plant = Assembly(objects=[recobj], subassemblies=[self.field]) def aim_field(self): hstat_az, hstat_elev = self.field.aim_to_sun(self.sun_az*degree, self.sun_elev*degree) return hstat_az, hstat_elev def calculate_area(self, hstat_az, hstat_elev): ''' Calculates the heliostats areas as seen from the source, necessary for shading calculations. ''' # CONVERSION # sun_vec az 0 -45 -90 +-180 +90 +45 # hstat_az -90 -45 0 +90 +-180 -135 hstat_az = -hstat_az - N.pi/2 for i in xrange(len(self.pos)): self.hstat_normals[i] = solar_vector(hstat_az[i], hstat_elev[i]) self.hstat_proj_areas = [0]*len(self.pos) for i in xrange(len(self.pos)): self.hstat_proj_areas[i] = (6.09**2) * abs(N.dot(-self.sun_vec, self.hstat_normals[i])) def trace(self): ''' Raytrace method. Raytraces successive bundles and stores the resultsogf the shading, blicking, incoming radiative power on the heliostats and the fluxmap on the receiver. ''' # Generate a large ray bundle using [a radial stagger much denser # than the field] a Buie sunshape with radius equal to the longest # dimension of the field. #============= render = False #============= sun_vec = solar_vector(self.sun_az*degree, self.sun_elev*degree) # Generate the following number of rays num_rays = 500000. iters = 40 # Results bins: incoming = N.zeros(len(self.pos)) prev_incoming = N.zeros(len(self.pos)) incoming_Q = N.zeros(len(self.pos)) incoming_stdev = N.zeros(len(self.pos)) shading = N.ones(len(self.pos)) prev_shading = N.zeros(len(self.pos)) shading_Q = N.zeros(len(self.pos)) shading_stdev = N.zeros(len(self.pos)) blocking = N.zeros(len(self.pos)) prev_blocking = N.zeros(len(self.pos)) blocking_Q= N.zeros(len(self.pos)) blocking_stdev= N.zeros(len(self.pos)) timer_mcrt = 0. timer_postprocess = 0. # Receiver bins: dl=11./50. bins = N.arange(-5.5,5.5+dl, dl) fluxmap = N.zeros((len(bins)-1,len(bins)-1)) # Raytrace: mcrt = time.clock() e = TracerEngineMP(self.plant) procs = 8 e.minener = 1e-10 timer_mcrt += time.clock()-mcrt hits_helios=0 i=0 #while hits_helios < 20e6: for i in xrange(iters): print ' ' print ' ' print 'ITERATION ', i+1#, ' of ', iters mcrt = time.clock() # Perform the trace: sources = [] self.flux = 1000. for s in xrange(procs): sources.append(self.gen_rays(num_rays/float(procs), flux=self.flux/float(procs))) e.multi_ray_sim(sources, procs) self.plant = e._asm self.field._heliostats = self.plant._assemblies[0].get_surfaces() self.rec = self.plant._objects[0].get_surfaces()[0] timer_mcrt += time.clock()-mcrt postprocess = time.clock() # Render: if render: trace_scene = Renderer(e) trace_scene.show_rays(resolution=10) # Get the energy and location of all hits using optics manager en, pts = self.rec.get_optics_manager().get_all_hits() x, y = self.rec.global_to_local(pts)[:2] # FLUX MAP OPERATIONS #=========================================================================== H, xbins, ybins = N.histogram2d(x, y, bins, weights=en) extent = [ybins[0], ybins[-1], xbins[-1], xbins[0]] fluxmap = (fluxmap*float(i)+H/(1000.*dl**2.))/(i+1.) #=========================================================================== # BLOCKAGE and SHADING #=========================================================================== # Detect blockage and look for the parents of the blocked rays. Identify from which heliostats teh oarents come and associate the blockage losses to the heliostats where blockage is suffered. hz = (e.tree._bunds[1].get_vertices()[2]) < (self.field._th-self.rec_h/2.) hits_helios += N.sum(hz) print 'Useful rays:', hits_helios # Get the 3rd bundle (after 2 hits): bund_2 = e.tree._bunds[2].get_vertices() bund_2_ener = e.tree._bunds[2].get_energy() # Remove receiver hits from the bundle to get only hits on heliostats: bund_2_helio_hits = N.ravel(N.nonzero(bund_2[2] < (self.field._th-self.rec_h/2.))) bund_2_bloc = bund_2[:, bund_2_helio_hits] # Get the bundle emitting the blocked rays and isolate the blocked rays: bund_1_helio_blocs = e.tree._bunds[2].get_parents()[bund_2_helio_hits] bund_1 = e.tree._bunds[1].get_vertices() bund_1_ener = e.tree._bunds[1].get_energy() bund_1_bloc = bund_1[:, bund_1_helio_blocs] # Screen the field to find where blocked rays originate: for h in xrange(len(self.pos)): # Get the information from the optics manager of the heliostat: abs_hstats, hits_hstats, dirs_hstats = self.field._heliostats[h].get_optics_manager().get_all_hits() blocs = [] hit_0s = [] # Check if any hits: if len(hits_hstats)!=0: # Screen through every hit: for r in xrange(N.shape(hits_hstats)[1]): # Is the hit a ray that will be blocked or a blocked ray? bloc = N.nonzero(hits_hstats[0,r] == bund_1_bloc[0])[0] # Next "if" is because if there are no valid hits the bloc returns an empty array or to isolate each hit in case of 2 hits matching. if len(bloc)>0: for b in xrange(len(bloc)): # If sthe first coordinate matches, do the rest of them? if (hits_hstats[:,r]==N.ravel(bund_1_bloc[:,bloc[b]])).all(): # If so add the blocked energy to the result bin. blocs.append(bund_1_helio_blocs[bloc[b]]) else: hit_0 = N.nonzero(hits_hstats[0,r] == bund_1[0])[0] if len(hit_0)>0: for s in xrange(len(hit_0)): if (hits_hstats[:,r]==N.ravel(bund_1[:,hit_0[s]])).all(): hit_0s.append(e.tree._bunds[1].get_parents()[hit_0[s]]) prev_blocking[h] = blocking[h] # Monte-Carlo sampling: blocking[h] = (blocking[h]*i+N.sum(bund_1_ener[blocs]))/(i+1.) # Shading is the theoretical energy hitting subtracted by the energy absorbed without the backside blocking. prev_incoming[h] = incoming[h] # Monte-Carlo sampling: incoming[h] = (incoming[h]*i+N.sum(e.tree._bunds[0].get_energy()[hit_0s]))/(i+1.) prev_shading[h] = shading[h] # Monte-Carlo sampling: shading[h] = (shading[h]*i+self.flux*self.hstat_proj_areas[h]-incoming[h])/(i+1.) # Streamlined stats variable: incoming_Q = incoming_Q+i/(i+1.)*(incoming-prev_incoming)**2. blocking_Q = blocking_Q+i/(i+1.)*(blocking-prev_blocking)**2. shading_Q = shading_Q+i/(i+1.)*(shading-prev_shading)**2. # Standard deviatiosn updates: if i>0: incoming_stdev = N.sqrt(incoming_Q/i) blocking_stdev = N.sqrt(blocking_Q/i) shading_stdev = N.sqrt(shading_Q/i) print 'Shading=', N.sum(shading) print 'Blockage=', N.sum(blocking) timer_postprocess += time.clock()-postprocess print 'timer_mcrt: ', timer_mcrt/60., 'min' print 'timer_postprocess: ', timer_postprocess/60., 'min' print 'Peak flux (kW/m2):', N.amax(fluxmap) print 'AVG flux (kW/m2): ', N.sum(fluxmap)/(N.shape(fluxmap)[0]*N.shape(fluxmap)[1]) print 'Total radiative power (kW): ', N.sum(fluxmap*(11./50.)**2) i+=1 #=========================================================================== e.tree._bunds = [] for clear in xrange(len(e._asm.get_surfaces())): e._asm.get_surfaces()[clear].get_optics_manager().reset() #=========================================================================== '''
class TowerScene(): # Location of the sun: sun_az = 0 sun_zenith = 35.05 #34.96 sun_vec = solar_vector(sun_az * degree, sun_zenith * degree) hstat_normals = N.zeros((218, 3)) # import custom coordinate file pos = N.loadtxt( "/home/charles/Documents/Tracer/examples/sandia_hstat_coordinates.csv", delimiter=',') # Field-based calculations for source size parameters #=================================================== t_pos = pos.T xc_min = t_pos[0][N.argmin(t_pos[0])] xc_max = t_pos[0][N.argmax(t_pos[0])] yc_min = t_pos[1][N.argmin(t_pos[1])] yc_max = t_pos[1][N.argmax(t_pos[1])] x_dist = xc_max - xc_min y_dist = yc_max - yc_min xc_cent = (xc_min + xc_max) / 2 yc_cent = (yc_min + yc_max) / 2 field_centre = N.r_[xc_cent, yc_cent, 0] #=================================================== def __init__(self): self.gen_plant() def gen_rays(self, num_rays, flux=1000.): #======================== individual_source = False #======================== if individual_source: # Pillbox source on a per-heliostat basis radius = 1.20 * math.sqrt(2 * 3.405**2) direction = N.array(-self.sun_vec) ray_list = [] num_surfs = self.pos.shape[0] for i in xrange(num_surfs): centre = N.c_[50 * self.sun_vec + self.pos[i]] #rayb = solar_disk_bundle(num_rays/num_surfs, centre, direction, radius, 4.65e-3, flux) rayb = buie_sunshape(num_rays / num_surfs, centre, direction, radius, CSR=0.01, flux=flux) ray_list.append(rayb) rays = concatenate_rays(ray_list) del ray_list else: # Large pillbox sunshape source disc source covering entire field area: radius = 1.10 * math.sqrt((self.x_dist / 2)**2 + (self.y_dist / 2)**2) self.source_area = N.pi * radius**2 centre = N.c_[300 * self.sun_vec + self.field_centre] direction = N.array(-self.sun_vec) #rays = solar_disk_bundle(num_rays, centre, direction, radius, 4.65e-3, flux) rays = buie_sunshape(num_rays, centre, direction, radius, CSR=0.01, flux=flux, pre_process_CSR=False) return rays def gen_plant(self, width=6.1, height=6.1, absorptivity=0.04, aim_height=60., sigma_xy=1e-3, rec_w=11., rec_h=11.): self.pos[:, 1] = self.pos[:, 1] - 4. # correction for the true position of the plate on the tower. self.width = width self.height = height self.absorptivity = absorptivity self.field = HeliostatField(self.pos, width, height, absorptivity, aim_height, sigma_xy) self.rec_w = rec_w self.rec_h = rec_h rec, recobj = one_sided_receiver(self.rec_w, self.rec_h) rec_trans = rotx(N.pi / -2) rec_trans[2, 3] = self.field._th # Evaluating just the receiver recobj.set_transform(rec_trans) self.plant = Assembly(objects=[recobj], subassemblies=[self.field]) def aim_field(self): hstat_az, hstat_elev = self.field.aim_to_sun(self.sun_az * degree, self.sun_zenith * degree) return hstat_az, hstat_elev def calculate_area(self, hstat_az, hstat_elev): ''' Calculates the heliostats areas as seen from the source, necessary for shading calculations. ''' # CONVERSION # sun_vec az 0 -45 -90 +-180 +90 +45 # hstat_az -90 -45 0 +90 +-180 -135 hstat_az = -hstat_az - N.pi / 2 for i in xrange(len(self.pos)): self.hstat_normals[i] = solar_vector(hstat_az[i], hstat_elev[i]) self.hstat_proj_areas = [0] * len(self.pos) for i in xrange(len(self.pos)): self.hstat_proj_areas[i] = (6.1**2) * abs( N.dot(-self.sun_vec, self.hstat_normals[i])) def trace(self, num_rays=1e5, nbins_w=50., nbins_h=50.): ''' Raytrace method. Raytraces successive bundles and stores the resultsogf the shading, blicking, incoming radiative power on the heliostats and the fluxmap on the receiver. ''' # Generate a large ray bundle using [a radial stagger much denser # than the field] a Buie sunshape with radius equal to the longest # dimension of the field. #============= render = False #============= sun_vec = solar_vector(self.sun_az * degree, self.sun_zenith * degree) bundlesize = 1e4 iters = int(num_rays / bundlesize) # Results bins: incoming = N.zeros(len(self.pos)) prev_incoming = N.zeros(len(self.pos)) incoming_Q = N.zeros(len(self.pos)) incoming_stdev = N.zeros(len(self.pos)) shading = N.ones(len(self.pos)) prev_shading = N.zeros(len(self.pos)) shading_Q = N.zeros(len(self.pos)) shading_stdev = N.zeros(len(self.pos)) blocking = N.zeros(len(self.pos)) prev_blocking = N.zeros(len(self.pos)) blocking_Q = N.zeros(len(self.pos)) blocking_stdev = N.zeros(len(self.pos)) timer_mcrt = 0. timer_postprocess = 0. # Receiver bins: dlw = self.rec_w / nbins_w dlh = self.rec_h / nbins_h bins_w = N.arange(-self.rec_w / 2., self.rec_w / 2. + dlw, dlw) bins_h = N.arange(-self.rec_h / 2., self.rec_h / 2. + dlh, dlh) bins = [bins_w, bins_h] self.bins = bins fluxmap = N.zeros((len(bins_w) - 1, len(bins_h) - 1)) # Raytrace: mcrt = time.clock() e = TracerEngineMP(self.plant) procs = 1 e.minener = 1e-10 timer_mcrt += time.clock() - mcrt hits_helios = 0 i = 0 #while hits_helios < num_rays: for i in xrange(iters): print ' ' print ' ' print 'ITERATION ', i + 1, ' of ', iters #print hits_helios, 'hits out of ', num_rays mcrt = time.clock() # Perform the trace: sources = [] self.flux = 1000. for s in xrange(procs): sources.append( self.gen_rays(num_rays=bundlesize / float(procs), flux=self.flux / float(procs))) e.multi_ray_sim(sources=sources, procs=procs) self.plant = e._asm self.field._heliostats = self.plant._assemblies[0].get_surfaces() self.rec = self.plant._objects[0].get_surfaces()[0] timer_mcrt += time.clock() - mcrt postprocess = time.clock() # Render: if render: trace_scene = Renderer(e) trace_scene.show_rays(resolution=10) # Get the energy and location of all hits using optics manager en, pts = self.rec.get_optics_manager().get_all_hits() x, y = self.rec.global_to_local(pts)[:2] # FLUX MAP OPERATIONS #=========================================================================== H, xbins, ybins = N.histogram2d(x, y, bins, weights=en / (dlw * dlh) * 1e-3) extent = [ybins[0], ybins[-1], xbins[-1], xbins[0]] fluxmap = (fluxmap * float(i) + H) / (i + 1.) #=========================================================================== # BLOCKAGE and SHADING #=========================================================================== # Detect blockage and look for the parents of the blocked rays. Identify from which heliostats the parents come and associate the blockage losses to the heliostats where blockage is suffered. hz = (e.tree._bunds[1].get_vertices()[2]) < (self.field._th - self.rec_h / 2.) hits_helios += N.sum(hz) print 'Useful rays:', hits_helios # Get the 3rd bundle (after 2 hits): bund_2 = e.tree._bunds[2].get_vertices() bund_2_ener = e.tree._bunds[2].get_energy() # Remove receiver hits from the bundle to get only hits on heliostats: bund_2_helio_hits = N.ravel( N.nonzero(bund_2[2] < (self.field._th - self.rec_h / 2.))) bund_2_bloc = bund_2[:, bund_2_helio_hits] # Get the bundle emitting the blocked rays and isolate the blocked rays: bund_1_helio_blocs = e.tree._bunds[2].get_parents( )[bund_2_helio_hits] bund_1 = e.tree._bunds[1].get_vertices() bund_1_ener = e.tree._bunds[1].get_energy() bund_1_bloc = bund_1[:, bund_1_helio_blocs] # Screen the field to find where blocked rays originate: for h in xrange(len(self.pos)): # Get the information from the optics manager of the heliostat: abs_hstats, hits_hstats, dirs_hstats = self.field._heliostats[ h].get_optics_manager().get_all_hits() blocs = [] hit_0s = [] # Check if any hits: if len(hits_hstats) != 0: # Screen through every hit: for r in xrange(N.shape(hits_hstats)[1]): # Is the hit a ray that will be blocked or a blocked ray? bloc = N.nonzero(hits_hstats[0, r] == bund_1_bloc[0])[0] # Next "if" is because if there are no valid hits the bloc returns an empty array or to isolate each hit in case of 2 hits matching. if len(bloc) > 0: for b in xrange(len(bloc)): # If sthe first coordinate matches, do the rest of them? if (hits_hstats[:, r] == N.ravel( bund_1_bloc[:, bloc[b]])).all(): # If so add the blocked energy to the result bin. blocs.append(bund_1_helio_blocs[bloc[b]]) else: hit_0 = N.nonzero(hits_hstats[0, r] == bund_1[0])[0] if len(hit_0) > 0: for s in xrange(len(hit_0)): if (hits_hstats[:, r] == N.ravel( bund_1[:, hit_0[s]])).all(): hit_0s.append( e.tree._bunds[1].get_parents()[ hit_0[s]]) prev_blocking[h] = blocking[h] # Monte-Carlo sampling: blocking[h] = (blocking[h] * i + N.sum(bund_1_ener[blocs])) / (i + 1.) # Shading is the theoretical energy hitting subtracted by the energy absorbed without the backside blocking. prev_incoming[h] = incoming[h] # Monte-Carlo sampling: incoming[h] = (incoming[h] * i + N.sum( e.tree._bunds[0].get_energy()[hit_0s])) / (i + 1.) prev_shading[h] = shading[h] # Monte-Carlo sampling: shading[h] = (shading[h] * i + self.flux * self.hstat_proj_areas[h] - incoming[h]) / (i + 1.) # Streamlined stats variable: incoming_Q = incoming_Q + i / (i + 1.) * (incoming - prev_incoming)**2. blocking_Q = blocking_Q + i / (i + 1.) * (blocking - prev_blocking)**2. shading_Q = shading_Q + i / (i + 1.) * (shading - prev_shading)**2. # Standard deviatiosn updates: if i > 0: incoming_stdev = N.sqrt(incoming_Q / i) blocking_stdev = N.sqrt(blocking_Q / i) shading_stdev = N.sqrt(shading_Q / i) print 'Shading=', N.sum(shading) print 'Blockage=', N.sum(blocking) timer_postprocess += time.clock() - postprocess print 'timer_mcrt: ', timer_mcrt / 60., 'min' print 'timer_postprocess: ', timer_postprocess / 60., 'min' print 'Peak flux (kW/m2):', N.amax(fluxmap) print 'AVG flux (kW/m2): ', N.sum(fluxmap) / (N.shape(fluxmap)[0] * N.shape(fluxmap)[1]) print 'Total radiative power (kW): ', N.sum(fluxmap * (11. / 50.)**2) i += 1 #=========================================================================== e.tree._bunds = [] for clear in xrange(len(e._asm.get_surfaces())): e._asm.get_surfaces()[clear].get_optics_manager().reset() #=========================================================================== del (self.plant) results = { 'positions': self.pos, 'blocking': blocking, 'blocking_stdev': blocking_stdev, 'shading': shading, 'shading_stdev': shading_stdev, 'incoming': incoming, 'incoming_stdev': incoming_stdev, 'fluxmap': fluxmap, 'extent': extent, 'width': self.width, 'height': self.height, 'absorptivity': self.absorptivity, 'rec_width': self.rec_w, 'rec_height': self.rec_h, 'rec_bins': self.bins } filesave = open( '/home/charles/Documents/Boulot/These/Heliostat field/Sandia_data', 'w') pickle.dump(results, filesave) filesave.close()
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(): """ 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(): # 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(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())))