Belle II Software  release-08-01-10
B_specific_apply.py
1 #!/usr/bin/env python3
2 
3 
10 
11 # Steering file to apply the specific FEI on Belle II MC, but it can be also easily adapted for converted Belle MC.
12 # For reference see Confluence and Thomas Keck's PhD thesis.
13 #
14 # Please adapt for your signal channel.
15 # This example is for hadronic tagging.
16 
17 
18 import fei
19 import basf2 as b2
20 import modularAnalysis as ma
21 
22 # Create path
23 path = b2.create_path()
24 
25 # Load input ROOT file
26 ma.inputMdst(filename=b2.find_file('mdst14.root', 'validation', False),
27  path=path)
28 
29 # Max 12 tracks per event - this avoids much computing time.
30 empty_path = b2.create_path()
31 skimfilter = b2.register_module('VariableToReturnValue')
32 skimfilter.param('variable', 'nCleanedTracks(dr < 2 and abs(dz) < 4)')
33 skimfilter.if_value('>12', empty_path, b2.AfterConditionPath.END)
34 path.add_module(skimfilter)
35 
36 # Signal side reconstruction
37 ma.fillParticleList('mu+', 'muonID > 0.8 and dr < 2 and abs(dz) < 4', writeOut=True, path=path)
38 ma.fillParticleList('e+', 'electronID > 0.8 and dr < 2 and abs(dz) < 4', writeOut=True, path=path)
39 ma.fillParticleList(
40  'gamma',
41  '[[clusterReg == 1 and E > 0.10] or [clusterReg == 2 and E > 0.09] or [clusterReg == 3 and E > 0.16]]',
42  writeOut=True,
43  path=path)
44 ma.reconstructDecay(
45  'B+:sig_e -> gamma e+',
46  '1.000 < M < 6.000 and cos(useRestFrame(daughterAngle(0, 1))) < 0.6',
47  dmID=1,
48  writeOut=True,
49  path=path)
50 ma.reconstructDecay(
51  'B+:sig_mu -> gamma mu+',
52  '1.000 < M < 6.000 and cos(useRestFrame(daughterAngle(0, 1))) < 0.6',
53  dmID=2,
54  writeOut=True,
55  path=path)
56 ma.copyLists('B+:sig', ['B+:sig_e', 'B+:sig_mu'], writeOut=True, path=path)
57 ma.looseMCTruth('B+:sig', path=path)
58 ma.rankByHighest('B+:sig', 'daughter(0,E)', outputVariable='PhotonCandidateRank', path=path)
59 ma.buildRestOfEvent('B+:sig', path=path)
60 clean_roe_mask = (
61  'CleanROE',
62  'dr < 2 and abs(dz) < 4',
63  'clusterE9E25 > 0.9 and clusterTiming < 50 and E > 0.9 and clusterTrackMatch==0')
64 ma.appendROEMasks('B+:sig', [clean_roe_mask], path=path)
65 ma.applyCuts('B+:sig', 'roeDeltae(CleanROE) < 2.0 and roeMbc(CleanROE) > 4.8', path=path)
66 
67 skimfilter = b2.register_module('SkimFilter')
68 skimfilter.param('particleLists', ['B+:sig'])
69 empty_path = b2.create_path()
70 skimfilter.if_value('=0', empty_path, b2.AfterConditionPath.END)
71 path.add_module(skimfilter)
72 
73 
74 # FEI config prefix
75 # Set the prefix
76 # fei_tag = 'my_specFEI'
77 
78 # Here we use a prefix of an existing FEI training
79 fei_tag = 'FEIv4_2021_MC14_release_05_01_12'
80 
81 # Add the necessary database
82 # b2.conditions.prepend_globaltag('name of database containing the specific training')
83 
84 # Here we use a generic FEI training to demonstrate applying the FEI in an ROE of the signal
85 b2.conditions.prepend_globaltag(ma.getAnalysisGlobaltag())
86 
87 belle_particles = fei.get_default_channels(KLong=False,
88  chargedB=True,
89  neutralB=False,
90  semileptonic=False,
91  B_extra_cut='nRemainingTracksInEvent <= 3',
92  specific=True)
93 
94 configuration = fei.config.FeiConfiguration(prefix=fei_tag, training=False, monitor=False, cache=0)
95 feistate = fei.get_path(belle_particles, configuration)
96 
97 
98 # Run the tagging and copy the lists
99 roe_path = feistate.path
100 empty_path = b2.create_path()
101 ma.copyLists('B-:generic_final', [], writeOut=True, path=path)
102 ma.copyLists('B-:generic_final', ['B-:generic'], writeOut=True, path=roe_path)
103 ma.signalSideParticleFilter('B+:sig', '', roe_path, empty_path)
104 path.for_each('RestOfEvent', 'RestOfEvents', roe_path)
105 
106 # Reconstruct the Upsilon
107 upsilon_cut = '7.5 <= M <= 10.5 and -2.0 <= m2RecoilSignalSide <= 4.0 and -0.15 <= daughter(0,deltaE) <= 0.1'
108 ma.reconstructDecay('Upsilon(4S):hadronic -> B-:generic_final B+:sig', upsilon_cut, dmID=1, path=path)
109 ma.copyLists('Upsilon(4S):all', ['Upsilon(4S):hadronic'], path=path)
110 ma.looseMCTruth('Upsilon(4S):all', path=path)
111 
112 ma.buildRestOfEvent('Upsilon(4S):all', path=path)
113 upsilon_roe = ('UpsilonROE', 'dr < 2 and abs(dz) < 4', 'goodBelleGamma == 1')
114 ma.appendROEMasks('Upsilon(4S):all', [upsilon_roe], path=path)
115 ma.applyCuts('Upsilon(4S):all', '-2.0 < m2RecoilSignalSide < 4.0', path=path)
116 ma.applyCuts('Upsilon(4S):all', 'roeEextra(UpsilonROE) <= 0.9', path=path)
117 ma.applyCuts('Upsilon(4S):all', 'nROE_Tracks(UpsilonROE) <= 4', path=path)
118 
119 # Best candidate selection - only one candidate per event
120 ma.rankByHighest('Upsilon(4S):all', 'daughter(0, extraInfo(SignalProbability))', numBest=1,
121  outputVariable='FEIProbabilityRank', path=path)
122 
123 # Write Ntuples
124 ma.variablesToNtuple('Upsilon(4S):all', ['M', 'm2RecoilSignalSide', 'E'], filename="Upsilon.root", path=path)
125 
126 # Process 100 events
127 b2.process(path, max_event=100)