junoPMTOpticalModel CPU tests reveal issues, all from FastSim use:
Replacing FastSim use with a CustomG4OpBoundaryProcess:
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
)
;
}
Reflect/Refract Polarization
void junoPMTOpticalModel::Reflect()
{
dir -= 2.*(dir*norm)*norm;
pol -= 2.*(pol*norm)*norm; // similar to G4 TIR pol, NOT Fresnel
}
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 |
Minor changes for standalone use #ifdef PMTFASTSIM_STANDALONE
Optical only Geant4 simulation, with full step point recording
N=0/1 ./U4SimulateTest.sh
Geant4 Simtrace intersection : for 2D geometry plotting
N=0/1 ./U4SimtraceTest.sh
CustomART integrated with InstrumentedG4OpBoundaryProcess
hama_UsePMTOpticalModel=0 REVERSE=1 NOGS=1 ~/opticks/extg4/xxv.sh
body hidden under pmt : WHY?
Old Surface POM
+---------------pmt-Pyrex----------------+ | +-------------body-Pyrex-------------+ | | | | | | | | | | | +------------------------+ | | | | | | | | | | | | | | | | | inner1-Vacuum | |-| | | | | |1e-3 | | | | | | | | +~~coincident~face~~~~~~~+ | | | | | | | | | | | | | | | | | inner2-Vacuum | | | | | | | | | | | | | | | | | +------------------------+ | | | | | | | | | | | +------------------------------------+ | +----------------------------------------+
photons DO NOT enter PMT vacuum
| m_Photocathode_opsurf | inner1<->body | pv<->pv |
| m_mirror_opsurf | inner2<->body | pv<->pv |
Why pmt+body Pyrex (1e-3 mm separation) ? GUESS:
Why split vacuum inner1/inner2 ?
Alternatively : with CustomG4OpBoundaryProcess
body hidden under inner1 inner2
body is FastSim envelope volume
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 }
SFastSim_Debug.hh
struct SYSRAP_API SFastSim_Debug
{
static std::vector<SFastSim_Debug> record ;
static constexpr const char* NAME = "SFastSim_Debug.npy" ;
static constexpr int LIMIT = 100000 ;
static void Save(const char* dir);
void add();
double posx ; // fs[:,0,:3]
double posy ;
double posz ;
double time ; // fs[:,0,3]
double dirx ; // fs[:,1,:3]
double diry ;
double dirz ;
double dist1 ; // fs[:,1,3]
double polx ; // fs[:,2,:3]
double poly ;
double polz ;
double dist2 ; // fs[:,2,3]
double ModelTrigger; // fs[:,3,0].astype(np.int64)
double whereAmI ; // fs[:,3,1].astype(np.int64)
double c ; // fs[:,3,2]
double PhotonId ; // fs[:,3,3].astype(np.int64)
};
epsilon:tests blyth$ PID=726 ./U4SimulateTest.sh nana
In [1]: extra.shape
Out[1]: (14, 4, 4)
In [2]: extra
Out[2]:
array([[[-112.83 , 0. , 164.918, 0.164],
[ 0.032, 0. , -0.999, 0.001], // dist1:1e-3 mm
[ 0. , -1. , 0. , 165.005],
[ 1. , 1. , 0. , 726. ]], ## ModelTrigger:1
[[-112.83 , 0. , 164.917, 0.164],
[ -0.138, 0. , -0.99 , 166.512], // dist1 and dist2 equal
[ 0. , -1. , 0. , 166.512],
[ 0. , 2. , 0. , 726. ]], ## ModelTrigger:0
[[-156.577, 0. , -148.846, 1.778],
[ 0.81 , 0. , 0.587, 253.614],
[ 0. , 1. , 0. , 0. ],
[ 0. , 1. , 0. , 726. ]],
[[ -95. , 0. , -104.211, 2.166],
[ -0.81 , 0. , 0.587, 177.561],
[ 0. , -1. , 0. , 169.042],
[ 0. , 1. , 0. , 726. ]],
[[-238.764, 0. , -0. , 3.071],
[ -0.81 , 0. , 0.587, 12.404],
[ 0. , -1. , 0. , -1. ],
[ 1. , 2. , 0. , 726. ]],
...
ModelTrigger "pre-triggering" dist1 ahead as DoIt will shift by dist1
Starts at upper left, here -->
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] == '@'
| Current FastSim POM | 4 Solid, 4 LV, 4 PV |
| Custom Boundary POM | 2 Solid, 2 LV, 2 PV |
CustomART<J>::maybe_doIt
template<typename J>
inline char CustomART<J>::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 "JPMT.h"
#include "Layr.h"
#include "U4Touchable.h"
template<typename J> struct CustomART
{
J* parameter_accessor ;
G4double& theTransmittance ;
G4double& theReflectivity ;
G4double& theEfficiency ;
const G4ThreeVector& theGlobalPoint ;
const G4ThreeVector& OldMomentum ;
const G4ThreeVector& OldPolarization ;
const G4ThreeVector& theRecoveredNormal ;
const G4double& thePhotonMomentum ;
CustomART(
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 );
};
TO BT BT BT BT SR SR BT BR BR BT SR SR SR BT BR BT SR BT SA 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 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 |
Save/Load g4state into NP array
Efficiently save/restore thousands of states
MixMaxRng (G4 1042 random engine)
State of engine : 38*uint32
U4Engine::SaveState( NP* states, int idx ); U4Engine::RestoreState( NP* states, int idx );
Rerun "big bouncer" photon
vi U4SimulateTest.sh # SRM_G4STATE_SAVE N=0 ./U4SimulateTest.sh # save g4state into ALL0 vi U4SimulateTest.sh # SRM_G4STATE_RERUN N=1 ./U4SimulateTest.sh # load ALL0, save SEL1
CustomG4OpBoundaryProcess ?
BUT : need to change very nasty Geant4 impl:
CPU
GPU
More standard approach and simpler geometry makes it easier