A preliminary guide to some basic concepts in PhAT

V0.03 April 27, 1998.  

So you started working with C++ and Root and want to start doing some Phobos specific stuff with the Phobos Analysis Toolkit. Well, welcome! We realize that some of the concepts in PhAT are somewhat new and  this guide is meant to give some initial guidance through the labyrinth of the 50 or so Phobos specific classes.  You will probably find that the best way to program and use PhAT and Root is to always have a web browser window handy. The development of the Root team is so dynamic that by the time that you print out all of the Root classes (apparently over 600 pages of paper) you will have to start all over again. PhAT classes will not quite cost four trees worth of paper, but we hope that the classes will be about as dynamic (that is hopefully adding and not deleting features...).

That said, here are some pages that are probably useful :

Table of Contents of this guide:

  1. Data Stream
     What are Raw Hits?
     What are Hit Arrays?
     What are Hits?
     What are Tracks?
  2. Data Storage
    Example: How to iterate over tracks inside a container
  3. Sensors
     Example of how to compile classes into PhAT
  4. Compiling your own classes into PhAT
  5. Simulations

Data Stream

The following is a representation of Phobos data objects coming from the sensors and how they are processed inside PhAT. Please note that the current implementation is geared towards the Si-sensors.

Representation of the Phobos Data Stream

What are Raw Hits?

The Raw Hits are the physical hits coming from the  sensors and stored in Raw Hit Buffers (these were encoded in the TPhRawHitBuffer class in  V3.3.3 and before and are encoded in the TPhRawHitFECn0 class for the non-zero suppressed and in TPhRawHitFECs0 class for the zero suppressed data in V3.4.0). They are characterized by an FEC ID, string and channel position in the string. They carry ADC values.
 

What are Hit Arrays?

Hit Arrays are 2D arrays of the individual hits on a sensor. They are constructed from the Raw Hit Buffers by mapping FEC, String, Ch -> Sensor ID, row, col. The Hit Arrays are encoded in TPhHitArray and they are needed for both the multiplicity and the tracking software. The values stored inside the Hit Arrays are still ADC values.
 

What are Hits?

Hits are constructed from the Hit Arrays. The idea is that the tracking sensors have a list of all the hits on a given sensor and that the tracking algorithm will connect hits on different sensors to construct a track. The hits are encoded in the TPhHit class. There is also a TPhMCHit class that inherits from TPhHit. The TPhMCHit class keeps the GEANT 'Truth' information. The inheritance  guarantees that the 'Truth' information is separated from the information that is only available from the detector.  Hits have an associated energy deposition in a silicon sensor and have an X, Y, Z position (although the clustering still needs to be implemented!).
 

What are Tracks?

Tracks are the final objects coming from the track reconstruction algorithm. They are implemented in the TPhTrack class. As with the hits, there is also a TPhMCTrack class that inherits from TPhTrack.

 

Data Storage

In order to have some logical organization of the various objects in a data (.root) file, we make use of the concept of data containers. Think of them as you would of directories in a file system. If you have a certain source file, you would probably put it in a directory called src. If you have a data file you put it in a directory called data. Well, it does not work any different in PhAT, except for the fact that we have (at the moment) two different containers: TPhObjectContainer and TPhHashContainer. This has to do with the trade off between the size of the objects stored and the time it takes to look up where they are. TPhObjectContainer is meant to store objects that are numerous and do not need to be looked up very often. These objects in general inherit from TObject (in our case TPhHit and TPhTrack).  TPhHashContainer is meant for objects that are fewer in number, but need to be looked up often (TPhRawHitFECn0 is an example. There are only a maximum of 42 such objects, one per FEC, in an event. But they need to be looked up often when the sensors find their corresponding FEC and need to convert the data belonging to them to a Hit Array.  TPhHitArray is another example). The objects stored in a TPhHashContainer should inherit from TPhDataObject and should have an ASCII name (the trick here is that TPhDataObject in turn inherits from TNamed, which is a Root base class that implements a very efficient Hash function for ASCII strings). The following diagram shows the structure how things are stored within one event:

 PhAT Object Storage  

 

The user sees on the top level the data containers "MCRawHitBuffers", "MCHitArrays", "MCHits" and "MCTracks" (one can use the TPhEvent::ls() member function with the argument "C", for containers, to list them). If we have several track reconstruction packages for instance (let's get one working one, but this is meant  only as an example....), we can very simply create two data containers in the same event. One could be called "TracksA" and the other "TracksB". This way the result of both reconstruction packages is nicely separated and both packages get the same data from a container called "Hits" (to distinguish it from the Monte Carlo container "MCHits"). One can then compare the results of both reconstruction packages.

The current convention is to call the TPhRawHitFECn0 objects "FEC_1" through "FEC_42" and TPhHitArrays get the same name as the generic sensor names "PA00" etc. 
 

This table summarizes the different classes that store the data:
 

Classes containing 'Physics' Objects:   

Class Name for Real Data

Corresponding MC Class
TPhRawHitFECn0 / TPhRawHitFECs0  
TPhHitArray  
TPhHit TPhMCHit
TPhTrack TPhMCTrack

  

Container Classes for data organization (like directories in a file system):

TPhEvent TPhObjectContainer TPhHashContainer

 

Here is also a DataObject Inheritance tree. The TPhAbs* classes are used to 'abstract' the implementation. This is a C++ design method to make sure that the classes inheriting from the abstract class implement a certain behavior. Using this, it does not matter to the user if the data that he/she wants to operate on is for instance zero-suppressed or not. The calls to TPhRawHitFECn0 and TPhRawHitFECs0 dealing with the data retrieval will look exactly the same and these classes have to deal with the exact translation (another example is the way Root implements the GUI. There is an abstract interface class called TGXW which defines the way that the real classes dealing with the GUI should be implemented. Needless to say that TGX11 implementing X11 windows and TGWin32 implementing  MS Windows are very different from each other).

Inheritance Tree of PhAT Data Objects

Example: How to iterate over tracks inside a container

It might not be immediately clear how to access the objects in a container. The objects in a TPhHashContainer can always be found through the GetObject member function, but this does not work with objects in a TPhObjectContainer (since they do not have distinct names).  TPhObjectContainer on the other hand, provides a member function called GetObjectList, which gives a pointer to a TObjArray, containing all the objects inside the container. This can not  be used in TPhHashContainer, since the purpose of a hash table is to keep some slots in a list open to add more objects at these positions at a later time. A more generic way is to use an iterator over the objects in a container, since this will work for both TPhHashContainer and TPhObjectContainer.

Here is an example macro how to access some tracks in some file. Notice how we connect to the file that has the events, get the next event and find the container with the monte carlo tracks (called "MCTracks") and then create an iterator over all the tracks inside the container.

{
    // Connect a file with some events
    TPhDST *dst="new" TPhDST("some_events.root");
    TPhEvent *event;
    TPhObjectContainer *TrackContainer;
    TPhTrack *track;
    // loop over all events.
    while(event = dst->GetNextEvent()) {
        TrackContainer = (TPhObjectContainer*)event->GetObject("MCTracks");
        TIter TrackIter(TrackContainer->GetIterator());
        while (track = (TPhTrack*) TrackIter()) {
        // do stuff with track
        printf("The momentum of the track is: %g GeV/c \n", track->GetP());
        ...
        }
    }
} 

 

Sensors

The basic processing unit in  PhAT  are sensors. They are organized into subdetectors (TPhSubDetector). The subdetectors in turn belong to a detector (TPhDetector). Currently we have the following subdetectors defined:

The geometry that is inside PhAT has been generated from  AOU files that were made using the Phobos Monte Carlo (PMC). The TPhASCIIData  class translates this geometry into a structure that can be used by PhAT (eg: detector->subdetector->layer->sensor) and this structure can be written out to file. Once this is done, it can be read back into memory, creating the previous structure. This mechanism uses the notion of persistent objects and Root provides the framework that is needed to make it work. One of the general characteristics of a class is that it  has a certain state. This state is generally achieved by the data member values inside the class. The behavior of a class is defined by the member functions that operate or query the data members. Hence, by writing out to file the data values  of a class at a certain moment and then reading them back in at a later time, one can achieve the notion of a persistent object. Root provides the tools to make it in a machine independent way by performing the translations between the 'native' machine format and the generic Root types (eg, an Int_t is always 4 bytes, regardless of the machine, byte swapping etc.).

All sensors in PhAT have some generic name, which is the same as the name used in PMC. All sensors inherit from TPhSensor. We then use multiple inheritance to implement the real classes. Most sensors inherit from TPhBoxSensor, which defines the rectangular shape. In addition all the Si-sensors also inherit from TPhSiSensor. To see the subtle differences note that TPhTOFSensor inherits from TPhBoxSensor (the slats are rectangular in shape) but not from TPhSiSensor. Also TPhRingSensor does not inherit from TPhBoxSensor, but directly from TPhSensor (it is not a rectangular sensor and there are no other ring-shaped sensors at this time, so it implements its shape by it self. This might change in the future.). It also inherits from TPhSiSensor since it is a Si-sensor.

 

 Sensor Inheritance in PhAT  

 

 

Compiling your own classes into PhAT

Detailed below are the steps that the user needs to do in order to compile and link code into PhAT.

 

 Create Dictionary from your class and compile it together to make your own shared lib

 

The user needs to obey by a few simple but necessary rules. These rules are necessary in order to make the persistent object writeout and HTML generation of the code possible.

The basic idea is that the header files are parsed by a special parser called rootcint. This parser will generate two files, a declaration and an implementation file (MyCint.h/cxx in the figure).  These files code the interface to the interpreter with special wrapper functions.  When one executes a certain function in the interpreter (be it a class constructor or any member function of a compiled class) the wrapper function will be called. The wrapper function in turn will call the actual compiled function. All of this is made automatically by rootcint. rootcint also generates some additional code to implement the Run Time Type Information   , the Streamer function for the Object I/O and some member functions that one can use to peek into the data members of a class (something that ordinary C++ does not allow if the data members are declared private).

All of this is possible because  C++ has the nice feature that  the class definition (which should always be put in a separate header file!) defines a 'contract' of how the class interacts with other classes.  This contract guarantees the behavior of the class without having to look at the actual implementation.  So only the header file is needed to make rootcint work.
 

Example of how to compile classes into PhAT

The files TPhUserClass.h/cxx  in the user directory of PhAT gives an example class that users can use to compile their own classes. Just insert the code at the TODO positions, type make and it should compile. This file is meant as a template. If you want to change the class name, you should also change the file name (we use the convention that the file name is the same as the class name, chaos will result otherwise) and the Makefile in the src directory. Then you will need to change the create_cint file in the inc directory of this module. So Makefile and create_cint are the only files that need to be modified besides the source files.

Simulations

The simulation is currently a three step process. We use GEANT in the Phobos Monte Carlo to track the particles through the detector. This program generates an AOU file, containing the Tracks and the Hits in the sensors. This AOU file is then used as input to another program that does the pixelization (K.Wozniak's NPIX program). The program generates an APX file, which in addition to the Hits and Tracks also has Hit Arrays. The following figure shows the data flow.

 

 Flow of Simulation Data

The macro ConvApxToRoot.C converts the APX file to a root file with the PhAT objects. The data file contains all the data objects that are available in the APX file. In addition to this, one can generate "Fake Data" within PhAT by converting the Hit Arrays into Raw Hits.

A. Olszewski is currently working on a way to interface the PMC and GEANT routines directly into PhAT, the TPhPMC class is the class doing the conversion. This class has been included in PhAT V3.4.0, but the real linking to PMC is still not fully developed and should best be left to the experts. Use the ConvApxToRoot.C macro, that is the best bet.
 



Written by Patrick Decowski