Rivet tutorials
Instructions for writing a Rivet routine/analysis
At this point you have probably run Rivet a few times, maybe different analyses, different generators, and played around with plotting options. To really utilize the full power of the framework, one does, however, need to write analysis code – it is an analysis framework after all.
The best tip for writing analyses, is to find an existing similar analysis, from the large library of already existing ones, and take inspiration from that. But even then, it is important to have the basics right.
Writing the analysis code
Here we are going to write a new analysis for use with Rivet. This is done “stand-alone”, i.e. you don’t have to modify the code of Rivet itself: in fact, you can follow these instructions using a system install of Rivet to which you have no write permissions.
All analysis routines are implemented as sub-classes of the Rivet “Analysis” class: pretty much all the magic that binds the analysis object into the Rivet system is handled in this base class, meaning that your code can really concentrate on implementing the physics goals of the analysis.
The analysis “wizard”
You could make your analysis by copying some example code and then
going through a load of search and replace fiddling, but in fact there
is a much easier way: the rivet-mkanalysis script. This
script is installed along with the rest of the Rivet system, and will
generate template analysis files for you.
You can get some help info by running
rivet-mkanalysis --help, but the basic usage (to generate
the files in your current directory) is
rivet-mkanalysis MY_ANALYSIS_NAME. A three part name,
separated by underscores, is a Rivet convention that we recommend you to
use: the first part is the experiment name, the second is the year of
publication, and the third is the ID code for the corresponding paper in
the Inspire HEP database, preceded
by an “I”. You can get the Inspire ID from an Inspire record page by
looking at the URL: it will be the numerical trailing part of the
address following record/, e.g. 849050 in the record http://inspirehep.net/record/849050.
So, for example, ATLAS_2010_I849050 would be the
standard name for the analysis link given above… although in fact that
one (the first ATLAS minimum bias paper) is in the Rivet collection
under the name ATLAS_2010_S8591806, which uses the older
‘’SPIRES’’ database ID. Please use Inspire IDs rather than SPIRES ones
for new analyses – we intend to update all analyses to the Inspire
naming in a future release.
Running the rivet-mkanalysis script with the appropriate
analysis name will have generated a .cc C++ source file
template, and template metadata files for information about the analysis
(.info) and specifications of titles, axis labels, etc. for
the plots which the analysis will produce (.plot). These
templates will include, if possible, extra analysis metadata such as a
BibTeX publication entry in the .info file.
Structure
For simplicity, Rivet analysis classes are usually written in just
one .cc file, i.e. no header declaration. This is because
classes are almost always not inherited from, and all that the Rivet
system needs to know is that it can be treated as an
Analysis* pointer: avoiding header files makes everything
more compact and removes a source of errors and annoyance.
An analysis has the following components: * a no-argument
constructor; * three analysis event loop methods: init,
analyze and finalize; * a minimal hook into
the plugin system
It is also possible to add some metadata methods which describe the
analysis, references to publications, experiment, etc., but we strongly
recommend that you put this information into the “YAML” format (see
http://www.yaml.org) .info template that the
rivet-mkanalysis script generated for you instead: this way
the code will remain clean and minimal, and you can update the metadata
without needing to recompile. All analyses bundled with Rivet store
their metadata in external files.
Useful analyses also contain member variables for the analysis: event
weight counters and histograms are the most common of these.
Conventionally, we declare the class member variables with a leading
underscore: see the Coding Style Guide for
more information on our recommended uniform coding style. Histogram
pointer members (for which we use special smart pointers with clever
machinery inside) are preferred to start with an “h”,
e.g. Histo1DPtr _h_pT.
Implementation
The constructor and three event loop methods are used for the following:
- Constructor: set whether the generator cross-section is needed. Minimal!
init: book histograms, initialise counters, etc.analyze: select particles, filter according to cuts, loop over combinations, construct observables, fill histograms. This is where the per-event aspect of the analysis algorithm goes.finalize: normalize/scale/divide histograms, tuples, etc.
This implementation is intentionally similar to other analysis systems to minimize the learning curve when switching to Rivet.
Rivet provides implementations of many calculational tools, called
“projections”, which are just objects that calculate observables. (They
automatically cache their results, to make Rivet automatically
efficient, but you don’t have to worry about that since it’s, well,
automatic.) The projections are used by calling the analysis’
apply(event) method. This will return a const reference to
the completed projection object and takes the type of the reference as a
template argument, e.g.
const FinalState& cfs = apply<FinalState>(event, "Tracks");
The name “Tracks” here will have been registered in the
init method as referring to a projection of type
ChargedFinalState — a calculator which provides a list of
charged particles with certain basic cuts applied. This is done via the
declare method. Note that a) you don’t have to manage the
memory yourself, and b) polymorphism via the reference is both allowed
and encouraged. If b) means nothing to you, don’t worry… we just want to
reassure C++ fiends who might think we’re cramping their style!
See the dedicated tutorial on projections for more information.
Example
Here is an example of the whole Rivet analysis shebang. As usual, it
is just a single .cc file:
#include "Rivet/Analysis.hh"
#include "Rivet/Projections/FinalState.hh"
#include "Rivet/Projections/ChargedFinalState.hh"
#include "Rivet/Projections/FastJets.hh"
namespace Rivet {
class MyAnalysis : public Analysis {
public:
/// Default constructor
RIVET_DEFAULT_ANALYSIS_CTOR(MyAnalysis);
/// @name Analysis methods
//@{
void init() {
const FinalState fs(Cuts::abseta < 5);
declare(FastJets(fs, JetAlg::ANTIKT, 0.5), "Jets");
declare(ChargedFinalState(Cuts::abseta < 2.5 && Cuts::pT > 500*MeV), "Tracks");
}
void analyze(const Event& event) {
const Jets& jets = apply<ChargedFinalState>(event, "Jets")
.jetsByPt(Cuts::pT > 20*GeV && Cuts::abseta < 4.4);
MSG_DEBUG("Jet multiplicity = " << jets.size());
const Particles& trks = apply<FinalState>(event, "Tracks").particles();
MSG_DEBUG("Track multiplicity = " << trks.size());
}
// No histos, so no need for a finalize()!
//@}
};
// Magic required by the plugin system
RIVET_DECLARE_PLUGIN(MyAnalysis);
}
Cut objects
Note the use of objects in the Cuts namespace to specify
kinematic cuts on particles or jets selected by projections, or returned
from them as lists. These predefined objects of type
Rivet::Cut can be combined together using arbitrary
combinations of logical operators, with the combined object also being
of type Cut.
Many functions in Rivet accept a (potentially compound)
Cut as an argument, so this is a very flexible,
unambiguous, and human-readable way to express analysis selection logic.
There is not much to know from the user point of view beyond what you
see above!
The standard Rivet predefined cuts are (all in the
Rivet::Cuts namespace): pT, Et,
mass, phi, eta,
abseta, rap, absrap.
Compiling and linking
To use your new analysis, you need to build it into a Rivet analysis
plugin library, with a name of the form Rivet*.so library.
You can do this manually, but to make life easier there is again a
helper script, used as follows:
rivet-buildplugin RivetMyAnalyses.so MyAnalysis.cc MyOtherAnalysis.cc # etc.
Note that the name of the library has to start with the word “Rivet”
or it will not get loaded at runtime. By default, if no “.so” first
argument is given, the name RivetAnalysis.so will be
used.
Running
You can now use your new analysis right away. Provided that the
RivetMyAnalysis.so shared library file, or a
similarly-named symbolic link to it, is in a directory listed in your
RIVET_ANALYSIS_PATH environment variable, it will work
right away with the rivet command:
> ls
RivetMyAnalysis.so MyAnalysis.cc
> export RIVET_ANALYSIS_PATH=$PWD
> rivet --list-analyses
[...]
MYANALYSIS
> rivet --show-analysis MYANALYSIS
MyAnalysis
==========
Inspire ID: <Insert the Inspire ID>
Inspire URL: https://inspirehep.net/literature/<Insert the Inspire ID>
Experiment: <Insert the experiment name>(<Insert the collider name>)
Year of publication: <Insert year of publication>
Description:
A do-nothing analysis for demonstrating how to make a plugin
References:
Alternatively, you can use the --analysis-path flag to
rivet:
> rivet --list-analyses --analysis-path=$PWD
Making it useful
Hopefully that’s enough to get you started. The other main things to learn are booking (and “auto-booking”) of histograms and other data objects, and use of the Rivet projections and analysis objects. For this, we recommend that you take a look at the code of some of the standard analyses, and read more information about projections and histogramming.
If you need “reconstruction level” objects, i.e. similar to what
might be seen in an experiment with resolution effects and
reconstruction biases applying to jets and particles, you can set up
emulation of those effects using the SmearedParticles,
SmearedJets and SmearedMET projections, and a
large family of standard smearing functions found in the smearing
headers in include/Rivet/Tools/. If using these, it can be
useful to temporarily disable the smearing so you can see how big an
effect it has: to do this and return to perfect-accuracy truth objects,
set the environment variable RIVET_DISABLE_SMEARING=1 at
runtime.