Var and Weight

These classes take individual flux entries and return a single value, namely a variable to fill in a histogram, and a weight to apply to the entry. They both have a list of branches to read from a Dk2Nu object and a function to evaluate an output based on the branches they read. They are specifically provided to FluxReader::AddSpectra as an argument, and they are used by the Spectra class to fill histograms. Consequently, after a Var or Weight has been constructed, there is nothing the user can do with the object except provide it as an argument to configure spectra. Each Spectra will evaluate the Var and Weight when necessary.

Predefined Objects

The Var and Weight are defined and implemented in Var.h/Var.cxx and Weight.h/Weight.cxx*, respectively. Other similarly named files, Vars.h and Weights.h, provide commonly used variables and weights. Users should feel free to browse and add to these lists.

*Note: Weight.h/Weight.cxx also define the default weight and a couple of weights that do not need to read any Dk2Nu branches. The default weight multiplies together the neutrino importance weight (decay.nimpwt), a location specific propagation weight (nuray.wgt), and an energy dependent cross section. The other weights defined here are a non-weight which weights everything by 1, and a constant weight which weights everything by a user specified constant.


Constructing either a Var or Weight involves several key components. However, they behave very similarly, so the constructors are not very different. To demonstrate the constructors, the definition of the energy variable, kEnergy, will be reproduced below, done in steps and explaining each addition. Next, another variable will be demonstrated that does not access the Dk2Nu nuray branch. The difference between the Var and Weight constructors will be discussed next. Finally, a Weight using external weights will be demonstrated.

One of the variables found in Vars.h is kEnergy, which outputs the energy of the neutrino at a particular detector location. The beginning of the object definition specifies the type of object, a Var, and a name for the object, kEnergy. The const keyword specifies that the object will not change, so the function that evaluates the return value will always be the same.

  const Var kEnergy...

This can be thought of, by analogy, like the statement "int x...", or more appropriately, "const int x...", where Var is like int, and kEnergy is like x.

The first argument in the constructor is a list of branch names that the Var needs to access. The branches do not need to be listed in any particular order, but it is important to present each branch as a std::string literal (the name in quotes), separating each branch by a comma, and enclosing the full list in curly braces.

  const Var kEnergy({"nuray", "nuray.E"}...

Just like with separating arguments in any other constructor, add a comma after the closing curly brace. The next (and only other) argument is known as a lambda function (or one of many synonymous names). The first line of the lambda function, as used in Var (and Weight), most importantly specifies what arguments are given to the function. Note that no comma is added after the parenthesis enclosed list of arguments, because the syntax of a lambda function does not separate this list from the return function that follows.

  const Var kEnergy({"nuray", "nuray.E"},
                    [](const bsim::Dk2Nu* nu, const int& i_nuray)...

EVERY Var will have this line, in almost exactly the same form. The only time this changes is if the Var does not access the Dk2Nu nuray branch, and the way this affects the line is shown in the second Var created below. Continuing with kEnergy, the last part of the input is the return function, enclosed in curly braces. For kEnergy, this return function fits on one line; it simply accesses the energy stored in the i_nuray index of the Dk2Nu nuray branch (which is actually a vector of the NuRay class). Note that for more complicated functions, the function enclosed in curly braces can span multiple lines; simply separate each line by a semicolon as in normal code.

  const Var kEnergy({"nuray", "nuray.E"},
                    [](const bsim::Dk2Nu* nu, const int& i_nuray)
                    { return nu->nuray[i_nuray].E; });

White space shown is not important aside from readability and style. This style should be duplicated to make sure all Var objects appear in the same way.

The next Var will return the neutrino parent's PDG code. This value is stored in the decay.ptype variable, and the Var will be called kParType.

  const Var kParType({"decay", "decay.ptype"}...

Since this variable does not need to access the nuray branch, the next line will appear differently than in kEnergy. Specifically, i_nuray can be omit from this line, though not the type specifiers const int&.

  const Var kParType({"decay", "decay.ptype"},
                     [](const bsim::Dk2Nu* nu, const int&)...

Finally, the return function must be provided. This again can be done on one line by simply accessing the proper branch. However, the decay branch is not a vector in Dk2Nu, so the variable can be accessed directly without an index.

  const Var kParType({"decay", "decay.ptype"},
                     [](const bsim::Dk2Nu* nu, const int&)
                     { return nu->decay.ptype; });

The Weight is constructed very similarly to the Var. In fact, the main difference is the arguments provided to the lambda function. While only two arguments are provided in a Var, there are four arguments in a Weight. The transformation is (const bsim::Dk2Nu* nu, const int& i_nuray) -> (const double& w, const bsim::Dk2Nu* nu, const int& i_nuray, const TObject* extW). The first argument, w, is the default weight referenced above. This is provided as an argument to ensure the default is always calculated the same way and that the user does not have to specify that the Weight will read the requisite branches (FluxReader makes sure these branches are read). The last argument is a set of external weights that can modify the Weight output. Following the c++ rules of inheritance, this input can actually be anything that inherits from a TObject, including all types of histograms, TSpline, TGraph, etc.

In a Var, if the nuray branch is not read, then the word "i_nuray" can be omit. This is true in a Weight as well. In fact, whenever a Var or Weight does not use one of the lambda function inputs, the name of the input (i_nuray, extW, etc) can be omit, though the type (const int&, TObject*, etc) must always be specified. If this seems confusing, just try to emulate one of the previously defined objects.

The last example here shows a Weight that adjusts the default weight using a set of external weights. We will assume that we have a TGraph of weights that are energy dependent. As such, the Weight will utilize the TGraph::Eval function; see the ROOT documentation to gain familiarity with it. First, we need to provide a name and list of branches to access. The name should be descriptive so let us call it kEnergyWei. Since the TGraph is energy dependent, the correct weight will depend on the neutrino energy, which is in the nuray.E branch.

  const Weight kEnergyWei({"nuray", "nuray.E"}...

This of course access the Dk2Nu nuray branch, so i_nuray is a necessary parameter. All of the lambda function inputs are necessary for this case.

  const Weight kEnergyWei({"nuray", "nuray.E"},
                          [](const double& w, const bsim::Dk2Nu* nu, const int& i_nuray, const TObject* extW)...

Next, the return function must be specified. To get the appropriate external weight, the TGraph needs to be evaluated at the neutrino energy, so this will be stored in a local variable.

  const Weight kEnergyWei({"nuray", "nuray.E"},
                          [](const double& w, const bsim::Dk2Nu* nu, const int& i_nuray, const TObject* extW)
                          { double energy = nu->nuray[i_nuray].E; ...

The next step is very important. Currently the Weight only knows that extW is a TObject, so it can only access TObject functions. In order to access TGraph functions, the external object must be cast as a TGraph!

  const Weight kEnergyWei({"nuray", "nuray.E"},
                          [](const double& w, const bsim::Dk2Nu* nu, const int& i_nuray, const TObject* extW)
                          { double energy = nu->nuray[i_nuray].E;
                            TGraph* extG = (TGraph*)extW; ...

At this point, the TGraph can be evaluated at the correct point, and this can be used to correct the default weight, completing the Weight object.

  const Weight kEnergyWei({"nuray", "nuray.E"},
                          [](const double& w, const bsim::Dk2Nu* nu, const int& i_nuray, const TObject* extW)
                          { double energy = nu->nuray[i_nuray].E;
                            TGraph* extG = (TGraph*)extW;
                            return w*extG->Eval(energy); });