def driver_funct(configname): from chroma.loader import load_bvh # Requires CUDA so only import it when necessary kabamland = Detector(lm.create_scintillation_material()) config = detectorconfig.configdict(configname) #get_lens_triangle_centers(vtx, rad_assembly, config.diameter_ratio, config.thickness_ratio, config.half_EPD, config.blockers, blocker_thickness_ratio=config.blocker_thickness_ratio, light_confinement=config.light_confinement, focal_length=config.focal_length, lens_system_name=config.lens_system_name) #print get_curved_surf_triangle_centers(config.vtx, config.half_EPD/config.EPD_ratio, config.detector_r, config.focal_length, config.nsteps, config.b_pixel)[0] build_lens_icosahedron( kabamland, config.vtx, config.half_EPD / config.EPD_ratio, config.diameter_ratio, config.thickness_ratio, config.half_EPD, config.blockers, blocker_thickness_ratio=config.blocker_thickness_ratio, light_confinement=config.light_confinement, focal_length=config.focal_length, lens_system_name=config.lens_system_name) #build_curvedsurface_icosahedron(kabamland, config.vtx, config.half_EPD/config.EPD_ratio, config.diameter_ratio, focal_length=config.focal_length, detector_r=config.detector_r, nsteps=config.nsteps, b_pxl=config.b_pixel) #build_pmt_icosahedron(kabamland, np.linalg.norm(config.vtx[0]), focal_length=config.focal_length) kabamland.flatten() kabamland.bvh = load_bvh(kabamland)
def load_or_build_detector(config, detector_material, g4_detector_parameters, force_build=False): configname = config.config_name filename_base = paths.detector_config_path + configname if not os.path.exists(paths.detector_config_path): os.makedirs(paths.detector_config_path) kabamland = None # How to ensure the material and detector parameters are correct?? if not force_build: try: detector_config = dd.io.load(filename_base + '.h5') kabamland = detector_config['detector'] logger.info("** Loaded HDF5 (deepdish) detector configuration: " + configname) except IOError as error: # Will dd throw an exception? try: with open(filename_base + '.pickle', 'rb') as pickle_file: kabamland = pickle.load(pickle_file) logger.info("** Loaded pickle detector configuration: " + configname) except IOError as error: pass if kabamland is not None: config_has_g4_dp = hasattr( kabamland, 'g4_detector_parameters' ) and kabamland.g4_detector_parameters is not None config_has_g4_dm = hasattr( kabamland, 'detector_material') and kabamland.detector_material is not None if g4_detector_parameters is not None: logger.info('*** Using Geant4 detector parameters specified' + (' - replacement' if config_has_g4_dp else '') + ' ***') kabamland.g4_detector_parameters = g4_detector_parameters elif config_has_g4_dp: logger.info( '*** Using Geant4 detector parameters found in loaded file ***' ) else: logger.info('*** No Geant4 detector parameters found at all ***') if detector_material is not None: logger.info('*** Using Geant4 detector material specified' + (' - replacement' if config_has_g4_dm else '') + ' ***') kabamland.detector_material = detector_material elif config_has_g4_dm: logger.info( '*** Using Geant4 detector material found in loaded file ***' ) else: logger.info('*** No Geant4 detector material found at all ***') else: from chroma.loader import load_bvh # Requires CUDA so only import it when necessary logger.info("** Building detector configuration: " + configname) kabamland = Detector(lm.create_scintillation_material(), g4_detector_parameters=g4_detector_parameters) kbl2.build_kabamland(kabamland, config) # view(kabamland) kabamland.flatten() kabamland.bvh = load_bvh(kabamland, bvh_name=config.config_name, read_bvh_cache=(not force_build)) ''' try: with open(filename_base+'.pickle','wb') as pickle_file: pickle.dump(kabamland, pickle_file) except IOError as error: logger.info("Error writing pickle file: " + filename_base+'.pickle') ''' # Write h5 file with configuration data structure logger.info('Saving h5 detector configuration. UUID: %s' % config.uuid) ''' # This was created to minimize what is saved from the Detector object. But for simplicity, we are currently pickling the whole object. detector_dict = { 'detector_material' : kabamland.detector_material, 'solids' : kabamland.solids, 'solid_rotations' : kabamland.solid_rotations, 'solid_displacements' : kabamland.solid_displacements, 'bvh' : kabamland.bvh, 'g4_detector_parameters' : kabamland.g4_detector_parameters, 'solid_id_to_channel_index' : kabamland.solid_id_to_channel_index, 'channel_index_to_solid_id' : kabamland.channel_index_to_solid_id, 'channel_index_to_channel_id' : kabamland.channel_index_to_channel_id, 'channel_id_to_channel_index' : kabamland.channel_id_to_channel_index, 'time_cdf' : kabamland.time_cdf, 'charge_cdf' : kabamland.charge_cdf } ''' # TODO: Saving the whole dict and the object is redundant # TODO: Also, saving all of kabamland vs. just the parameters above adds about 1 Meg to the file size (I think) import lenssystem ld_name = configname.split('_')[0][2:] lens_design = lenssystem.get_lens_sys(ld_name) config_data = { 'detector_config': config, 'detector_config_dict': vars(config), 'lens_config_dict': vars(lens_design) } detector_data = {'config': config_data, 'detector': kabamland} dd.io.save(filename_base + '.h5', detector_data) return kabamland
class ColladaToChroma(object): secs = {} surface_props = "detect absorb reemit reflect_diffuse reflect_specular eta k reemission_cdf".split( ) def __init__(self, nodecls, bvh=False, dump_node_info=False): """ :param nodecls: typically DAENode """ log.debug("ColladaToChroma") self.dump_node_info = dump_node_info # for debug self.nodecls = nodecls self.bvh = bvh #self.chroma_geometry = Geometry(detector_material=None) # bialkali ? self.chroma_geometry = Detector(detector_material=None) pass self.vcount = 0 self.surfaces = {} self.materials = {} # dict of chroma.geometry.Material self._materialmap = {} # dict with short name keys self._surfacemap = {} # dict with short name keys # idmap checking self.channel_count = 0 self.channel_ids = set() def convert_opticalsurfaces(self, debug=False): """ """ log.info("convert_opticalsurfaces") for dsurf in self.nodecls.extra.opticalsurface: surface = self.make_opticalsurface(dsurf, debug=debug) self.surfaces[surface.name] = surface pass #assert len(self.surfaces) == len(self.nodecls.extra.opticalsurface), "opticalsurface with duplicate names ? " log.info("convert_opticalsurfaces creates %s from %s " % (len(self.surfaces), len(self.nodecls.extra.opticalsurface))) def make_opticalsurface(self, dsurf, debug=False): """ :param dsurf: G4DAE surface :return: Chroma surface * name * model ? defaults to 0 G4DAE Optical Surface properties * REFLECTIVITY (the only property to be widely defined) * RINDEX (seems odd for a surface, looks to always be zero) * SPECULARLOBECONSTANT (set to 0.85 for a few surface) * BACKSCATTERCONSTANT,SPECULARSPIKECONSTANT (often present, always zero) `chroma/geometry_types.h`:: enum { SURFACE_DEFAULT, SURFACE_COMPLEX, SURFACE_WLS }; Potentially wavelength dependent props all default to zero. Having values for these is necessary to get SURFACE_DETECT, SURFACE_ABSORB * detect * absorb * reflect_diffuse * reflect_specular * reemit * eta * k * reemission_cdf `chroma/cuda/photon.h`:: 701 __device__ int 702 propagate_at_surface(Photon &p, State &s, curandState &rng, Geometry *geometry, 703 bool use_weights=false) 704 { 705 Surface *surface = geometry->surfaces[s.surface_index]; 706 707 if (surface->model == SURFACE_COMPLEX) 708 return propagate_complex(p, s, rng, surface, use_weights); 709 else if (surface->model == SURFACE_WLS) 710 return propagate_at_wls(p, s, rng, surface, use_weights); 711 else 712 { 713 // use default surface model: do a combination of specular and 714 // diffuse reflection, detection, and absorption based on relative 715 // probabilties 716 717 // since the surface properties are interpolated linearly, we are 718 // guaranteed that they still sum to 1.0. 719 float detect = interp_property(surface, p.wavelength, surface->detect); 720 float absorb = interp_property(surface, p.wavelength, surface->absorb); 721 float reflect_diffuse = interp_property(surface, p.wavelength, surface->reflect_diffuse); 722 float reflect_specular = interp_property(surface, p.wavelength, surface->reflect_specular); 723 """ if debug: print "%-75s %s " % (dsurf.name, dsurf) surface = Surface(dsurf.name) finish_map = { OpticalSurfaceFinish.polished: 'reflect_specular', OpticalSurfaceFinish.ground: 'reflect_diffuse', } if 'EFFICIENCY' in dsurf.properties: EFFICIENCY = dsurf.properties.get('EFFICIENCY', None) surface.set('detect', EFFICIENCY[:, 1], wavelengths=EFFICIENCY[:, 0]) pass elif 'REFLECTIVITY' in dsurf.properties: REFLECTIVITY = dsurf.properties.get('REFLECTIVITY', None) key = finish_map.get(int(dsurf.finish), None) if key is None or REFLECTIVITY is None: log.warn( "miss REFLECTIVITY key : not setting REFLECTIVITY for %s " % surface.name) else: log.debug("setting prop %s for surface %s " % (key, surface.name)) surface.set(key, REFLECTIVITY[:, 1], wavelengths=REFLECTIVITY[:, 0]) pass else: log.warn(" no REFLECTIVITY/EFFICIENCY in dsurf.properties %s " % repr(dsurf.properties)) pass return surface def collada_materials_summary(self, names=['GdDopedLS', 'LiquidScintillator']): collada = self.nodecls.orig find_ = lambda name: filter(lambda m: m.id.find(name) > -1, collada. materials) for name in names: mats = find_(name) assert len(mats) == 1, "name is ambiguous or missing" self.dump_collada_material(mats[0]) pass def dump_collada_material(self, mat): extra = getattr(mat, 'extra', None) keys = extra.properties.keys() if extra else [] print mat.id keys = sorted(keys, key=lambda k: extra.properties[k].shape[0], reverse=True) for k in keys: xy = extra.properties[k] x = xy[:, 0] y = xy[:, 1] print "%30s %10s %10.3f %10.3f %10.3f %10.3f " % ( k, repr(xy.shape), x.min(), x.max(), y.min(), y.max()) def convert_materials(self, debug=False): """ #. creates chroma Material instances for each collada material #. fills in properties from the collada extras #. records materials in a map keyed by material.name Chroma materials default to None, 3 settings: * refractive_index * absorption_length * scattering_length And defaults to zero, 2 settings: * reemission_prob * reemission_cdf G4DAE materials have an extra attribute dict that contains keys such as * RINDEX * ABSLENGTH * RAYLEIGH Uncertain of key correspondence, especially REEMISSIONPROB and what about reemission_cdf ? Possibly the many Scintillator keys can provide that ? Probably many the scintillator keys are only relevant to photon production rather than photon propagation, so they are irrelevant to Chroma. Which materials have each:: EFFICIENCY [1 ] Bialkali -------------- assumed to not apply to optical photons --------- FASTTIMECONSTANT [2 ] GdDopedLS,LiquidScintillator SLOWTIMECONSTANT [2 ] GdDopedLS,LiquidScintillator YIELDRATIO [2 ] GdDopedLS,LiquidScintillator GammaFASTTIMECONSTANT [2 ] GdDopedLS,LiquidScintillator GammaSLOWTIMECONSTANT [2 ] GdDopedLS,LiquidScintillator GammaYIELDRATIO [2 ] GdDopedLS,LiquidScintillator AlphaFASTTIMECONSTANT [2 ] GdDopedLS,LiquidScintillator AlphaSLOWTIMECONSTANT [2 ] GdDopedLS,LiquidScintillator AlphaYIELDRATIO [2 ] GdDopedLS,LiquidScintillator NeutronFASTTIMECONSTANT [2 ] GdDopedLS,LiquidScintillator NeutronSLOWTIMECONSTANT [2 ] GdDopedLS,LiquidScintillator NeutronYIELDRATIO [2 ] GdDopedLS,LiquidScintillator SCINTILLATIONYIELD [2 ] GdDopedLS,LiquidScintillator RESOLUTIONSCALE [2 ] GdDopedLS,LiquidScintillator --------------------------------------------------------------------- ReemissionFASTTIMECONSTANT [2 ] GdDopedLS,LiquidScintillator for opticalphoton ReemissionSLOWTIMECONSTANT [2 ] GdDopedLS,LiquidScintillator ReemissionYIELDRATIO [2 ] GdDopedLS,LiquidScintillator FASTCOMPONENT [2 ] GdDopedLS,LiquidScintillator "Fast_Intensity" SLOWCOMPONENT [2 ] GdDopedLS,LiquidScintillator "Slow_Intensity" REEMISSIONPROB [2 ] GdDopedLS,LiquidScintillator "Reemission_Prob" ------------------------------------------------------------------------ RAYLEIGH [5 ] GdDopedLS,Acrylic,Teflon,LiquidScintillator,MineralOil RINDEX [14] Air,GdDopedLS,Acrylic,Teflon,LiquidScintillator,Bialkali, Vacuum,Pyrex,MineralOil,Water,NitrogenGas,IwsWater,OwsWater,DeadWater ABSLENGTH [20] PPE,Air,GdDopedLS,Acrylic,Teflon,LiquidScintillator,Bialkali, Vacuum,Pyrex,UnstStainlessSteel,StainlessSteel, ESR,MineralOil,Water,NitrogenGas,IwsWater,ADTableStainlessSteel,Tyvek,OwsWater,DeadWater Observations: #. no RAYLEIGH for water """ keymap = { "RINDEX": 'refractive_index', "ABSLENGTH": 'absorption_length', "RAYLEIGH": 'scattering_length', "REEMISSIONPROB": 'reemission_prob', } keymat = {} collada = self.nodecls.orig for dmaterial in collada.materials: material = Material(dmaterial.id) if DEBUG: material.dae = dmaterial # vacuum like defaults ? is that appropriate ? what is the G4 equivalent ? material.set('refractive_index', 1.0) material.set('absorption_length', 1e6) material.set('scattering_length', 1e6) if dmaterial.extra is not None: props = dmaterial.extra.properties for dkey, dval in props.items(): if dkey not in keymat: keymat[dkey] = [] keymat[dkey].append( material.name ) # record of materials that have each key if dkey in keymap: key = keymap[dkey] material.set(key, dval[:, 1], wavelengths=dval[:, 0]) log.debug( "for material %s set Chroma prop %s from G4DAE prop %s vals %s " % (material.name, key, dkey, len(dval))) else: log.debug( "for material %s skipping G4DAE prop %s vals %s " % (material.name, dkey, len(dval))) pass self.setup_cdf(material, props) pass pass self.materials[material.name] = material pass log.debug("convert_materials G4DAE keys encountered : %s " % len(keymat)) if debug: for dkey in sorted(keymat, key=lambda _: len(keymat[_])): mats = keymat[dkey] print " %-30s [%-2s] %s " % (dkey, len(mats), ",".join( map(matshorten, mats))) def setup_cdf(self, material, props): """ Chroma uses "reemission_cdf" cumulative distribution function to generate the wavelength of reemission photons. Currently think that the name "reemission_cdf" is misleading, as it is the RHS normalized CDF obtained from an intensity distribution (photon intensity as function of wavelength) NB REEMISSIONPROB->reemission_prob is handled as a normal keymapped property, no need to integrate to construct the cdf for that. Compare this with the C++ DsChromaG4Scintillation::BuildThePhysicsTable() """ fast = props.get('FASTCOMPONENT', None) slow = props.get('SLOWCOMPONENT', None) reem = props.get('REEMISSIONPROB', None) if fast is None or slow is None or reem is None: return assert not fast is None assert not slow is None assert not reem is None assert np.all(fast == slow) # CURIOUS, that these are the same reemission_cdf = construct_cdf_energywise(fast) ## yep "fast" : need intensity distribution # # reem_cdf = construct_cdf( reem ) # # Nope the CDF are used to generate wavelengths # following the desired slow/fast intensity distribution # [ie number of photons in wavelength ranges] # (which happen to be the same) # # conversely the reemission probability gives the # fraction that reemit at the wavelength # that value can be used directly by random uniform throws # to decide whether to reemit no cdf gymnastics needed # as are just determining whether somethinh happens not # the wavelength distribution of photons # # log.debug("setting reemission_cdf for %s to %s " % (material.name, repr(reemission_cdf))) #material.set('reemission_cdf', reemission_cdf[:,1], wavelengths=reemission_cdf[:,0]) material.setraw('reemission_cdf', reemission_cdf) def _get_materialmap(self): """ Dict of chroma.geometry.Material instances with short name keys """ if len(self._materialmap) == 0: prefix = '__dd__Materials__' for name, mat in self.materials.items(): if name.startswith(prefix): name = name[len(prefix):] if name[-9:-7] == '0x': name = name[:-9] pass self._materialmap[name] = mat pass return self._materialmap materialmap = property(_get_materialmap) def _get_surfacemap(self): """ Dict of chroma.geometry.Surface instances with short name keys """ postfix = 'Surface' if len(self._surfacemap) == 0: for name, surf in self.surfaces.items(): prefix = "__".join(name.split("__")[:-1]) + "__" if name.startswith(prefix): nam = name[len(prefix):] if nam.endswith(postfix): nam = nam[:-len(postfix)] pass self._surfacemap[nam] = surf pass return self._surfacemap surfacemap = property(_get_surfacemap) def property_plot(self, matname, propname): import matplotlib.pyplot as plt mat = self.materialmap[matname] xy = mat.daeprops[propname] #plt.plot( xy[:,0], xy[:,1] ) plt.plot(*xy.T) def convert_geometry_traverse(self, nodes=None): log.debug("convert_geometry_traverse") if nodes is None: self.nodecls.vwalk(self.visit) else: for node in nodes: self.visit(node) pass self.dump_channel_info() def convert_flatten(self): log.debug("ColladaToChroma convert_geometry flattening %s " % len(self.chroma_geometry.solids)) self.chroma_geometry.flatten() def convert_make_maps(self): self.cmm = self.make_chroma_material_map(self.chroma_geometry) self.csm = self.make_chroma_surface_map(self.chroma_geometry) def convert_geometry(self, nodes=None): """ :param nodes: list of DAENode instances or None Converts DAENode/pycollada geometry into Chroma geometry. When `nodes=None` the entire DAENode tree is visited and converted, otherwise just the listed nodes. """ log.debug("convert_geometry") self.convert_materials() self.convert_opticalsurfaces() self.convert_geometry_traverse(nodes) self.convert_flatten() self.convert_make_maps() if self.bvh: self.add_bvh() return self.chroma_geometry def convert_geometry_partial(self, nodes=None): """ splitting the conversion in order to provide a point at which to hack the geometry. this includes removing sibling overlapping triangles or defining wire meshes. """ log.debug("convert_geometry") self.convert_materials() self.convert_opticalsurfaces() self.convert_geometry_traverse(nodes) return self.chroma_geometry def finish_converting_geometry(self): self.convert_flatten() self.convert_make_maps() if self.bvh: self.add_bvh() return self.chroma_geometry def make_chroma_material_map(self, chroma_geometry): """ Curiously the order of chroma_geometry.unique_materials on different invokations is "fairly constant" but not precisely so. How is that possible ? Perfect or random would seem more likely outcomes. """ unique_materials = chroma_geometry.unique_materials material_lookup = dict( zip(unique_materials, range(len(unique_materials)))) cmm = dict([(material_lookup[m], m.name) for m in filter(None, unique_materials)]) cmm[-1] = "ANY" cmm[999] = "UNKNOWN" return cmm def make_chroma_surface_map(self, chroma_geometry): unique_surfaces = chroma_geometry.unique_surfaces surface_lookup = dict(zip(unique_surfaces, range(len(unique_surfaces)))) csm = dict([(surface_lookup[s], s.name) for s in filter(None, unique_surfaces)]) csm[-1] = "ANY" csm[999] = "UNKNOWN" return csm def add_bvh(self, bvh_name="default", auto_build_bvh=True, read_bvh_cache=True, update_bvh_cache=True, cache_dir=None, cuda_device=None): """ As done by chroma.loader """ log.debug("ColladaToChroma adding BVH") self.chroma_geometry.bvh = load_bvh(self.chroma_geometry, bvh_name=bvh_name, auto_build_bvh=auto_build_bvh, read_bvh_cache=read_bvh_cache, update_bvh_cache=update_bvh_cache, cache_dir=cache_dir, cuda_device=cuda_device) log.debug("completed adding BVH") def find_outer_inner_materials(self, node): """ :param node: DAENode instance :return: Chroma Material instances for outer and inner materials #. Parent node material regarded as outside #. Current node material regarded as inside Think about a leaf node to see the sense of that. Caveat, the meanings of "inner" and "outer" depend on the orientation of the triangles that make up the surface... So just adopt a convention and try to verify it later. """ assert node.__class__.__name__ == 'DAENode' this_material = self.materials[node.matid] if node.parent is None: parent_material = this_material log.warning( "setting parent_material to %s as parent is None for node %s " % (parent_material.name, node.id)) else: parent_material = self.materials[node.parent.matid] log.debug("find_outer_inner_materials node %s %s %s" % (node, this_material, parent_material)) return parent_material, this_material def find_skinsurface(self, node): """ :param node: DAENode instance :return: G4DAE Surface instance corresponding to G4LogicalSkinSurface if one is available for the LV of the current node * ambiguous skin for lvid __dd__Geometry__PMT__lvPmtHemiCathode0xc2cdca0 found 672 * if the properties are the same then ambiguity not a problem ? """ assert node.__class__.__name__ == 'DAENode' assert self.nodecls.extra.__class__.__name__ == 'DAEExtra' ssid = self.nodecls.sensitive_surface_id(node) if not ssid is None: skin = self.nodecls.extra.skinmap.get(ssid, None) log.debug("ssid %s skin %s " % (ssid, repr(skin))) if skin is not None: if len(skin) > 0: # expected for sensitives skin = skin[0] pass else: lvid = node.lv.id skin = self.nodecls.extra.skinmap.get(lvid, None) if skin is not None: assert len( skin) == 1, "ambiguous skin for lvid %s found %s " % ( lvid, len(skin)) ##log.warn("ambiguous skin for lvid %s found %s : USING FIRST " % (lvid, len(skin))) skin = skin[0] pass return skin def find_bordersurface(self, node): """ :param node: DAENode instance :return: G4DAE Surface instance corresponding to G4LogicalBorderSurface if one is available for the PVs of the current node and its parent Ambiguity bug makes this difficult """ assert node.__class__.__name__ == 'DAENode' pass #pvid = node.pv.id #ppvid = node.parent.pv.id #border = self.nodecls.extra.bordermap.get(pvid, None) return None def find_surface(self, node): """ :param node: DAENode instance :return Chroma Surface instance or None: G4DAE persists the below surface elements which both reference "opticalsurface" containing the keyed properties * "skinsurface" (single volumeref, ref by lv.id) * "boundarysurface" (physvolref ordered pair, identified by pv1.id,pv2.id) The boundary pairs are always parent/child nodes in dyb Near geometry, they could in principal be siblings. """ assert node.__class__.__name__ == 'DAENode' skin = self.find_skinsurface(node) border = self.find_bordersurface(node) dsurf = filter(None, [skin, border]) assert len( dsurf ) < 2, "Not expecting both skin %s and border %s surface for the same node %s " % ( skin, border, node) if len(dsurf) == 1: dsurface = dsurf[0] log.debug("found dsurface %s for node %s " % (dsurface, node)) surface = self.surfaces.get(dsurface.name, None) assert surface is not None, "dsurface %s without corresponding chroma surface of name %s for node %s " % ( dsurface, dsurface.name, node.id) else: surface = None pass return surface def visit(self, node, debug=False): """ :param node: DAENode instance DAENode instances and their pycollada underpinnings meet chroma here Chroma needs sensitive detectors to have an associated surface with detect property ... """ #assert node.__class__.__name__ == 'DAENode' self.vcount += 1 if self.vcount < 10: log.debug("visit : vcount %s node.index %s node.id %s " % (self.vcount, node.index, node.id)) bps = list(node.boundgeom.primitives()) bpl = bps[0] assert len(bps) == 1 and bpl.__class__.__name__ == 'BoundPolylist' tris = bpl.triangleset() vertices = tris._vertex triangles = tris._vertex_index mesh = Mesh(vertices, triangles, remove_duplicate_vertices=False) material2, material1 = self.find_outer_inner_materials(node) surface = self.find_surface( node) # lookup Chroma surface corresponding to the node if surface == None: surfacename = "NOT SPECIFIED" else: surfacename = surface.name if self.dump_node_info: print "[NODE %05d:%s]" % ( node.index, node.lv.id ), " NTriangles=%d OuterMat=%s InnerMat=%s Surface=%s" % (len( mesh.triangles), material2.name, material1.name, surfacename) color = 0x33ffffff solid = Solid(mesh, material1, material2, surface, color) solid.node = node # # hmm a PMT is comprised of several volumes all of which # have the same associated channel_id # channel_id = getattr(node, 'channel_id', None) if not channel_id is None and channel_id > 0: self.channel_count += 1 # nodes with associated non zero channel_id self.channel_ids.add(channel_id) self.chroma_geometry.add_pmt(solid, channel_id=channel_id) else: self.chroma_geometry.add_solid(solid) pass if debug and self.vcount % 1000 == 0: print node.id print self.vcount, bpl, tris, tris.material print mesh #print mesh.assemble() bounds = mesh.get_bounds() extent = bounds[1] - bounds[0] print extent def dump_channel_info(self): log.info( "channel_count (nodes with channel_id > 0) : %s uniques %s " % (self.channel_count, len(set(self.channel_ids)))) log.debug("channel_ids %s " % repr(self.channel_ids)) def surface_props_table(self): """ :: plt.plot(*cc.surfacemap['NearOWSLiner'].reflect_diffuse.T) plt.plot(*cc.surfacemap['NearDeadLiner'].reflect_diffuse.T) plt.plot(*cc.surfacemap['NearIWSCurtain'].reflect_diffuse.T) ## all three the same, up to plateau plt.plot(*cc.surfacemap['RSOil'].reflect_diffuse.T) ## falloff plt.plot(*cc.surfacemap['ESRAirSurfaceBot'].reflect_specular.T) plt.plot(*cc.surfacemap['ESRAirSurfaceTop'].reflect_specular.T) ## Bot and Top the same, cliff """ def smry(spt, suppress="60.0:800.0 =0.0"): x, y = spt.T xsmr = "%3.1f:%3.1f" % (x.min(), x.max()) ymin, ymax = y.min(), y.max() ysmr = "=%3.1f" % ymin if ymin == ymax else "%3.1f:%3.1f" % (ymin, ymax) s = "%s %s" % (xsmr, ysmr) return "-" if s == suppress else s pass lfmt_ = lambda _: "%-23s" % _ bfmt_ = lambda _: " ".join(["%-25s" % s for s in _]) print lfmt_("surf") + bfmt_(self.surface_props) for nam, surf in self.surfacemap.items(): sprop = map(lambda prop: smry(getattr(surf, prop)), self.surface_props) print lfmt_(nam) + bfmt_(sprop)
photons = gpu_photons.get() photon_track[i, :, 0] = photons.pos[:, 0] photon_track[i, :, 1] = photons.pos[:, 1] photon_track[i, :, 2] = photons.pos[:, 2] return photons, photon_track if __name__ == '__main__': config = "cfJiani3_2" #config = "cfSam1_1" kabamland = Detector(lm.ls) lens, surf = build_kabamland(kabamland, config) kabamland.flatten() kabamland.bvh = load_bvh(kabamland) #view(kabamland) #quit() sim = Simulation(kabamland, geant4_processes=0) runs = 1 numPhotons = 6 events, pos = photon_angle(rep=numPhotons) nr_hits = np.zeros((runs)) doRandom = getRandom() for ii in range(runs): photons, photon_track = propagate_photon(events, numPhotons, 20, kabamland, doRandom[0], doRandom[1], doRandom[2]) detected = (photons.flags & (0x1 << 2)).astype(bool) nr_hits[ii] = len(photons.pos[detected])