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