FunTuple event-by-event primary b- and c-hadrons and their descendants

This script demonstrates how to use the FunTuple_Event framework to create a tuple with information on the MC particles for each event to study and reconstruct the decay of prompt b and c hadrons (ancestors).

from PyConf.reading import get_mc_header
import Functors as F
from FunTuple import FunctorCollection
from FunTuple import FunTuple_Event
from DaVinci import Options, make_config


def main(options: Options):

    # filters functors to get the number of decay prongs
    inacc = F.math.in_range(2, F.ETA, 5)

    # number of charged descendants in acceptance
    num_charged_descendants_inacc = F.SUM_RANGE @ F.MAP(
        F.COUNT_IF((F.CHARGE != 0) & inacc) @ F.GET_ALL_DESCENDANTS
    )
    # number of decay prongs to reconstruct the vertex
    num_decay_prongs = (
        (num_charged_descendants_inacc @ F.FILTER((F.CHARGE == 0) & inacc))
        + F.COUNT_IF((F.CHARGE != 0) & inacc)
    ) @ F.GET_CHILDREN

    # distance between the origin vertex and the end vertex
    orig_vtx_pos = F.TOLINALG @ F.POSITION @ F.MC_ORIGINVERTEX
    end_vtx_pos = F.TOLINALG @ F.POSITION @ F.ENDVERTEX
    decaylength = F.MAGNITUDE @ (orig_vtx_pos - end_vtx_pos)

    # common functors
    common_vars = (
        ("PID", F.PARTICLE_ID),
        ("KEY", F.OBJECT_KEY),
        ("NPRONGS", num_decay_prongs),
        ("DL", decaylength),
    )
    # functors for ancestors
    ancestor_vars = (
        (("Ndescendants", F.SIZE_OF @ F.GET_ALL_DESCENDANTS),)
        + common_vars
        + (("Q", F.CHARGE),)
    )
    # functors for descendants
    children_vars = common_vars + (
        (
            "VTX_type",
            F.VALUE @ F.MC_VTX_TYPE @ F.MC_ORIGINVERTEX,
        ),
        (
            "Mother_KEY",
            F.MAP(F.VALUE @ F.MC_MOTHER(1, F.OBJECT_KEY)),
        ),
    )

    vars_ = lambda prefix, index, source, named_vars: {
        f"{prefix}_{label}[{index}]": F.FLATTEN
        @ F.MAP(F.VALUE @ F.MAP(f) @ source)
        @ F.MC_ALLPVS(mc_header=get_mc_header())
        for label, f in named_vars
    }

    # functors to filter on prompt B and C hadrons
    ancestors_if = (
        lambda pred: F.FILTER(pred & F.IS_HADRON @ F.PARTICLE_ID_OBJ)
        @ F.MC_VTX_PRODUCTS
    )
    # turn list of MC Particle into a single list of all of their descendants
    desc = F.FLATTEN @ F.MAP(F.GET_ALL_DESCENDANTS)

    evt_variables = FunctorCollection(
        vars_("B_Ancestor", "ba_indx", ancestors_if(F.HAS_BOTTOM), ancestor_vars)
        | vars_(
            "B_Children", "bc_indx", desc @ ancestors_if(F.HAS_BOTTOM), children_vars
        )
        | vars_("C_Ancestor", "ca_indx", ancestors_if(F.HAS_CHARM), ancestor_vars)
        | vars_(
            "C_Children", "cc_indx", desc @ ancestors_if(F.HAS_CHARM), children_vars
        )
    )

    # create the tuple
    my_tuple = FunTuple_Event(
        name="Tuple", tuple_name="EventInfo", variables=evt_variables
    )

    return make_config(options, [my_tuple])

To run the example:

lbexec DaVinciExamples.tupling.option_davinci_tupling_FunTupleEvent_dfei:main $DAVINCIEXAMPLESROOT/example_data/test_hlt1_trigger_decisions.yaml

For reference, these are the options of this example