Opticks Presentations, Refs
Details in above presentations + notes : https://bitbucket.org/simoncblyth/opticks/src/master/notes/progress.rst
JUNO ISSUES
NNVT : MaskTail impinges MaskVirtual
HAMA : BodySolid impinges MaskTail
BirksConstant1 : 1,000,000x TOO BIG
OPTICKS PRIM ISSUES
nmskSolidMask : ellipsoid hole at "apex" issue
nmskSolidMaskTail : very thin cylinder "lip" issues
nmskSolidMaskVirtual : cone precision + near-apex issues
https://simoncblyth.bitbucket.io/env/presentation/opticks_20221117_mask_debug_and_tmm.html
NNVT : ONE overlap issue visible, MaskTail impinges MaskVirtual
(using ct.sh : Opticks CSG on CPU)
HAMA : ONE overlap issue, BodySolid impinges MaskTail
(using ct.sh : Opticks CSG on CPU)
HAMA : BodySolid impinges MaskTail (mct.sh)
HAMA : after bug 33 fix
junosw/Simulation/SimSvc/MultiFilmSimSvc
epsilon:MultiFilmSimSvc blyth$ find . -type f ./CMakeLists.txt ./python/MultiFilmSimSvc/__init__.py ./MultiFilmSimSvc/MultiFilmModel.h ./src/OpticalSystem.h ./src/Layer.h ./src/Material.h ./src/Layer.cc ## Layer, ThickLayer, ThinLayer ./src/Matrix.h ./src/OpticalSystem.cc ./src/MultiFilmModel.cc ./src/Material.cc ./src/Matrix.cc
CPU/GPU : using complex
#ifdef WITH_THRUST
    using thrust::complex ; //GPU
#else
    using std::complex ;    //CPU
#endif
    // same complex TMM math on CPU/GPU 
template<typename T> struct Layr
{
    T  d, pad   ; // d:thickness : zero means incoherent
#ifdef WITH_THRUST
    thrust::complex  n, st, ct, rs, rp, ts, tp ;
#else
    std::complex     n, st, ct, rs, rp, ts, tp ;
#endif
    Matx S, P ;
};
template <typename T, int N> struct Stack
{
    Layr<T> ll[N] ; // stack of N layers   
    Layr<T> comp ;  // composite for the N layers  
    ART_<T>  art ;  // results eg A,R,T
    Stack(T wl, T minus_cos_theta, const StackSpec<T>& ss);
};
   https://github.com/simoncblyth/j/blob/main/Layr/Layr.h
ALT: multi-GB "ART" textures (Yuxiang Hu) TODO: compare
Impl: arrays of simple struct
 epsilon:Layr blyth$ ./LayrTest.sh ana
 CFLayrTest
  a :            EGet : scan__EGet__cpu_thr_double
  b :            EGet : scan__EGet__cpu_thr_float
  c :            EGet : scan__EGet__gpu_thr_double
  d :            EGet : scan__EGet__gpu_thr_float
  ...
  m :          R12860 : scan__R12860__cpu_pom_double
  n :          R12860 : scan__R12860__cpu_thr_double
  o :          R12860 : scan__R12860__cpu_thr_float
  p :          R12860 : scan__R12860__gpu_thr_double
  q :          R12860 : scan__R12860__gpu_thr_float
 In [1]: CF(m,q,0.05)
 Out[1]:
 CF(m,q,0.05) : scan__R12860__cpu_pom_double vs scan__R12860__gpu_thr_float
        lls :   0.000442 :   0.000442 :  -0.000414
      comps :   0.000341 :   0.000341 :  -6.17e-05
       arts :    6.2e-05 :    6.2e-05 :   -6.2e-05
 pmtcat:R12860 tt:5 lt:q : j/Layr/LayrTest scan__R12860__gpu_thr_float ni 900 wl 440
 +------------------------------+----------+----------+----------+----------+----------+
 |        R12860 arts\comps 0.05|     m:cpd|     n:ctd|     o:ctf|     p:gtd|     q:gtf|
 +==============================+==========+==========+==========+==========+==========+
 |                         m:cpd|         0| 0.0003325|  0.000301| 0.0003325| 0.0003407|
 +------------------------------+----------+----------+----------+----------+----------+   max difference of all param between scans 
 |                         n:ctd| 6.064e-05|         0| 4.829e-05| 7.445e-14| 4.829e-05|
 +------------------------------+----------+----------+----------+----------+----------+
 |                         o:ctf| 5.454e-05| 6.101e-06|         0| 4.829e-05| 3.977e-05|
 +------------------------------+----------+----------+----------+----------+----------+
 |                         p:gtd| 6.064e-05| 1.321e-14| 6.101e-06|         0| 4.829e-05|
 +------------------------------+----------+----------+----------+----------+----------+
 |                         q:gtf| 6.199e-05| 1.523e-06| 7.451e-06| 1.523e-06|         0|
 +------------------------------+----------+----------+----------+----------+----------+
 In [1]: ARTPlot(m, 0.05)
        
        S/P-Pol : perp./within plane
S/P Polarization : has huge effect on A,R,T in both directions : AOI > 90 is reversed stack
4 : A
Absorption : large S/P-polarization difference in both directions
4 : R
Only reflection at glancing incidence (90 deg) : large S/P-pol. difference for Vacuum->Pyrex
4 : T
Pyrex->Vacuum : Above critical angle : no transmission, only reflect or absorb
Standalone Advantages
| https://github.com/simoncblyth/j/ | 
| https://bitbucket.org/simoncblyth/opticks/ | 
"CustomG4OpBoundaryProcess" FIX
opticks:u4/InstrumentedG4OpBoundaryProcess.cc
j:PMTFastSim Standalone PMT geometry and Optical Model
Optical only Geant4 simulation, with full step point recording
N=0/1 ./U4SimulateTest.sh
Geant4 Simtrace intersection : 2D geometry plotting
N=0/1 ./U4SimtraceTest.sh
CustomG4OpBoundaryProcess ?
BUT : need to change very nasty Geant4 impl:
inline sboundary::sboundary():
_E2_t(make_float2( 2.f*n1c1/(n1c1+n2c2) ,
                   2.f*n1c1/(n2c1+n1c2) )),// (ts,tp)
_E2_r(make_float2( _E2_t.x - 1.f       ,
                   n2*_E2_t.y/n1 - 1.f  )),// (rs,rp)
A_transverse(cross(p.mom, orient*normal)),
E1_perp(dot(p.pol, A_transverse)),
E1(make_float2(E1_perp,sqrtf(1.f-E1_perp*E1_perp)))),
E2_t(_E2_t*E1),RR(normalize(E2_r)),
E2_r(_E2_r*E1),TT(normalize(E2_t)),
{
p.mom = reflect
      ?
         p.mom + 2.0f*c1*orient*normal
      :
         eta*(p.mom) + (eta*c1 - c2)*orient*normal
      ;
A_parallel = normalize(cross(p.mom, A_transverse));
p.pol =
        ( reflect ?
             ( tir ?
                   -p.pol + 2.f*EdotN*orient*normal
             :
                   RR.x*A_transverse + RR.y*A_parallel
             )
          :
             TT.x*A_transverse + TT.y*A_parallel
        )
      ;
}  // IMPL BASED ON + VALIDATED AGAINST GEANT4
Reflect/Refract : INCORRECT POLARIZATION
 void junoPMTOpticalModel::Reflect()
 {
     dir -= 2.*(dir*norm)*norm;
     pol -= 2.*(pol*norm)*norm;
 }
 void junoPMTOpticalModel::Refract()
 {
     dir = (real(_cos_theta4) -
            _cos_theta1*_n1/_n4)*norm + (_n1/_n4)*dir;
     pol = (pol-(pol*dir)*dir).unit();
 }
G4OpBoundaryProcess polarization from:
Opticks : sysrap/sboundary.h qudarap/qsim.h follows G4, ===>
Derive Fresnel from Maxwell Boundary Conditions
| G4OpBoundaryProcess/qsim.h/sboundary.h : Only S-polarized survives | junoPMTOpticalModel::Reflect : very different | 
Brewster (or polarizing) incident angle th1 : tan(th1) = n2/n1 ; th1 + th2 = pi/2
| G4OpBoundaryProcess/qsim.h/sboundary.h : partial pol | junoPMTOpticalModel::Refract : not partially pol | 
Natural 2-volume PMT (Pyrex + Vacuum) : NOT COMPATIBLE WITH FastSim
body hidden under inner1 inner2
body is FastSim envelope volume
CURRENT : Unnatural 4-volume PMT (Pyrex+Pyrex+Vacuum+Vacuum) => Half Fake
Current MultiLayer POM
+---------------pmt-Pyrex----------------+ | | | | | +----body-Pyrex-(FSim-env)---+ | | | +------------------------+ | | | | | | | | | | | | | | | | | inner1-Vacuum |-| | | | | |1e-3 | | | | | | | | | +~~coincident~face~~~~~~~+ | | | | | | | | | | | | | | | | | inner2-Vacuum | | | | | | | | | | | | | | | | | +------------------------+ | | | +----------------------------+ | | | | | +----------------------------------------+
   80 G4bool junoPMTOpticalModel::ModelTrigger(const G4FastTrack &fastTrack)
   81 {   //  Contorted as "pre-trigger" on where next 
   83     if(fastTrack.GetPrimaryTrack()->GetVolume() == _inner2_phys){
   84         return false;
   85     }
   ...
   89     pos     = fastTrack.GetPrimaryTrackLocalPosition();
   90     dir     = fastTrack.GetPrimaryTrackLocalDirection();
   94
   95     if(fastTrack.GetPrimaryTrack()->GetVolume() == _inner1_phys){
   96         whereAmI = kInVacuum;
   97     }else{
   98         whereAmI = kInGlass;
   99     }
  100
  101     if(whereAmI == kInGlass){
  102         dist1 = _inner1_solid->DistanceToIn(pos, dir);
  103         dist2 = _inner2_solid->DistanceToIn(pos, dir);
  104
  105         if(dist1 == kInfinity){
  106             return false;
  107         }else if(dist1>dist2){
  108             return false;
  109         }else{
  110             return true;
  111         }
  112     }else{
  113         dist1 = _inner1_solid->DistanceToOut(pos, dir);
  114         dist2 = _inner2_solid->DistanceToIn(pos, dir);
  115
  116         if(dist2 == kInfinity){
  117             return true;
  118         }
  119     }
  120     return false;
  121 }
        
        Simplified MultiLayer POM
+---------------pmt-Pyrex----------------+ | | | | | | | +~inner~Vacuum~(FSim~env)+ | | ! ! | | ! ! | | ! ! | | ! ! | | ! ! | | + + | | | | | | | | | | | | | | | | | | | | | | +------------------------+ | | | | | | | +----------------------------------------+
 G4bool junoPMTOpticalModelSimple::ModelTrigger(
         const G4FastTrack &fastTrack)
 {
      //  Simple + Cheap due to natural geometry 
     return fastTrack.GetPrimaryTrackLocalPosition().z() > 0. ;
 }
https://github.com/simoncblyth/j/blob/main/PMTFastSim/junoPMTOpticalModelSimple.cc
Simplified MultiLayer POM
+---------------pmt-Pyrex----------------+ | | | | | | | +~inner~Vacuum~(FSim~env)+ | | ! ! | | ! ! | | ! ! | | ! ! | | ! ! | | + + | | | | | | | | | | | | | | | | | | | | | | +------------------------+ | | | | | | | +----------------------------------------+
 void junoPMTOpticalModelSimple::DoIt(const G4FastTrack& fastTrack,
       G4FastStep &fastStep)
 {
     G4double energy = fastTrack.GetPrimaryTrack()->GetKineticEnergy();
     wavelength_nm = twopi*hbarc/energy/nm ;
     position = fastTrack.GetPrimaryTrackLocalPosition();
     direction = fastTrack.GetPrimaryTrackLocalDirection();
     polarization = fastTrack.GetPrimaryTrackLocalPolarization();
     G4VSolid* envelope_solid = fastTrack.GetEnvelopeSolid();
     surface_normal = envelope_solid->SurfaceNormal(position);
     minus_cos_theta = direction*surface_normal ;
     whereAmI = minus_cos_theta < 0. ? kInGlass : kInVacuum ;
     StackSpec spec ;
     // ... skip : collect refractive indices, thickness into spec ...
     Stack stack(      wavelength_nm, minus_cos_theta, spec );
     Stack stackNormal(wavelength_nm, -1.            , spec );
     // ...
}
   Custom Boundary POM
+---------------pmt-Pyrex----------------+ | | | | | | | +~inner~Vacuum~~~~~~~~~~~+ | | ! ! | | ! ! | | ! ! | | ! ! | | ! ! | | + + | | | | | | | | | | | | | | | | | | | | | | +------------------------+ | | | | | | | +----------------------------------------+
OpSurfaceName[0] == '@'
Custom Boundary Process : Advantages
| Current FastSim POM | 4 Solid, 4 LV, 4 PV | 
| Custom Boundary POM | 2 Solid, 2 LV, 2 PV | 
Disadvantages
Advantages far outweigh disadvantages
CustomART::maybe_doIt
inline char CustomART::maybe_doIt(
     const char* OpticalSurfaceName,
     const G4Track& aTrack, const G4Step& aStep )
{
    if( OpticalSurfaceName == nullptr ||
        OpticalSurfaceName[0] != '@') return 'N' ;
    const G4AffineTransform& transform =
      aTrack.GetTouchable()
      ->GetHistory()->GetTopTransform();
    G4ThreeVector localPoint =
      transform.TransformPoint(theGlobalPoint);
    if(localPoint.z() <= 0) return 'Z' ;
    return doIt(aTrack, aStep) ;
}
doIt TMM calc, runs only for:
#include "IPMTAccessor.h"
#include "Layr.h"
#include "U4Touchable.h"
struct CustomART
{
    const IPMTAccessor* accessor ; // JPMT.h OR PMTAccessor.h 
    G4double& theTransmittance ;   // doIt sets these
    G4double& theReflectivity ;
    G4double& theEfficiency ;
    const G4ThreeVector& theGlobalPoint ;
    const G4ThreeVector& OldMomentum ;
    const G4ThreeVector& OldPolarization ;
    const G4ThreeVector& theRecoveredNormal ;
    const G4double& thePhotonMomentum ;
    CustomART(
        const IPMTAccessor* accessor,
        G4double& theTransmittance,
        G4double& theReflectivity,
        G4double& theEfficiency,
        const G4ThreeVector& theGlobalPoint,
        const G4ThreeVector& OldMomentum,
        const G4ThreeVector& OldPolarization,
        const G4ThreeVector& theRecoveredNormal,
        const G4double& thePhotonMomentum
    );
    char maybe_doIt(const char* OpticalSurfaceName,
              const G4Track& aTrack, const G4Step& aStep );
    char doIt(const G4Track& aTrack, const G4Step& aStep );
};
opticks/src/master/u4/CustomART.h
TO  BT  BT  BT  BT  SR  SR  BT  BR  BR  BT  SR  SR  SR  BT  BR  BT  SR  BT  SA    Lots of fakes 
00  01 [02] 03 [04] 05  06 [07] 08  09 [10] 11  12  13 [14] 15 [16] 17 [18] 19   (7/20 Fake)
        
        TO BT BT SR SR BR BR SR SR SR BR SR BR SR SA 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 Simpler history, no fakes 00 01 03 05 06 08 09 11 12 13 15 17 19
u4t ; ./U4SimulateTest.sh cf
In [12]: np.c_[ar[:,0],np.arange(len(ar)),br[:,0]]
Out[12]:
array([[-113.   ,    0.   ,  200.   ,    0.   ,    0.   , -113.   ,    0.   ,  200.   ,    0.   ],
       [-113.   ,    0.   ,  170.163,    0.137,    1.   , -113.   ,    0.   ,  170.163,    0.137],
       [-112.83 ,    0.   ,  164.918,    0.164,    2.   , -112.83 ,    0.   ,  164.917,    0.164],
       [-112.83 ,    0.   ,  164.917,    0.164,    3.   , -156.577,    0.   , -148.846,    1.22 ],
       [-135.824,    0.   ,    0.   ,    1.012,    4.   ,  -95.   ,    0.   , -104.211,    1.474],
       [-156.577,    0.   , -148.846,    1.778,    5.   , -248.807,    0.   ,    7.28 ,    2.108],
       [ -95.   ,    0.   , -104.211,    2.166,    6.   ,   53.206,    0.   ,  180.727,    3.269],
       [-238.764,    0.   ,   -0.   ,    3.071,    7.   ,  245.605,    0.   ,  -35.443,    4.235],
       [-248.807,    0.   ,    7.28 ,    3.112,    8.   ,   95.   ,    0.   ,  -99.428,    4.781],
       [  53.205,    0.   ,  180.727,    4.274,    9.   ,  177.724,    0.   , -134.574,    5.08 ],
       [ 214.06 ,    0.   ,    0.   ,    5.507,   10.   ,  141.059,    0.   ,  152.451,    6.046],
       [ 245.605,    0.   ,  -35.443,    5.749,   11.   , -239.66 ,    0.   ,  -55.195,    7.492],
       [  95.   ,    0.   ,  -99.428,    6.583,   12.   ,  237.91 ,    0.   ,   54.597,    9.127],
       [ 177.724,    0.   , -134.574,    7.041,   13.   ,   50.   ,    0.   ,  -63.74 ,    9.867],
       [ 160.533,    0.   ,    0.   ,    7.732,   14.   ,   58.352,    0.   ,  -69.   ,    9.9  ],
       [ 141.059,    0.   ,  152.451,    8.245,   15.   ,    0.   ,    0.   ,    0.   ,    0.   ],
       [-138.46 ,    0.   ,    0.   ,    9.867,   16.   ,    0.   ,    0.   ,    0.   ,    0.   ],
       [-239.66 ,    0.   ,  -55.195,   10.455,   17.   ,    0.   ,    0.   ,    0.   ,    0.   ],
       [   0.427,    0.   ,    0.   ,   11.71 ,   18.   ,    0.   ,    0.   ,    0.   ,    0.   ],
       [ 237.91 ,    0.   ,   54.596,   12.523,   19.   ,    0.   ,    0.   ,    0.   ,    0.   ],
       [   0.   ,    0.   ,    0.   ,    0.   ,   20.   ,    0.   ,    0.   ,    0.   ,    0.   ],
       [   0.   ,    0.   ,    0.   ,    0.   ,   21.   ,    0.   ,    0.   ,    0.   ,    0.   ],
       [   0.   ,    0.   ,    0.   ,    0.   ,   22.   ,    0.   ,    0.   ,    0.   ,    0.   ],
       [   0.   ,    0.   ,    0.   ,    0.   ,   23.   ,    0.   ,    0.   ,    0.   ,    0.   ],
| ar | a.record[PID] | N=0 current geom | degenerate and fake intersect points | 
| bb | b.record[PID] | N=1 natural geom | less points, simpler history | 
Need point-to-point mapping to compare
 u4t ; ./U4SimulateTest.sh cf
 In [2]: b2a  ## point-to-point mapping to skip a fakes
 Out[2]: array([ 0,  1,  3,  5,  6,  8,  9, 11, 12, 13, 15, 17, 19])
 In [4]: abr = np.c_[ar[b2a,0],br[:len(b2a),0],ar[b2a,0]-br[:len(b2a),0]] ; abr
 Out[4]:
 array([[-113.   ,    0.   ,  200.   ,    0.   , -113.   ,    0.   ,  200.   ,    0.   ,    0.   ,    0.   ,    0.   ,    0.   ],
        [-113.   ,    0.   ,  170.163,    0.137, -113.   ,    0.   ,  170.163,    0.137,    0.   ,    0.   ,    0.   ,    0.   ],
        [-112.83 ,    0.   ,  164.917,    0.164, -112.83 ,    0.   ,  164.917,    0.164,    0.   ,    0.   ,    0.   ,   -0.   ],
        [-156.577,    0.   , -148.846,    1.778, -156.577,    0.   , -148.846,    1.22 ,   -0.   ,    0.   ,    0.   ,    0.558],
        [ -95.   ,    0.   , -104.211,    2.166,  -95.   ,    0.   , -104.211,    1.474,    0.   ,    0.   ,    0.   ,    0.692],
        [-248.807,    0.   ,    7.28 ,    3.112, -248.807,    0.   ,    7.28 ,    2.108,    0.   ,    0.   ,   -0.   ,    1.004],
        [  53.205,    0.   ,  180.727,    4.274,   53.206,    0.   ,  180.727,    3.269,   -0.   ,    0.   ,    0.   ,    1.004],
        [ 245.605,    0.   ,  -35.443,    5.749,  245.605,    0.   ,  -35.443,    4.235,    0.   ,    0.   ,    0.   ,    1.514],
        [  95.   ,    0.   ,  -99.428,    6.583,   95.   ,    0.   ,  -99.428,    4.781,    0.   ,    0.   ,    0.   ,    1.802],
        [ 177.724,    0.   , -134.574,    7.041,  177.724,    0.   , -134.574,    5.08 ,    0.   ,    0.   ,    0.   ,    1.96 ],
        [ 141.059,    0.   ,  152.451,    8.245,  141.059,    0.   ,  152.451,    6.046,   -0.   ,    0.   ,    0.   ,    2.199],
        [-239.66 ,    0.   ,  -55.195,   10.455, -239.66 ,    0.   ,  -55.195,    7.492,    0.   ,    0.   ,    0.   ,    2.963],
        [ 237.91 ,    0.   ,   54.596,   12.523,  237.91 ,    0.   ,   54.597,    9.127,    0.   ,    0.   ,   -0.   ,    3.397]], dtype=float32)
Positions match closely, some times are way off
rvtime_ = lambda r:np.diff(r[:,0,3])
rvstep_ = lambda r:np.diff(r[:,0,:3],axis=0 )
rvdist_ = lambda r:np.sqrt(np.sum(rvstep_(r)*rvstep_(r),axis=1))
rvspeed_ = lambda r:rvdist_(r)/rvtime_(r)
In [14]: np.c_[rvtime_(ar[b2a]), rvtime_(br[:len(b2a)]),
               rvdist_(ar[b2a]), rvdist_(br[:len(b2a)]),
              rvspeed_(ar[b2a]),rvspeed_(br[:len(b2a)])]
Out[14]:
array([[  0.137,   0.137,  29.837,  29.837, 218.038, 218.038],  ## Water
       [  0.027,   0.027,   5.249,   5.249, 196.216, 196.215],  ## Pyrex
       [  1.615,   1.057, 316.798, 316.798, 196.215, 299.792],  ## Vacuum
       [  0.388,   0.254,  76.053,  76.053, 196.215, 299.792],
       [  0.946,   0.634, 189.965, 189.965, 200.744, 299.792],  ## comb. of Vacuum and Pyrex speeds, split at Fake 
       [  1.162,   1.162, 348.275, 348.275, 299.792, 299.792],
       [  1.475,   0.965, 289.392, 289.392, 196.215, 299.792],
       [  0.834,   0.546, 163.634, 163.634, 196.215, 299.792],
       [  0.458,   0.3  ,  89.881,  89.881, 196.215, 299.792],
       [  1.204,   0.965, 289.357, 289.357, 240.315, 299.792],  ## comb. of Vacuum and Pyrex speeds, split at Fake
       [  2.21 ,   1.447, 433.663, 433.663, 196.215, 299.792],
       [  2.068,   1.635, 490.027, 490.027, 236.919, 299.792]], dtype=float32)
| N=0 | Pyrex speed within PMT Vacuum | FastSim->SlowSim transitions miss speed setup ? | 
| N=1 | Always Vacuum speed in Vacuum | All standard Geant4 with customized G4OpBoundaryProcess | 
Why Low Dependency Access ?
QE scan over 100 energy points, all PMTs
get_pmtid_qe( pmtid, en );
| IPMTSimParamSvc | 7.70 s | 
| PMTSimParamData | 0.30 s | 
| IPMTSimParamSvc/PMTSimParamData | 26.10 | 
| junosw/-/merge_requests/126 MERGED AFTER ~3 WEEKS | 
| junosw/-/issues/66 | 
| junosw/-/tree/blyth-66-low-dependency-PMT-data-access | 
Current Approach : extg4 translated
| Geant4 -> GGeo -> CSG | 
DRASTIC CODE REDUCTION NEAR
| Geant4 -> U4Tree.h/stree.h -> CSG | 
  | 
| stree/snode/scsg/snd -> CSGFoundry/CSGSolid/CSGPrim/CSGNode | 
junosw : Natural PMT Geometry Branch
| Primary | Support | 
|---|---|
| CustomG4OpBoundaryProcess CustomART.h Layr.h | PMTSimParamData (merged in MR 126) PMTAccessor/IPMTAccessor (j/Layr) HamamatsuR12860PMTManager NNVTMCPPMTManager | 
Opticks : Custom boundary equivalent