Belle II Software development
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
18import fei
19import basf2 as b2
20import modularAnalysis as ma
21
22# Create path
23path = b2.create_path()
24
25# Load input ROOT file
26ma.inputMdst(filename=b2.find_file('mdst16.root', 'validation', False),
27 path=path)
28
29# Max 12 tracks per event - this avoids much computing time.
30empty_path = b2.create_path()
31skimfilter = b2.register_module('VariableToReturnValue')
32skimfilter.param('variable', 'nCleanedTracks(dr < 2 and abs(dz) < 4)')
33skimfilter.if_value('>12', empty_path, b2.AfterConditionPath.END)
34path.add_module(skimfilter)
35
36# Signal side reconstruction
37ma.fillParticleList('mu+', 'muonID > 0.8 and dr < 2 and abs(dz) < 4', writeOut=True, path=path)
38ma.fillParticleList('e+', 'electronID > 0.8 and dr < 2 and abs(dz) < 4', writeOut=True, path=path)
39ma.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)
44ma.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)
50ma.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)
56ma.copyLists('B+:sig', ['B+:sig_e', 'B+:sig_mu'], writeOut=True, path=path)
57ma.looseMCTruth('B+:sig', path=path)
58ma.rankByHighest('B+:sig', 'daughter(0,E)', outputVariable='PhotonCandidateRank', path=path)
59ma.buildRestOfEvent('B+:sig', path=path)
60clean_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')
64ma.appendROEMasks('B+:sig', [clean_roe_mask], path=path)
65ma.applyCuts('B+:sig', 'roeDeltae(CleanROE) < 2.0 and roeMbc(CleanROE) > 4.8', path=path)
66
67skimfilter = b2.register_module('SkimFilter')
68skimfilter.param('particleLists', ['B+:sig'])
69empty_path = b2.create_path()
70skimfilter.if_value('=0', empty_path, b2.AfterConditionPath.END)
71path.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
79fei_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
85b2.conditions.prepend_globaltag(ma.getAnalysisGlobaltag())
86
87belle_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
94configuration = fei.config.FeiConfiguration(prefix=fei_tag, training=False, monitor=False, cache=0)
95feistate = fei.get_path(belle_particles, configuration)
96
97
98# Run the tagging and copy the lists
99roe_path = feistate.path
100empty_path = b2.create_path()
101ma.copyLists('B-:generic_final', [], writeOut=True, path=path)
102ma.copyLists('B-:generic_final', ['B-:generic'], writeOut=True, path=roe_path)
103ma.signalSideParticleFilter('B+:sig', '', roe_path, empty_path)
104path.for_each('RestOfEvent', 'RestOfEvents', roe_path)
105
106# Reconstruct the Upsilon
107upsilon_cut = '7.5 <= M <= 10.5 and -2.0 <= m2RecoilSignalSide <= 4.0 and -0.15 <= daughter(0,deltaE) <= 0.1'
108ma.reconstructDecay('Upsilon(4S):hadronic -> B-:generic_final B+:sig', upsilon_cut, dmID=1, path=path)
109ma.copyLists('Upsilon(4S):all', ['Upsilon(4S):hadronic'], path=path)
110ma.looseMCTruth('Upsilon(4S):all', path=path)
111
112ma.buildRestOfEvent('Upsilon(4S):all', path=path)
113upsilon_roe = ('UpsilonROE', 'dr < 2 and abs(dz) < 4', 'goodBelleGamma == 1')
114ma.appendROEMasks('Upsilon(4S):all', [upsilon_roe], path=path)
115ma.applyCuts('Upsilon(4S):all', '-2.0 < m2RecoilSignalSide < 4.0', path=path)
116ma.applyCuts('Upsilon(4S):all', 'roeEextra(UpsilonROE) <= 0.9', path=path)
117ma.applyCuts('Upsilon(4S):all', 'nROE_Tracks(UpsilonROE) <= 4', path=path)
118
119# Best candidate selection - only one candidate per event
120ma.rankByHighest('Upsilon(4S):all', 'daughter(0, extraInfo(SignalProbability))', numBest=1,
121 outputVariable='FEIProbabilityRank', path=path)
122
123# Write Ntuples
124ma.variablesToNtuple('Upsilon(4S):all', ['M', 'm2RecoilSignalSide', 'E'], filename="Upsilon.root", path=path)
125
126# Process 100 events
127b2.process(path, max_event=100)