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:
58 - 'all' with no cuts on track
59 - 'good' high purity lists for data studies
60 - 'loosepid' loose selections for skimming, PID cut only
61 - 'loose' loose selections for skimming
62 - 'higheff' high efficiency list with loose global ID cut for data studies
63 - 'mostlikely' list with the highest PID likelihood
65 Also the following lists, which may or may not be available depending on the release
67 - '99eff' with 99% selection efficiency (calculated for 1<p<4 GeV) and good track (MC only)
68 - '95eff' with 95% selection efficiency (calculated for 1<p<4 GeV) and good track (MC only)
69 - '90eff' with 90% selection efficiency (calculated for 1<p<4 GeV) and good track (MC only)
70 - '85eff' with 85% selection efficiency (calculated for 1<p<4 GeV) and good track (MC only)
72 @param particletype type of charged particle to make a list of
73 @param listtype name of standard list
74 @param path modules are added to this path
75 @param writeOut whether RootOutput module should save the created ParticleList
79 trackQuality =
'thetaInCDCAcceptance'
80 ipCut =
'dr < 0.5 and abs(dz) < 2'
81 goodTrack = trackQuality +
' and ' + ipCut
83 if particletype
not in _chargednames:
84 b2.B2ERROR(
"The requested list is not a standard charged particle. Use one of pi, K, e, mu, p.")
87 ma.fillParticleList(particletype +
'+:all',
'', writeOut=writeOut, path=path)
88 elif listtype ==
'good':
90 particletype +
'+:good',
91 _pidnames[_chargednames.index(particletype)] +
' > 0.5 and ' + goodTrack,
94 elif listtype ==
'loose':
96 particletype +
'+:loose',
97 _pidnames[_chargednames.index(particletype)] +
' > 0.1 and ' + goodTrack,
100 elif listtype ==
'loosepid':
102 particletype +
'+:loosepid',
103 _pidnames[_chargednames.index(particletype)] +
' > 0.1',
106 elif listtype ==
'higheff':
108 particletype +
'+:higheff',
109 _pidnames[_chargednames.index(particletype)] +
' > 0.002 and ' + goodTrack,
112 elif listtype
not in _effnames:
113 b2.B2ERROR(
"The requested list is not defined. Please refer to the stdCharged documentation.")
115 pidcut = _stdChargedEffCuts(particletype, listtype)
116 if 0.0 < pidcut < 1.0:
121 _pidnames[_chargednames.index(particletype)] +
129 b2.B2ERROR(
'The requested standard particle list ' + particletype +
130 '+:' + listtype +
' is not available in this release.')
133def stdPi(listtype=_defaultlist, path=None, writeOut=True):
135 Function to prepare standard pion lists, refer to `stdCharged` for details
137 @param listtype name of standard list
138 @param path modules are added to this path
139 @param writeOut whether RootOutput module should save the created ParticleList
144def stdK(listtype=_defaultlist, path=None, writeOut=True):
146 Function to prepare standard kaon lists, refer to `stdCharged` for details
148 @param listtype name of standard list
149 @param path modules are added to this path
150 @param writeOut whether RootOutput module should save the created ParticleList
155def stdPr(listtype=_defaultlist, path=None, writeOut=True):
157 Function to prepare standard proton lists, refer to `stdCharged` for details
159 @param listtype name of standard list
160 @param path modules are added to this path
161 @param writeOut whether RootOutput module should save the created ParticleList
172 channel_eff="combination",
173 channel_misid_pi="combination",
174 channel_misid_K="combination",
176 outputListLabel=None,
177 trainingModeMulticlass=_TrainingMode.c_Multiclass,
178 trainingModeBinary=_TrainingMode.c_Classification,
181 Function to prepare one of several standardized types of lepton (:math:`e,\\mu`) lists, with the following working points:
183 * 'FixedThresh05', PID cut of > 0.5 for each particle in the list.
184 * 'FixedThresh09', PID cut of > 0.9 for each particle in the list.
185 * 'FixedThresh095', PID cut of > 0.95 for each particle in the list.
186 * 'FixedThresh099', PID cut of > 0.99 for each particle in the list.
187 * 'UniformEff60' 60% lepton efficiency list, uniform in a given multi-dimensional parametrisation.
188 * 'UniformEff70' 70% lepton efficiency list, uniform in a given multi-dimensional parametrisation.
189 * 'UniformEff80' 80% lepton efficiency list, uniform in a given multi-dimensional parametrisation.
190 * 'UniformEff90' 90% lepton efficiency list, uniform in a given multi-dimensional parametrisation.
191 * 'UniformEff95' 95% lepton efficiency list, uniform in a given multi-dimensional parametrisation.
192 * 'UniformPiFR5EM1' 50% pion to lepton fake rate, uniform in a given multi-dimensional parametrisation.
193 * 'UniformPiFR1EM1' 10% pion to lepton fake rate, uniform in a given multi-dimensional parametrisation.
194 * 'UniformPiFR5EM2' 5% pion to lepton fake rate, uniform in a given multi-dimensional parametrisation.
195 * 'UniformPiFR1EM2' 1% pion to lepton fake rate, uniform in a given multi-dimensional parametrisation.
196 * 'UniformPiFR5EM3' 0.5% pion to lepton fake rate, uniform in a given multi-dimensional parametrisation.
197 * 'UniformPiFR1EM3' 0.1% pion to lepton fake rate, uniform in a given multi-dimensional parametrisation.
199 The function creates a ``ParticleList``, selecting particles according to the chosen ``working_point``,
200 and decorates each candidate in the list with the nominal Data/MC :math:`\\ell` ID efficiency and
201 :math:`\\pi,K` fake rate correction factors and their stat, syst uncertainty, reading the info
202 from the Conditions Database (CDB) according to the chosen input global tag (GT).
205 Particles will **not** be selected if they are outside the Data/MC *efficiency* corrections' phase space coverage
206 for the given working point.
207 In fact, the threshold value for the PID cut in such cases is set to NaN.
210 At the moment, the only supported *binary* lepton identification is against the **pion** hypothesis.
211 This implies that, if binary classification is chosen, only :math:`\\pi` fake rate corrections will be added to the
212 resulting particle list.
215 pdgId (int): the lepton pdg code.
216 working_point (str): name of the chosen working point that defines the content of the list. Choose among the above values.
217 method (str): the PID method: 'likelihood' or 'bdt'.
218 classification (str): the type of classifier: 'binary' (one-vs-pion) or 'global' (one-vs-all).
219 lid_weights_gt (str): the name identifier of the global tag with the recommended Data/MC correction weights.
223 `Charged PID XWiki page <https://xwiki.desy.de/xwiki/rest/p/fab3e>`_
224 for info about the recommended global tags.
226 release (Optional[int]): the major release number of the data and MC campaigns considered.
227 If specified, it ensures the correct :math:`\\ell` ID variables are used.
231 `Charged PID XWiki page <https://xwiki.desy.de/xwiki/rest/p/fab3e>`_
232 for info about lepton identification variables and campaigns.
234 channel_eff (Optional[str]): the channel used to derive the :math:`\\ell` ID efficiency corrections.
235 By default, 'combination' is set, meaning they are obtained by combining results
236 of several hadronic and low multiplicity channels, wherever they overlap.
240 `Charged PID XWiki page <https://xwiki.desy.de/xwiki/rest/p/fab3e>`_
241 for other possible choices (if any).
243 channel_misid_pi (Optional[str]): the channel used to derive the :math:`\\pi` fake rate corrections.
244 channel_misid_K (Optional[str]): the channel used to derive the :math:`K` fake rate corrections.
245 inputListName (Optional[str]): the name of a pre-existing ``ParticleList`` object (defined as a full ``decayString``,
246 e.g. 'e-:my_input_electrons') of which the standard lepton list will be a subset.
247 For instance, users might want to apply a Bremsstrahlung correction to electrons first,
248 which modifies their 4-momentum, and only later define the subset passing the PID selection,
249 including the appropriate PID weights and uncertainties (which are :math:`p`-dependent).
250 By default, the standard lepton list is created from all ``Track`` objects in the event.
253 Do **not** apply any PID selection on the input list, otherwise results could be biased.
255 outputListLabel (Optional[str]): the name of the output lepton list label, i.e.,
256 the string that follows the particle identifier ('e-:', 'mu-:').
257 By default, it is assigned as:
258 ``'{method}_{classification}_{working_point}'``.
260 trainingModeMulticlass (Optional[``Belle2.ChargedPidMVAWeights.ChargedPidMVATrainingMode``]): enum identifier
261 of the multi-class (global PID) training mode.
262 See `modularAnalysis.applyChargedPidMVA` docs for available options.
263 trainingModeBinary (Optional[``Belle2.ChargedPidMVAWeights.ChargedPidMVATrainingMode``]): enum identifier
264 of the classification (binary PID) training mode.
265 See `modularAnalysis.applyChargedPidMVA` docs for available options.
266 path (basf2.Path): modules are added to this path.
269 tuple(str, list(str)): the alias for the lepton ID variable, and the list of aliases for the weights.
290 available_methods = (
"likelihood",
"bdt")
291 available_classificators = (
"global",
"binary")
293 if working_point
not in working_points:
294 b2.B2ERROR(f
"The requested lepton list working point: {working_point} is not defined. \
295 Please refer to the stdLep and stdCharged documentation.")
298 if method
not in available_methods:
299 b2.B2ERROR(f
"method: {method}. Must be any of: {available_methods}.")
302 if classification
not in available_classificators:
303 b2.B2ERROR(f
"classification: {classification}. Must be any of: {available_classificators}.")
306 b2.B2INFO(f
"Prepending GT with LID corrections: {lid_weights_gt}")
307 b2.conditions.prepend_globaltag(lid_weights_gt)
313 electron = Const.electron.getPDGCode()
314 muon = Const.muon.getPDGCode()
315 pion = Const.pion.getPDGCode()
317 if lepton
not in (electron, muon):
318 b2.B2ERROR(f
"{pdgId} is not that of a light charged lepton.")
326 electron:
"electronID",
330 electron:
"electronID",
336 electron: f
"binaryPID({electron}, {pion})",
337 muon: f
"binaryPID({muon}, {pion})",
340 electron:
"binaryPID_e_pi",
341 muon:
"binaryPID_mu_pi",
348 lepton: f
"pidChargedBDTScore({lepton}, ALL)",
351 lepton: re.sub(
r"\W+",
"", f
"pidChargedBDTScore_{lepton_name}"),
356 lepton: f
"pidPairChargedBDTScore({lepton}, {pion}, ALL)",
359 lepton: re.sub(
r"\W+",
"", f
"pidPairChargedBDTScore_{lepton_name}_pi"),
367 if int(release)
in [5, 6]:
368 if lepton == electron:
369 b2.B2INFO(f
"The likelihood-based electron ID in release {release} samples is defined w/o the SVD and the TOP")
370 pid_variables[
"likelihood"][
"global"][
"var"][electron] =
"electronID_noSVD_noTOP"
371 pid_variables[
"likelihood"][
"global"][
"alias"][electron] =
"electronID_noSVD_noTOP"
372 pid_variables[
"likelihood"][
"binary"][
"var"][electron] = f
"binaryElectronID_noSVD_noTOP({pion})"
373 pid_variables[
"likelihood"][
"binary"][
"alias"][electron] =
"binaryElectronID_noSVD_noTOP_pi"
375 b2.B2INFO(f
"The likelihood-based muon ID in release {release} samples is defined w/o the SVD")
376 pid_variables[
"likelihood"][
"global"][
"var"][muon] =
"muonID_noSVD"
377 pid_variables[
"likelihood"][
"global"][
"alias"][muon] =
"muonID_noSVD"
378 pid_variables[
"likelihood"][
"binary"][
"var"][muon] = f
"binaryPID_noSVD({muon}, {pion})"
379 pid_variables[
"likelihood"][
"binary"][
"alias"][muon] =
"binaryMuonID_noSVD_pi"
382 pid_var = pid_variables[method][classification][
"var"][lepton]
383 pid_alias = pid_variables[method][classification][
"alias"][lepton]
384 if pid_alias != pid_var:
385 variables.addAlias(pid_alias, pid_var)
388 outputListName = f
"{lepton_name}:{method}_{classification}_{working_point}"
389 if outputListLabel
is not None:
390 outputListName = f
"{lepton_name}:{outputListLabel}"
392 if inputListName
is None:
393 ma.fillParticleList(outputListName,
"", path=path)
396 f
"The standard lepton list: '{outputListName}' will be created as a subset \
397 of the following ParticleList: '{inputListName}'")
398 ma.copyList(outputListName, inputListName, path=path)
402 if classification ==
"global":
403 ma.applyChargedPidMVA(particleLists=[outputListName],
405 trainingMode=trainingModeMulticlass)
406 elif classification ==
"binary":
407 ma.applyChargedPidMVA(particleLists=[outputListName],
409 binaryHypoPDGCodes=(lepton, pion),
410 trainingMode=trainingModeBinary)
413 payload_eff = f
"ParticleReweighting:{pid_alias}_eff_{channel_eff}_{working_point}"
414 payload_misid_pi = f
"ParticleReweighting:{pid_alias}_misid_pi_{channel_misid_pi}_{working_point}"
417 path.add_module(
"ParticleWeighting",
418 particleList=outputListName,
419 tableName=payload_eff).set_name(f
"ParticleWeighting_eff_{outputListName}")
420 path.add_module(
"ParticleWeighting",
421 particleList=outputListName,
422 tableName=payload_misid_pi,
423 allowToSkip=
True).set_name(f
"ParticleWeighting_misid_pi_{outputListName}")
425 if classification ==
"global":
426 payload_misid_K = f
"ParticleReweighting:{pid_alias}_misid_K_{channel_misid_K}_{working_point}"
427 path.add_module(
"ParticleWeighting",
428 particleList=outputListName,
429 tableName=payload_misid_K,
430 allowToSkip=
True).set_name(f
"ParticleWeighting_misid_K_{outputListName}")
434 cut = f
"[{pid_alias} >= extraInfo({payload_eff}_threshold)]"
435 ma.applyCuts(outputListName, cut, path=path)
438 weight_aliases_to_var = {
439 f
"weight_{pid_alias}_eff_{working_point}": f
"extraInfo({payload_eff}_data_MC_ratio)",
440 f
"weight_{pid_alias}_misid_pi_{working_point}": f
"extraInfo({payload_misid_pi}_data_MC_ratio)",
442 f
"weight_{pid_alias}_eff_{working_point}_stat_up": f
"extraInfo({payload_eff}_data_MC_uncertainty_stat_up)",
443 f
"weight_{pid_alias}_eff_{working_point}_stat_dn": f
"extraInfo({payload_eff}_data_MC_uncertainty_stat_dn)",
444 f
"weight_{pid_alias}_eff_{working_point}_sys_up": f
"extraInfo({payload_eff}_data_MC_uncertainty_sys_up)",
445 f
"weight_{pid_alias}_eff_{working_point}_sys_dn": f
"extraInfo({payload_eff}_data_MC_uncertainty_sys_dn)",
446 f
"weight_{pid_alias}_misid_pi_{working_point}_stat_up": f
"extraInfo({payload_misid_pi}_data_MC_uncertainty_stat_up)",
447 f
"weight_{pid_alias}_misid_pi_{working_point}_stat_dn": f
"extraInfo({payload_misid_pi}_data_MC_uncertainty_stat_dn)",
448 f
"weight_{pid_alias}_misid_pi_{working_point}_sys_up": f
"extraInfo({payload_misid_pi}_data_MC_uncertainty_sys_up)",
449 f
"weight_{pid_alias}_misid_pi_{working_point}_sys_dn": f
"extraInfo({payload_misid_pi}_data_MC_uncertainty_sys_dn)",
458 f
"weight_{pid_alias}_eff_{working_point}_rel_stat_up":
459 f
"formula(1+[extraInfo({payload_eff}_data_MC_uncertainty_stat_up)/extraInfo({payload_eff}_data_MC_ratio)])",
460 f
"weight_{pid_alias}_eff_{working_point}_rel_stat_dn":
461 f
"formula(1-[extraInfo({payload_eff}_data_MC_uncertainty_stat_dn)/extraInfo({payload_eff}_data_MC_ratio)])",
462 f
"weight_{pid_alias}_eff_{working_point}_rel_sys_up":
463 f
"formula(1+[extraInfo({payload_eff}_data_MC_uncertainty_sys_up)/extraInfo({payload_eff}_data_MC_ratio)])",
464 f
"weight_{pid_alias}_eff_{working_point}_rel_sys_dn":
465 f
"formula(1-[extraInfo({payload_eff}_data_MC_uncertainty_sys_dn)/extraInfo({payload_eff}_data_MC_ratio)])",
466 f
"weight_{pid_alias}_misid_pi_{working_point}_rel_stat_up":
467 f
"formula(1+[extraInfo({payload_misid_pi}_data_MC_uncertainty_stat_up)/extraInfo({payload_misid_pi}_data_MC_ratio)])",
468 f
"weight_{pid_alias}_misid_pi_{working_point}_rel_stat_dn":
469 f
"formula(1-[extraInfo({payload_misid_pi}_data_MC_uncertainty_stat_dn)/extraInfo({payload_misid_pi}_data_MC_ratio)])",
470 f
"weight_{pid_alias}_misid_pi_{working_point}_rel_sys_up":
471 f
"formula(1+[extraInfo({payload_misid_pi}_data_MC_uncertainty_sys_up)/extraInfo({payload_misid_pi}_data_MC_ratio)])",
472 f
"weight_{pid_alias}_misid_pi_{working_point}_rel_sys_dn":
473 f
"formula(1-[extraInfo({payload_misid_pi}_data_MC_uncertainty_sys_dn)/extraInfo({payload_misid_pi}_data_MC_ratio)])",
475 if classification ==
"global":
476 weight_aliases_to_var.update({
477 f
"weight_{pid_alias}_misid_K_{working_point}": f
"extraInfo({payload_misid_K}_data_MC_ratio)",
479 f
"weight_{pid_alias}_misid_K_{working_point}_stat_up": f
"extraInfo({payload_misid_K}_data_MC_uncertainty_stat_up)",
480 f
"weight_{pid_alias}_misid_K_{working_point}_stat_dn": f
"extraInfo({payload_misid_K}_data_MC_uncertainty_stat_dn)",
481 f
"weight_{pid_alias}_misid_K_{working_point}_sys_up": f
"extraInfo({payload_misid_K}_data_MC_uncertainty_sys_up)",
482 f
"weight_{pid_alias}_misid_K_{working_point}_sys_dn": f
"extraInfo({payload_misid_K}_data_MC_uncertainty_sys_dn)",
484 f
"weight_{pid_alias}_misid_K_{working_point}_rel_stat_up":
485 f
"formula(1+[extraInfo({payload_misid_K}_data_MC_uncertainty_stat_up)/extraInfo({payload_misid_K}_data_MC_ratio)])",
486 f
"weight_{pid_alias}_misid_K_{working_point}_rel_stat_dn":
487 f
"formula(1-[extraInfo({payload_misid_K}_data_MC_uncertainty_stat_dn)/extraInfo({payload_misid_K}_data_MC_ratio)])",
488 f
"weight_{pid_alias}_misid_K_{working_point}_rel_sys_up":
489 f
"formula(1+[extraInfo({payload_misid_K}_data_MC_uncertainty_sys_up)/extraInfo({payload_misid_K}_data_MC_ratio)])",
490 f
"weight_{pid_alias}_misid_K_{working_point}_rel_sys_dn":
491 f
"formula(1-[extraInfo({payload_misid_K}_data_MC_uncertainty_sys_dn)/extraInfo({payload_misid_K}_data_MC_ratio)])",
494 for alias, var
in weight_aliases_to_var.items():
495 variables.addAlias(alias, var)
497 return pid_alias, list(weight_aliases_to_var.keys())
500def stdE(listtype=_defaultlist,
505 channel_eff="combination",
506 channel_misid_pi="combination",
507 channel_misid_K="combination",
509 outputListLabel=None,
510 trainingModeMulticlass=_TrainingMode.c_Multiclass,
511 trainingModeBinary=_TrainingMode.c_Classification,
513 """ Function to prepare one of several standardized types of electron lists.
514 See the documentation of `stdLep` for details.
516 It also accepts any of the legacy definitions
517 for the ``listtype`` parameter (aka ``working_point`` in `stdLep`) to fall back to the `stdCharged` behaviour:
529 tuple(str, list(str)): the alias for the electron ID variable, and the list of aliases for the weights.
532 if listtype
in _stdnames + _effnames:
534 return _pidnames[_chargednames.index(
"e")],
None
536 return stdLep(Const.electron.getPDGCode(), listtype, method, classification, lid_weights_gt,
538 channel_eff=channel_eff,
539 channel_misid_pi=channel_misid_pi,
540 channel_misid_K=channel_misid_K,
541 inputListName=inputListName,
542 outputListLabel=outputListLabel,
543 trainingModeMulticlass=trainingModeMulticlass,
544 trainingModeBinary=trainingModeBinary,
548def stdMu(listtype=_defaultlist,
553 channel_eff="combination",
554 channel_misid_pi="combination",
555 channel_misid_K="combination",
557 outputListLabel=None,
558 trainingModeMulticlass=_TrainingMode.c_Multiclass,
559 trainingModeBinary=_TrainingMode.c_Classification,
561 """ Function to prepare one of several standardized types of muon lists.
562 See the documentation of `stdLep` for details.
564 It also accepts any of the legacy definitions
565 for the ``listtype`` parameter (aka ``working_point`` in `stdLep`) to fall back to the `stdCharged` behaviour:
577 tuple(str, list(str)): the alias for the muon ID variable, and the list of aliases for the weights.
580 if listtype
in _stdnames + _effnames:
582 return _pidnames[_chargednames.index(
"mu")],
None
584 return stdLep(Const.muon.getPDGCode(), listtype, method, classification, lid_weights_gt,
586 channel_eff=channel_eff,
587 channel_misid_pi=channel_misid_pi,
588 channel_misid_K=channel_misid_K,
589 inputListName=inputListName,
590 outputListLabel=outputListLabel,
591 trainingModeMulticlass=trainingModeMulticlass,
592 trainingModeBinary=trainingModeBinary,
596def stdMostLikely(pidPriors=None, suffix='', custom_cuts='', path=None, writeOut=True):
598 Function to prepare most likely particle lists according to PID likelihood, refer to stdCharged for details
600 @param pidPriors list of 6 float numbers used to reweight PID likelihoods, for e, mu, pi, K, p and d
601 @param suffix string added to the end of particle list names
602 @param custom_cuts custom selection cut string, if empty, standard track quality cuts will be applied
603 @param path modules are added to this path
604 @param writeOut whether RootOutput module should save the created ParticleList
610 if pidPriors
is not None:
611 args = str(pidPriors)[1:-1]
612 trackQuality =
'thetaInCDCAcceptance and nCDCHits>20'
613 if custom_cuts !=
'':
614 trackQuality = custom_cuts
615 for name
in _chargednames:
616 ma.fillParticleList(f
'{name}+:{_mostLikelyList}{suffix}',
617 f
'pidIsMostLikely({args}) > 0 and {trackQuality}', writeOut=writeOut, path=path)
This class provides a set of constants for the framework.