Belle II Software  release-06-02-00
B_specific_train.py
1 #!/usr/bin/env python3
2 
3 
10 
11 # Steering file to train the specific FEI on Belle II MC, but it can be also easily adapted for converted Belle MC.
12 # This steering file is called several times (so-called stages) during the training process of the FEI.
13 # For reference see Confluence and Thomas Keck's PhD thesis.
14 #
15 # Please adapt for your signal channel. Note that a large amount of MC is needed to train the specific FEI.
16 # I usually use 100 million of signal events for each final state, mixed and charged MC.
17 # This example is for hadronic tagging.
18 
19 import fei
20 import basf2 as b2
21 import modularAnalysis as ma
22 
23 # Create path
24 path = b2.create_path()
25 
26 # Load input ROOT file
27 ma.inputMdst(environmentType='default',
28  filename=b2.find_file('mdst14.root', 'validation', False),
29  path=path)
30 
31 # Max 12 tracks per event - this avoids much computing time.
32 empty_path = b2.create_path()
33 skimfilter = b2.register_module('VariableToReturnValue')
34 skimfilter.param('variable', 'nCleanedTracks(dr < 2 and abs(dz) < 4)')
35 skimfilter.if_value('>12', empty_path, b2.AfterConditionPath.END)
36 path.add_module(skimfilter)
37 
38 # Signal side reconstruction
39 ma.fillParticleList('mu+', 'muonID > 0.8 and dr < 2 and abs(dz) < 4', writeOut=True, path=path)
40 ma.fillParticleList('e+', 'electronID > 0.8 and dr < 2 and abs(dz) < 4', writeOut=True, path=path)
41 ma.fillParticleList(
42  'gamma',
43  '[[clusterReg == 1 and E > 0.10] or [clusterReg == 2 and E > 0.09] or [clusterReg == 3 and E > 0.16]]',
44  writeOut=True,
45  path=path)
46 ma.reconstructDecay(
47  'B+:sig_e -> gamma e+',
48  '1.000 < M < 6.000 and cos(useRestFrame(daughterAngle(0, 1))) < 0.6',
49  dmID=1,
50  writeOut=True,
51  path=path)
52 ma.reconstructDecay(
53  'B+:sig_mu -> gamma mu+',
54  '1.000 < M < 6.000 and cos(useRestFrame(daughterAngle(0, 1))) < 0.6',
55  dmID=2,
56  writeOut=True,
57  path=path)
58 ma.copyLists('B+:sig', ['B+:sig_e', 'B+:sig_mu'], writeOut=True, path=path)
59 ma.looseMCTruth('B+:sig', path=path)
60 ma.rankByHighest('B+:sig', 'daughter(0,E)', outputVariable='PhotonCandidateRank', path=path)
61 ma.buildRestOfEvent('B+:sig', path=path)
62 clean_roe_mask = (
63  'CleanROE',
64  'dr < 2 and abs(dz) < 4',
65  'clusterE9E25 > 0.9 and clusterTiming < 50 and E > 0.09 and trackMatchType==0')
66 ma.appendROEMasks('B+:sig', [clean_roe_mask], path=path)
67 ma.applyCuts('B+:sig', 'roeDeltae(CleanROE) < 2.0 and roeMbc(CleanROE) > 4.8', path=path)
68 
69 skimfilter = b2.register_module('SkimFilter')
70 skimfilter.param('particleLists', ['B+:sig'])
71 empty_path = b2.create_path()
72 skimfilter.if_value('=0', empty_path, b2.AfterConditionPath.END)
73 path.add_module(skimfilter)
74 
75 # Prepare list for the training.
76 path.add_module('MCDecayFinder', decayString='B+ -> e+ nu_e gamma', listName='B+:FEIMC_e', writeOut=True)
77 path.add_module('MCDecayFinder', decayString='B+ -> mu+ nu_mu gamma', listName='B+:FEIMC_mu', writeOut=True)
78 ma.copyLists('B+:FEIMC', ['B+:FEIMC_e', 'B+:FEIMC_mu'], writeOut=True, path=path)
79 
80 
81 # We want the FEI to be only trained on a correctly reconstruced signal side and on wrongly reconstructed background.
82 isSignal = 'isSignalAcceptMissingNeutrino'
83 signalMC = 'eventCached(countInList(B+:FEIMC))'
84 cut = '[[{mc} > 0 and {sig} == 1] or [{mc} == 0 and {sig} != 1]]'.format(mc=signalMC, sig=isSignal)
85 ma.applyCuts('B+:sig', cut, path=path)
86 
87 # Set up FEI configuration specifying the FEI prefix
88 fei_tag = 'my_specFEI'
89 belle_particles = fei.get_default_channels(KLong=False,
90  chargedB=True,
91  neutralB=True,
92  semileptonic=False,
93  B_extra_cut='nRemainingTracksInEvent <= 3',
94  specific=True)
95 
96 # Get FEI path
97 configuration = fei.config.FeiConfiguration(prefix=fei_tag, training=True, monitor=False, cache=-1)
98 
99 
100 # Add FEI path to the path to be processed
101 feistate = fei.get_path(belle_particles, configuration)
102 
103 # FEI training
104 if feistate.stage == 0:
105  # Write out the rest of event, we train only on the rest of event of our signal side.
106  # This is the main difference compared to the generic FEI.
107  rO = b2.register_module('RootOutput')
108  rO.set_name('ROE_RootOutput')
109  rO.param('additionalBranchNames', ['RestOfEvent'])
110  feistate.path.add_module(rO)
111  roe_path = b2.create_path()
112  cond_module = b2.register_module('SignalSideParticleFilter')
113  cond_module.param('particleLists', ['B+:sig'])
114  cond_module.if_true(feistate.path, b2.AfterConditionPath.END)
115  roe_path.add_module(cond_module)
116  path.for_each('RestOfEvent', 'RestOfEvents', roe_path)
117 else:
118  # After stage 0, the training is done only on the written out rest of event.
119  path = b2.create_path()
120  ma.inputMdstList('default', [], path)
121  path.add_path(feistate.path)
122  r1 = b2.register_module('RootOutput')
123  r1.set_name('ROE_RootOutput')
124  r1.param('additionalBranchNames', ['RestOfEvent'])
125  path.add_module(r1)
126 
127 
128 # Process 100 events
129 b2.process(path, max_event=100)