Tupling from HLT2

This example shows how to read an HLT2 output file and create a n-tuple. It defines various functors operating on the head and daughters of the decay channel and creates a FunTuple.

from PyConf.reading import get_particles, get_pvs
import Functors as F
from DaVinciMCTools import MCTruthAndBkgCat
from FunTuple import FunctorCollection
from FunTuple import FunTuple_Particles as Funtuple
import FunTuple.functorcollections as FC
from DaVinci.algorithms import create_lines_filter
from DaVinci import Options, make_config


def main(options: Options):
    fields = {
        "D0": "[D0 -> K- pi+]CC",
        "Kminus": "[D0 -> ^K- pi+]CC",
        "piplus": "[D0 -> K- ^pi+]CC",
    }

    # Creating v2 reconstructed vertices to be used in the following functor
    v2_pvs = get_pvs()

    d0_variables = FunctorCollection(
        {
            "ID": F.PARTICLE_ID,
            "KEY": F.OBJECT_KEY,
            "PT": F.PT,
            "PX": F.PX,
            "PY": F.PY,
            "PZ": F.PZ,
            "ENERGY": F.ENERGY,
            "P": F.P,
            "FOURMOMENTUM": F.FOURMOMENTUM,
            "BPVDIRA": F.BPVDIRA(v2_pvs),
            "BPVFDCHI2": F.BPVFDCHI2(v2_pvs),
            "BPVIPCHI2": F.BPVIPCHI2(v2_pvs),
            # The ^ character denotes the particle, which should be treated as mother (first argument) or
            # child (second argument), for which the helicity angle should be estimated
            # PS: in the case of D0, the ^ is not needed as it will be picked by default (as head of a decay chain)
            "COSTHETA": F.COS_SIMPLIFIED_HELICITY_ANGLE(
                "[D0 -> K- pi+]CC", "[D0 -> ^K- pi+]CC"
            ),
        }
    )

    daughter_variables = FunctorCollection(
        {
            "ID": F.PARTICLE_ID,
            "PT": F.PT,
            "PX": F.PX,
            "PY": F.PY,
            "PZ": F.PZ,
            "ENERGY": F.ENERGY,
            "P": F.P,
            "FOURMOMENTUM": F.FOURMOMENTUM,
        }
    )

    variables = {
        "D0": d0_variables,
        "Kminus": daughter_variables,
        "piplus": daughter_variables,
    }

    line_name = "Hlt2Charm_D0ToKmPip_Line"
    d02kpi_data = get_particles(f"/Event/HLT2/{line_name}/Particles")

    my_filter = create_lines_filter(name="HDRFilter_D0Kpi", lines=[f"{line_name}"])

    # get configured "MCTruthAndBkgCatAlg" algorithm for HLT2 output
    MCTRUTH = MCTruthAndBkgCat(d02kpi_data, name="MCTruthAndBkgCat_hlt2")
    trueid_bkgcat_info = {
        # Important note: specify an invalid value for integer functors if there exists no truth info.
        #                 The invalid value for floating point functors is set to nan.
        "TRUEID": F.VALUE_OR(0) @ MCTRUTH(F.PARTICLE_ID),
        "TRUEKEY": F.VALUE_OR(-1) @ MCTRUTH(F.OBJECT_KEY),
        "TRUEPT": MCTRUTH(F.PT),
        "TRUEPX": MCTRUTH(F.PX),
        "TRUEPY": MCTRUTH(F.PY),
        "TRUEPZ": MCTRUTH(F.PZ),
        "TRUEENERGY": MCTRUTH(F.ENERGY),
        "TRUEP": MCTRUTH(F.P),
        "TRUEFOURMOMENTUM": MCTRUTH(F.FOURMOMENTUM),
        "BKGCAT": MCTRUTH.BkgCat,
    }
    for field in variables.keys():
        variables[field] += FunctorCollection(trueid_bkgcat_info)
    trueid_bkgcat_info_d0 = {
        # The same rules regarding ^ character as in case of COS_SIMPLIFIED_HELICITY_ANGLE functor apply here
        "TRUECOSTHETA": MCTRUTH(
            F.MC_COS_SIMPLIFIED_HELICITY_ANGLE("[D0 -> K- pi+]CC", "[D0 -> ^K- pi+]CC")
        ),
    }
    variables["D0"] += FunctorCollection(trueid_bkgcat_info_d0)

    ##doesn't work since no "decayProducts" method in MCParticle
    # sort of related issue (https://gitlab.cern.ch/lhcb/Rec/-/issues/356)
    # variables['D0'] += FunctorCollection({'TRUEPT_Kaon': MCTRUTH(F.CHILD(1, F.PT))})

    # define event level variables (just as an example)
    evt_variables = FC.SelectionInfo(selection_type="Hlt2", trigger_lines=[line_name])
    # rename the long branch name to something shorter
    evt_variables["Hlt2Decision"] = evt_variables.functor_dict.pop(
        "Hlt2Charm_D0ToKmPip_LineDecision"
    )
    ##For now remove: The 'Hlt2' line decision is written okay but breaks unit test with an error.
    ##This is possibly related to having long TBranch names.
    ##see linked issue here: https://gitlab.cern.ch/lhcb/DaVinci/-/merge_requests/654#note_5320732
    # evt_variables.pop('Hlt2Charm_D0ToKmPip_LineDecision')

    # define FunTuple instance
    my_tuple = Funtuple(
        name="Tuple",
        tuple_name="DecayTree",
        fields=fields,
        variables=variables,
        event_variables=evt_variables,
        inputs=d02kpi_data,
    )

    return make_config(options, [my_filter, my_tuple])

To run the example:

lb-run DaVinci/vXXrY lbexec DaVinciExamples.tupling.option_davinci_tupling_from_hlt2 $DAVINCIEXAMPLESROOT/example_data/FEST_November_2021_dst_newPacking.yaml

For reference, these are the options of this example

testfiledb_key: FEST_November_2021
input_manifest_file: root://eoslhcb.cern.ch//eos/lhcb/wg/dpa/wp3/tests/hlt2_D0_Kpi_100evts_newPacking_newDst.tck.json
histo_file: v2_example_histos.root
ntuple_file: v2_example_ntuples.root
input_process: Hlt2
input_stream: default
print_freq: 1000
input_raw_format: 0.5
persistreco_version: 0.0
lumi: False
write_fsr: False