DecayTreeFitter with PV constraints

This example shows how to use DecayTreeFitter with PV constraints. It creates two ntuples. The first ntuple is filled with versions of the DecayTreeFitter algorithm:

  1. use a constraint to the PV associated to the particle (“OWNPV”);

  2. use a constraint to the best PV from a list;

  3. perform multiple fits, each with another PV from the list in the event (WH advices against using this).

To fill the second ntuple, the PV associated to each candidate is first unbiased. Then it fits the new particle list with DecayTreeFitter using the associated PV (method A).


from PyConf.reading import get_particles, get_pvs, get_extended_pvs
from DecayTreeFitter import DecayTreeFitter
from FunTuple import FunTuple_Particles as Funtuple
from FunTuple import functorcollections as FC
from DaVinci.algorithms import create_lines_filter
from DaVinci import Options, make_config
from PyConf.Algorithms import ParticleUnbiasedPVAdder


def main(options: Options):
    B_Line = "Hlt2B2CC_BsToJpsiPhi_Detached"
    B_Data = get_particles(f"/Event/HLT2/{B_Line}/Particles")

    my_filter = create_lines_filter(name="HDRFilter_Bs2JpsiPhi", lines=[B_Line])

    # DTF works for both v1 and v2 vertices
    pvs = get_pvs()

    fields = {
        "Bs": "[ B_s0 -> (J/psi(1S) -> mu+ mu-) (phi(1020) -> K+ K-) ]CC",
        "Jpsi": "[ B_s0 -> ^(J/psi(1S) -> mu+ mu-) (phi(1020) -> K+ K-) ]CC",
        "Phi": "[ B_s0 -> (J/psi(1S) -> mu+ mu-) ^(phi(1020) -> K+ K-) ]CC",
        "MuP": "[ B_s0 -> (J/psi(1S) -> ^mu+ mu-) (phi(1020) -> K+ K-) ]CC",
        "MuM": "[ B_s0 -> (J/psi(1S) -> mu+ ^mu-) (phi(1020) -> K+ K-) ]CC",
        "KP": "[ B_s0 -> (J/psi(1S) -> mu+ mu-) (phi(1020) -> ^K+ K-) ]CC",
        "KM": "[ B_s0 -> (J/psi(1S) -> mu+ mu-) (phi(1020) -> K+ ^K-) ]CC",
    }

    # create a new B list with unbiased PVs
    B_Data_unbiasedpv = ParticleUnbiasedPVAdder(
        InputParticles=B_Data, PrimaryVertices=get_extended_pvs()
    ).OutputParticles

    DTF_UnbiasedPV = DecayTreeFitter(
        name="DTF_UnbiasedPV",
        input_particles=B_Data_unbiasedpv,
        mass_constraints=["B_s0", "J/psi(1S)"],
        constrain_to_ownpv=True,
    )

    DTF_OwnPV = DecayTreeFitter(
        name="DTF_OwnPV",
        input_particles=B_Data,
        mass_constraints=["B_s0", "J/psi(1S)"],
        constrain_to_ownpv=True,
    )

    DTF_BestPV = DecayTreeFitter(
        name="DTF_BestPV",
        input_particles=B_Data,
        mass_constraints=["B_s0", "J/psi(1S)"],
        input_pvs=pvs,
        fit_all_pvs=False,
    )

    DTF_AllPVs = DecayTreeFitter(
        name="DTF_AllPVs",
        input_particles=B_Data,
        mass_constraints=["B_s0", "J/psi(1S)"],
        input_pvs=pvs,
        fit_all_pvs=True,
    )

    variables = {
        "Bs": FC.DecayTreeFitterResults(
            DTF=DTF_OwnPV,
            prefix="DTF_OwnPV",
            decay_origin=True,
            with_lifetime=True,
            with_kinematics=False,
        )
        + FC.DecayTreeFitterResults(
            DTF=DTF_BestPV,
            prefix="DTF_BestPV",
            decay_origin=True,
            with_lifetime=True,
            with_kinematics=False,
        )
        + FC.DecayTreeFitterResults(
            DTF=DTF_AllPVs,
            prefix="DTF_AllPVs",
            decay_origin=True,
            with_lifetime=True,
            with_kinematics=False,
        )
        + FC.ParticlePVInfo(),
        "ALL": FC.DecayTreeFitterResults(
            DTF=DTF_BestPV,
            prefix="DTF_BestPV",
            decay_origin=False,
            with_lifetime=False,
            with_kinematics=True,
        )
        + FC.DecayTreeFitterResults(
            DTF=DTF_AllPVs,
            prefix="DTF_AllPVs",
            decay_origin=False,
            with_lifetime=False,
            with_kinematics=True,
        ),
    }

    # Configure Funtuple algorithm
    funtuple = Funtuple(
        name="JpsiPhi_Tuple",
        tuple_name="DecayTree",
        fields=fields,
        variables=variables,
        inputs=B_Data,
    )

    # FIXME: Because the unbiased PVs are associated to a 'copy' of the Particle,
    # we need to create a separate funtuple alg. Is there a way to solve this?
    variables_unbiased = {
        "Bs": FC.DecayTreeFitterResults(
            DTF=DTF_UnbiasedPV,
            prefix="DTF_UnbiasedPV",
            decay_origin=True,
            with_lifetime=True,
            with_kinematics=False,
        )
        + FC.ParticlePVInfo()
    }

    funtupleunbiased = Funtuple(
        name="JpsiPhiUnbiased_Tuple",
        tuple_name="DecayTree",
        fields=fields,
        variables=variables_unbiased,
        inputs=B_Data_unbiasedpv,
    )

    # Run
    algs = {
        "JpsiPhi_Tuple": [my_filter, funtuple, funtupleunbiased],
    }
    return make_config(options, algs)

To run the example:

lbexec DaVinciExamples.tupling.option_davinci_tupling_DTF_pvs:main $DAVINCIEXAMPLESROOT/example_data/test_passthrough_thor_lines.yaml

For reference, these are the options of this example

input_files:
- root://eoslhcb.cern.ch//eos/lhcb/wg/dpa/wp3/tests/logs/spruce_bsjpsiphi_passthrough_with_pv_pointer..dst
input_manifest_file: root://eoslhcb.cern.ch//eos/lhcb/wg/dpa/wp3/tests/logs/spruce_bsjpsiphi_passthrough_with_pv_pointer.tck.json
input_type: ROOT
simulation: true
conddb_tag: sim-20180530-vc-md100
dddb_tag: dddb-20180815
conditions_version: master
geometry_version: run3/trunk
histo_file: passthrough_histos.root
ntuple_file: passthrough_tuple.root
input_process: TurboPass
print_freq: 1000
persistreco_version: 0.0
lumi: False
write_fsr: False