Пример #1
0
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)
Пример #2
0
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
Пример #3
0
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)
Пример #4
0
        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])