14 The classes defined here are used to uniquely define a FEI training.
16 - The global configuration like database prefix, cache mode, monitoring, ... (FeiConfiguration)
17 - The reconstructed Particles (Particle)
18 - The reconstructed Channels of each particle (DecayChannel)
19 - The MVA configuration for each channel (MVAConfiguration)
20 - The Cut definitions of each channel (PreCutConfiguration)
21 - The Cut definitions of each particle (PostCutConfiguration)
34FeiConfiguration = collections.namedtuple(
'FeiConfiguration',
35 'prefix, cache, monitor, legacy, externTeacher, training, roundMode, monitoring_path')
36FeiConfiguration.__new__.__defaults__ = (
'FEI_TEST',
None,
True,
None,
'basf2_mva_teacher',
False, 0,
'')
37FeiConfiguration.__doc__ =
"Fei Global Configuration class"
38FeiConfiguration.prefix.__doc__ =
"The database prefix used for all weight files"
39FeiConfiguration.cache.__doc__ =
"The stage which is passed as input, it is assumed that all previous stages"\
40 " do not have to be reconstructed again. Can be either a number or"\
41 " a filename containing a pickled number or"\
42 " None in this case the environment variable FEI_STAGE is used."
43FeiConfiguration.monitor.__doc__ = (
44 "Determines the level of monitoring histograms to create. "
45 "Set to False to disable monitoring. "
46 "Set to 'simple' to enable lightweight histograms. "
47 "Any other value will enable full monitoring histograms."
49FeiConfiguration.legacy.__doc__ =
"Pass the summary file of a legacy FEI training,"\
50 " and the algorithm will be able to apply this training."
51FeiConfiguration.externTeacher.__doc__ =
"Teacher command e.g. basf2_mva_teacher, b2mva-kekcc-cluster-teacher"
52FeiConfiguration.training.__doc__ =
"If you train the FEI set this to True, otherwise to False"
53FeiConfiguration.roundMode.__doc__ =
"Round mode for the training. 0 default, 1 resuming, 2 finishing, 3 retraining."
54FeiConfiguration.monitoring_path.__doc__ =
"Path where monitoring histograms are stored."
57MVAConfiguration = collections.namedtuple(
'MVAConfiguration',
'method, config, variables, target, sPlotVariable, spectators')
58MVAConfiguration.__new__.__defaults__ = (
'FastBDT',
59 '--nTrees 400 --nCutLevels 10 --nLevels 3 --shrinkage 0.1 --randRatio 0.5',
60 None,
'isSignal',
None, {})
61MVAConfiguration.__doc__ =
"Multivariate analysis configuration class."
62MVAConfiguration.method.__doc__ =
"Method used by MVAInterface."
63MVAConfiguration.config.__doc__ =
"Method specific configuration string passed to basf2_mva_teacher"
64MVAConfiguration.variables.__doc__ =
"List of variables from the VariableManager."\
65 " {} is expanded to one variable per daughter particle."
66MVAConfiguration.target.__doc__ =
"Target variable from the VariableManager."
67MVAConfiguration.sPlotVariable.__doc__ =
"Discriminating variable used by sPlot to do data-driven training."
68MVAConfiguration.spectators.__doc__ =
"Dictionary of spectator variables with their ranges from the VariableManager."
71PreCutConfiguration = collections.namedtuple(
72 'PreCutConfiguration',
'userCut, vertexCut, noBackgroundSampling,'
73 'bestCandidateVariable, bestCandidateCut, bestCandidateMode, noSignalSampling, bkgSamplingFactor')
74PreCutConfiguration.__new__.__defaults__ = (
'', -2,
False,
None, 0,
'lowest',
False, 1.0)
75PreCutConfiguration.__doc__ =
"PreCut configuration class. These cuts is employed before training the mva classifier."
76PreCutConfiguration.userCut.__doc__ =
"The user cut is passed directly to the ParticleCombiner."\
77 " Particles which do not pass this cut are immediately discarded."
78PreCutConfiguration.vertexCut.__doc__ =
"The vertex cut is passed as confidence level to the VertexFitter."
79PreCutConfiguration.noBackgroundSampling.__doc__ =
"For very pure channels, the background sampling factor is too high" \
80 " and the MVA can't be trained. This disables background sampling."
81PreCutConfiguration.bestCandidateVariable.__doc__ =
"Variable from the VariableManager which is used to rank all candidates."
82PreCutConfiguration.bestCandidateCut.__doc__ =
"Number of best-candidates to keep after the best-candidate ranking."
83PreCutConfiguration.bestCandidateMode.__doc__ =
"Either lowest or highest."
84PreCutConfiguration.noSignalSampling.__doc__ =
"For channels with unknown br. frac., the signal sampling factor can be" \
85 " overestimated and you loose signal samples in the training." \
86 " This disables signal sampling."
87PreCutConfiguration.bkgSamplingFactor.__doc__ =
"Add additional multiplicative bkg. sampling factor, less than 1.0 to reduce."
89PostCutConfiguration = collections.namedtuple(
'PostCutConfiguration',
'value, bestCandidateCut')
90PostCutConfiguration.__new__.__defaults__ = (0.0, 0)
91PostCutConfiguration.__doc__ =
"PostCut configuration class. This cut is employed after the training of the mva classifier."
92PostCutConfiguration.value.__doc__ =
"Absolute value used to cut on the SignalProbability of each candidate."
93PostCutConfiguration.bestCandidateCut.__doc__ =
"Number of best-candidates to keep, ranked by SignalProbability."
95DecayChannel = collections.namedtuple(
97 'name, label, decayString, daughters, mvaConfig, preCutConfig, decayModeID, pi0veto, extraPathSpec')
98DecayChannel.__new__.__defaults__ = (
None,
None,
None,
None,
None,
None,
None,
False,
None)
99DecayChannel.__doc__ =
"Decay channel of a Particle."
100DecayChannel.name.__doc__ =
"str:Name of the channel e.g. :code:`D0:generic_0`"
101DecayChannel.label.__doc__ =
"Label used to identify the decay channel e.g. for weight files independent of decayModeID"
102DecayChannel.decayString.__doc__ =
"DecayDescriptor of the channel e.g. D0 -> K+ pi-"
103DecayChannel.daughters.__doc__ =
"List of daughter particles of the decay channel e.g. [K+, pi-]"
104DecayChannel.mvaConfig.__doc__ =
"MVAConfiguration object which is used for this channel."
105DecayChannel.preCutConfig.__doc__ =
"PreCutConfiguration object which is used for this channel."
106DecayChannel.decayModeID.__doc__ =
"DecayModeID of this channel. Unique ID for each channel of this particle."
107DecayChannel.pi0veto.__doc__ =
"If true, additional pi0veto variables are added to the MVAs, useful only for decays with gammas."
108DecayChannel.extraPathSpec.__doc__ =
"Dictionary with {module, function, args, kwargs} to add extra basf2 modules for this channel."
110MonitoringVariableBinning = {
'mcErrors': (
'mcErrors', 513, -0.5, 512.5),
111 'mcParticleStatus': (
'mcParticleStatus', 257, -0.5, 256.5),
112 'dM': (
'dM', 100, -1.0, 1.0),
113 'dQ': (
'dQ', 100, -1.0, 1.0),
114 'abs(dM)': (
'abs(dM)', 100, 0.0, 1.0),
115 'abs(dQ)': (
'abs(dQ)', 100, 0.0, 1.0),
116 'pionID': (
'pionID', 100, 0.0, 1.0),
117 'kaonID': (
'kaonID', 100, 0.0, 1.0),
118 'protonID': (
'protonID', 100, 0.0, 1.0),
119 'electronID': (
'electronID', 100, 0.0, 1.0),
120 'muonID': (
'muonID', 100, 0.0, 1.0),
121 'isSignal': (
'isSignal', 2, -0.5, 1.5),
122 'isSignalAcceptMissingNeutrino': (
'isSignalAcceptMissingNeutrino', 2, -0.5, 1.5),
123 'isPrimarySignal': (
'isPrimarySignal', 2, -0.5, 1.5),
124 'chiProb': (
'chiProb', 100, 0.0, 1.0),
125 'Mbc': (
'Mbc', 100, 5.1, 5.4),
126 'cosThetaBetweenParticleAndNominalB': (
'cosThetaBetweenParticleAndNominalB', 100, -10.0, 10.0),
127 'extraInfo(SignalProbability)': (
'extraInfo(SignalProbability)', 100, 0.0, 1.0),
128 'extraInfo(decayModeID)': (
'extraInfo(decayModeID)', 101, -0.5, 100.5),
129 'extraInfo(uniqueSignal)': (
'extraInfo(uniqueSignal)', 2, -0.5, 1.5),
130 'extraInfo(preCut_rank)': (
'extraInfo(preCut_rank)', 41, -0.5, 40.5),
131 'extraInfo(postCut_rank)': (
'extraInfo(postCut_rank)', 41, -0.5, 40.5),
132 'daughterProductOf(extraInfo(SignalProbability))':
133 (
'daughterProductOf(extraInfo(SignalProbability))', 100, 0.0, 1.0),
134 'pValueCombinationOfDaughters(extraInfo(SignalProbability))':
135 (
'pValueCombinationOfDaughters(extraInfo(SignalProbability))', 100, 0.0, 1.0),
139def variables2binnings(variables):
141 Convert given variables into a tuples which can be given to VariableToHistogram
143 return [MonitoringVariableBinning[v]
if v
in MonitoringVariableBinning
else (v, 100, -10.0, 10.0)
for v
in variables]
146def variables2binnings_2d(variables):
148 Convert given variables into a tuples which can be given to VariableToHistogram
151 for v1, v2
in variables:
152 b1 = MonitoringVariableBinning[v1]
if v1
in MonitoringVariableBinning
else (v1, 100, -10.0, 10.0)
153 b2 = MonitoringVariableBinning[v2]
if v2
in MonitoringVariableBinning
else (v2, 100, -10.0, 10.0)
154 result.append(b1 + b2)
158def removeJPsiSlash(string: str) -> str:
160 Remove the / in the J/psi particle name
162 return string.replace(
'/',
'')
168 The Particle class is the only class the end-user gets into contact with.
169 The user creates an instance of this class for every particle he wants to reconstruct with the FEI algorithm,
170 and provides MVAConfiguration, PreCutConfiguration and PostCutConfiguration. These can be overwritten per channel.
174 mvaConfig: MVAConfiguration,
175 preCutConfig: PreCutConfiguration = PreCutConfiguration(),
176 postCutConfig: PostCutConfiguration = PostCutConfiguration(),
177 extraPathSpec: dict |
None =
None):
179 Creates a Particle without any decay channels. To add decay channels use addChannel method.
180 @param identifier is the pdg name of the particle as a string
181 with an optional additional user label separated by ':'
182 @param mvaConfig multivariate analysis configuration
183 @param preCutConfig intermediate pre cut configuration
184 @param postCutConfig post cut configuration
185 @param extraPathSpec extra module specifications
188 self.
identifier = identifier +
':generic' if len(identifier.split(
':')) < 2
else identifier
207 Compares to Particle objects.
208 They are equal if their identifier, name, label, all channels, preCutConfig and postCutConfig is equal
209 @param a another Particle object
211 return (self.
identifier == a.identifier
and self.
name == a.name
and self.
label == a.label
and
216 Creates a string representation of a Particle object.
222 Creates a hash of a Particle object.
223 This is necessary to use this as a key in a dictionary
229 """ Property returning list of unique daughter particles of all channels """
230 return list(frozenset([daughter
for channel
in self.
channels for daughter
in channel.daughters]))
233 daughters: typing.Sequence[str],
234 mvaConfig: MVAConfiguration =
None,
235 preCutConfig: PreCutConfiguration =
None,
236 pi0veto: bool =
False,
237 extraPathSpec: dict |
None =
None):
239 Appends a new decay channel to the Particle object.
240 @param daughters is a list of pdg particle names e.g. ['pi+','K-']
241 @param mvaConfig multivariate analysis configuration
242 @param preCutConfig pre cut configuration object
243 @param pi0veto if true, additional pi0veto variables are added to the MVA configuration
244 @param extraPathSpec extra module specifications
247 daughters = [d +
':generic' if ':' not in d
else d
for d
in daughters]
249 mvaConfig = copy.deepcopy(self.
mvaConfig if mvaConfig
is None else mvaConfig)
251 preCutConfig = copy.deepcopy(self.
preCutConfig if preCutConfig
is None else preCutConfig)
253 extraPathSpec = copy.deepcopy(self.
extraPathSpec if extraPathSpec
is None else extraPathSpec)
256 if mvaConfig
is not None and mvaConfig.target != self.
mvaConfig.target:
258 f
'Particle {self.identifier} has common target {self.mvaConfig.target}, while channel '
259 f
'{" ".join(daughters)} has {mvaConfig.target}. Each particle must have exactly one target!')
263 for v
in mvaConfig.variables:
264 if v.count(
'{') == 0:
267 matches = re.findall(
r'\{\s*\d*\s*\.\.\s*\d*\s*\}', v)
268 if len(matches) == 0
and v.count(
'{}') == 0:
270 elif v.count(
'{}') > 0
and len(matches) > 0:
271 basf2.B2FATAL(f
'Variable {v} contains both '+
'{}'+f
' and {matches}. Only one is allowed!')
272 elif len(matches) > 0:
275 for match
in matches:
276 tempRange = match[1:-1].split(
'..')
277 if tempRange[0] ==
'':
280 tempRange[0] = int(tempRange[0])
281 if tempRange[0] >= len(daughters):
282 basf2.B2DEBUG(11, f
'Variable {v} contains index {tempRange[0]} which is more than daughters, skipping!')
285 if tempRange[1] ==
'':
286 tempRange[1] = len(daughters)
288 tempRange[1] = int(tempRange[1])
289 if tempRange[1] > len(daughters):
290 basf2.B2DEBUG(11, f
'Variable {v} contains index {tempRange[1]} which is more than daughters, skipping!')
293 ranges.append(tempRange)
297 mvaVars += [v.replace(matches[0], str(c))
for c
in range(ranges[0][0], ranges[0][1])]
299 for match
in matches:
300 v = v.replace(match,
'{}')
301 mvaVars += [v.format(*c)
for c
in itertools.product(*[range(r[0], r[1])
for r
in ranges])]
302 elif v.count(
'{}') <= len(daughters):
303 mvaVars += [v.format(*c)
for c
in itertools.combinations(list(range(0, len(daughters))), v.count(
'{}'))]
304 elif v.count(
'{}') > len(daughters):
305 basf2.B2DEBUG(11, f
'Variable {v} contains more brackets than daughters, which is why it will be ignored!')
308 basf2.B2FATAL(f
'Something went wrong with variable {v}!')
309 mvaConfig = mvaConfig._replace(variables=mvaVars)
313 label=removeJPsiSlash(self.
identifier +
' ==> ' +
' '.join(daughters)),
314 decayString=self.
identifier +
'_' + str(decayModeID) +
' -> ' +
' '.join(daughters),
317 preCutConfig=preCutConfig,
318 decayModeID=decayModeID,
320 extraPathSpec=extraPathSpec))
mvaConfig
multivariate analysis configuration (see MVAConfiguration)
label
Additional label like hasMissing or has2Daughters.
postCutConfig
post cut configuration (see PostCutConfiguration)
preCutConfig
intermediate cut configuration (see PreCutConfiguration)
__init__(self, str identifier, MVAConfiguration mvaConfig, PreCutConfiguration preCutConfig=PreCutConfiguration(), PostCutConfiguration postCutConfig=PostCutConfiguration(), dict|None extraPathSpec=None)
extraPathSpec
specifications for running extra modules
addChannel(self, typing.Sequence[str] daughters, MVAConfiguration mvaConfig=None, PreCutConfiguration preCutConfig=None, bool pi0veto=False, dict|None extraPathSpec=None)
name
The name of the particle as correct pdg name e.g.
list channels
DecayChannel objects added by addChannel() method.
str identifier
pdg name of the particle with an optional additional user label separated by :