DaVinci Configuration

Creating an ntuple

A basic example of creating a ntuple with FunTuple is:

import Functors as F

from Hlt2Conf.standard_particles import make_detached_mumu, make_KsDD
from FunTuple import FunctorCollection
from FunTuple import FunTuple_Particles as Funtuple

from DaVinci import Options, make_config


def main(options: Options):
    # selections
    dimuons = make_detached_mumu()
    kshorts = make_KsDD()

    # FunTuple: make fields (branches) to tuple
    fields = {}
    fields["Jpsi"] = "J/psi(1S) -> mu+ mu-"
    fields["MuPlus"] = "J/psi(1S) -> ^mu+ mu-"

    # FunTuple: make collection of functors for Jpsi
    variables_jpsi = {
        "LOKI_P": "P",
        "LOKI_PT": "PT",
        "LOKI_Muonp_PT": "CHILD(PT, 1)",
        "LOKI_Muonm_PT": "CHILD(PT, 2)",
        "LOKI_MAXPT": "TRACK_MAX_PT",
        "LOKI_N_HIGHPT_TRCKS": "NINTREE(ISBASIC & HASTRACK & (PT > 1500*MeV))",
        "THOR_P": F.P,
        "THOR_PT": F.PT,
    }

    # FunTuple: make collection of functors for Muplus
    variables_muplus = {"LOKI_P": "P", "THOR_P": F.P}

    # FunTuple: associate functor collections to field (branch) name
    variables = {}
    variables["Jpsi"] = FunctorCollection(variables_jpsi)
    variables["MuPlus"] = FunctorCollection(variables_muplus)

    # FunTuple: define list of preambles for loki
    loki_preamble = ["TRACK_MAX_PT = MAXTREE(ISBASIC & HASTRACK, PT, -1)"]

    # FunTuple: Configure Funtuple algorithm
    tuple_dimuons = Funtuple(
        name="DimuonsTuple",
        tuple_name="DecayTree",
        fields=fields,
        variables=variables,
        loki_preamble=loki_preamble,
        inputs=dimuons,
    )

    # FunTuple: similarly for Ks we can make fields (branches), functor collections, variables and FunTuple instance
    fields_KS = {}
    fields_KS["KS"] = "KS0 -> pi+ pi-"
    # associate the functor collections to KS field name (NB: here we use functor collection used for jpsi)
    variables_KS = {}
    variables_KS["KS"] = FunctorCollection(variables_jpsi)
    # funtuple instance
    tuple_kshorts = Funtuple(
        name="KsTuple",
        tuple_name="DecayTree",
        fields=fields_KS,
        variables=variables_KS,
        loki_preamble=loki_preamble,
        inputs=kshorts,
    )

    algs = {
        "DiMuons": [tuple_dimuons],
        "KShorts": [tuple_kshorts],
    }

    return make_config(options, algs)

This example can be run using lbexec with the following options.yaml file:

input_files: root://eoslhcb.cern.ch//eos/lhcb/grid/prod/lhcb/MC/Upgrade/LDST/00076720/0000/00076720_000000{02,04,43,68}_1.ldst
input_type: ROOT
input_raw_format: 4.3
simulation: true
dddb_tag: dddb-20171126
conddb_tag: sim-20171127-vc-md100
ntuple_file: basic-funtuple-example.root
evt_max: 10

How to add an event pre-filter

Event unpacking, FunTuple and many other algorithms run in a job can be very time consuming, so the processing of the input files could be much faster and efficient if an event pre-filter is applied on top of the whole job chain selecting only the events that satisfy a specific set of conditions. For example, the filter can ensure the trigger/sprucing line fired in that specific event. A filter can be instantiated directly using the create_lines_filter function:

from DaVinci.algorithms import create_lines_filter
filter = create_lines_filter("Hlt2TopoLineFilter", lines=["Hlt2Topo2BodyLineDecision"]).

Additional examples can be found here and here. Additional information on how to implement a filter code can be found here and here.

Using the configured_FunTuple wrapper

The configured_FunTuple wrapper has been implemented to make easier the configuration of a FunTuple algorithm instance in DaVinci, combining in a single step the instantiation of different objects:

  1. LoKi__HDRfilter: to select only the events passing a specific set of trigger lines,

  2. make_data_with_FetchDataFromFile: to obtain the correct DataHandle object needed by FunTuple,

  3. Funtuple: object containing all the branches and variables defined by the user.

Thanks to this wrapper the user can instantiate in the user file all three objects in the following way:

from FunTuple import FunctorCollection
import FunTuple.functorcollections as FC
from DaVinci.algorithms import configured_FunTuple
from DaVinci import Options, make_config


def main(options: Options):
    # FunTuple: define fields (branches)
    fields = {
        "B0": "[B0 -> D_s- pi+]CC",
        "Ds": "[B0 -> ^D_s- pi+]CC",
        "pip": "[B0 -> D_s- ^pi+]CC",
    }

    # FunTuple: define variables for the B meson
    variables_B = {
        "LOKI_MAXPT": "TRACK_MAX_PT",
        "LOKI_Muonp_PT": "CHILD(PT, 1)",
        "LOKI_Muonm_PT": "CHILD(PT, 2)",
        "LOKI_NTRCKS_ABV_THRSHLD": "NINTREE(ISBASIC & (PT > 15*MeV))",
    }

    # FunTuple: make functor collection from the imported functor library Kinematics
    variables_all = FC.Kinematics()

    # FunTuple: associate functor collections to field (branch) name
    variables = {
        "ALL": variables_all,  # adds variables to all fields
        "B0": FunctorCollection(variables_B),
    }

    line = "SpruceB2OC_BdToDsmPi_DsmToKpKmPim"
    config = {
        "location": f"/Event/Spruce/{line}/Particles",
        "filters": [f"{line}Decision"],
        "preamble": ["TRACK_MAX_PT = MAXTREE(ISBASIC & HASTRACK, PT, -1)"],
        "tuple": "DecayTree",
        "fields": fields,
        "variables": variables,
    }

    algs = configured_FunTuple({"B0Dspi": config})

    return make_config(options, algs)

configured_FunTuple takes as input a dictionary containing an entry for each FunTuple instance that has to be created, where the the two elements are the FunTuple name and a configuration dictionary with the following information:

  • "location": string with input location to be used as input to FunTuple,

  • "filters": list of filters to be applied in front of FunTuple,

  • "preamble": list of LoKi functors to simplify the code that is used to fill the FunTuple leaves,

  • "tree": name of the FunTuple tree,

  • "branches": dictionary with the FunTuple branches,

  • "variables": dictionary with the FunTuple variables for each branch.

configured_FunTuple(...) returns a dictionary containing lists of all the algorithms that have to be implemented for running all the defined FunTuple instances. This wrapper is meant to be used only for the simplest cases where no other algorithms have to be added between the HDRfilter and the FunTuple; however, it can still be used as starting point for more complex wrappers.

How to run a job on an XGEN file

The DaVinci application can also be run over an XGEN (extended generator) file by setting two keys in the options.yaml file:

  • Setting the input_type: ROOT

The new FunTupleMC algorithm is used to create the tuple. A working example can be found here. This example can be run with:

lb-run DaVinci/vXrY lbexec DaVinciExamples.tupling.option_davinci_tupling_from_xgen:main '$DAVINCIEXAMPLESROOT/example_data/Gauss_12143001_xgen.yaml'

Running over old simulation files produced using Brunel

For Run 3 data-taking, the Moore application is used for the offline reconstruction, a job that the Brunel application was responsible for during the Run 1 and Run 2 periods. During the writing of this section (Nov 2022), majority of the simulation samples used for Run 3 upgrade studies in the bookkeeping are old and are produced using Brunel (note that the majority of the upgrade simulation samples in the TestFileDB are new and produced using Moore). Therefore, care needs to be taken when running Run 3 DaVinci software with the old simulation files as input. Particularly, to retrieve reconstructed objects one needs to use the functions defined in RecoConf.reconstruction_objects, for example make_pvs() that retrieves the reconstructed primary vertices. This is in contrast to running over the new simulation samples produced using Moore, where the get_pvs() function defined in PyConf.reading must be used to retrieve the reconstructed primary vertices. Another example includes combining reconstructed tracks and clusters in DaVinci to build composite particles, where in case of old simulation files one makes use of the combiners defined in Hlt2Conf.standard_particles (e.g. make_detached_mumu that to builds the di-muion candidates). These same combiners can also be used when running over the new simulation files, however right inputs from PyConf.reading need to be passed to them (if in doubt please ask the experts on the DPA WP3 mattermost channel <https://mattermost.web.cern.ch/lhcb/channels/dpa-wp3-offline-analysis-tools>`__).