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