TQObservable
The TQObservable class acts on a TQSample with the aim of extracting
numerical data from a TTree inside the event loop.
TQObservable itself is an abstract base class that is inherited from by
different observable variants. The most notable ones are
- TQTreeFormulaObservable: A wrapper around TTreeFormula, capable of
evaluating arithmetic expressions on the TTree branches.
- TQConstObservable: A trivial observable that has the same value for
all events of a tree. While generally being of limited use, the existence
of this observable type allows fast and convenient definition of cuts by
providing an extremely fast way of implementing trivial cut or weight
expressions.
- TQMVATreeObservable: A more elaborate observable type that acts as a
wrapper around a TMVA::Reader object and is capable of evaluating
multivariate methods on the fly while only requiring a filepath to a
valid TMVA weights.xml file as input.
- TQMultiTreeObservable: A wrapper for an expression that consists of only
constant values and names of other TQObservables, where the latter
are required to be wrapped in brackets [...]. This observable type is
required to cut on values provided by TQMVATreeObservable, but can also
be used to combine outputs from different MVA methods or custom
observable types.
While any type of TQObservable may also be instantiated directly, it is
generally advisable to use the factory functions
TQObservable::getTreeObservable(name,expression): creates a
TQObservable with the given name and expression. The type of
observable created depends on the expression and may be indicated by a
prefix if necessary (see function documentation of
TQObservable::createObservable for details). The observable is
automatically added to the observable database and is only created if
necessary - if an observable with the same name and expression already
exists, it is retrieved instead. If an observable with the same name, but
a different expression exists, an error message is generated.
TQObservable::getTreeObservable(expression): same as above, but the
expression is also used as the name.
The pure getter functions TQObservable::getTreeObservableByName(name)
and TQObservable::getTreeObservableByExpression(expression) do not
create observables, but only retrieve them from the central database.
Defining Custom Observable Classes
The TQObservable class can and should be used to define custom observable
classes to implement a more complex behaviour. In order to have your custom
observable class operate as expected, you need to implement the following
methods:
double getValue() const;
This method returns the value of this observable. When implementing this
method, please bear in mind that it will be called once for each event
in all trees, possibly various times at each cut step. Try to implement
this method as efficient as possible, even if this means spending more
computation time in the other functions.
bool initializeSelf();
This method is supposed to initialize your observable in such a way that
your getValue can safely be called. When this method is called, the
values of the fields fSample, and fToken are already set and
initialized, and you may access them in any way you seem fitting.
If all went well, this method should return true. If an error occurred,
you need to undo any changes performed previously and return false.
bool finalizeSelf();
This method is supposed to finalize your observable in such a way that
all changes performed by initialzeSelf are undone and all data members
allocated by initializeSelf are properly freed. The values of the
fields fSample and fToken are still set and valid, and you
may access them in any way you seem fitting. If all went well, this
method should return true. In the unlikely case of an error, this method
should return false.
TObjArray* getBranchNames(TQSample* s) const;
This method is supposed to return a TObjArray containing TObjStrings,
each representing the name of a branch of a tree. The argument with
which this function is called is a pointer to a TQSample object which
contains the tree and possibly meta-information in the form of tags. A
typical implementation of this method may look like this:
TObjArray* getBranchNames(TQSample* s) const {
return TQUtils::getBranchNames(this->getCompiledExpression(s));
}
Here, the called to TQObservable::getCompiledExpression produces the
final expression string, taking into account all meta-information of the
sample, and TQUtils::getBranchNames parses the expression to extract all
literal strings that may represent branch names.
Depending on the complexity of your observable class, it may be required to
implement
void setExpression(const TString&) as well. If you
re-implement this method, you should call it in your constructor to ensure
consistent behaviour of your class.
For examples on how an actual implementation might look like, you may want
to browse the source code of any of the aforementioned observable types.
You may also choose to modify TQObservable::createObservable to be
capable of creating your own observable type. For this purpose, you might
choose a prefix string to help identifying your observable type.
When you have implemented and created an instance of your class, you may use
it as follows:
- call TQObservable::printObservables() to see if it's in the database
- add your observable to a cut by either of the following methods:
- call TQCut::setCutObservableName or TQCut::setWeightObservableName
with the name of your observable
- call TQCut::setCutObservableName or TQCut::setWeightObservableName
with the name of your observable
- provide the name at construction time when creating via
TQFolder::importFolder by setting the tags .cutObservableName or
.weightObservableName of the folder in question
- choose the name of your observable as CUTNAME_cut or CUTNAME_weight
in the first place, where CUTNAME is the name of the cut in
question
- add your observable to a histogram, eventlist or anything else by
providing the name or expression of the cut as an argument to the
associated analysis job
Of course, if you have modified TQObservable::createObservable to
support your observable type, you don't have to add the observable yourself
but can instead rely on automatic creation of your observable at the time it
is requested by the analysis chain. You may, however, want to check if
everything works as expected by calling
TQObservable::printObservables()
You may be surpised that the number of observables in the database is
small. This is because most observables are only created when they are first
requested. Usually, this happens when an analysis job is configured or when
a cut is assigned to a sample visitor. In the case of the cut, however, you
may force premature creation of the observables by calling
TQCut::setupObservables() on the instance in question.