Belle II Software development
charmFlavorTagger.py
1#!/usr/bin/env python3
2
3
10
11import basf2
12import variables as va
13import modularAnalysis as ma
14
15
16def charmFlavorTagger(particle_list, uniqueIdentifier='CFT_ragdoll',
17 path=None):
18 """
19 Interfacing for the Charm Flavor Tagger.
20
21 This function requires a reconstructed D meson signal particle list with a built RestOfEvent.
22
23 :param particle_list: string, particle list of the reconstructed signal D meson
24 :param uniqueIdentifier: string, database identifier for the method
25 :param path: basf2 path obj
26 :return: None
27 """
28
29 # create roe specific paths
30 roe_path = basf2.create_path()
31 dead_end_path = basf2.create_path()
32
33 # define cft specific lists to enable multiple calls, if someone really wants to do that
34 extension = particle_list.replace(':', '_to_')
35 roe_particle_list_cut = 'isInRestOfEvent == 1 and dr < 1 and abs(dz) < 3'
36 roe_particle_list = 'pi+:cft' + '_' + extension
37
38 # filter rest of events only for specific particle list
39 ma.signalSideParticleFilter(particle_list, '', roe_path, dead_end_path)
40
41 # create final state particle lists
42 ma.fillParticleList(roe_particle_list, roe_particle_list_cut, path=roe_path)
43
44 # compute ranking variable and additional CFT input variables, PID_diff=pionID-kaonID, deltaR=sqrt(deltaPhi**2+deltaEta**2)
45 rank_variable = 'opang_shift'
46 va.variables.addAlias(rank_variable, f"abs(formula(angleToClosestInList({particle_list}) - 3.14159265359/2))")
47 va.variables.addAlias("eta", "formula(-1*log(tan(formula(theta/2))))")
48 va.variables.addAlias("phi_sig", "particleRelatedToCurrentROE(phi)")
49 va.variables.addAlias("eta_sig", "particleRelatedToCurrentROE(eta)")
50 va.variables.addAlias("deltaPhi_temp", "abs(formula(phi-phi_sig))")
51 va.variables.addAlias(
52 "deltaPhi",
53 "conditionalVariableSelector(deltaPhi_temp>3.14159265359,formula(deltaPhi_temp-2*3.14159265359),deltaPhi_temp)")
54 va.variables.addAlias("deltaR", "formula(((deltaPhi)**2+(eta-eta_sig)**2)**0.5)")
55 va.variables.addAlias("PID_diff", "formula(pionID-kaonID)")
56
57 # split tracks by charge, rank them (keep only the three highest ranking) and write CFT input to extraInfo of signal particle
58 var_list = ['mRecoil', 'PID_diff', 'deltaR']
59 cft_particle_dict = {'pi+:pos_charge': ['charge > 0 and p < infinity', 'p'],
60 'pi+:neg_charge': ['charge < 0 and p < infinity', 'n']}
61
62 for listName, config in cft_particle_dict.items():
63 ma.cutAndCopyList(listName, roe_particle_list, config[0], writeOut=True, path=roe_path)
64 ma.rankByHighest(listName, rank_variable, numBest=3, path=roe_path)
65 roe_dict = {}
66 suffix = config[1]
67 for var in var_list:
68 for i_num in range(1, 3 + 1):
69 roe_dict[f'eventCached(getVariableByRank({listName}, {rank_variable}, {var}, {i_num}))'] = (
70 f'pi_{i_num}_{suffix}_{var}')
71 va.variables.addAlias(f'pi_{i_num}_{suffix}_{var}', f'extraInfo(pi_{i_num}_{suffix}_{var})')
72
73 ma.variableToSignalSideExtraInfo(listName, roe_dict, path=roe_path)
74
75 # apply CFT with MVAExpert module and write output to extraInfo
76 expert_module = basf2.register_module('MVAExpert')
77 expert_module.param('listNames', [particle_list])
78 expert_module.param('identifier', uniqueIdentifier)
79
80 expert_module.param('extraInfoName', 'CFT_out')
81
82 roe_path.add_module(expert_module)
83
84 # The CFT output probability should be 0.5 when no track is reconstructed in the ROE
85 va.variables.addAlias(
86 'CFT_prob',
87 'conditionalVariableSelector(isNAN(pi_1_p_deltaR) and isNAN(pi_1_n_deltaR),0.5,formula(1-extraInfo(CFT_out)))')
88 va.variables.addAlias('CFT_qr', 'formula(2*CFT_prob-1)')
89
90 path.for_each('RestOfEvent', 'RestOfEvents', roe_path)