Introduction
Sometimes the TMBTree doesn't have all the information in it that you want.
Adding a new branch to the resulting root tuple isn't very difficult -- though
it is the type of task with lots of little things that have to be done
correctly.
The TMBTree is made up of root TTree branches. Each branch contains a certain
type of object (like a TMBGlob for the global event info) or a TClonesArray of
an object (like a TMBJets object). Root I/O allows the actual object to be
written to the TTree, and for the program using the root object to fluf
it up again. The MakeTMBTreeClasses_so.C file builds the shared object library
that root uses to read these objects.
The TMBTree is made by the program TMBAnalyze_x, which is built in the cvs
package tmb_analyze. For each TMBTree object, there is a maker. For
example -- for TMBGlob there is a TMBGlobMaker object. The TMBTree objects are
all located in the tmb_tree cvs package by convention. Most of the makers
can be found in the tmb_tree_maker package, though some are spread around
in other packages.
By convention all TMBTree objects are put in the tmb_tree package, and
all makers are put in other packages. This is because the Maker objects often
link with things from the D0 infrastructure software -- a bad thing for the
actual root objects.
What Goes Where
There is one global design decision to make when you create your new TMB
Object. Will there be only a single object in each event (like the TMBGlob
object) or will there by many (like a TMBJet object)?
- If there will be many you will be creating a TTree branch that contains a
TClonesArray of the objects.
- If there will be a single one, you'll create only one such object for the
TTree branch.
The TMBTree object should contain only the bare minimum of information that
you wish to store in the TMBTree. For example, a series of floats: px, py, pz or
similar. No D0 data structures should be stored in this object. You may have
getter and setter methods to access these variables, or you may make the
variables public so that is not required. The getter and setters can, of course,
contain code that will make it easy for the user (especially the analysis user)
to use the data. Above all, keep it simple. The TMBTree object doesn't really
participate in the making process other than as a container.
The TMBTreeMaker object can store whatever it likes -- it is only built and
running during the time the TMBAnalyze_x program is running. If you need to
store a reference to a data chunk for some reason, that is fine. The
TMBTreeMaker's job is to convert the D0 Chunk data into your TMBTree objects.
There are several important coding steps to getting a Maker up and running:
- In the constructor you should create a TClonesArray or your object and
store it in the _Fruits inherited instance variable. You will never
delete this object. This sounds funny, but you won't. You'll just
reset it (or clear it) between events. This is because root tracks the address
of this variable. The other thing you'll do in the constructor is set the
Branch Name that root will write this to.
- The next important thing is the Make method. This method is passed
an edm event, and is responsible for converting that data into your
TMBTree objects.
- Finally, the Clear method will be called between events. In this
method you should reset anything that needs reset. Be sure to call the
inherited Clear method -- that will, in the end, call the Clear
method on the _Fruits instance variable, which will reset the
TClonesArray, or your object (if you've implemented the overloaded Clear
method).
That is it!
Step-by-Step
I created a small script to help me generate these TMBTree objects and Maker
files -- there is enough boiler plate code that you can easily go wrong. The
process isn't totally smooth, as I'm only just learning it myself.
Setup Work Environment
- Create a new local release. I did most of my work in p15.02.00, but your
favorite will probably be just fine.
- Add the tmb_tree and tmb_tree_maker packages to the release.
- Add the /d0mino/gwatts/scripts/new_tmb to your PATH.
- Don't forget to do a d0setwa!
To Add A TMBTree Object
- Choose a name. Start the name with TMB. For example, TMBL1CTT.
- cd into the tmb_tree_maker package -- top level -- and run the
script make_tmb_obj <name>. This will create template TMBTree object and
maker files. Unfortunately, it will put both the TMBTree object and Maker in the tmb_tree_maker
package (wrote script before I knew better). So...
- Move your TMBL1CTT.cpp, _t.cpp, .hpp, and _linkdef.h files from the
tmb_tree_maker package into the tmb_tree package. Remove the TMBL1CTT line
from the COMPONENTS file in the tmb_tree_maker package and add it to the
tmb_tree package's COMPONENTS file. You will also have to fix up include paths
in the maker files.
- Next you have to instantiate your object in the framework package. In the
tmb_tree_maker src directory edit the file
TMBCorePkg.cpp -- it instantiates each maker. Add yours to the end,
following the pattern there (and you'll have to add an instance variable to
the .hpp file too).
- At this point you should be able to build everything.
- Next, add the instance variables you wish to save to the TMBL1CTT object's
definition. Add any getters or setters you like.
- Next, get the constructor for the TMBL1CTTMaker correct. If you are going
to have lots of objects and want to store them in a TClonesArray, you can
leave it as is -- the template code assumes this. If you want only a single
object, modify the _Fruits line to look like "_Fruits=new TMBL1CTT();"
- If your object is only a single one per event, make sure to change the
Clear method so it doesn't call the TMBMaker::Clear inherited
method -- that will end up deleting your object. Instead, call
_Fruits->Clear() directly, or otherwise reset your _Fruit to prepare it for
the next event.
- In the Make method, add code to extract the information you need.
Use the supplied addTMBL1CTT routine to add a new object if you need an
array. Otherwise just typecast _Fruits to your object type for a single
object.
- Compile, build, and run on a small sample. You should be able to open the
resulting tmb_tree.root file in root and see all of your objects instance
variables.
Processing the root Tree
The code modifications to process the root tree are pretty simple. You should
already have the basic up and running.
- If not there already, copy down from the macros directory a
TMBTreeClasses.C and
TMBTreeClasses_linkdef.h, and MakeTMBTreeClasses.C file.
- Edit these files and add the appropriate lines for your new class (it will
be obvious what to do when you look at these guys).
- If your new class is in a package/directory other than tmb_tree/src
and tmb_tree/tmb_tree, you'll need to add the include path to the
MakeTMBTreeClasses.C file.
- run root -q -l MakeTMBTreeClasses_so.C to make sure everything
compiles.
- If you modify the
TMBTree_bu.h file that is in the macros
subdirectory, you can add the new branch. Follow the pattern of something like
a TMBGlob for a single object, or TMBJet for data stored in a TClonesArray.
- Add code to the Loop() method of
TMBTree_bu.C and see it work!
Examples
The best way to see how it is done is to look at something that already
works!
- Single Object Style -- TMBGlob
- Multiple Object Style -- TMBJets
If you want to look at some advanced coding check out the
bcjet objects
and their makers.
Gotchas
I have been nailed by and spent an inordinate amount of time on:
- If your TMBTree object contains a pointer to any other object, make sure
that you zero out that pointer in all constructors. This will cause all
sorts of weird behavior during read-back!
- Do not delete the _Fruits object. Bad things will happen.
- If you are creating a single object, make sure you don't call
TMBMaker::Clear in your Maker's clear method. This will wind up deleting your
_Fruit, and that would be rotten. Instead, call _Fruits->Clear() on your own
(or otherwise clean up and prep for the next event).
|