Analytic Geometry
===================

Principal
----------

Triangle meshes are a convenient initial step to the GPU 
as all geometry can be treated with the same code.
Special treatment of important geometry (PMTs) however
is expected to have large performance gains.

Ray intersection with CSG solids boils down to 
analytic solving quadratic/cubic polynomials. There is 
a technique to handle union intersections by applying boolean operations
to intersection segments of the sub volumes. 


Aligning three geometries
-----------------------------

Actually not 2 but 3 geometries:

OpenGL 
      GMergedMesh triangulated geometry, to first order only impacts visualization not propagation/tracing,
      but the solid identity infomation including boundary indices comes from GMergedMesh.

      Geometry is modified by *--box*  option according to *--boxconfig*. The orignal instance-1 
      GMergedMesh is combined with the GSolid fabricated by GTestBox 


OptiX Triangulated 
      translation of GMergedMesh triangulated into OptiX

OptiX Analytic 
      somewhat manual addon entering via GPmt and created in python by *pmt-parts*


Containment Box Handling
--------------------------

Actually the parameters of the containment bbox should be obtained from GMergedMesh.1, 
it should be enlarged according to input parameter and the resulting gbbox 
should then be fed down via GGeo::modifyGeometry to: 

GTestBox
     to fabricate the GSolid 

GPmt
     to create a *Part* struct to tack on to the parts buffer  


* Avoids mal-duplication.
* allows pslice to apply to content and not the containment box
  (but pslicing is a low level Analytic Part debug thing, 
  as it cannot be applied at triangulated level)


GPmt Operation
---------------

* reads *pmt-parts* written float parts buffer, deriving a solid buffer from it 
  using solid or node indices which are embedded into the parts buffer

* these five indices 0:4 hail from the detdesc tree parse of *hemi-pmt.xml* 
  and these must match the indices from the GMergedMesh identity buffer 

* for debug want to skip some solids, eg to see behavior with a solid lump of Pyrex/Vacuum/Bialkali
  but at the moment the identity would then be misaligned ?


Photocathode
-------------

Geant4/detdesc model is with seperate very thin spherical parts, including 
a shared boundary with the inside of the pyrex.

For surface based geometry coincident boundaries are unhealthy, so instead model it 
similar to how Pyrex/Vacuum modelling of the pyrex envelope is done.

Again this might entail adding solids, which will mess up identity.  Seems 


Test Box Debugging
--------------------

::

    ggv-box(){
       ggv --box \
            --animtimemax 7 \
            --boxconfig "size=2;boundary=MineralOil/Rock//;" \
            --torchconfig "source=0,0,400;target=0,0,0;radius=150;zenith_azimuth=1,0,1,0" \
             $*   
    }


* need to sort out analytic boundary identity labelling, the missers
  think they are in Pyrex, when should be MO
  [FIXED] 


* GGeo/GMergedMesh should be orchestrating the analytic PMT for commality, 
  currently OGeo/GPmt just grabbing from GCache
  [FIXED]

* where do i set the boundary of the analytic test box ?
  again need to bring control of triangulated and analytic together 
  to avoid confusion
  [FIXED]


From OptiX point of view there 5 (or 6 with the container) primitives.
These need to line up with the triangulated solids for identity to work.
Each primitive has a small numbers of parts (up to 4).
Total of 16 parts.

When put the container at the end in pmt-parts the material mapping 
works better as that aligns with GMergedMesh::combine order.
This is brittle, will fail when slicing.

::

    [2015-Nov-05 19:50:49.364081]:info: OGeo::makeAnalyticGeometry identity buffer BufferId   -1 BufferTarget    0 NumBytes      96 ItemSize      16 NumElements       4 NumItems       6 NumElementsTotal      24

    (  0)        3199          47          27           0 
    (  1)        3200          46          28           0 
    (  2)        3201          43          29           3 
    (  3)        3202          44          30           0 
    (  4)        3203          45          30           0 
    (  5)           5        1000         123           0 



::

    [2015-11-05 19:53:00.317306] [0x000007fff7448031] [info]    GBndLib::dump
     (  0) im:                   Vacuum om:                   Vacuum is:                          os:                         
     ...
     ( 27) im:                    Pyrex om:               MineralOil is:                          os:                         
     ( 28) im:                   Vacuum om:                    Pyrex is:                          os:                         
     ( 29) im:                 Bialkali om:                   Vacuum is:                          os:lvPmtHemiCathodeSensorSurface
     ( 30) im:             OpaqueVacuum om:                   Vacuum is:                          os:                         
     ...
     (122) im:                  RadRock om:                     Rock is:                          os:                         




Implementing container making C++ side ? 

::

    simon:pmt blyth$ ggv --pmt 0:15
    [2015-Nov-05 20:44:09.782958]:info: 0:/usr/local/env/optix/ggeo/bin/GPmtTest
    [2015-Nov-05 20:44:09.783831]:info: 1:0:15
    [2015-Nov-05 20:44:09.784031]:info: NPY::make_slice from 16 -> 15 slice NSlice      0 :    15 :     1 
    [2015-Nov-05 20:44:09.784205]:info: GPmt::loadFromCache slicing partBuf  origBuf 16,4,4 partBuf 15,4,4
    GPmt::make_container pbb min   -101.168   -101.168    -23.838  max    101.168    101.168     56.000 
    ...
    GPmt::make_container pbb min    -27.500    -27.500   -164.500  max     27.500     27.500      1.500 
    GPmt::make_container bb min   -101.168   -101.168   -169.000  max    101.168    101.168    131.000 
    GPmt::make_container bb factor 3.0  min   -551.168   -551.168   -619.000  max    551.168    551.168    581.000 
    [2015-Nov-05 20:44:09.784475]:info: parts shape: 15,4,4
         0.0000      0.0000     69.0000    102.0000 

::

    simon:pmt blyth$ ggv --pmt 0:16
    [2015-Nov-05 20:44:54.266290]:info: 0:/usr/local/env/optix/ggeo/bin/GPmtTest
    [2015-Nov-05 20:44:54.266963]:info: 1:0:16
    [2015-Nov-05 20:44:54.267173]:info: NPY::make_slice from 16 -> 16 slice NSlice      0 :    16 :     1 
    [2015-Nov-05 20:44:54.267336]:info: GPmt::loadFromCache slicing partBuf  origBuf 16,4,4 partBuf 16,4,4
    GPmt::make_container pbb min   -101.168   -101.168    -23.838  max    101.168    101.168     56.000 
    GPmt::make_container pbb min   -101.168   -101.168     56.000  max    101.168    101.168    100.070 
    GPmt::make_container pbb min    -84.540    -84.540    100.070  max     84.540     84.540    131.000 
    ...
    GPmt::make_container pbb min    -98.143    -98.143    -30.000  max     98.143     98.143     56.000 
    GPmt::make_container pbb min    -97.151    -97.151    -29.000  max     97.151     97.151     56.131 
    GPmt::make_container pbb min    -27.500    -27.500   -164.500  max     27.500     27.500      1.500 
    GPmt::make_container pbb min   -551.168   -551.168   -619.000  max    551.168    551.168    581.000 
    GPmt::make_container bb min   -551.168   -551.168   -619.000  max    551.168    551.168    581.000 
    GPmt::make_container bb factor 3.0  min  -2351.168  -2351.168  -2419.000  max   2351.168   2351.168   2381.000 
    [2015-Nov-05 20:44:54.267608]:info: parts shape: 16,4,4


Fixing box normals
-------------------

After fixing ray box normals, get very pretty Lambertian render of PMT in box with *ggv-pmt* ie::

    ggv-pmt () 
    { 
        ggv.sh --tracer --restrictmesh 1 --analyticmesh 1 --islice 0 --target 3199 $*
    }

But the OptiX mode of *ggv-box* is far less pretty with nasty black faces, thats with::

    ggv-box () 
    { 
        ggv --box --animtimemax 7 --boxconfig "size=2;boundary=MineralOil/Rock//;" --torchconfig "source=0,0,400;target=0,0,0;radius=102;zenith_azimuth=1,0,1,0" $*
    }

Also photon reflections show non-symmetric behaviour, discriminating againt two of the box faces.

How is that possible ? 

* different code in propagator and tracer ? 
* different geometry ? 
* normal issue or iimpinging other geometry ?
 

::

    ggv.sh --tracer  --analyticmesh 1 --islice 0 --target 3199 $*
    # not restricting to instanced, see pretty render of analytic PMT with no extra box ?  

    ggv.sh --tracer  --islice 0 --target 3199 $*
    # triangulated PMT 


After fixing *ggv-box* mismatch, changing to *size=3* get the pretty render in OptiX mode and symmetric reflections::

    ggv-box () 
    { 
        ggv --box --animtimemax 7 --boxconfig "size=3;boundary=MineralOil/Rock//;" --torchconfig "source=0,0,400;target=0,0,0;radius=102;zenith_azimuth=1,0,1,0" $*
    }


* Explain that ?

* Also, still material colors seem wrong.



Face Slicing
-------------

::

   ggv-pmt --fslice 0:720
   ggv-pmt --fslice 720:1392
   ggv-pmt --fslice 1392:2352
   ggv-pmt --fslice 2352:2832
   ggv-pmt --fslice 2832:2928

       # selecting faces of single solids, nodeinfo.npy provides the face index ranges 

::

    In [1]: ni = np.load("GMergedMesh/1/nodeinfo.npy")

    In [2]: ni
    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)

    In [3]: np.cumsum(ni[:,0])
    Out[3]: array([ 720, 1392, 2352, 2832, 2928], dtype=uint64)


Photocathode
-------------

::

    pmt-parts   # move to writing full partition file, and pslicing as needed

    ggv-pmt --fslice 1392:2352 --pslice 8:10

    ggv-pmt --fslice 1392:2352 --pslice 8:12   # after add inner spheres


First and Second Solids, Pyrex and contained vacuum
-------------------------------------------------------

OptiX render is as would expect, with pyrex and vacuum very thinly separated, 
to make the inner volume visible adjust near to control the ray trace epsilon

OpenGL render not as would expect, much fatter to the back. 
As if pushed out by the dynode ?

::

   pmt-parts 0:8 
   ggv-pmt --fslice 0:1392



Tubs Issue FIXED, was caused by cylinder poking outside its bbox
-------------------------------------------------------------------

* enable ENDCAP_P only in pmt-/dd.py and regen with

::

   pmt-parts 3:4

* setup coloring in cu/pinhole_camera.cu

::

   100   // BGRA
   101   uchar4 color = prd.flag == HP_PCAP_I ? RED :  make_color( prd.result );


* get expected behavior for outer and inner HP_PCAP_O and HP_PCAP_I

* PCAP endcap is to the right(in default initial ggv-pmt viewpoint) 

* doing the same for QCAP see view dependent shape mis-behaviour, but disabling the 
  partition_union resetting of bbox avoids it

* the problem was the bbox was clipped in at the 3spehere interseciton plane 
  but ZSize was not changed

* from point of view of cylinder rendering the relevant PQ vector is not (0,0,sizeZ)
  but rather (0,0,clipped_sizeZ)

::

    194 static __device__
    195 void intersect_ztubs(quad& q0, quad& q1, quad& q2, quad& q3, const uint4& identity )
    196 {
    197 /* 
    198 Position shift below is to match between different cylinder Z origin conventions
    199 
    200 * Ericson calc implemented below has cylinder origin at endcap P  
    201 * detdesc/G4 Tubs has cylinder origin in the center 
    202 
    203 */
    204     float sizeZ = q1.f.x ;
    205     float z0 = q0.f.z - sizeZ/2.f ;
    206     float3 position = make_float3( q0.f.x, q0.f.y, z0 );  // 0,0,-169.
    207     float clipped_sizeZ = q3.f.z - q2.f.z ;
    208 
    209     float radius = q0.f.w ;
    210     int flags = q1.i.w ;  
    211     
    212     bool PCAP = flags & ENDCAP_P ;
    213     bool QCAP = flags & ENDCAP_Q ;
    214     
    215     //rtPrintf("intersect_ztubs position %10.4f %10.4f %10.4f \n", position.x, position.y, position.z );
    216     //rtPrintf("intersect_ztubs flags %d PCAP %d QCAP %d \n", flags, PCAP, QCAP);
    217     
    218     float3 m = ray.origin - position ;
    219     float3 n = ray.direction ; 
    220     float3 d = make_float3(0.f, 0.f, clipped_sizeZ );
    221     
    222     float rr = radius*radius ;
    223     float3 dnorm = normalize(d);
    224     





   


Just Tubs
----------

Some funny straight lines as rotate around::

   pmt-parts 3:4   # just tubs

   ggv-pmt 

Either a bug or maybe optical illusion due to:

* perspective projection 
* no depth/inside/outside queues 

Perhaps Z cut happening in wrong frame ? 

TODO:

* get orthographic projection working for OptiX raygen 
* matplotlib projection plot of points of the mesh 

::




    In [4]: v = np.load("GMergedMesh/1/vertices.npy")

    In [5]: v
    Out[5]: 
    array([[   0.   ,    0.   ,  131.   ],
           [  33.905,    0.   ,  126.536],
           [  32.75 ,    8.775,  126.536],
           ..., 
           [  26.563,   -7.118,    1.5  ],
           [   0.   ,    0.   ,    1.5  ],
           [   0.   ,    0.   , -164.5  ]], dtype=float32)

    In [6]: v.shape
    Out[6]: (1474, 3)

    In [7]: ni[:,1].sum()  ## sum of vertices, it matches as these are fixed meshes with no dupes
    Out[7]: 1474


    In [10]: i = np.load("GMergedMesh/1/indices.npy").reshape(-1,3)

    In [11]: i.shape
    Out[11]: (2928, 3)

    In [15]: np.unique(i[:720]).min()
    Out[15]: 0

    In [16]: np.unique(i[:720]).max()
    Out[16]: 361

    n [12]: ni[:,0].sum()
    Out[12]: 2928

    In [19]: np.unique(i[:720]).size    # hmm no need for doing indices look up into the vertices, its all contiguous
    Out[19]: 362



Just Tracing a single instance
--------------------------------

Using OTracerTest with the below is much faster than with 
full context (including all those propagate buffers) and full geometry::

   pmt-parts 0:4   # 3sphere + tubs


   ggv --tracer --restrictmesh 1 --analyticmesh 1 --islice 0 --target 3199

   ggv-pmt    # abbreviation for above

   ggv-allpmt --stack $((1024 + 512))      # stack can be reduced a bit with just the tracer


   ggv --tracer --restrictmesh 1 --analyticmesh 1 
    
   ggv-allpmt 



Plumbing check
----------------

::

    ggv --restrictmesh 1 --analyticmesh 1 --torchconfig "radius=300;frame=3199;source=0,0,1000;target=0,0,0"


How to OptiX intersect with CSG solid ?
-----------------------------------------
::

    simon:OptiX_380_sdk blyth$ find . -name '*.cu'  -exec grep -l intersect {} \;
    ./ambocc/parallelogram.cu
    ./ambocc/sphere.cu
    ./buffersOfBuffers/parallelogram.cu
    ./buffersOfBuffers/sphere_texcoord.cu
    ./cook/clearcoat.cu
    ./cook/dof_camera.cu
    ./cook/parallelogram.cu
    ./cook/sphere.cu
    ./cook/sphere_texcoord.cu
    ./cuda/triangle_mesh.cu
    ./cuda/triangle_mesh_small.cu
    ./device_exceptions/device_exceptions.cu
    ./displacement/geometry_programs.cu
    ./glass/glass.cu
    ./glass/triangle_mesh_iterative.cu
    ./heightfield/heightfield.cu
    ./hybridShadows/triangle_mesh_fat.cu
    ./isgReflections/parallelogram.cu
    ./isgReflections/triangle_mesh_fat.cu
    ./isgShadows/triangle_mesh_fat.cu
    ./julia/block_floor.cu
    ./julia/julia.cu
    ...

    simon:OptiX_380_sdk blyth$ find . -type f -exec grep -l union {} \;
    ./julia/block_floor.cu
    ./julia/distance_field.h


Julia sample has lots of non-trivial intersection examples


julia/block_floor.cu::

    538 RT_PROGRAM void intersect(int primIdx)
    539 {
    540   object_factory<false>::Object obj;
    541   object_factory<false>::make_object(obj, ray.direction);
    542 
    543   // first check for intersection between the ray and aabb
    544   optix::Ray tmp_ray = ray;
    545   if(intersect_aabb(tmp_ray, obj)) {
    546     float epsilon = 1.25e-3f;
    547     float max_epsilon = 2.5e-2f;
    548 
    549     float3 hit_point;
    550     float t = adaptive_sphere_trace<1000>(tmp_ray, make_distance_to_primitive(obj), hit_point, epsilon, max_epsilon);
    551     if(t < tmp_ray.tmax)
    552     {
    553       if(rtPotentialIntersection(t))

 
julia/distance_field.h::

    216 // The union of two primitives
    217 template<typename Primitive1, typename Primitive2>
    218   class PrimitiveUnion
    219 {
    220   public:
    221     // null constructor creates an undefined DistanceUnion
    222     HD_DECL
    223     PrimitiveUnion(void){}
    224 
    225     HD_DECL
    226     PrimitiveUnion(Primitive1 p1, Primitive2 p2):m_prim1(p1),m_prim2(p2){}
    227 
    228     HD_DECL
    229     float distance(const float3 &x) const
    230     {
    231       return fminf(m_prim1.distance(x), m_prim2.distance(x));
    232     }
    ...
      


shadeTree/parallelogram.cu::

     37 RT_PROGRAM void intersect(int primIdx)
     38 {
     39   float3 n = make_float3( plane );
     40   float dt = dot(ray.direction, n );
     41   float t = (plane.w - dot(n, ray.origin))/dt;
     42   if( t > ray.tmin && t < ray.tmax ) {
     43     float3 p = ray.origin + ray.direction * t;
     44     float3 vi = p - anchor;
     45     float a1 = dot(v1, vi);
     46     if(a1 >= 0 && a1 <= 1){
     47       float a2 = dot(v2, vi);
     48       if(a2 >= 0 && a2 <= 1){
     49         if( rtPotentialIntersection( t ) ) {
     50           geometric_normal = n;
     51           shading_normal = n;
     52           uv = make_float2(a1, a2);
     53           rtReportIntersection( 0 );
     54         }
     55       }
     56     }
     57   }
     58 }


tutorial.cpp::

    238 float4 make_plane( float3 n, float3 p )
    239 {
    240   n = normalize(n);
    241   float d = -dot(n, p);
    242   return make_float4( n, d );
    243 }


tutorial10.cu::

    313 //
    314 // Intersection program for programmable convex hull primitive
    ///
    ///     https://en.wikipedia.org/wiki/Line–plane_intersection
    ///     http://geomalgorithms.com/index.html
    ///
    315 //
    316 rtBuffer<float4> planes;
    317 RT_PROGRAM void chull_intersect(int primIdx)
    318 {
    319   int n = planes.size();
    320   float t0 = -FLT_MAX;
    321   float t1 = FLT_MAX;
    322   float3 t0_normal = make_float3(0);
    323   float3 t1_normal = make_float3(0);
    324   for(int i = 0; i < n && t0 < t1; ++i ) {
    325     float4 plane = planes[i];
    326     float3 n = make_float3(plane);
    327     float  d = plane.w;
    328 
    329     float denom = dot(n, ray.direction);
    330     float t = -(d + dot(n, ray.origin))/denom;
    ///
    ///  Plane eqn, p0 is point in plane, n is normal 
    ///     (p - p0).n = 0
    ///
    ///  Line 
    ///      p = ray.origin + t * ray.direction
    ///
    ///  Intersect
    ///
    ///    (ray.origin + t * ray.direction - p0 ).n = 0 
    ///
    ///     dot(n, ray.origin) + t * dot(n, ray.direction) - dot(p0, n) = 0  
    ///                
    ///                  dot(p0,n) - dot(n, ray.origin)
    ///            t =  --------------------------------           
    ///                     dot(n, ray.direction)
    ///
    ///

    331     if( denom < 0){
    332       // enter
    333       if(t > t0){
    334         t0 = t;
    335         t0_normal = n;
    336       }
    337     } else {
    338       //exit
    339       if(t < t1){
    340         t1 = t;
    341         t1_normal = n;
    342       }
    343     }
    344   }
    345 
    346   if(t0 > t1)
    347     return;
    348 
    349   if(rtPotentialIntersection( t0 )){
    350     shading_normal = geometric_normal = t0_normal;
    351     rtReportIntersection(0);
    352   } else if(rtPotentialIntersection( t1 )){
    353     shading_normal = geometric_normal = t1_normal;
    354     rtReportIntersection(0);
    355   }
    356 }







How to proceed ?
------------------

* on revisiting G4DAE include GDML G4 CSG model description together
  with the triangulated COLLADA 


detdesc PMT is involved
------------------------

Complicated assemblies of CSG solids. Implementing analytic is non-trivial.

G5:/home/blyth/local/env/dyb/NuWa-trunk/dybgaudi/Detector/XmlDetDesc/DDDB/PMT/geometry.xml::

     08   <catalog name="PMT">
     09 
     10     <logvolref href="hemi-pmt.xml#lvPmtHemiFrame"/>
     11     <logvolref href="hemi-pmt.xml#lvPmtHemi"/>
     12     <logvolref href="hemi-pmt.xml#lvPmtHemiwPmtHolder"/>
     13     <logvolref href="hemi-pmt.xml#lvAdPmtCollar"/>
     14     <logvolref href="hemi-pmt.xml#lvPmtHemiCathode"/>
     15     <logvolref href="hemi-pmt.xml#lvPmtHemiVacuum"/>
     16     <logvolref href="hemi-pmt.xml#lvPmtHemiBottom"/>
     ..

dybgaudi/Detector/XmlDetDesc/DDDB/PMT/hemi-pmt.xml::

     37   <!-- The PMT glass -->
     38   <logvol name="lvPmtHemi" material="Pyrex">
     39     <union name="pmt-hemi">
     40       <intersection name="pmt-hemi-glass-bulb">
     41           <sphere name="pmt-hemi-face-glass"
     42                 outerRadius="PmtHemiFaceROC"/>
     43 
     44           <sphere name="pmt-hemi-top-glass"
     45                outerRadius="PmtHemiBellyROC"/>
     46           <posXYZ z="PmtHemiFaceOff-PmtHemiBellyOff"/>
     47 
     48           <sphere name="pmt-hemi-bot-glass"
     49                 outerRadius="PmtHemiBellyROC"/>
     50           <posXYZ z="PmtHemiFaceOff+PmtHemiBellyOff"/>
     51 
     52       </intersection>
     53       <tubs name="pmt-hemi-base"
     54         sizeZ="PmtHemiGlassBaseLength"
     55         outerRadius="PmtHemiGlassBaseRadius"/>
     56       <posXYZ z="-0.5*PmtHemiGlassBaseLength"/>
     57     </union>
     58 
     59     <physvol name="pvPmtHemiVacuum"
     60          logvol="/dd/Geometry/PMT/lvPmtHemiVacuum"/>
     61 
     62   </logvol>


::

    118   <!-- The Photo Cathode -->
    119   <!-- use if limit photocathode to a face on diameter gt 167mm. -->
    120   <logvol name="lvPmtHemiCathode" material="Bialkali" sensdet="DsPmtSensDet">
    121     <union name="pmt-hemi-cathode">
    122       <sphere name="pmt-hemi-cathode-face"
    123           outerRadius="PmtHemiFaceROCvac"
    124           innerRadius="PmtHemiFaceROCvac-PmtHemiCathodeThickness"
    125           deltaThetaAngle="PmtHemiFaceCathodeAngle"/>
    126       <sphere name="pmt-hemi-cathode-belly"
    127           outerRadius="PmtHemiBellyROCvac"
    128           innerRadius="PmtHemiBellyROCvac-PmtHemiCathodeThickness"
    129           startThetaAngle="PmtHemiBellyCathodeAngleStart"
    130           deltaThetaAngle="PmtHemiBellyCathodeAngleDelta"/>
    131       <posXYZ z="PmtHemiFaceOff-PmtHemiBellyOff"/>
    132     </union>
    133   </logvol>