Other detector/truth

Table Of Contents

Previous topic


Next topic

Truth Parameters

This Page

Daya Bay Links

Content Skeleton

Truth Information

Besides hits, information on the “true” simulated quantities is available in the form of a particle history and a collection of unobservable statistics.

Particle Histories

Geant 4 is good at simulating particles efficiently. To this end, it uses a continually-evolving stack of particles that require processing. As particles are simulated, they are permanently removed from the stack. This allows many particles to be simulated in a large event without requiring the entire event to be stored at one time.

However, users frequently wish to know about more than simply the input (primary particles) and output (hits) of a simulation, and instead want to know about the intermediate particles. But simply storing all intermediate particles is problematic for the reason above: too many particles will bring a computer’s virtual memory to it’s knees.

Particle Histories attempts to give the user tools to investigate event evolution without generating too much extraneous data. The philosophy here is to generate only what the user requests, up to the granularity of the simulation, and to deliver the output in a Geant-agnostic way, so that data may be persisted and used outside the Geant framework.

Particle History Data Objects

Let us briefly review how Geant operates. A particle is taken off the stack, and a G4Track object is initialized to hold it’s data. The particle is then moved forward a step, with an associated G4Step object to hold the relevant information. In particular, a G4Step holds two G4StepPoint representing the start and end states of the that particle.

The Particle Histories package crudely corresponds to these structures. There are two main data objects: SimTrack which corresponds to G4Track, and SimVertex which corresponds to a G4StepPoint. [1]

So, each particle that is simulated in by Geant can create a SimTrack. If the particle takes n steps in the Geant simulation, then it can create at most n+1 SimVertex objects (one at the start, and one for each step thereafter). If all vertices are saved, then this represents the finest granularity possible for saving the history of the simulation.

The data saved in a Track or Vertex is shown in Figures f:simtrack_accessors and f:simvertex_accessors. Generally speaking, a SimTrack simply holds the PDG code for the particle, while a SimVertex holds a the state: position, time, volume, momentum, energy, and the process appropriate for that point in the simulation. Other information may be derived from these variables. For instance, the properties of a particle may be derived by looking up the PDG code via the ParticlePropertiesSvc, and the material of a step may be looked up by accessing the IPVolume pointer. (If there are two vertices with different materials, the material in between is represented by the first vertex. This is not true if vertices have been pruned.)

Each track contains a list of vertices that correspond to the state of the particle at different locations in it’s history. Each track contains at least one vertex, the start vertex. Each Vertex has a pointer to it’s parent Track. The relationship between SimVertices and SimTracks is shown in Figure f:simtrack_and_simvertex.



SimTrack Accessors. A list of accessible data from the SimTrack object.

class SimTrack {
        /// Geant4 track ID
        int trackId() const;

        /// PDG code of this track
        int particle() const;

        /// PDG code of the immediate parent to this track
        int parentParticle() const;

        /// Reference to the parent or ancestor of this track.
        const DayaBay::SimTrackReference& ancestorTrack() const;

        /// Reference to the parent or ancestor of this track.
        const DayaBay::SimVertexReference& ancestorVertex() const;

        /// Pointer to the ancestor primary kinematics particle
        const HepMC::GenParticle* primaryParticle() const;

        /// Pointers to the vertices along this track. Not owned.
        const vertex_list& vertices() const;

        /// Get number of unrecordeds for given pdg type
        unsigned int unrecordedDescendants(int pdg) const;


SimVertex Accessors. A list of accessible data from the SimVertex object.

class SimVertex {
  const SimTrackReference&    track()          const;
  const SimProcess&           process()        const;
  double                      time()           const;
  Gaudi::XYZPoint             position()       const;
  double                      totalEnergy()    const;
  Gaudi::XYZVector            momentum()       const;

  double                      mass()           const; // Approximate from 4-momentum.
  double                      kineticEnergy()  const; // Approximate from 4-momentum.

  const std::vector<SimTrackReference>& secondaries() const;


Relationship between SimTrack and SimVertex Track 1 represents a primary SimTrack, and Track 2 a secondary particle created at the end of Track 1s first step. Thus, the position, time, volume, and process may be the same for the two highlighted vertices. Track 2 contains a link both to its parent track (Track 1) and to its parent vertex (Vertex 2 of Track 1). There is also a forward link from Vertex 2 of Track 1 to Track 2. Not shown is that every SimVertex has pointer to its parent SimTrack, and each SimTrack has a list of its daughter SimVertices.

The user may decide which vertices or tracks get saved, as described in Sec Creation Rules. If a SimVertex is pruned from the output, then any references that should have gone to that SimVertex instead point to the SimVertex preceeding it on the Track. If a SimTrack is pruned from the output, then any references that would have pointed to that track in fact point back to that track’s parent. The output is guaranteed to have at least one SimTrack created for each primary particle that the generator makes, and each SimTrack is guaranteed to have at least one vertex, the start vertex for that particle, so all of these references eventually hand somewhere. An example of this pruning is shown in Figure f:history_pruning.

To keep track of this indirect parentage, links to a SimTrack or SimVertex actually use lightweight objects called SimTrackReference and SimVertexReference. These objects record not only a pointer to the object in question, but also a count of how indirect the reference is.. i.e. how many intervening tracks were removed during the pruning process.





History Pruning The first figure shows a hypothetical case before pruning. The second case shows the links after pruning Track 2. The dotted lines indicate that the data objects record that the links are indirect.

Because pruning necessarily throws away information, some detail is kept in the parent track about those daughters that were pruned. This is kept as map by pdg code of “Unrecorded Descendents”. This allows the user to see, for instance, how many optical photons came from a given track when those photons are not recorded with their own SimTracks. The only information recorded is the number of tracks pruned - for more elaborate information, users are advised to try Unobservable Statistics.

To get ahold of Particle Histories, you need to get the SimHeader. Each running of the Geant simulation creates a single SimHeader object, which contains a pointer to a single SimParticleHistory object. A SimParticleHistory object contains a list of primary tracks, which act as entrance points to the history for those who wish to navigate from first causes to final state. Alternatively, you may instead start with SimHit objects, which each contain a SimTrackReference. The references point back to the particles that created the hit (e.g. optical photons in the case of a PMT), or the ancestor of that particle if its been pruned from the output.

Creation Rules

The Historian module makes use of the BOOST “Spirit” parser to build rules to select whether particles get saved as tracks and vertices. The user provides two selection strings: one for vertices and one for tracks. At initialization, these strings are parsed to create a set of fast Rule objects that are used to quickly and efficiently select whether candidate G4Tracks and G4StepPoints get turned into SimTracks or SimVertices respectively.

The selection strings describe the criteria neccessary for acceptance, not for rejection. Thus, the default strings are both “none”, indicating that no tracks or vertices meet the criteria. In fact, the Historian knows to always record primary SimTracks and the first SimVertex on every track as the minimal set.

Selection strings may be:

Only the default items are selected
All items are created
An expression
which is interpreted left-to-right.

Expressions consist of comparisons which are separated by boolean operators, grouped by parentheses. For example, a valid selection string could be: — * "(pdg != 20022 and totalEnergy<10 eV) or (materialName =='MineralOil')" Each comparison must be of the form <PARAMETER OPERATOR CONSTANT [UNIT]>. A list of valid PARAMETERs is given in table t:truthiness_parameters. Valid OPERATORs consist of >,>=,<,<=,==,!= for numerical parameters, and ==,!= for string parameters. A few parameters accept custom operators - such as in for the detector element relational parameter. For numerical operators, CONSTANT is a floating-point number. For string paramters, CONSTANT should be of the form 'CaseSensitiveString', using a single quote to delimit the string. For numerical parameters, the user may (should) use the optional UNIT. Units include all the standard CLHEP-defined constants. All parameters and unit names are case-insensitive.

Boolean operators must come only in pairs. Use parentheses to limit them. This is a limitation of the parser. For instance, "a<2 and b>2 and c==1" will fail, but "(a<2 and b>2) and c==1" will be acceptable. This ensures the user has grouped his ‘and’ and ‘or’ operators correctly.

Because these selections are applied to every single G4Track and every single G4Step, having efficient selection improves simulation time. After compilation, selection is evaluated in the same order as provided by the user, left-to-right. Efficient selection is obtained if the user puts the easiest-to-compute parameters early in the selection. The slowest parameters to evaluate are those that derive from DetectorElement, including NicheID, Niche, DetectorId, SiteId, Site, AD, AdNumber, local_(xyz), DetectorElementName, etc. The fastest parameters are those that are already in the G4 data structures, such as particle code IDs, energy, global position, etc. String comparisons are of medium speed.

Examples, Tips, Tricks

Choosing specific particle types is easy. For instance, the following selects all particles except for optical photons. (This is an excellent use case for low-energy events like IBD.)

historian.TrackSelection = "(pdg != 20022)"

Here is a brief list of the more important PDG codes. A complete list can be found at the PDG website. (Antiparticles are denoted by negative numbers.)

e^- 11
\mu^- 13
\gamma 22
optical photon 20022
neutron 2112
proton 2212
\pi^0 111
\pi- 211

This example will save all tracks that are not optical photons, plus save one out of every 100 optical photons. This might be nice for an event viewer: — *

historian.TrackSelection = "(pdg != 20022) or (prescale by 100)"

This example will select any track created by a neutron capture (likely gamma rays): — *

historian.TrackSelection = "CreatorProcess == 'G4NeutronCapture'"

This should be contrasted with this example, which will save vertices with a neutron capture. This means: the vertex saved will be a neutron capture vertex, and is only valid for neutron tracks:

historian.VertexSelection = "Process == 'G4NeutronCapture'"

This example is slightly tricksy, but useful for muon-induced showers. It will select muons and particles that came off the muon, but not sub-particles of those. This lets you see delta rays or muon-induced neutrons, for example, but not record the entire shower.

historian.Track = "((AncestorTrackPdg = 13 or AncestorTrackPdg = -13)
                     and AncestorIndirection < 2)
                or (pdg == 13 or pdg == -13)"

This example selects only vertices which are inside the oil volume or sub-volume of the oil at the LingAo detector 1. i.e. in oil, AVs, or scintillator volumes: — *

historian.VertexSelection = "DetElem in '/dd/Structure/AD/la-oil1'"

This example selects vertices which are in the oil, not any subvolumes: — *

historian.VertexSelection = "DetectorElementName == '/dd/Structure/AD/la-oil1'"

This example saves only start and end vertices, as well as vertices that change materials: — *

historian.VertexSelection = "IsStopping ==1 and MaterialChanged > 0"

This example saves a vertex about every 20 cm, or if the track direction changes by more than 15 degrees:

historian.VertexSelection = "distanceFromLastVertex > 20 cm or AngleFromLastVertex > 15 deg"

Users should fill out more useful examples here.

Unobservable Statistics


Although users may be able to answer nearly any question about the history of an event with the Particle Histories, it may be awkward or time-consuming to compile certain variables. To this end, users may request “Unobservable” statistics to be compiled during the running of the code.

For instance, let us say we want to know how many meters of water were traversed by all the muons in the event. We could do this above by turning on SimTracks for all muons and turning on all the SimVertecies at which the muon changed material.

historian.TrackSelection = "(pdg == 13 or pdg == -13)"
historian.VertexSelection = "(pdg == 13 or pdg == -13)
                             and (MaterialChanged >0 )"

Then, after the event had been completed, we would need to go through all the saved SimTracks and look for the tracks that were muons. For each muon SimTrack, we would need to go through each pair of adjacent SimVertices, and find the distance between each pair, where the first SimVertex was in water. Then we would need to add up all these distances. This would get us exactly what we wanted, but considerable code would need to be written, and we’ve cluttered up memory with a lot of SimVertices that we’re only using for one little task.

To do the same job with the Unobserverable Statistics method, we need only run the “Unobserver” SteppingTask, and give it the following configuration:

UnObserver.Stats =[ ["mu_track_length_in_water" , "dx" ,
                     "(pdg == 13 or pdg == -13) and MaterialName=='Water'" ] ]

This creates a new statistic with the name mu_track_length_in_water, and fills it with exactly what we want to know!

This method is very powerful and allows the description of some sophisticated analysis questions at run-time. However, compiling many of these Statistics can be time-consuming during the execution of the simulaton. For serious, repeated analyses, using the Particle Histories may yield better results in the long run.

“Unobservable” Statistic Objects

Unobservable Statistics are stored in a SimStatistic object shown in Figure f:simstatistic.



SimStatistic A Statistic object used for Unobservable Statistics.

class SimStatistic {
    SimStatistic() : m_count(0),
                     m_squaredsum(0) {}

    double count() const;     /// Counts of increment() call
    double sum() const;       /// Total of x over all counts.
    double squaredsum() const;/// Total of x^2 over all counts.
    double mean() const;      /// sum()/count()
    double rms() const;       /// Root mean square

    void increment(double x);   /// count+=1, sum+=x, sum2+=x*x

    double m_count;      ///< No. of increments
    double m_sum;        ///< Total of x over all counts.
    double m_squaredsum; ///< Total of x^2 over all counts.

These statistic objects are stored in a map, referenced by name, in the SimUnobservableStatisticsHeader. This object in turn is stored in the SimHeader, once per simulated event.

Creation Rules

The Unobserver module operates using the same principles as the Particle History selector, above. At initialization, a selection string and variable string is parsed into a set of Rule objects that can be rapidly evaluated on the current G4Step. The user supplies a list of Statistics to the module. Each Statistic is defined as follows: — * ["STATNAME" , "VARIABLE" , "EXPRESSION"] or — *

 ... , "EXPRESSION"]

Here, STATNAME is a string of the user’s choosing that describes the statistic, and is used to name the statistic in the SimUnobservableStatisticsHeader for later retrieval. VARIABLE is a parameter listed in Table t:truthiness_parameters that is the actual value to be filled. Only numeric parameters may be used as variables. EXPRESSION is a selection string, as described in Sec. Creation Rules. In the second form of listing, several different variables may be defined using the same selection string, to improve runtime performance (and make the configuration clearer).

Any number of statistics may be defined, at the cost of run-time during the simulation.

The statistics are filled as follows. At each step of the simulation, the current G4Step is tested against each EXPRESSION rule to see if the current step is valid for that statistic. If it is, then the VARIABLE is computed, and the Statistic object is incremented with the value of the variable.

Examples, Tips, Trucks

Statistics are per-step. For example: — *

UnObserver.Stats =[ ["x_vertex" , "global_x" ,
                      "(pdg == 13 or pdg == -13)'" ] ]

will yield a statistic n entries, where n is the number of steps taken by the muon, with each entry being that step’s global X coordinate. However, you can do something like the following: — *

UnObserver.Stats =[ ["x_vertex" , "global_x" ,
             "(pdg == 13 or pdg == -13)' and IsStarting==1" ] ]

which will select only the start points for muon tracks. If you know that there will be at most one muon per event, this will yield a statistic with one entry at the muon start vertex. However, this solution is not generally useful, because a second muon in the event will confuse the issue - all you will be able to retrieve is the mean X start position, which is not usually informative. For specific queries of this kind, users are advised to use Particle Histories.

Users should fill out more useful examples here.

Parameter Reference

The Particle History parser and the Unobservable Statistics parser recognize the parameter names listed in table t:truthiness_parameters

The DrawHistoryAlg Algorithm

These lines in your python script will allow you to run the DrawHistoryAlg and the DumpUnobservableStatisticsAlg, which provide a straightforward way of viewing the output of the Particle Histories and Unobservables, respectively:

simseq.Members = [ "GiGaInputStream/GGInStream",

The DrawHistoryAlg produces two “dot” files which can be processed by the GraphViz application. (A very nice, user-friendly version of this exists for the Mac.) The dot files describe the inter-relation of the output objects so that they can be drawn in tree-like structures. Sample output is shown in Figures f:drawhistoryalg_tracks and f:drawhistoryalg_tracksandvertices.



Output of tracks file for a single 1 MeV positron. Circles denote SimTracks - values listed are starting values. In this example, do_hits was set to zero.


Output of tracks-and-vertices file for a single 1 MeV position. Boxes represent SimTracks, and circles represent SimVertecies.

The DrawHistoryAlg can be configured like so:

app.algorithm("DrawHistory").do_hits = 0
app.algorithm("DrawHistory").track_filename = 'tracks_%d.dot'
app.algorithm("DrawHistory").trackandvertex_filename = 'vertices_and_tracks_%d.dot'

The filename configuration is for two output files. Using ‘%d’ indicates that the event number should be used, to output one file per event. The do_hits option indicates whether SimHits should be shown on the plot. (For scintillator events, this often generates much too much detail.)

The DumpUnobservableStatisticsAlg algorithm simply prints out the counts, sum, mean, and rms for each statistic that was declared, for each event. This is useful for simple debugging.


[1]Another way to describe this is that a SimTrack corresponds to a single G4Trajectory, and SimVertex corresponds to a single G4TrajectoryPoint. The G4Trajectory objects, however, are relatively lightweight objects that are used by nothing other than the Geant visualization. It was decided not to use the G4Trajectory objects as our basis so as to remain Geant-independent in our output files. The similarity between the Particle Histories output and the G4Trajectories is largely the product of convergent evolution.


latexparser did not recognize : color columnwidth