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