Surface Debug : lacking hits due to surface/volume model mismatch ==================================================================== *FIXED* long ago Not getting any SURFACE_DETECT despite photons obviously traversing PMTs because the requisite boundaries have no associated surfaces. Surface flags:: SURFACE_DETECT SURFACE_ABSORB SURFACE_DREFLECT SURFACE_SREFLECT generate.cu:: 281 282 command = propagate_to_boundary( p, s, rng ); 283 if(command == BREAK) break ; // BULK_ABSORB 284 if(command == CONTINUE) continue ; // BULK_REEMIT/BULK_SCATTER 285 // PASS : survivors will go on to pick up one of the below flags, 286 287 288 if(s.optical.x > 0 ) // x/y/z/w:index/type/finish/value 289 { 290 command = propagate_at_surface(p, s, rng); 291 if(command == BREAK) break ; // SURFACE_DETECT/SURFACE_ABSORB 292 if(command == CONTINUE) continue ; // SURFACE_DREFLECT/SURFACE_SREFLECT 293 } 294 else 295 { 296 propagate_at_boundary(p, s, rng); // BOUNDARY_RELECT/BOUNDARY_TRANSMIT 297 // tacit CONTINUE 298 } state.h:: 19 __device__ void fill_state( State& s, int boundary, int sensor, float wavelength ) 20 { 21 // boundary : 1 based code, signed by cos_theta of photon direction to outward geometric normal 22 // >0 outward going photon 23 // <0 inward going photon 24 25 int line = boundary > 0 ? (boundary - 1)*6 : (-boundary - 1)*6 ; 26 27 // pick relevant lines depening on boundary sign, ie photon direction relative to normal 28 // 29 int m1_line = boundary > 0 ? line + 0 : line + 1 ; // inner-material / outer-material 30 int m2_line = boundary > 0 ? line + 1 : line + 0 ; // outer-material / inner-material 31 int su_line = boundary > 0 ? line + 2 : line + 3 ; // inner-surface / outer-surface 32 33 s.material1 = wavelength_lookup( wavelength, m1_line ); 34 s.material2 = wavelength_lookup( wavelength, m2_line ) ; 35 s.surface = wavelength_lookup( wavelength, su_line ); 36 37 s.optical = optical_buffer[su_line] ; // index/type/finish/value 38 39 s.index.x = optical_buffer[m1_line].x ; 40 s.index.y = optical_buffer[m2_line].x ; 41 s.index.z = optical_buffer[su_line].x ; 42 s.index.w = sensor ; 43 44 } Chroma Solution to same issue -------------------------------- Addressed this with G4DAEChroma by adding "fake" surfaces * env/geant4/geometry/surfaces/surfaces_roundtrip.rst * http://simoncblyth.bitbucket.org/env/notes/geant4/geometry/surfaces/surfaces_roundtrip/ :: delta:env blyth$ hg shortlog | grep sensitive ef4ab750f29e | 2014-10-10 20:15:22 +0800 | simoncblyth: debug the tranlation of sensitive materials into surfaces for G4 to Chroma model translation 4b938256ea50 | 2014-10-10 14:02:43 +0800 | simoncblyth: add extra SkinSurface and OpticalSurface objects to DAE level geometry in order to be transformed into sensitive surfaces needed for chroma SURFACE_DETECT d41b1d971f68 | 2014-10-09 21:02:37 +0800 | simoncblyth: working out how to reconcile the G4 and Chroma models regards sensitive detectors, in order to get Chroma to come up with photon hit data b192e176d992 | 2009-06-23 18:31:51 +0800 | simoncblyth: tg-quickstart 1st checkin of OfflineDB project ... untouched other than exclusions of sensitive {{{.ini}}} files 3b26e5c356f4 | 2008-08-21 13:00:42 +0800 | simoncblyth: improved access control to sensitive variables 101ef1dc0491 | 2007-12-24 08:16:45 +0800 | thho: sensitive skin opacity setting b04e2e8719ab | 2007-07-27 12:00:47 +0800 | simoncblyth: sensitive skin testing delta:env blyth$ * https://bitbucket.org/simoncblyth/env/commits/4b938256ea50 * g4daenode.py:add_sensitive_surfaces * https://bitbucket.org/simoncblyth/env/src/tip/geant4/geometry/collada/g4daenode.py * env/geant4/geometry/collada/g4daenode.py :: 395 @classmethod 396 def add_sensitive_surfaces(cls, matid='__dd__Materials__Bialkali', qeprop='EFFICIENCY'): 397 """ 398 Chroma expects sensitive detectors to have an Optical Surface 399 with channel_id associated. 400 Whereas Geant4 just has sensitive LV. 401 402 This attempts to bridge from Geant4 to Chroma model 403 by creation of "fake" chroma skinsurfaces. 404 405 Effectively sensitive materials are translated 406 into sensitive surfaces 407 408 :: 409 410 In [57]: DAENode.orig.materials['__dd__Materials__Bialkali0xc2f2428'].extra 411 Out[57]: 412 413 414 #. Different efficiency for different cathodes ? 415 416 """ 417 log.info("add_sensitive_surfaces matid %s qeprop %s " % (matid, qeprop)) 418 sensitive_material = cls.materialsearch(matid) 419 assert sensitive_material 420 421 if sensitive_material.extra is None: 422 log.warn("sensitive_material.extra not available cannot sensitize ") 423 return 424 425 efficiency = sensitive_material.extra.properties[qeprop] 426 assert not efficiency is None 427 428 cls.sensitize(matid=matid) 429 430 # follow convention used in G4DAE exports of using same names for 431 # the SkinSurface and the OpticalSurface it refers too 432 433 for node in cls.sensitive_nodes: 434 ssid = cls.sensitive_surface_id(node) 435 volumeref = node.lv.id 436 437 surf = OpticalSurface.sensitive(name=ssid, properties={qeprop:efficiency}) 438 cls.add_extra_opticalsurface(surf) 439 440 skin = SkinSurface.sensitive(name=ssid, surfaceproperty=surf, volumeref=volumeref ) 441 cls.add_extra_skinsurface(skin) 442 pass How to do this with GGeo ? --------------------------- Which level to add the fake cathode surfaces at ? * AssimpGGeo::convertMaterials, creates and adds to GGeo instances of GOpticalSurface, GSkinSurface, GBorderSurface, GMaterial based on the properties that the assimp "materials" have * AssimpGGeo::convertStructureVisit pulls GBoundary into existance based on boundary identity combining imat/omat/isur/osur :: 603 GSolid* solid = new GSolid(nodeIndex, gtransform, mesh, NULL, NULL ); // boundary and sensor start NULL 604 solid->setLevelTransform(ltransform); 605 606 const char* lv = node->getName(0); 607 const char* pv = node->getName(1); 608 const char* pv_p = pnode->getName(1); 609 610 gg->countMeshUsage(msi, nodeIndex, lv, pv); 611 612 GBorderSurface* obs = gg->findBorderSurface(pv_p, pv); // outer surface (parent->self) 613 GBorderSurface* ibs = gg->findBorderSurface(pv, pv_p); // inner surface (self->parent) 614 GSkinSurface* sks = gg->findSkinSurface(lv); 615 Avoiding interference with this structure means would need to add the surfaces prior to AssimpGGeo::convertStructure Approach using AssimpGGeo::convertSensors -------------------------------------------- 2 sensor skin surfaces are added:: lvPmtHemiCathodeSensorSurface lvHeadonPmtCathodeSensorSurface But only one shows up in boundarylib (may be due to identity digest not including the name):: ggv --blib boundary : index 21 x6 126 e554f1b518cd18fae063073e9147b70d Bialkali/Vacuum/-/lvPmtHemiCathodeSensorSurface Running does not yet yield any SURFACE_DETECT, but getting lots of SURFACE_SREFLECT:: 288 if(s.optical.x > 0 ) // x/y/z/w:index/type/finish/value 289 { 290 command = propagate_at_surface(p, s, rng); 291 if(command == BREAK) break ; // SURFACE_DETECT/SURFACE_ABSORB 292 if(command == CONTINUE) continue ; // SURFACE_DREFLECT/SURFACE_SREFLECT 293 } 402 __device__ int 403 propagate_at_surface(Photon &p, State &s, curandState &rng) 404 { 405 406 float u = curand_uniform(&rng); 407 408 if( u < s.surface.y ) // absorb 409 { 410 s.flag = SURFACE_ABSORB ; 411 return BREAK ; 412 } 413 else if ( u < s.surface.y + s.surface.x ) // absorb + detect 414 { 415 s.flag = SURFACE_DETECT ; 416 return BREAK ; 417 } 418 else if (u < s.surface.y + s.surface.x + s.surface.w ) // absorb + detect + reflect_diffuse 419 { 420 s.flag = SURFACE_DREFLECT ; 421 propagate_at_diffuse_reflector(p, s, rng); 422 return CONTINUE; 423 } 424 else 425 { 426 s.flag = SURFACE_SREFLECT ; 427 propagate_at_specular_reflector(p, s, rng ); 428 return CONTINUE; 429 } 430 } Hmm setting efficiency to 1.0 still getting nothing other than SURFACE_SREFLECT :: delta:env blyth$ ggv --blib 126 127 128 129 130 131 [2015-10-06 13:22:01.832171] [0x000007fff7448031] [warning] GBoundaryLib::setWavelengthBuffer didnt see 54, numBoundary: 57 boundary : index 0 x6 0 019d50af046b6733287e43af2e8f7fa2 Vacuum/Vacuum/-/- ... boundary : index 21 x6 126 31ec4ad900fe9b40be261fa11af380b7 Bialkali/Vacuum/-/lvPmtHemiCathodeSensorSurface GBoundaryLib.dumpWavelengthBuffer 126 GBoundaryLib::dumpWavelengthBuffer wline 126 numSub 57 domainLength 39 numQuad 6 126 | 21/ 0 __dd__Materials__Bialkali0xc2f2428 1.458 1.458 1.458 1.458 1.458 1.458 1.458 1.458 1000.000 1000.000 1000.000 1077.339 1736.682 1393.428 821.650 529.476 1000000.000 1000000.000 1000000.000 1000000.000 1000000.000 1000000.000 1000000.000 1000000.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 GBoundaryLib.dumpWavelengthBuffer 127 GBoundaryLib::dumpWavelengthBuffer wline 127 numSub 57 domainLength 39 numQuad 6 127 | 21/ 1 __dd__Materials__Vacuum0xbf9fcc0 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 10000000.000 10000000.000 10000000.000 10000000.000 10000000.000 10000000.000 10000000.000 10000000.000 1000000.000 1000000.000 1000000.000 1000000.000 1000000.000 1000000.000 1000000.000 1000000.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 GBoundaryLib.dumpWavelengthBuffer 128 GBoundaryLib::dumpWavelengthBuffer wline 128 numSub 57 domainLength 39 numQuad 6 128 | 21/ 2 - -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 GBoundaryLib.dumpWavelengthBuffer 129 GBoundaryLib::dumpWavelengthBuffer wline 129 numSub 57 domainLength 39 numQuad 6 129 | 21/ 3 __dd__Geometry__PMT__lvPmtHemiCathodeSensorSurface 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 GBoundaryLib.dumpWavelengthBuffer 130 GBoundaryLib::dumpWavelengthBuffer wline 130 numSub 57 domainLength 39 numQuad 6 130 | 21/ 4 - -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 GBoundaryLib.dumpWavelengthBuffer 131 GBoundaryLib::dumpWavelengthBuffer wline 131 numSub 57 domainLength 39 numQuad 6 131 | 21/ 5 - -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 delta:env blyth$ Hmm is inner/outer surface swapped somewhere ? Dont think so. Possibly a problem with PMT normals ? --------------------------------------- Suspect issue with PMT front face normals. The Q normal view shows no normals coming out the front of PMTs :: 43 (v 482 f 960 ) (t 1 oe 0) : x 98.143 : n 672 : n*v 323904 : pmt-hemi-cathode : 3201,3207,3213,3219,3225, 44 (v 242 f 480 ) (t 1 oe 0) : x 98.143 : n 672 : n*v 162624 : pmt-hemi-bot : 3202,3208,3214,3220,3226, 45 (v 50 f 96 ) (t 1 oe 0) : x 83.000 : n 672 : n*v 33600 : pmt-hemi-dynode : 3203,3209,3215,3221,3227, 46 (v 338 f 672 ) (t 1 oe 0) : x 146.252 : n 672 : n*v 227136 : pmt-hemi-vac : 3200,3206,3212,3218,3224, 47 (v 362 f 720 ) (t 1 oe 0) : x 149.997 : n 672 : n*v 243264 : pmt-hemi : 3199,3205,3211,3217,3223, Wow 960 faces for the cathode ? Add *mdyb* for checking pmt-hemi-cathode geometry, its a flikering mess and a cats cradle of normals:: ggv --mdyb -G --noinstanced ggv --mdyb -O udp.py --target 3201 ggv --mdyb --torchconfig="pos_target=3201;pos_offset=500,0,0" ggv --mdyb --torchconfig "pos_target=3201;pos_offset=800,0,0;radius=100" # hmm dont see photons that miss ggv --mdyb --torchconfig "pos_target=3201;pos_offset=0,1000,0;radius=100;direction=0,-1,0" --geocenter # targetting the beam is not easier as can only see the photons when they hit ggv --mdyb --torchconfig "pos_target=3154;radius=3000;direction=0,0,-1" # added SST but do not see records that just get absorbed either # that means are propagating in a lump of Steel ggv --mdyb --torchconfig "pos_target=3154;radius=3000;direction=0,0,-1" --save # export GGEOVIEW_QUERY="range:3201:3202,range:3153:3154" # 2 volumes : first pmt-hemi-cathode and ADE # change envelope volume to ADE much better, as photons get somewhere in IwsWater/IwsWater ggv --mdyb --torchconfig "frame=3201;source=0,0,1000;target=0,0,0;radius=300;" --save # head on beam strarting 1m out in front of PMT cathode # # using reworked the Torch configuration to be frame based with source and target positions # specified in the identified frame # # note effect of material inconsistency, photons destined to hit the cathode # think they are in a vacuum, hence they lead ahead of those destined to hit ADE envelope Hmm would be easiest to target the PMT using its own frame, hmm view targetting did something similar ? Detdesc dive -------------- Looks like need to replace the cathode with something simpler ? G5:/home/blyth/local/env/dyb/NuWa-trunk/dybgaudi/Detector/XmlDetDesc/DDDB/PMT/hemi-pmt.xml:: 118 119 120 121 122 /// /// PmtHemiFaceROC-PmtHemiGlassThickness : 131. - 3. = 128. /// 128. - 0.05 = 127.95 /// 126 /// /// /// /// 131 /// /// 56. - 17. = 39. /// 132 133 G5:/home/blyth/local/env/dyb/NuWa-trunk/dybgaudi/Detector/XmlDetDesc/DDDB/PMT/hemi-parameters.xml:: 010 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 44 45 46 47 48 .. 68 69 70 // 131. - 3. = 128. // 72 73 // 102.-3. = 99. 74 75 76 85 86 // (128.*128.- 99.*99. - (56.-13.)*(56.-13.))/(2.*(56.-13.)) // // In [1]: (128.*128.- 99.*99. - (56.-13.)*(56.-13.))/(2.*(56.-13.)) // Out[1]: 55.04651162790697 // // // 87 88 91 // // math.acos((55.0465+(56.-13.))/128.) // // In [8]: 0.5+math.acos((55.0465+(56.-13.))/128.)*180./math.pi // Out[8]: 40.50500580674586 // 92 93 96 97 98 101 104 105 106 107 108 Sphere Sphere Intersection ---------------------------- * http://mathworld.wolfram.com/Sphere-SphereIntersection.html How to try some simple replacement cathode ? ----------------------------------------------- * adding analytic spheres to OptiX at the positions corresponding to front face of cathode : would allow a simple geometry check :: OGeo::makeGeometryInstance(GMergedMesh* mergedmesh) Five volumes within repeated PMT instance:: [2015-Oct-07 12:26:54.103163]:info: GGeo::dumpNodeInfo mmindex 1 solids 5 720 362 3199 3155 lv __dd__Geometry__PMT__lvPmtHemi0xc133740 pv __dd__Geometry__AD__lvOIL--pvAdPmtArray--pvAdPmtA.......--pvAdPmtUnit--pvAdPmt0xc2a6b40 672 338 3200 3199 lv __dd__Geometry__PMT__lvPmtHemiVacuum0xc2c7cc8 pv __dd__Geometry__PMT__lvPmtHemi--pvPmtHemiVacuum0xc1340e8 960 482 3201 3200 lv __dd__Geometry__PMT__lvPmtHemiCathode0xc2cdca0 pv __dd__Geometry__PMT__lvPmtHemiVacuum--pvPmtHemiCathode0xc02c380 480 242 3202 3200 lv __dd__Geometry__PMT__lvPmtHemiBottom0xc12ad60 pv __dd__Geometry__PMT__lvPmtHemiVacuum--pvPmtHemiBottom0xc21de78 96 50 3203 3200 lv __dd__Geometry__PMT__lvPmtHemiDynode0xc02b280 pv __dd__Geometry__PMT__lvPmtHemiVacuum--pvPmtHemiDynode0xc04ad28 Note identity relative transform for 1st three:: In [5]: n = np.load("nodeinfo.npy") In [6]: n Out[6]: array([[ 720, 362, 3199, 3155], [ 672, 338, 3200, 3199], [ 960, 482, 3201, 3200], [ 480, 242, 3202, 3200], [ 96, 50, 3203, 3200]], dtype=uint32) In [1]: t = np.load("transforms.npy") In [4]: t.reshape(-1,4,4) Out[4]: array([[[ 1. , 0. , 0. , 0. ], [ 0. , 1. , 0. , 0. ], [ 0. , 0. , 1. , 0. ], [ 0. , 0. , 0. , 1. ]], [[ 1. , 0. , 0. , 0. ], [ 0. , 1. , 0. , 0. ], [ 0. , 0. , 1. , 0. ], [ 0. , 0. , 0. , 1. ]], [[ 1. , 0. , 0. , 0. ], [ 0. , 1. , 0. , 0. ], [ 0. , 0. , 1. , 0. ], [ 0. , 0. , 0. , 1. ]], [[ 1. , 0. , 0. , 0. ], [ 0. , 1. , 0. , 0. ], [ 0. , 0. , 1. , 0. ], [ 0. , 0. , 69. , 1. ]], [[ 1. , 0. , 0. , 0. ], [ 0. , 1. , 0. , 0. ], [ 0. , 0. , 1. , 0. ], [ 0. , 0. , -81.5, 1. ]]], dtype=float32) Analytic OptiX geometry ------------------------ Per triangle buffers with boundaries, nodes and sensors are used by TriangleMesh to set attributes based on primIdx:: In [1]: b = np.load("boundaries.npy") In [2]: b Out[2]: array([[11], [11], [11], ..., [12], [12], [12]], dtype=int32) In [3]: b.shape Out[3]: (434816, 1) In [4]: n = np.load("nodes.npy") In [5]: n.shape Out[5]: (434816, 1) In [6]: n Out[6]: array([[ 3153], [ 3153], [ 3153], ..., [12220], [12220], [12220]], dtype=int32) In [7]: s = np.load("sensors.npy") In [8]: s.shape Out[8]: (434816, 1) In [9]: s Out[9]: array([[3154], [3154], [3154], ..., Whats the equivalent for instanced analytic geometry ? In the full analytic treatment might have 10-20 primitives per instance arranged into a CSG tree of boolean operations and transforms. Although there are only 5 volumes there are multiple primitives (spheres, cones, boxes) inside each. On top of identifying the primitive also have the instance index. So need an analytic index:: instance_index*numPrim + prim_index Triangulated Case ------------------- OGeo.cc:: 283 optix::Geometry geometry = m_context->createGeometry(); 284 RayTraceConfig* cfg = RayTraceConfig::getInstance(); 285 geometry->setIntersectionProgram(cfg->createProgram("TriangleMesh.cu.ptx", "mesh_intersect")); 286 geometry->setBoundingBoxProgram(cfg->createProgram("TriangleMesh.cu.ptx", "mesh_bounds")); ... 296 geometry->setPrimitiveCount(numFaces); Gross structure of geometry communicated to OptiX by returning bounding boxes from the *BoundingBoxProgram* for each primIdx. The range of primIdx is specified by *setPrimitiveCount* When a ray intersects with a bbox the associated *primIdx* is used to invoke the *IntersectionProgram* which reports the parametric t with *rtPotentialIntersection(t)* cu/TriangleMesh.cu:: 96 RT_PROGRAM void mesh_bounds (int primIdx, float result[6]) 34 RT_PROGRAM void mesh_intersect(int primIdx) With instanced geometry:: 166 optix::Group OGeo::makeRepeatedGroup(GMergedMesh* mm, unsigned int limit) 167 { 168 GBuffer* tbuf = mm->getITransformsBuffer(); 169 unsigned int numTransforms = limit > 0 ? std::min(tbuf->getNumItems(), limit) : tbuf->getNumItems() ; 170 assert(tbuf && numTransforms > 0); 171 172 LOG(info) << "OGeo::makeRepeatedGroup numTransforms " << numTransforms ; 173 174 float* tptr = (float*)tbuf->getPointer(); 175 176 optix::Group group = m_context->createGroup(); 177 group->setChildCount(numTransforms); 178 179 optix::GeometryInstance gi = makeGeometryInstance(mm); 180 optix::GeometryGroup repeated = m_context->createGeometryGroup(); 181 repeated->addChild(gi); 182 repeated->setAcceleration( makeAcceleration() ); /// /// can an id be planted in GeometryGroup ? /// seems not but can with GeometryInstance according to docs, /// so need to adjust to having a GeometryInstance for every xform /// to plant an instance index /// 183 184 bool transpose = true ; 185 for(unsigned int i=0 ; icreateTransform(); 188 group->setChild(i, xform); 189 xform->setChild(repeated); 190 const float* tdata = tptr + 16*i ; 191 optix::Matrix4x4 m(tdata) ; 192 xform->setMatrix(transpose, m.getData(), 0); 193 //dump("OGeo::makeRepeatedGroup", m.getData()); 194 } 195 return group ; 196 } Hmm how with instanced geometry to know which instance was landed on, because all the geometry info lives within the instance island ? * https://devtalk.nvidia.com/default/topic/541450/?comment=3791463 :: Is there an easy way to know withing the closest hit or any hit program which object was hit? I'd prefer to use a single material for all but can encode the information into the material. I do however wish to use the same hit program to be flexible with the number of objects. (Detlef Roettger) This should be pretty easy, if there aren't any other circumstances involved. - If you have exactly one Transform node per object, let your Material have a variable rtDeclareVariable(unsigned int, objectID, , ); - In your given node hierarchy you load the Geometry only once, but assign different Material parameters (=> objectID) per hierarchy path to each GeometryInstance (i.e. per Transform) to identify the Geometry as you wish. - To report back to the ray generation program that you hit a specific object, add a member unsigned int objectID; to your custom PerRayData payload. - Inside the closest-hit program write the objectID from the material into the objectID of the current ray payload. - Inside the ray generation program add a switch-case which writes the other PerRayData results you generated into the output buffer you select with the PerRayData objectID. :: In [1]: i = np.load("identity.npy") In [2]: i Out[2]: array([[3199, 47, 19, 1], [3200, 46, 20, 2], [3201, 43, 21, 3], [3202, 44, 1, 4], [3203, 45, 1, 5]], dtype=uint32) # hmm those boundary 1 look wrong ? Rock inside the PMT ? off-by-one problem ? In [1]: n = np.load("nodeinfo.npy") In [2]: n Out[2]: array([[ 720, 362, 3199, 3155], [ 672, 338, 3200, 3199], [ 960, 482, 3201, 3200], [ 480, 242, 3202, 3200], [ 96, 50, 3203, 3200]], dtype=uint32) boundary : index 0 x6 0 019d50af046b6733287e43af2e8f7fa2 Vacuum/Vacuum/-/- boundary : index 1 x6 6 1c71e6371ce86dec9ed4f0e2395f1933 Rock/Vacuum/-/- boundary : index 18 x6 108 77a84102a9c397d91eed17b2bc0988ce GdDopedLS/LiquidScintillator/-/- boundary : index 19 x6 114 b609b4350dfeebd16df30bc7c0132459 Pyrex/MineralOil/-/- boundary : index 20 x6 120 05fb49644888940aab8eb466d69d3693 Vacuum/Pyrex/-/- boundary : index 21 x6 126 31ec4ad900fe9b40be261fa11af380b7 Bialkali/Vacuum/-/lvPmtHemiCathodeSensorSurface boundary : index 22 x6 132 df883e53b3d96edda237a45ba82a0e94 UnstStainlessSteel/MineralOil/-/- Does OpaqueVacuum have same properties as Rock ?:: [2015-Oct-07 16:35:33.313017]:info: AssimpGGeo::convertStructureVisit nodeIndex 3199 ( mti 68 mt 0x7fd03def9750 ) Pyrex ( mti_p 59 mt_p 0x7fd040460690 ) MineralOil ( msi 47 mesh 0x7fd03ded9840 ) pmt-hemi0xc0fed90 [2015-Oct-07 16:35:33.313442]:info: AssimpGGeo::convertStructureVisit nodeIndex 3200 ( mti 76 mt 0x7fd040453ff0 ) Vacuum ( mti_p 68 mt_p 0x7fd03def9750 ) Pyrex ( msi 46 mesh 0x7fd04044cbc0 ) pmt-hemi-vac0xc21e248 [2015-Oct-07 16:35:33.314093]:info: AssimpGGeo::convertStructureVisit nodeIndex 3201 ( mti 48 mt 0x7fd03874b5e0 ) Bialkali ( mti_p 76 mt_p 0x7fd040453ff0 ) Vacuum ( msi 43 mesh 0x7fd03dee03c0 ) pmt-hemi-cathode0xc2f1ce8 [2015-Oct-07 16:35:33.314487]:info: AssimpGGeo::convertStructureVisit nodeIndex 3202 ( mti 64 mt 0x7fd03dec5580 ) OpaqueVacuum ( mti_p 76 mt_p 0x7fd040453ff0 ) Vacuum ( msi 44 mesh 0x7fd0403e6940 ) pmt-hemi-bot0xc22a958 [2015-Oct-07 16:35:33.314863]:info: AssimpGGeo::convertStructureVisit nodeIndex 3203 ( mti 64 mt 0x7fd03dec5580 ) OpaqueVacuum ( mti_p 76 mt_p 0x7fd040453ff0 ) Vacuum ( msi 45 mesh 0x7fd03def9290 ) pmt-hemi-dynode0xc346c50 Now with simplified OptiX geometry ------------------------------------- :: ggv --mdyb -G ggv --mdyb --torchconfig "frame=3201;source=0,0,1000;target=0,0,0;radius=300;" --save --simplify ggv --torchconfig "frame=3201;source=0,0,1000;target=0,0,0;radius=300;" --save