Debug

Debugging optical photon propagation using NumPy + ipython

Using num_hits to debug an optical propagation is hopeless.

You need to enable photon step-by-step recording and save the corresponding arrays in NumPy .npy files. Then you can examine the parameters of all the photons including history flags at every step point of their propagation (up to a configured maximum number of step points) from interactive ipython sessions.:

ipython> a = np.load("/path/to/photon.npy")

You could then make plots drawing the paths of the photons. I recommend pyvista if your want to do that A convenient way to install pyvista is to use anaconda. The more commonly used matplotlib python plotting library is not good with 3D plotting or large data sets.

To save the arrays you need to:

export OPTICKS_EVENT_MODE=StandardFullDebug   # configure step point recording
export G4CXOpticks__simulate_saveEvent=1      # enable saveEvent from G4CXOpticks::simulate

# optionally enable logging in relevant classes
export G4CXOpticks=INFO
export SEventConfig=INFO

What OPICKS_EVENT_MODE does

To see how OPTICKS_EVENT_MODE works look at:

sysrap/SEventConfig.hh
sysrap/SEventConfig.cc

Especially:

324 int SEventConfig::Initialize() // static
325 {
326     const char* mode = EventMode();
327     int maxbounce = MaxBounce();
328
329     if(strcmp(mode, Default) == 0 )
330     {
331         SetCompMaskAuto() ;   // comp set based on Max values
332     }
333     else if(strcmp(mode, StandardFullDebug) == 0 )
334     {
335         SEventConfig::SetMaxRecord(maxbounce+1);
336         SEventConfig::SetMaxRec(maxbounce+1);
337         SEventConfig::SetMaxSeq(maxbounce+1);
338         SEventConfig::SetMaxPrd(maxbounce+1);
339
340         // since moved to compound sflat/stag so MaxFlat/MaxTag should now either be 0 or 1, nothing else
341         SEventConfig::SetMaxTag(1);
342         SEventConfig::SetMaxFlat(1);
343         SetCompMaskAuto() ;   // comp set based on Max values
344     }
345     else
346     {
347         LOG(fatal) << "mode [" << mode << "] IS NOT RECOGNIZED "  ;
348         LOG(fatal) << " options : " << Default << "," << StandardFullDebug ;
349         assert(0);
350     }

Possible arrays saved by sysrap/SEvt

The default is not saving any arrays.

Array saving is regarded as purely a debugging activity that should not be done in production, as it will greatly slow down the simulation and write many large files.

Configure what arrays to save and their limits with SEventConfig via envvars or static methods.

array shape notes
genstep (num_gs,6,4) parameters of Cerenkov/Scintillation/Torch/… generation
photon (num_ph,4,4) sysrap/sphoton.h : final photon params : position, time, mom, pol, flags
record (num_ph,16,4,4) sysrap/sphoton.h : step point records (configurable num points)
hit (num_ht,4,4) sysrap/sphoton.h : selection of photon
rec (num_ph,16,4,4) compressed rec (no longer used, use record instead)
seq (num_ph,2) sysrap/sseq.h photon level step-by-step history and material recording
prd    

SEventConfigTest

The SEventConfigTest binary can be used to dump the configuration.

epsilon:sysrap blyth$ SEventConfigTest
test_EstimateAlloc@20:
SEventConfig::Desc
       OPTICKS_EVENT_MODE          EventMode  : Default
     OPTICKS_RUNNING_MODE        RunningMode  : 0
                            RunningModeLabel  : SRM_DEFAULT
     OPTICKS_G4STATE_SPEC        G4StateSpec  : 1000:38
                            G4StateSpecNotes  :  38=2*17+4 is appropriate for MixMaxRng
    OPTICKS_G4STATE_RERUN       G4StateRerun  : -1
      OPTICKS_MAX_GENSTEP         MaxGenstep  : 1000000
       OPTICKS_MAX_PHOTON          MaxPhoton  : 1000000
     OPTICKS_MAX_SIMTRACE        MaxSimtrace  : 1000000     MaxCurandState  : 1000000
       OPTICKS_MAX_BOUNCE          MaxBounce  : 9
       OPTICKS_MAX_RECORD          MaxRecord  : 0
          OPTICKS_MAX_REC             MaxRec  : 0
          OPTICKS_MAX_SEQ             MaxSeq  : 0
          OPTICKS_MAX_PRD             MaxPrd  : 0
          OPTICKS_MAX_TAG             MaxTag  : 0
         OPTICKS_MAX_FLAT            MaxFlat  : 0
         OPTICKS_HIT_MASK            HitMask  : 64
                                HitMaskLabel  : SD
       OPTICKS_MAX_EXTENT          MaxExtent  : 1000
         OPTICKS_MAX_TIME            MaxTime  : 10
          OPTICKS_RG_MODE             RGMode  : 2
                                 RGModeLabel  : simulate
        OPTICKS_COMP_MASK           CompMask  : 262
                               CompMaskLabel  : genstep,photon,hit
         OPTICKS_OUT_FOLD            OutFold  : $DefaultOutputDir
         OPTICKS_OUT_NAME            OutName  : -
OPTICKS_PROPAGATE_EPSILON   PropagateEpsilon  :     0.0500
     OPTICKS_INPUT_PHOTON        InputPhoton  : -

test_EstimateAlloc@25: al.desc
-
epsilon:sysrap blyth$

Changing the OPTICKS_EVENT_MODE envvar to “StandardFullDebug” has a large effect on the config:

epsilon:sysrap blyth$ OPTICKS_EVENT_MODE=StandardFullDebug SEventConfigTest
test_EstimateAlloc@20:
SEventConfig::Desc
       OPTICKS_EVENT_MODE          EventMode  : StandardFullDebug
     OPTICKS_RUNNING_MODE        RunningMode  : 0
                            RunningModeLabel  : SRM_DEFAULT
     OPTICKS_G4STATE_SPEC        G4StateSpec  : 1000:38
                            G4StateSpecNotes  :  38=2*17+4 is appropriate for MixMaxRng
    OPTICKS_G4STATE_RERUN       G4StateRerun  : -1
      OPTICKS_MAX_GENSTEP         MaxGenstep  : 1000000
       OPTICKS_MAX_PHOTON          MaxPhoton  : 1000000
     OPTICKS_MAX_SIMTRACE        MaxSimtrace  : 1000000     MaxCurandState  : 1000000
       OPTICKS_MAX_BOUNCE          MaxBounce  : 9
       OPTICKS_MAX_RECORD          MaxRecord  : 10
          OPTICKS_MAX_REC             MaxRec  : 10
          OPTICKS_MAX_SEQ             MaxSeq  : 10
          OPTICKS_MAX_PRD             MaxPrd  : 10
          OPTICKS_MAX_TAG             MaxTag  : 1
         OPTICKS_MAX_FLAT            MaxFlat  : 1
         OPTICKS_HIT_MASK            HitMask  : 64
                                HitMaskLabel  : SD
       OPTICKS_MAX_EXTENT          MaxExtent  : 1000
         OPTICKS_MAX_TIME            MaxTime  : 10
          OPTICKS_RG_MODE             RGMode  : 2
                                 RGModeLabel  : simulate
        OPTICKS_COMP_MASK           CompMask  : 274814
                               CompMaskLabel  : genstep,photon,record,rec,seq,prd,hit,tag,flat,aux
         OPTICKS_OUT_FOLD            OutFold  : $DefaultOutputDir
         OPTICKS_OUT_NAME            OutName  : -
OPTICKS_PROPAGATE_EPSILON   PropagateEpsilon  :     0.0500
     OPTICKS_INPUT_PHOTON        InputPhoton  : -

test_EstimateAlloc@25: al.desc
-
epsilon:sysrap blyth$

Saving Photon Propagations into NumPy arrays

To see what G4CXOpticks__simulate_saveEvent is doing look at g4cx/G4CXOpticks.cc simulate method.

The directory where the numpy arrays is saved is based on your executable name and the event index you set with:

SEvt::SetIndex(eventid);

Enable logging in SEvt to see what it is:

export SEvt=INFO

Opticks has lots of python machinery for loading and presenting such NumPy .npy arrays in the “ana” directory and all over the place. However it is better to examine them manually using ipython to begin with, because most people will need to improve their NumPy+ipython skills to make best use of this debugging info and to be able to understand the python machinery.

Debugging Lack of Hits

When none of your photons have flagmask SURFACE_DETECT you will get no hits. You will still have gensteps, photons and records without having hits.

If your photon propagation histories are as expected and you are still not getting hits then your problem is probably that the geometry translation did not notice your sensitive detectors somehow OR that you do not have any sensitive detectors.

A common cause of this is loading a geometry from GDML and not reinstating the association.

hits are the subset of photons with flagmask matching the hitmask (default SURFACE_DETECT) so when you get no hits it means that none of your photons .flagmask has all the bits of the hitmask set.

You can of course select the hits array from the photons array using one line of NumPy, but that will just match with NumPy what the C++/CUDA would do.

You can learn about the mechanics of hit selection in:

~/opticks/notes/mechanics_of_hit_selection.rst
https://bitbucket.org/simoncblyth/opticks/src/master/notes/mechanics_of_hit_selection.rst

Checking photon propagation histories

So in ipython:

import numpy as np
r = np.load("/tmp/ami/opticks/GEOM/example_pet/ALL/007/record.npy”)

r[:,:5,0]   # (pos, time) of first 5 step point of all photons
r[0, :5, 0]   # (pos, time) of first 5 step points of the first photon (slot 0)

So to start debugging I would look at the sequence of positions, times, momentum directions and polarizations and step point flags to see if they are what I would expect.

~/opticks/ana/tests/check.sh : setup environment for NumPy debug

#!/bin/bash -l

usage(){ cat << EOU
check.sh
==========

Setup environment:

PYTHONPATH=$HOME
   allows python scripts to import opticks python machinery
   eg with  "from opticks.ana.fold import Fold"

CFBASE=$HOME/.opticks/GEOM/J004
   configures where to load geometry from

FOLD=$CFBASE/G4CXSimulateTest/ALL
   configures the directory to load event arrays from,
   the directory is up to the user

To a large degree the directory positions of geometry
and event files are controlled by the user.
However the example of versioning a geometry name "J004"
and keeping event folders within the corresponding
geometry folder is a good one to follow as it is important
to retain the connection between event data and the geometry used
to create the event data.

EOU
}

export PYTHONPATH=$HOME
export CFBASE=$HOME/.opticks/GEOM/J004
export FOLD=$CFBASE/G4CXSimulateTest/ALL

${IPYTHON:-ipython} --pdb -i check.py

~/opticks/ana/tests/check.py : python basic example

epsilon:tests blyth$ cat check.py
#!/usr/bin/env python

import numpy as np
from opticks.ana.fold import Fold
from opticks.ana.p import *
from opticks.ana.histype import HisType

if __name__ == '__main__':
    f = Fold.Load(symbol="f")
    print(repr(f))

    p = f.photon
    r = f.record
    s = f.seq
    h = f.hit

Debugging Geometry

To debug geometry issues you need to have some familiarity with the translation. It starts from G4CXOpticks::setGeometry.

210 void G4CXOpticks::setGeometry(const G4VPhysicalVolume* world )
211 {
212     LOG(LEVEL) << " G4VPhysicalVolume world " << world ;
213     assert(world);
214     wd = world ;
215
216     sim = SSim::Create();
217     stree* st = sim->get_tree();
219     tr = U4Tree::Create(st, world, SensorIdentifier ) ;
220
221
222     // GGeo creation done when starting from a gdml or live G4,  still needs Opticks instance
223     Opticks::Configure("--gparts_transform_offset --allownokey" );
224
225     GGeo* gg_ = X4Geo::Translate(wd) ;
226     setGeometry(gg_);
227 }
X4Geo::Translate
old way with loads of code, entire extg4 package
U4Tree::Create
is a simpler approach to translation that I am starting to develop which is aiming to go directly

The translation code is still in flux with both old and new approaches in use and an entire geometry model too many.:

.       extg4         CSG_GGeo
Geant4  ---->   GGeo ------->   CSG

The CSG_GGeo package translates the GGeo geometry model into CSG which gets upload to GPU.

While I dont suggest you try to understand the geometry translation in detail, you need some familiarity with how the sensitive detectors get translated in order to debug your issue.

The best way to start debugging geometry is to persist it by rerunning with:

export G4CXOpticks=INFO
export G4CXOpticks__setGeometry_saveGeometry=$HOME/.opticks/GEOM/$GEOM

The above envvar configures directory to save the geometry.

Then you can run small executables/scripts which load various parts of the persisted geometry and run tests. One, of many, of such tests is sysrap/tests/stree_test.sh Build and use that:

cd ~/opticks/sysrap/tests

./stree_test.sh build
    ## builds stree_test binary

./stree_test.sh run
    ## these load the geometry into C++ and run tests against it
    ## one of the tests dumps sensor info

./stree_test.sh ana
    ## this loads the same geometry into ipython
    ## and run tests against it