14import modularAnalysis
as ma
15from variables
import variables
18from ROOT
import Belle2
20_TrainingMode = Belle2.ChargedPidMVAWeights.ChargedPidMVATrainingMode
24_chargednames = [
'pi',
'K',
'p',
'e',
'mu']
25_pidnames = [
'pionID',
'kaonID',
'protonID',
'electronID',
'muonID']
26_stdnames = [
'all',
'loose',
'loosepid',
'good',
'higheff']
27_effnames = [
'95eff',
'90eff',
'85eff']
30_mostLikelyList =
'mostlikely'
33def _stdChargedEffCuts(particletype, listtype):
35 Provides the PID cut corresponding to a given efficiency percentile
37 @param particletype type of charged particle (pi, K, p, e, mu)
38 @param listtype efficiency percentile
for the list (95eff, 90eff, 85eff)
41 particleindex = _chargednames.index(particletype)
42 effindex = _effnames.index(listtype)
45 effcuts = [[0.001, 0.019, 0.098],
47 [0.000, 0.043, 0.251],
48 [0.093, 0.301, 0.709],
49 [0.187, 0.418, 0.909]]
51 return effcuts[particleindex][effindex]
54def stdCharged(particletype, listtype, path, writeOut=True):
56 Function to prepare one of several standardized types of charged particle lists:
57 - 'all' with no cuts on track
58 -
'good' high purity lists
for data studies
59 -
'loosepid' loose selections
for skimming, PID cut only
60 -
'loose' loose selections
for skimming
61 -
'higheff' high efficiency list
with loose
global ID cut
for data studies
62 -
'mostlikely' list
with the highest PID likelihood
63 Also the following lists, which may
or may
not be available depending on the release
64 -
'99eff' with 99% selection efficiency (calculated
for 1<p<4 GeV)
and good track (MC only)
65 -
'95eff' with 95% selection efficiency (calculated
for 1<p<4 GeV)
and good track (MC only)
66 -
'90eff' with 90% selection efficiency (calculated
for 1<p<4 GeV)
and good track (MC only)
67 -
'85eff' with 85% selection efficiency (calculated
for 1<p<4 GeV)
and good track (MC only)
69 @param particletype type of charged particle to make a list of
70 @param listtype name of standard list
71 @param path modules are added to this path
72 @param writeOut whether RootOutput module should save the created ParticleList
76 trackQuality =
'thetaInCDCAcceptance and nCDCHits>20'
77 ipCut =
'dr < 0.5 and abs(dz) < 2'
78 goodTrack = trackQuality +
' and ' + ipCut
80 if particletype
not in _chargednames:
81 b2.B2ERROR(
"The requested list is not a standard charged particle. Use one of pi, K, e, mu, p.")
84 ma.fillParticleList(particletype +
'+:all',
'', writeOut=writeOut, path=path)
85 elif listtype ==
'good':
87 particletype +
'+:good',
88 _pidnames[_chargednames.index(particletype)] +
' > 0.5 and ' + goodTrack,
91 elif listtype ==
'loose':
93 particletype +
'+:loose',
94 _pidnames[_chargednames.index(particletype)] +
' > 0.1 and ' + goodTrack,
97 elif listtype ==
'loosepid':
99 particletype +
'+:loosepid',
100 _pidnames[_chargednames.index(particletype)] +
' > 0.1',
103 elif listtype ==
'higheff':
105 particletype +
'+:higheff',
106 _pidnames[_chargednames.index(particletype)] +
' > 0.002 and ' + goodTrack,
109 elif listtype
not in _effnames:
110 b2.B2ERROR(
"The requested list is not defined. Please refer to the stdCharged documentation.")
112 pidcut = _stdChargedEffCuts(particletype, listtype)
113 if 0.0 < pidcut < 1.0:
118 _pidnames[_chargednames.index(particletype)] +
126 b2.B2ERROR(
'The requested standard particle list ' + particletype +
127 '+:' + listtype +
' is not available in this release.')
130def stdPi(listtype=_defaultlist, path=None, writeOut=True):
132 Function to prepare standard pion lists, refer to `stdCharged` for details
134 @param listtype name of standard list
135 @param path modules are added to this path
136 @param writeOut whether RootOutput module should save the created ParticleList
141def stdK(listtype=_defaultlist, path=None, writeOut=True):
143 Function to prepare standard kaon lists, refer to `stdCharged` for details
145 @param listtype name of standard list
146 @param path modules are added to this path
147 @param writeOut whether RootOutput module should save the created ParticleList
152def stdPr(listtype=_defaultlist, path=None, writeOut=True):
154 Function to prepare standard proton lists, refer to `stdCharged` for details
156 @param listtype name of standard list
157 @param path modules are added to this path
158 @param writeOut whether RootOutput module should save the created ParticleList
169 channel_eff="combination",
170 channel_misid_pi="combination",
171 channel_misid_K="combination",
173 outputListLabel=None,
174 trainingModeMulticlass=_TrainingMode.c_Multiclass,
175 trainingModeBinary=_TrainingMode.c_Classification,
178 Function to prepare one of several standardized types of lepton (:math:`e,\\mu`) lists, with the following working points:
180 *
'FixedThresh05', PID cut of > 0.5
for each particle
in the list.
181 *
'FixedThresh09', PID cut of > 0.9
for each particle
in the list.
182 *
'FixedThresh095', PID cut of > 0.95
for each particle
in the list.
183 *
'FixedThresh099', PID cut of > 0.99
for each particle
in the list.
184 *
'UniformEff60' 60% lepton efficiency list, uniform
in a given multi-dimensional parametrisation.
185 *
'UniformEff70' 70% lepton efficiency list, uniform
in a given multi-dimensional parametrisation.
186 *
'UniformEff80' 80% lepton efficiency list, uniform
in a given multi-dimensional parametrisation.
187 *
'UniformEff90' 90% lepton efficiency list, uniform
in a given multi-dimensional parametrisation.
188 *
'UniformEff95' 95% lepton efficiency list, uniform
in a given multi-dimensional parametrisation.
189 *
'UniformPiFR5EM1' 50% pion to lepton fake rate, uniform
in a given multi-dimensional parametrisation.
190 *
'UniformPiFR1EM1' 10% pion to lepton fake rate, uniform
in a given multi-dimensional parametrisation.
191 *
'UniformPiFR5EM2' 5% pion to lepton fake rate, uniform
in a given multi-dimensional parametrisation.
192 *
'UniformPiFR1EM2' 1% pion to lepton fake rate, uniform
in a given multi-dimensional parametrisation.
193 *
'UniformPiFR5EM3' 0.5% pion to lepton fake rate, uniform
in a given multi-dimensional parametrisation.
194 *
'UniformPiFR1EM3' 0.1% pion to lepton fake rate, uniform
in a given multi-dimensional parametrisation.
196 The function creates a ``ParticleList``, selecting particles according to the chosen ``working_point``,
197 and decorates each candidate
in the list
with the nominal Data/MC :math:`\\ell` ID efficiency
and
198 :math:`\\pi,K` fake rate correction factors
and their stat, syst uncertainty, reading the info
199 from the Conditions Database (CDB) according to the chosen input
global tag (GT).
202 Particles will **
not** be selected
if they are outside the Data/MC *efficiency* corrections
' phase space coverage for the given working point.
203 In fact, the threshold value
for the PID cut
in such cases
is set to NaN.
206 At the moment, the only supported *binary* lepton identification
is against the **pion** hypothesis.
207 This implies that,
if binary classification
is chosen, only :math:`\\pi` fake rate corrections will be added to the
208 resulting particle list.
211 pdgId (int): the lepton pdg code.
212 working_point (str): name of the chosen working point that defines the content of the list. Choose among the above values.
213 method (str): the PID method:
'likelihood' or 'bdt'.
214 classification (str): the type of classifier:
'binary' (one-vs-pion)
or 'global' (one-vs-all).
215 lid_weights_gt (str): the name identifier of the
global tag
with the recommended Data/MC correction weights.
219 `Lepton ID Confluence page <https://confluence.desy.de/display/BI/Lepton+ID+Performance>`_
220 for info about the recommended
global tags.
222 release (Optional[int]): the major release number of the data
and MC campaigns considered.
223 If specified, it ensures the correct :math:`\\ell` ID variables are used.
227 `Lepton ID Confluence page <https://confluence.desy.de/display/BI/Lepton+ID+Performance>`_
228 for info about lepton identification variables
and campaigns.
230 channel_eff (Optional[str]): the channel used to derive the :math:`\\ell` ID efficiency corrections.
231 By default,
'combination' is set, meaning they are obtained by combining results
232 of several hadronic
and low multiplicity channels, wherever they overlap.
236 `Lepton ID Confluence page <https://confluence.desy.de/display/BI/Lepton+ID+Performance>`_
237 for other possible choices (
if any).
239 channel_misid_pi (Optional[str]): the channel used to derive the :math:`\\pi` fake rate corrections.
240 channel_misid_K (Optional[str]): the channel used to derive the :math:`K` fake rate corrections.
241 inputListName (Optional[str]): the name of a pre-existing ``ParticleList`` object (defined
as a full ``decayString``,
242 e.g.
'e-:my_input_electrons') of which the standard lepton list will be a subset.
243 For instance, users might want to apply a Bremsstrahlung correction to electrons first,
244 which modifies their 4-momentum,
and only later define the subset passing the PID selection,
245 including the appropriate PID weights
and uncertainties (which are :math:`p`-dependent).
246 By default, the standard lepton list
is created
from all ``Track`` objects
in the event.
249 Do **
not** apply any PID selection on the input list, otherwise results could be biased.
251 outputListLabel (Optional[str]): the name of the output lepton list label, i.e.,
252 the string that follows the particle identifier (
'e-:',
'mu-:').
253 By default, it
is assigned
as:
254 ``
'{method}_{classification}_{working_point}'``.
256 trainingModeMulticlass (Optional[``Belle2.ChargedPidMVAWeights.ChargedPidMVATrainingMode``]): enum identifier
257 of the multi-
class (
global PID) training mode.
258 See `modularAnalysis.applyChargedPidMVA` docs
for available options.
259 trainingModeBinary (Optional[``Belle2.ChargedPidMVAWeights.ChargedPidMVATrainingMode``]): enum identifier
260 of the classification (binary PID) training mode.
261 See `modularAnalysis.applyChargedPidMVA` docs
for available options.
262 path (basf2.Path): modules are added to this path.
265 tuple(str, list(str)): the alias
for the lepton ID variable,
and the list of aliases
for the weights.
286 available_methods = (
"likelihood",
"bdt")
287 available_classificators = (
"global",
"binary")
289 if working_point
not in working_points:
290 b2.B2ERROR(f
"The requested lepton list working point: {working_point} is not defined. \
291 Please refer to the stdLep and stdCharged documentation.")
294 if method
not in available_methods:
295 b2.B2ERROR(f
"method: {method}. Must be any of: {available_methods}.")
298 if classification
not in available_classificators:
299 b2.B2ERROR(f
"classification: {classification}. Must be any of: {available_classificators}.")
302 b2.B2INFO(f
"Prepending GT with LID corrections: {lid_weights_gt}")
303 b2.conditions.prepend_globaltag(lid_weights_gt)
309 electron = Const.electron.getPDGCode()
310 muon = Const.muon.getPDGCode()
311 pion = Const.pion.getPDGCode()
313 if lepton
not in (electron, muon):
314 b2.B2ERROR(f
"{pdgId} is not that of a light charged lepton.")
322 electron:
"electronID",
326 electron:
"electronID",
332 electron: f
"binaryPID({electron}, {pion})",
333 muon: f
"binaryPID({muon}, {pion})",
336 electron:
"binaryPID_e_pi",
337 muon:
"binaryPID_mu_pi",
344 lepton: f
"pidChargedBDTScore({lepton}, ALL)",
347 lepton: re.sub(
r"\W+",
"", f
"pidChargedBDTScore_{lepton_name}"),
352 lepton: f
"pidPairChargedBDTScore({lepton}, {pion}, ALL)",
355 lepton: re.sub(
r"\W+",
"", f
"pidPairChargedBDTScore_{lepton_name}_pi"),
363 if int(release)
in [5, 6]:
364 if lepton == electron:
365 b2.B2INFO(f
"The likelihood-based electron ID in release {release} samples is defined w/o the SVD and the TOP")
366 pid_variables[
"likelihood"][
"global"][
"var"][electron] =
"electronID_noSVD_noTOP"
367 pid_variables[
"likelihood"][
"global"][
"alias"][electron] =
"electronID_noSVD_noTOP"
368 pid_variables[
"likelihood"][
"binary"][
"var"][electron] = f
"binaryElectronID_noSVD_noTOP({pion})"
369 pid_variables[
"likelihood"][
"binary"][
"alias"][electron] =
"binaryElectronID_noSVD_noTOP_pi"
371 b2.B2INFO(f
"The likelihood-based muon ID in release {release} samples is defined w/o the SVD")
372 pid_variables[
"likelihood"][
"global"][
"var"][muon] =
"muonID_noSVD"
373 pid_variables[
"likelihood"][
"global"][
"alias"][muon] =
"muonID_noSVD"
374 pid_variables[
"likelihood"][
"binary"][
"var"][muon] = f
"binaryPID_noSVD({muon}, {pion})"
375 pid_variables[
"likelihood"][
"binary"][
"alias"][muon] =
"binaryMuonID_noSVD_pi"
378 pid_var = pid_variables[method][classification][
"var"][lepton]
379 pid_alias = pid_variables[method][classification][
"alias"][lepton]
380 if pid_alias != pid_var:
381 variables.addAlias(pid_alias, pid_var)
384 outputListName = f
"{lepton_name}:{method}_{classification}_{working_point}"
385 if outputListLabel
is not None:
386 outputListName = f
"{lepton_name}:{outputListLabel}"
388 if inputListName
is None:
389 ma.fillParticleList(outputListName,
"", path=path)
392 f
"The standard lepton list: '{outputListName}' will be created as a subset \
393 of the following ParticleList: '{inputListName}'")
394 ma.copyList(outputListName, inputListName, path=path)
398 if classification ==
"global":
399 ma.applyChargedPidMVA(particleLists=[outputListName],
401 trainingMode=trainingModeMulticlass)
402 elif classification ==
"binary":
403 ma.applyChargedPidMVA(particleLists=[outputListName],
405 binaryHypoPDGCodes=(lepton, pion),
406 trainingMode=trainingModeBinary)
409 payload_eff = f
"ParticleReweighting:{pid_alias}_eff_{channel_eff}_{working_point}"
410 payload_misid_pi = f
"ParticleReweighting:{pid_alias}_misid_pi_{channel_misid_pi}_{working_point}"
413 path.add_module(
"ParticleWeighting",
414 particleList=outputListName,
415 tableName=payload_eff).set_name(f
"ParticleWeighting_eff_{outputListName}")
416 path.add_module(
"ParticleWeighting",
417 particleList=outputListName,
418 tableName=payload_misid_pi,
419 allowToSkip=
True).set_name(f
"ParticleWeighting_misid_pi_{outputListName}")
421 if classification ==
"global":
422 payload_misid_K = f
"ParticleReweighting:{pid_alias}_misid_K_{channel_misid_K}_{working_point}"
423 path.add_module(
"ParticleWeighting",
424 particleList=outputListName,
425 tableName=payload_misid_K,
426 allowToSkip=
True).set_name(f
"ParticleWeighting_misid_K_{outputListName}")
430 cut = f
"[{pid_alias} >= extraInfo({payload_eff}_threshold)]"
431 ma.applyCuts(outputListName, cut, path=path)
434 weight_aliases_to_var = {
435 f
"weight_{pid_alias}_eff_{working_point}": f
"extraInfo({payload_eff}_data_MC_ratio)",
436 f
"weight_{pid_alias}_misid_pi_{working_point}": f
"extraInfo({payload_misid_pi}_data_MC_ratio)",
438 f
"weight_{pid_alias}_eff_{working_point}_stat_up": f
"extraInfo({payload_eff}_data_MC_uncertainty_stat_up)",
439 f
"weight_{pid_alias}_eff_{working_point}_stat_dn": f
"extraInfo({payload_eff}_data_MC_uncertainty_stat_dn)",
440 f
"weight_{pid_alias}_eff_{working_point}_sys_up": f
"extraInfo({payload_eff}_data_MC_uncertainty_sys_up)",
441 f
"weight_{pid_alias}_eff_{working_point}_sys_dn": f
"extraInfo({payload_eff}_data_MC_uncertainty_sys_dn)",
442 f
"weight_{pid_alias}_misid_pi_{working_point}_stat_up": f
"extraInfo({payload_misid_pi}_data_MC_uncertainty_stat_up)",
443 f
"weight_{pid_alias}_misid_pi_{working_point}_stat_dn": f
"extraInfo({payload_misid_pi}_data_MC_uncertainty_stat_dn)",
444 f
"weight_{pid_alias}_misid_pi_{working_point}_sys_up": f
"extraInfo({payload_misid_pi}_data_MC_uncertainty_sys_up)",
445 f
"weight_{pid_alias}_misid_pi_{working_point}_sys_dn": f
"extraInfo({payload_misid_pi}_data_MC_uncertainty_sys_dn)",
454 f
"weight_{pid_alias}_eff_{working_point}_rel_stat_up":
455 f
"formula(1+[extraInfo({payload_eff}_data_MC_uncertainty_stat_up)/extraInfo({payload_eff}_data_MC_ratio)])",
456 f
"weight_{pid_alias}_eff_{working_point}_rel_stat_dn":
457 f
"formula(1-[extraInfo({payload_eff}_data_MC_uncertainty_stat_dn)/extraInfo({payload_eff}_data_MC_ratio)])",
458 f
"weight_{pid_alias}_eff_{working_point}_rel_sys_up":
459 f
"formula(1+[extraInfo({payload_eff}_data_MC_uncertainty_sys_up)/extraInfo({payload_eff}_data_MC_ratio)])",
460 f
"weight_{pid_alias}_eff_{working_point}_rel_sys_dn":
461 f
"formula(1-[extraInfo({payload_eff}_data_MC_uncertainty_sys_dn)/extraInfo({payload_eff}_data_MC_ratio)])",
462 f
"weight_{pid_alias}_misid_pi_{working_point}_rel_stat_up":
463 f
"formula(1+[extraInfo({payload_misid_pi}_data_MC_uncertainty_stat_up)/extraInfo({payload_misid_pi}_data_MC_ratio)])",
464 f
"weight_{pid_alias}_misid_pi_{working_point}_rel_stat_dn":
465 f
"formula(1-[extraInfo({payload_misid_pi}_data_MC_uncertainty_stat_dn)/extraInfo({payload_misid_pi}_data_MC_ratio)])",
466 f
"weight_{pid_alias}_misid_pi_{working_point}_rel_sys_up":
467 f
"formula(1+[extraInfo({payload_misid_pi}_data_MC_uncertainty_sys_up)/extraInfo({payload_misid_pi}_data_MC_ratio)])",
468 f
"weight_{pid_alias}_misid_pi_{working_point}_rel_sys_dn":
469 f
"formula(1-[extraInfo({payload_misid_pi}_data_MC_uncertainty_sys_dn)/extraInfo({payload_misid_pi}_data_MC_ratio)])",
471 if classification ==
"global":
472 weight_aliases_to_var.update({
473 f
"weight_{pid_alias}_misid_K_{working_point}": f
"extraInfo({payload_misid_K}_data_MC_ratio)",
475 f
"weight_{pid_alias}_misid_K_{working_point}_stat_up": f
"extraInfo({payload_misid_K}_data_MC_uncertainty_stat_up)",
476 f
"weight_{pid_alias}_misid_K_{working_point}_stat_dn": f
"extraInfo({payload_misid_K}_data_MC_uncertainty_stat_dn)",
477 f
"weight_{pid_alias}_misid_K_{working_point}_sys_up": f
"extraInfo({payload_misid_K}_data_MC_uncertainty_sys_up)",
478 f
"weight_{pid_alias}_misid_K_{working_point}_sys_dn": f
"extraInfo({payload_misid_K}_data_MC_uncertainty_sys_dn)",
480 f
"weight_{pid_alias}_misid_K_{working_point}_rel_stat_up":
481 f
"formula(1+[extraInfo({payload_misid_K}_data_MC_uncertainty_stat_up)/extraInfo({payload_misid_K}_data_MC_ratio)])",
482 f
"weight_{pid_alias}_misid_K_{working_point}_rel_stat_dn":
483 f
"formula(1-[extraInfo({payload_misid_K}_data_MC_uncertainty_stat_dn)/extraInfo({payload_misid_K}_data_MC_ratio)])",
484 f
"weight_{pid_alias}_misid_K_{working_point}_rel_sys_up":
485 f
"formula(1+[extraInfo({payload_misid_K}_data_MC_uncertainty_sys_up)/extraInfo({payload_misid_K}_data_MC_ratio)])",
486 f
"weight_{pid_alias}_misid_K_{working_point}_rel_sys_dn":
487 f
"formula(1-[extraInfo({payload_misid_K}_data_MC_uncertainty_sys_dn)/extraInfo({payload_misid_K}_data_MC_ratio)])",
490 for alias, var
in weight_aliases_to_var.items():
491 variables.addAlias(alias, var)
493 return pid_alias, list(weight_aliases_to_var.keys())
496def stdE(listtype=_defaultlist,
501 channel_eff="combination",
502 channel_misid_pi="combination",
503 channel_misid_K="combination",
505 outputListLabel=None,
506 trainingModeMulticlass=_TrainingMode.c_Multiclass,
507 trainingModeBinary=_TrainingMode.c_Classification,
509 """ Function to prepare one of several standardized types of electron lists.
510 See the documentation of `stdLep` for details.
512 It also accepts any of the legacy definitions
513 for the ``listtype`` parameter (aka ``working_point``
in `stdLep`) to fall back to the `stdCharged` behaviour:
525 tuple(str, list(str)): the alias
for the electron ID variable,
and the list of aliases
for the weights.
528 if listtype
in _stdnames + _effnames:
530 return _pidnames[_chargednames.index(
"e")],
None
532 return stdLep(Const.electron.getPDGCode(), listtype, method, classification, lid_weights_gt,
534 channel_eff=channel_eff,
535 channel_misid_pi=channel_misid_pi,
536 channel_misid_K=channel_misid_K,
537 inputListName=inputListName,
538 outputListLabel=outputListLabel,
539 trainingModeMulticlass=trainingModeMulticlass,
540 trainingModeBinary=trainingModeBinary,
544def stdMu(listtype=_defaultlist,
549 channel_eff="combination",
550 channel_misid_pi="combination",
551 channel_misid_K="combination",
553 outputListLabel=None,
554 trainingModeMulticlass=_TrainingMode.c_Multiclass,
555 trainingModeBinary=_TrainingMode.c_Classification,
557 """ Function to prepare one of several standardized types of muon lists.
558 See the documentation of `stdLep` for details.
560 It also accepts any of the legacy definitions
561 for the ``listtype`` parameter (aka ``working_point``
in `stdLep`) to fall back to the `stdCharged` behaviour:
573 tuple(str, list(str)): the alias
for the muon ID variable,
and the list of aliases
for the weights.
576 if listtype
in _stdnames + _effnames:
578 return _pidnames[_chargednames.index(
"mu")],
None
580 return stdLep(Const.muon.getPDGCode(), listtype, method, classification, lid_weights_gt,
582 channel_eff=channel_eff,
583 channel_misid_pi=channel_misid_pi,
584 channel_misid_K=channel_misid_K,
585 inputListName=inputListName,
586 outputListLabel=outputListLabel,
587 trainingModeMulticlass=trainingModeMulticlass,
588 trainingModeBinary=trainingModeBinary,
592def stdMostLikely(pidPriors=None, suffix='', custom_cuts='', path=None, writeOut=True):
594 Function to prepare most likely particle lists according to PID likelihood, refer to stdCharged for details
596 @param pidPriors list of 6 float numbers used to reweight PID likelihoods,
for e, mu, pi, K, p
and d
597 @param suffix string added to the end of particle list names
598 @param custom_cuts custom selection cut string,
if empty, standard track quality cuts will be applied
599 @param path modules are added to this path
600 @param writeOut whether RootOutput module should save the created ParticleList
606 if pidPriors
is not None:
607 args = str(pidPriors)[1:-1]
608 trackQuality =
'thetaInCDCAcceptance and nCDCHits>20'
609 if custom_cuts !=
'':
610 trackQuality = custom_cuts
611 for name
in _chargednames:
612 ma.fillParticleList(f
'{name}+:{_mostLikelyList}{suffix}',
613 f
'pidIsMostLikely({args}) > 0 and {trackQuality}', writeOut=writeOut, path=path)
This class provides a set of constants for the framework.