File indexing completed on 2026-04-10 07:49:16
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021 """
0022 Genstep Sequence Material Mismatch
0023 ===================================
0024
0025 Comparing material codes of the first intersection with the
0026 material code of the genstep observe many mismatches.
0027
0028 Current understanding of problem
0029 ------------------------------------
0030
0031 ::
0032
0033 GdLS |Ac| LS |Ac| MO
0034 | | | |
0035 | | | |
0036 *---------x | |
0037 | | | |
0038 | | | |
0039 | | | |
0040 *------x | | |
0041 | | | |
0042
0043 Many (~14%) photons with gensteps in Gd have a 1st boundary intersection
0044 with Ac/LS when expected to intersect with Gd/Ac
0045
0046 Questions
0047 -----------
0048
0049 * geometric epsilon
0050 * thin acrylic problem ?
0051
0052 * make simplified geometry: concentric spheres (or boxes) with varying Ac thickness
0053
0054 Analytic Geometry
0055 -----------------
0056
0057 * including GDML elements within in G4DAE exported COLLADA extra elements
0058 would provide the analytic geometry description needed to swap out
0059 triangulated for analytic geometry in some cases
0060
0061 * OpenGL needs triangulated, the analytic can be applied to OptiX geometry
0062
0063
0064
0065 Confirmed Geometric Problem : inner AV top lid is not intersecting
0066 --------------------------------------------------------------------
0067
0068 Using a "torch" at AD center all photons with less than 0.31pi (AV top lid)
0069 to the zenith are not intersecting with Gd/Ac but rather Ac/LS.
0070 The problem area coincides with OpenGL renderering z-fighting flickering.
0071
0072 To chop out or focus on the problem area::
0073
0074 ggv --torchconfig "zenith_azimuth:0.31,1.0,0,1"
0075 ggv --torchconfig "zenith_azimuth:0,0.31,0,1"
0076
0077 Focus on just the problematic 1st step::
0078
0079 ggv --torchconfig "zenith_azimuth:0,0.31,0,1" --bouncemax 1
0080
0081 TODO: orthographic projection would be useful for eyeballing geometry in cross section
0082
0083
0084 Material Codes
0085 -----------------
0086
0087 *gs.MaterialIndex*
0088 from Geant4, converted into wavelength texture line number in G4StepNPY
0089
0090 *m1/m2/su*
0091 OptiX intersects with a triangle, reading the assigned boundary code, sign
0092 from cosTheta of angle between photon and triangle normal
0093 pulled from optical buffer using the absolute boundary code and its sign
0094 to map inner/outer to m1/m2
0095
0096
0097 OptiX epsilon
0098 --------------
0099
0100 ::
0101
0102 324 void OEngine::preprocess()
0103 325 {
0104 326 LOG(info)<< "OEngine::preprocess";
0105 327
0106 328 m_context[ "scene_epsilon"]->setFloat(m_composition->getNear());
0107 329
0108 330 float pe = 0.1f ;
0109 331 m_context[ "propagate_epsilon"]->setFloat(pe); // TODO: check impact of changing propagate_epsilon
0110 332
0111
0112 cu/generate.cu::
0113
0114 171 rtTrace(top_object, optix::make_Ray(p.position, p.direction, propagate_ray_type, propagate_epsilon, RT_DEFAULT_MAX), prd );
0115
0116
0117
0118 Extent of Problem : 9 percent
0119 -------------------------------
0120
0121 Test using Aux buffer containing in 1st slot of each set of 10
0122
0123 * m1
0124 * m2
0125 * "m1" translated from genstep material line
0126 * signed boundary code
0127
0128 ::
0129
0130 In [1]: a = auc_(1).reshape(-1,10,4)
0131 INFO:opticks.ana.types:loading /usr/local/env/dayabay/aucerenkov/1.npy
0132 -rw-r--r-- 1 blyth staff 49027360 Sep 19 13:01 /usr/local/env/dayabay/aucerenkov/1.npy
0133
0134 In [39]: aa = a[:500000] ## exclude tail where there are geometry misses (still using sub-geometry), so no boundary-m1 to compare with
0135
0136 # TODO: move to fuller geometry include radslabs around pool to avoid misses, can still skip RPC
0137
0138 In [40]: aa
0139 Out[40]:
0140 array([[[ 6, 6, 6, -12], ## 1st and 3rd indices here should match boundary-m1 and genstep-m1
0141 [ 6, 12, 4, -13],
0142 [ 12, 4, 0, -14],
0143 ...,
0144 [ 0, 0, 0, 0],
0145 [ 0, 0, 0, 0],
0146 [ 0, 0, 0, 0]],
0147
0148
0149 In [47]: ix = np.arange(len(aa))[aa[:,0,0] != aa[:,0,2]]
0150
0151 In [48]: ix
0152 Out[48]: array([ 3006, 8521, 8524, ..., 497348, 497349, 497350])
0153
0154 In [49]: len(ix)
0155 Out[49]: 44479
0156
0157 In [50]: len(aa)
0158 Out[50]: 500000
0159
0160 In [54]: float(len(ix))/float(len(aa)) ## 9 percent level mismatch
0161 Out[54]: 0.088958
0162
0163
0164 How often can match be made using material from other side of boundary ? Only 1 percent
0165 ----------------------------------------------------------------------------------------
0166
0167 Suggests not a geometric normal (vertex winding order) problem.
0168
0169 ::
0170
0171 In [58]: aa[:,0][ix]
0172 Out[58]:
0173 array([[ 4, 14, 6, -20],
0174 [ 4, 11, 6, -23],
0175 [ 4, 11, 6, -23],
0176 ...,
0177 [ 4, 3, 3, -25],
0178 [ 4, 14, 3, -20],
0179 [ 4, 14, 3, -20]], dtype=int16)
0180
0181 ::
0182
0183 In [64]: aaa = aa[:,0][ix]
0184
0185 In [66]: farside = np.arange(len(aaa))[aaa[:,1] == aaa[:,2]]
0186
0187 In [70]: float(len(farside))/len(aaa)
0188 Out[70]: 0.011398637559297646
0189
0190
0191
0192 Is there preponderance of particular genstep materials with mismatch ? yes: Gd dominates
0193 -------------------------------------------------------------------------------------------
0194
0195 ::
0196
0197 In [81]: count_unique(aaa[:,2])
0198 Out[81]:
0199 array([[ 1, 43666],
0200 [ 2, 419],
0201 [ 3, 390],
0202 [ 4, 1],
0203 [ 6, 3]])
0204
0205 In [82]: im_ = lambda _:im.get(_,'?')
0206
0207 In [83]: map(im_,[1,2,3,4,6])
0208 Out[83]: ['GdDopedLS', 'LiquidScintillator', 'Acrylic', 'MineralOil', 'IwsWater']
0209
0210
0211 How about the boundary ? predominantly imat:Acrylic omat:LiquidScintillator
0212 -------------------------------------------------------------------------------
0213
0214 TODO: visualize where the problem is, suspect AD lids
0215
0216 ::
0217
0218 In [85]: count_unique(aaa[:,3])
0219 Out[85]:
0220 array([[ -25, 125],
0221 [ -23, 5],
0222 [ -21, 1],
0223 [ -20, 12],
0224 [ -18, 123],
0225 [ -17, 137],
0226 [ -16, 8],
0227 [ -15, 11],
0228 [ 15, 411],
0229 [ 16, 103],
0230 [ 17, 43543]]) imat:Acrylic omat:LiquidScintillator
0231
0232 ::
0233
0234 delta:ggeo blyth$ ./GBoundaryLibMetadata.py
0235 INFO:__main__:['./GBoundaryLibMetadata.py']
0236 0 : 1 : Vacuum Vacuum - -
0237 1 : 2 : Rock Vacuum - -
0238 2 : 3 : Air Rock - -
0239 3 : 4 : PPE Air - __dd__Geometry__PoolDetails__NearPoolSurfaces__NearPoolCoverSurface
0240 4 : 5 : Aluminium Air - -
0241 5 : 6 : Foam Aluminium - -
0242 6 : 7 : Air Air - -
0243 7 : 8 : DeadWater Rock - -
0244 8 : 9 : Tyvek DeadWater - __dd__Geometry__PoolDetails__NearPoolSurfaces__NearDeadLinerSurface
0245 9 : 10 : OwsWater Tyvek __dd__Geometry__PoolDetails__NearPoolSurfaces__NearOWSLinerSurface -
0246 10 : 11 : Tyvek OwsWater - -
0247 11 : 12 : IwsWater IwsWater - -
0248 12 : 13 : StainlessSteel IwsWater - __dd__Geometry__AdDetails__AdSurfacesNear__SSTWaterSurfaceNear1
0249 13 : 14 : MineralOil StainlessSteel __dd__Geometry__AdDetails__AdSurfacesAll__SSTOilSurface -
0250 14 : 15 : Acrylic MineralOil - -
0251 15 : 16 : LiquidScintillator Acrylic - -
0252 16 :*17*: Acrylic LiquidScintillator - -
0253 17 : 18 : GdDopedLS Acrylic - -
0254 18 : 19 : GdDopedLS LiquidScintillator - -
0255 19 : 20 : Pyrex MineralOil
0256
0257
0258
0259 What boundaries do GdLs gensteps have ?
0260 ------------------------------------------
0261
0262 ::
0263
0264 In [96]: gd = np.arange(len(aa))[aa[:,0,2] == 1]
0265
0266 In [97]: gd
0267 Out[97]: array([ 90402, 90403, 90404, ..., 453773, 453774, 453775])
0268
0269 In [98]: aa[gd]
0270 Out[98]:
0271 array([[[ 1, 3, 1, 18],
0272 [ 1, 3, 1, 18],
0273 [ 0, 0, 0, 0],
0274 ...,
0275 [ 0, 0, 0, 0],
0276 [ 0, 0, 0, 0],
0277 [ 0, 0, 0, 0]],
0278
0279 In [99]: aa[gd][:,0,3]
0280 Out[99]: array([18, 18, 18, ..., 17, 17, 17], dtype=int16)
0281
0282 In [100]: count_unique(aa[gd][:,0,3])
0283 Out[100]:
0284 array([[ -18, 123],
0285 [ 17, 43543],
0286 [ 18, 308763]])
0287
0288 ::
0289
0290 In [103]: 43543./308763.
0291 Out[103]: 0.14102402166062644
0292
0293 ::
0294
0295 +1
0296
0297 16 :*17*: Acrylic LiquidScintillator - -
0298 17 :*18*: GdDopedLS Acrylic - -
0299
0300
0301
0302 What 1st boundaries do LS gensteps have ? Only 0.4% mismatch
0303 ---------------------------------------------------------------
0304
0305 Hmm low level mismatch ? Something special regards inner AV.
0306
0307 ::
0308
0309 In [105]: ls = np.arange(len(aa))[aa[:,0,2] == 2]
0310
0311 In [125]: ac = np.arange(len(aa))[aa[:,0,2] == 3]
0312
0313 In [106]: count_unique(aa[ls][:,0,3])
0314 Out[106]:
0315 array([[ -17, 51348], # Ac/LS but negated so from Ls
0316 [ -16, 8], # LS/Ac but negated so from Ac?
0317 [ 15, 411], # Ac/MO
0318 [ 16, 43249]]) # LS/Ac
0319
0320
0321 ::
0322
0323 GdLS |Ac| LS |Ac| MO
0324 | | | |
0325 | | -17 | |
0326 | x------* | |
0327 | | | |
0328 | | | |
0329 | | +16 | |
0330 | | *------x |
0331 | | | |
0332
0333
0334 ::
0335
0336 (+1)
0337 14 : 15 : Acrylic MineralOil - -
0338 15 : 16 : LiquidScintillator Acrylic - -
0339 16 :*17*: Acrylic LiquidScintillator - -
0340 17 : 18 : GdDopedLS Acrylic - -
0341
0342 ::
0343
0344 In [116]: bb = aa[ls]
0345
0346 In [119]: obb = np.arange(len(bb))[bb[:,0,0] != bb[:,0,2]]
0347
0348 In [121]: len(obb)
0349 Out[121]: 419
0350
0351 ::
0352
0353 In [123]: float(len(obb))/len(bb)
0354 Out[123]: 0.004409783615391092
0355
0356
0357
0358 First boundaries of Ac gensteps
0359 --------------------------------
0360
0361 ::
0362
0363 In [126]: acb = count_unique(aa[ac][:,0,3])
0364 Out[126]:
0365 array([[ -25, 125],
0366 [ -23, 3],
0367 [ -20, 11],
0368 [ -18, 1977],
0369 [ -17, 137],
0370 [ -16, 1699],
0371 [ -15, 11],
0372 [ 15, 1565],
0373 [ 16, 103],
0374 [ 17, 791]])
0375
0376 In [173]: bnd = bnd_()
0377
0378 In [168]: bd_ = lambda _:bnd[_]
0379
0380 In [174]: map(bd_, acb[:,0] )
0381 Out[174]:
0382 [u'(-25) Acrylic/MineralOil/-/RSOilSurface ',
0383 u'(-23) UnstStainlessSteel/MineralOil/-/- ',
0384 u'(-20) Pyrex/MineralOil/-/- ',
0385 u'(-18) GdDopedLS/Acrylic/-/- ',
0386 u'(-17) Acrylic/LiquidScintillator/-/- ',
0387 u'(-16) LiquidScintillator/Acrylic/-/- ',
0388 u'(-15) Acrylic/MineralOil/-/- ',
0389 u'(+15) Acrylic/MineralOil/-/- ',
0390 u'(+16) LiquidScintillator/Acrylic/-/- ',
0391 u'(+17) Acrylic/LiquidScintillator/-/- ']
0392
0393
0394 Observations from Aux buffer : boundary 0 (MISS)
0395 -------------------------------------------------
0396
0397 ::
0398
0399 In [1]: a = auc_(1).reshape(-1,10,4)
0400 INFO:opticks.ana.types:loading /usr/local/env/dayabay/aucerenkov/1.npy
0401 -rw-r--r-- 1 blyth staff 49027360 Sep 19 11:59 /usr/local/env/dayabay/aucerenkov/1.npy
0402
0403 In [2]: a
0404 Out[2]:
0405 array([[[ 6, 6, 0, -12],
0406 [ 6, 12, 4, -13],
0407 [ 12, 4, 0, -14],
0408 ...,
0409 [ 0, 0, 0, 0],
0410 [ 0, 0, 0, 0],
0411 [ 0, 0, 0, 0]],
0412
0413
0414 In [7]: im = imat_()
0415 INFO:opticks.ana.types:parsing json for key ~/.opticks/GMaterialIndexLocal.json
0416
0417 In [11]: map(lambda _:im[_], [6,6,6,12,12,4] )
0418 Out[11]:
0419 ['IwsWater',
0420 'IwsWater',
0421 'IwsWater',
0422 'StainlessSteel',
0423 'StainlessSteel',
0424 'MineralOil']
0425
0426
0427
0428
0429
0430
0431
0432 What could go wrong ? The normal ?
0433 ------------------------------------
0434
0435 Winding order of vertex indices in indexBuffer determines the sidedness
0436 of the triangle (ie in/out direction of the normal).
0437
0438 TODO: duplicate normal calc in geometry shader to visualize the normals
0439
0440
0441 env/graphics/optixrap/cu/TriangleMesh.cu::
0442
0443 34 RT_PROGRAM void mesh_intersect(int primIdx)
0444 35 {
0445 36 int3 index = indexBuffer[primIdx];
0446 37
0447 38 // tried flipping vertex order in unsuccessful attempt to
0448 39 // get normal shader colors to match OpenGL
0449 40 // observe with touch mode that n.z often small
0450 41 // ... this is just because surfaces are very often vertical
0451 42 //
0452 43 float3 p0 = vertexBuffer[index.x];
0453 44 float3 p1 = vertexBuffer[index.y];
0454 45 float3 p2 = vertexBuffer[index.z];
0455 46
0456 47 float3 n;
0457 48 float t, beta, gamma;
0458 49 if(intersect_triangle(ray, p0, p1, p2, n, t, beta, gamma))
0459 50 {
0460 51 if(rtPotentialIntersection( t ))
0461 52 {
0462 53 // attributes should be set between rtPotential and rtReport
0463 54 geometricNormal = normalize(n);
0464 55
0465 56 nodeIndex = nodeBuffer[primIdx];
0466 57 boundaryIndex = boundaryBuffer[primIdx];
0467 58 sensorIndex = sensorBuffer[primIdx];
0468 59
0469 60 // doing calculation here might (depending on OptiX compiler cleverness)
0470 61 // repeat for all intersections encountered unnecessarily
0471 62 // instead move the calculation into closest_hit
0472 63 //
0473 64 // intersectionPosition = ray.origin + t*ray.direction ;
0474 65 // http://en.wikipedia.org/wiki/Line–plane_intersection
0475 66
0476 67 rtReportIntersection(0);
0477
0478
0479
0480
0481 /Developer/OptiX/include/optixu/optixu_math_namespace.h::
0482
0483 2022 /** Intersect ray with CCW wound triangle. Returns non-normalize normal vector. */
0484 2023 OPTIXU_INLINE RT_HOSTDEVICE bool intersect_triangle(const Ray& ray,
0485 2024 const float3& p0,
0486 2025 const float3& p1,
0487 2026 const float3& p2,
0488 2027 float3& n,
0489 2028 float& t,
0490 2029 float& beta,
0491 2030 float& gamma)
0492 2031 {
0493 2032 return intersect_triangle_branchless(ray, p0, p1, p2, n, t, beta, gamma);
0494 2033 }
0495 ....
0496 1956 /** Branchless intesection avoids divergence.
0497 1957 */
0498 1958 OPTIXU_INLINE RT_HOSTDEVICE bool intersect_triangle_branchless(const Ray& ray,
0499 1959 const float3& p0,
0500 1960 const float3& p1,
0501 1961 const float3& p2,
0502 1962 float3& n,
0503 1963 float& t,
0504 1964 float& beta,
0505 1965 float& gamma)
0506 1966 {
0507 1967 const float3 e0 = p1 - p0;
0508 1968 const float3 e1 = p0 - p2;
0509 1969 n = cross( e1, e0 );
0510 1970
0511 1971 const float3 e2 = ( 1.0f / dot( n, ray.direction ) ) * ( p0 - ray.origin );
0512 1972 const float3 i = cross( ray.direction, e2 );
0513 1973
0514 1974 beta = dot( i, e1 );
0515 1975 gamma = dot( i, e0 );
0516 1976 t = dot( n, e2 );
0517 1977
0518 1978 return ( (t<ray.tmax) & (t>ray.tmin) & (beta>=0.0f) & (gamma>=0.0f) & (beta+gamma<=1) );
0519 1979 }
0520
0521
0522
0523
0524 """
0525
0526 import sys
0527 import numpy as np
0528 from opticks.ana.types import phc_, iflags_, imat_, iabmat_, ihex_, seqhis_, seqmat_, stc_, c2g_
0529
0530
0531 def sequence(h,a,b, gm, qm):
0532
0533
0534 im = imat_()
0535
0536 for i,p in enumerate(h[a:b]):
0537 ix = a+i
0538 his,mat = p[0],p[1]
0539 print " %7d : %16s %16s : %30s %30s : %10s %10s " % ( ix,ihex_(his),ihex_(mat),
0540 seqhis_(his), seqmat_(mat),
0541 im[gm[ix]], im[qm[ix]]
0542 )
0543
0544
0545 if __name__ == '__main__':
0546
0547 tag = 1
0548
0549 gs = stc_(tag)
0550 seq = phc_(tag).reshape(-1,2)
0551 c2g = c2g_()
0552 im = iabmat_()
0553
0554 gsnph = gs.view(np.int32)[:,0,3]
0555 gspdg = gs.view(np.int32)[:,3,0]
0556
0557 gsmat_c = gs.view(np.int32)[:,0,2]
0558 gsmat = np.array( map(lambda _:c2g.get(_,-1), gsmat_c ), dtype=np.int32)
0559
0560
0561 p_gsmat = np.repeat(gsmat, gsnph)
0562 p_seqmat = seq[:,1] & 0xF
0563 off = np.arange(len(p_gsmat))[ p_gsmat != p_seqmat ]
0564
0565 p_gsidx = np.repeat(np.arange(len(gsnph)), gsnph )
0566
0567
0568
0569
0570
0571
0572
0573
0574
0575 for i in off:
0576 his,mat = seq[i,0],seq[i,1]
0577 seqhis = seqhis_(his)
0578 if 'MI' in seqhis:continue
0579 print " %7d : %16s %16s : %30s %30s : gs:%2s sq:%2s " % ( i,ihex_(his),ihex_(mat),
0580 seqhis, seqmat_(mat),
0581 im[p_gsmat[i]], im[p_seqmat[i]]
0582 )
0583
0584
0585
0586
0587
0588
0589
0590
0591