358 def makeAlgs (self, config) :
360 log = logging.getLogger(
'ElectronWorkingPointConfig')
362 if self.forceFullSimConfig:
363 log.warning(
"You are running ElectronWorkingPointConfig forcing full sim config")
364 log.warning(
"This is only intended to be used for testing purposes")
366 selectionPostfix = self.selectionName
367 if selectionPostfix !=
'' and selectionPostfix[0] !=
'_' :
368 selectionPostfix =
'_' + selectionPostfix
371 if config.geometry()
is LHCPeriod.Run1:
372 raise ValueError (
"Can't set up the ElectronWorkingPointConfig with %s, there must be something wrong!" % config.geometry().value)
374 postfix = self.postfix
376 postfix = self.selectionName
377 if postfix !=
'' and postfix[0] !=
'_' :
378 postfix =
'_' + postfix
381 if self.trackSelection :
382 alg = config.createAlgorithm(
'CP::AsgLeptonTrackSelectionAlg',
383 'ElectronTrackSelectionAlg',
385 alg.selectionDecoration =
'trackSelection' + postfix +
',as_bits'
386 alg.maxD0Significance = self.maxD0Significance
387 alg.maxDeltaZ0SinTheta = self.maxDeltaZ0SinTheta
388 alg.particles = config.readName (self.containerName)
389 alg.preselection = config.getPreselection (self.containerName,
'')
390 if self.trackSelection :
391 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
392 preselection=self.addSelectionToPreselection)
394 if 'LH' in self.identificationWP:
397 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'ElectronLikelihoodAlg' )
398 alg.selectionDecoration =
'selectLikelihood' + selectionPostfix +
',as_char'
401 config.addPrivateTool(
'selectionTool',
'AsgElectronLikelihoodTool' )
402 alg.selectionTool.primaryVertexContainer =
'PrimaryVertices'
405 if config.geometry() >= LHCPeriod.Run3:
406 if 'HI' not in self.identificationWP:
407 alg.selectionTool.WorkingPoint = self.identificationWP.
replace(
"BLayer",
"BL") +
'Electron'
409 alg.selectionTool.WorkingPoint = self.identificationWP.
replace(
'_HI',
'Electron_HI')
410 elif config.geometry()
is LHCPeriod.Run2:
411 alg.selectionTool.WorkingPoint = self.identificationWP.
replace(
"BLayer",
"BL") +
'Electron_Run2'
414 config.addPrivateTool(
'selectionTool',
'CP::AsgFlagSelectionTool' )
415 dfFlag =
"DFCommonElectronsLH" + self.identificationWP.
split(
'LH')[0]
416 dfFlag = dfFlag.replace(
"BLayer",
"BL")
417 alg.selectionTool.selectionFlags = [dfFlag]
418 elif 'SiHit' in self.identificationWP:
420 algVeto = config.createAlgorithm(
'CP::AsgSelectionAlg',
'ElectronLikelihoodAlgVeto')
421 algVeto.selectionDecoration =
'selectLikelihoodVeto' + postfix +
',as_char'
422 config.addPrivateTool(
'selectionTool',
'CP::AsgFlagSelectionTool' )
423 algVeto.selectionTool.selectionFlags = [
"DFCommonElectronsLHLoose"]
424 algVeto.selectionTool.invertFlags = [
True]
425 algVeto.particles = config.readName (self.containerName)
426 algVeto.preselection = config.getPreselection (self.containerName, self.selectionName)
428 config.addSelection (self.containerName, self.selectionName, algVeto.selectionDecoration,
429 preselection=self.addSelectionToPreselection)
432 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'ElectronLikelihoodAlg' )
433 alg.selectionDecoration =
'selectSiHit' + selectionPostfix +
',as_char'
435 config.addPrivateTool(
'selectionTool',
'CP::AsgMaskSelectionTool' )
436 dfVar =
"DFCommonElectronsLHLooseBLIsEMValue"
437 alg.selectionTool.selectionVars = [dfVar]
438 mask =
int( 0 | 0x1 << 1 | 0x1 << 2)
439 alg.selectionTool.selectionMasks = [mask]
440 elif 'DNN' in self.identificationWP:
441 if self.chargeIDSelectionRun2:
442 raise ValueError(
'DNN is not intended to be used with '
443 '`chargeIDSelectionRun2` option as there are '
444 'DNN WPs containing charge flip rejection.')
446 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'ElectronDNNAlg' )
447 alg.selectionDecoration =
'selectDNN' + selectionPostfix +
',as_char'
450 config.addPrivateTool(
'selectionTool',
'AsgElectronSelectorTool' )
452 if config.geometry()
is LHCPeriod.Run3:
453 raise ValueError (
"DNN working points are not available for Run 3 yet.")
455 alg.selectionTool.WorkingPoint = self.identificationWP +
'Electron'
458 config.addPrivateTool(
'selectionTool',
'CP::AsgFlagSelectionTool' )
459 dfFlag =
"DFCommonElectronsDNN" + self.identificationWP.
split(
'DNN')[0]
460 alg.selectionTool.selectionFlags = [dfFlag]
461 elif self.identificationWP ==
'NoID':
464 raise ValueError (f
"Electron ID working point '{self.identificationWP}' is not recognised!")
467 alg.particles = config.readName (self.containerName)
468 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
469 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
470 preselection=self.addSelectionToPreselection)
473 if 'SiHit' in self.identificationWP:
475 algDec = config.createAlgorithm(
'CP::ElectronSiHitDecAlg',
'ElectronSiHitDecAlg' )
476 selDec =
'siHitEvtHasLeptonPair' + selectionPostfix +
',as_char'
477 algDec.selectionName = selDec.split(
",")[0]
478 algDec.ElectronContainer = config.readName (self.containerName)
480 algDec.RequireTwoLeptons =
True
481 config.addSelection (self.containerName, self.selectionName, selDec,
482 preselection=self.addSelectionToPreselection)
485 if self.convSelection
is not None:
487 if self.identificationWP !=
'TightLH':
488 raise ValueError(f
"convSelection can only be used with TightLH ID, "
489 f
"whereas {self.identificationWP} has been selected. convSelection option will be ignored.")
491 allowedValues = [
"Veto",
"GammaStar",
"MatConv"]
492 if self.convSelection
not in allowedValues:
493 raise ValueError(f
"convSelection has been set to {self.convSelection}, which is not a valid option. "
494 f
"convSelection option must be one of {allowedValues}.")
497 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'ElectronAmbiguityTypeAlg' )
498 alg.selectionDecoration =
'selectAmbiguityType' + selectionPostfix +
',as_char'
499 config.addPrivateTool(
'selectionTool',
'CP::AsgNumDecorationSelectionToolUInt8' )
500 alg.selectionTool.decorationName =
"ambiguityType"
501 alg.selectionTool.doEqual =
True
502 alg.selectionTool.equal = 0
503 alg.particles = config.readName (self.containerName)
504 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
505 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
506 preselection=self.addSelectionToPreselection)
509 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'ElectronDFCommonAddAmbiguityAlg' )
510 alg.selectionDecoration =
'selectDFCommonAddAmbiguity' + selectionPostfix +
',as_char'
511 config.addPrivateTool(
'selectionTool',
'CP::AsgNumDecorationSelectionToolInt' )
512 alg.selectionTool.decorationName =
"DFCommonAddAmbiguity"
513 if self.convSelection ==
"Veto":
514 alg.selectionTool.doMax =
True
515 alg.selectionTool.max = 1
516 elif self.convSelection ==
"GammaStar":
517 alg.selectionTool.doEqual =
True
518 alg.selectionTool.equal = 1
519 elif self.convSelection ==
"MatConv":
520 alg.selectionTool.doEqual =
True
521 alg.selectionTool.equal = 2
522 alg.particles = config.readName (self.containerName)
523 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
524 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
525 preselection=self.addSelectionToPreselection)
528 if self.doFSRSelection :
530 wpFlag = alg.selectionDecoration.split(
",")[0]
531 alg = config.createAlgorithm(
'CP::EgammaFSRForMuonsCollectorAlg',
'EgammaFSRForMuonsCollectorAlg' )
532 alg.selectionDecoration = wpFlag
533 alg.ElectronOrPhotonContKey = config.readName (self.containerName)
537 if 'SiHit' in self.identificationWP:
541 if self.isolationWP !=
'NonIso' :
542 alg = config.createAlgorithm(
'CP::EgammaIsolationSelectionAlg',
543 'ElectronIsolationSelectionAlg' )
544 alg.selectionDecoration =
'isolated' + selectionPostfix +
',as_char'
545 config.addPrivateTool(
'selectionTool',
'CP::IsolationSelectionTool' )
546 alg.selectionTool.ElectronWP = self.isolationWP
547 if self.closeByCorrection:
548 alg.selectionTool.IsoDecSuffix =
"CloseByCorr"
549 alg.egammas = config.readName (self.containerName)
550 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
551 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
552 preselection=self.addSelectionToPreselection)
554 if self.chargeIDSelectionRun2
and config.geometry() >= LHCPeriod.Run3:
555 log.warning(
"ECIDS is only available for Run 2 and will not have any effect in Run 3.")
558 if self.chargeIDSelectionRun2
and config.geometry() < LHCPeriod.Run3:
559 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
560 'ElectronChargeIDSelectionAlg' )
561 alg.selectionDecoration =
'chargeID' + selectionPostfix +
',as_char'
562 if self.recomputeChargeID:
564 config.addPrivateTool(
'selectionTool',
565 'AsgElectronChargeIDSelectorTool' )
566 alg.selectionTool.TrainingFile = \
567 'ElectronPhotonSelectorTools/ChargeID/ECIDS_20180731rel21Summer2018.root'
568 alg.selectionTool.WorkingPoint =
'Loose'
569 alg.selectionTool.CutOnBDT = -0.337671
572 config.addPrivateTool(
'selectionTool',
'CP::AsgFlagSelectionTool' )
573 alg.selectionTool.selectionFlags = [
"DFCommonElectronsECIDS"]
575 alg.particles = config.readName (self.containerName)
576 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
577 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
578 preselection=self.addSelectionToPreselection)
580 correlationModels = [
"SIMPLIFIED",
"FULL",
"TOTAL",
"TOYS"]
581 map_file =
'ElectronEfficiencyCorrection/2015_2025/rel22.2/2025_Run2Rel22_Recommendation_v3/map0.txt' \
582 if config.geometry()
is LHCPeriod.Run2
else \
583 'ElectronEfficiencyCorrection/2015_2025/rel22.2/2025_Run3_Consolidated_Prerecom_v3/map1.txt'
586 if config.dataType()
is not DataType.Data
and not self.noEffSF:
587 if 'DNN' in self.identificationWP:
588 raise ValueError(
'DNN does not yet have efficiency correction, '
589 'please disable it by setting `noEffSF` to True.')
591 alg = config.createAlgorithm(
'CP::ElectronEfficiencyCorrectionAlg',
592 'ElectronEfficiencyCorrectionAlgReco' )
593 config.addPrivateTool(
'efficiencyCorrectionTool',
594 'AsgElectronEfficiencyCorrectionTool' )
595 alg.scaleFactorDecoration =
'el_reco_effSF' + selectionPostfix +
'_%SYS%'
596 alg.efficiencyCorrectionTool.MapFilePath = map_file
597 alg.efficiencyCorrectionTool.RecoKey =
"Reconstruction"
598 if self.correlationModelReco
not in correlationModels:
599 raise ValueError(
'Invalid correlation model for reconstruction efficiency, '
600 f
'has to be one of: {", ".join(correlationModels)}')
601 if config.geometry() >= LHCPeriod.Run3
and self.correlationModelReco !=
"TOTAL":
602 log.warning(
"Only TOTAL correlation model is currently supported "
603 "for reconstruction efficiency correction in Run 3.")
604 alg.efficiencyCorrectionTool.CorrelationModel =
"TOTAL"
606 alg.efficiencyCorrectionTool.CorrelationModel = self.correlationModelReco
607 if config.dataType()
is DataType.FastSim:
608 alg.efficiencyCorrectionTool.ForceDataType = (
609 PATCore.ParticleDataType.Full
if self.forceFullSimConfig
610 else PATCore.ParticleDataType.Fast)
611 elif config.dataType()
is DataType.FullSim:
612 alg.efficiencyCorrectionTool.ForceDataType = \
613 PATCore.ParticleDataType.Full
614 alg.outOfValidity = 2
615 alg.outOfValidityDeco =
'el_reco_bad_eff' + selectionPostfix
616 alg.electrons = config.readName (self.containerName)
617 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
618 if self.saveDetailedSF:
619 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
620 'reco_effSF' + postfix)
621 sfList += [alg.scaleFactorDecoration]
624 if config.dataType()
is not DataType.Data
and not self.noEffSF
and self.identificationWP !=
'NoID':
626 alg = config.createAlgorithm(
'CP::ElectronEfficiencyCorrectionAlg',
627 'ElectronEfficiencyCorrectionAlgID' )
628 config.addPrivateTool(
'efficiencyCorrectionTool',
629 'AsgElectronEfficiencyCorrectionTool' )
630 alg.scaleFactorDecoration =
'el_id_effSF' + selectionPostfix +
'_%SYS%'
631 alg.efficiencyCorrectionTool.MapFilePath = map_file
632 alg.efficiencyCorrectionTool.IdKey = self.identificationWP.
replace(
"LH",
"")
633 if self.correlationModelId
not in correlationModels:
634 raise ValueError(
'Invalid correlation model for identification efficiency, '
635 f
'has to be one of: {", ".join(correlationModels)}')
636 alg.efficiencyCorrectionTool.CorrelationModel = self.correlationModelId
637 if config.dataType()
is DataType.FastSim:
638 alg.efficiencyCorrectionTool.ForceDataType = (
639 PATCore.ParticleDataType.Full
if self.forceFullSimConfig
640 else PATCore.ParticleDataType.Fast)
641 elif config.dataType()
is DataType.FullSim:
642 alg.efficiencyCorrectionTool.ForceDataType = \
643 PATCore.ParticleDataType.Full
644 alg.outOfValidity = 2
645 alg.outOfValidityDeco =
'el_id_bad_eff' + selectionPostfix
646 alg.electrons = config.readName (self.containerName)
647 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
648 if self.saveDetailedSF:
649 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
650 'id_effSF' + postfix)
651 sfList += [alg.scaleFactorDecoration]
654 if config.dataType()
is not DataType.Data
and self.isolationWP !=
'NonIso' and not self.noEffSF:
655 alg = config.createAlgorithm(
'CP::ElectronEfficiencyCorrectionAlg',
656 'ElectronEfficiencyCorrectionAlgIsol' )
657 config.addPrivateTool(
'efficiencyCorrectionTool',
658 'AsgElectronEfficiencyCorrectionTool' )
659 alg.scaleFactorDecoration =
'el_isol_effSF' + selectionPostfix +
'_%SYS%'
660 alg.efficiencyCorrectionTool.MapFilePath = map_file
661 alg.efficiencyCorrectionTool.IdKey = self.identificationWP.
replace(
"LH",
"")
662 alg.efficiencyCorrectionTool.IsoKey = self.isolationWP
663 if self.correlationModelIso
not in correlationModels:
664 raise ValueError(
'Invalid correlation model for isolation efficiency, '
665 f
'has to be one of: {", ".join(correlationModels)}')
666 if self.correlationModelIso !=
'TOTAL':
667 log.warning(
"Only TOTAL correlation model is currently supported "
668 "for isolation efficiency correction in Run 3.")
669 alg.efficiencyCorrectionTool.CorrelationModel =
"TOTAL"
670 if config.dataType()
is DataType.FastSim:
671 alg.efficiencyCorrectionTool.ForceDataType = (
672 PATCore.ParticleDataType.Full
if self.forceFullSimConfig
673 else PATCore.ParticleDataType.Fast)
674 elif config.dataType()
is DataType.FullSim:
675 alg.efficiencyCorrectionTool.ForceDataType = \
676 PATCore.ParticleDataType.Full
677 alg.outOfValidity = 2
678 alg.outOfValidityDeco =
'el_isol_bad_eff' + selectionPostfix
679 alg.electrons = config.readName (self.containerName)
680 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
681 if self.saveDetailedSF:
682 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
683 'isol_effSF' + postfix)
684 sfList += [alg.scaleFactorDecoration]
686 if (self.chargeIDSelectionRun2
and config.geometry() < LHCPeriod.Run3
and
687 config.dataType()
is not DataType.Data
and not self.noEffSF):
688 alg = config.createAlgorithm(
'CP::ElectronEfficiencyCorrectionAlg',
689 'ElectronEfficiencyCorrectionAlgEcids' )
690 config.addPrivateTool(
'efficiencyCorrectionTool',
691 'AsgElectronEfficiencyCorrectionTool' )
692 alg.scaleFactorDecoration =
'el_ecids_effSF' + selectionPostfix +
'_%SYS%'
693 if self.isolationWP !=
'Tight_VarRad':
694 raise ValueError(
'ECIDS SFs are supported only for Tight_VarRad isolation.')
695 if self.identificationWP ==
'LooseBLayerLH':
697 elif self.identificationWP ==
'MediumLH':
699 elif self.identificationWP ==
'TightLH':
702 raise ValueError(
'ECIDS SFs are supported only for ID LooseBLayerLH, MediumLH, or TightLH')
704 alg.efficiencyCorrectionTool.CorrelationModel =
"TOTAL"
705 alg.efficiencyCorrectionTool.CorrectionFileNameList = \
706 [f
'ElectronEfficiencyCorrection/2015_2025/rel22.2/2025_Run2Rel22_Recommendation_v2/ecids/efficiencySF.ChargeID.{ecids_lh}_ECIDS_Tight_VarRad.root']
707 if config.dataType()
is DataType.FastSim:
708 alg.efficiencyCorrectionTool.ForceDataType = (
709 PATCore.ParticleDataType.Full
if self.forceFullSimConfig
710 else PATCore.ParticleDataType.Fast)
711 elif config.dataType()
is DataType.FullSim:
712 alg.efficiencyCorrectionTool.ForceDataType = \
713 PATCore.ParticleDataType.Full
714 alg.outOfValidity = 2
715 alg.outOfValidityDeco =
'el_ecids_bad_eff' + selectionPostfix
716 alg.electrons = config.readName (self.containerName)
717 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
718 if self.saveDetailedSF:
719 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
720 'ecids_effSF' + postfix)
721 sfList += [alg.scaleFactorDecoration]
723 if self.addChargeMisIDSF
and config.dataType()
is not DataType.Data
and not self.noEffSF:
724 if config.geometry() >= LHCPeriod.Run3:
725 raise ValueError(
'Run 3 does not yet have charge mis-ID correction, '
726 'please disable it by setting `noEffSF` to False.')
728 alg = config.createAlgorithm(
'CP::ElectronEfficiencyCorrectionAlg',
729 'ElectronEfficiencyCorrectionAlgMisid' )
730 config.addPrivateTool(
'efficiencyCorrectionTool',
731 'CP::ElectronChargeEfficiencyCorrectionTool' )
732 alg.scaleFactorDecoration =
'el_charge_misid_effSF' + selectionPostfix +
'_%SYS%'
733 if self.isolationWP !=
'Tight_VarRad':
734 raise ValueError(
'Charge mis-ID SFs are supported only for Tight_VarRad isolation.')
735 if self.identificationWP ==
'LooseBLayerLH':
736 misid_lh =
'LooseAndBLayerLLH'
737 elif self.identificationWP ==
'MediumLH':
738 misid_lh =
'MediumLLH'
739 elif self.identificationWP ==
'TightLH':
740 misid_lh =
'TightLLH'
742 raise ValueError(
'Charge mis-ID SFs are supported only for ID LooseBLayerLH, MediumLH, or TightLH')
743 misid_suffix =
'_ECIDSloose' if self.chargeIDSelectionRun2
else ''
745 alg.efficiencyCorrectionTool.CorrectionFileName = \
746 f
'ElectronEfficiencyCorrection/2015_2025/rel22.2/2025_Run2Rel22_Recommendation_v2/charge_misID/chargeEfficiencySF.{misid_lh}_d0z0_TightVarRad{misid_suffix}.root'
747 if config.dataType()
is DataType.FastSim:
748 alg.efficiencyCorrectionTool.ForceDataType = (
749 PATCore.ParticleDataType.Full
if self.forceFullSimConfig
750 else PATCore.ParticleDataType.Fast)
751 elif config.dataType()
is DataType.FullSim:
752 alg.efficiencyCorrectionTool.ForceDataType = \
753 PATCore.ParticleDataType.Full
754 alg.outOfValidity = 2
755 alg.outOfValidityDeco =
'el_misid_bad_eff' + selectionPostfix
756 alg.electrons = config.readName (self.containerName)
757 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
758 if self.saveDetailedSF:
759 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
760 'charge_misid_effSF' + postfix)
761 sfList += [alg.scaleFactorDecoration]
763 if config.dataType()
is not DataType.Data
and not self.noEffSF
and self.saveCombinedSF:
764 alg = config.createAlgorithm(
'CP::AsgObjectScaleFactorAlg',
765 'ElectronCombinedEfficiencyScaleFactorAlg' )
766 alg.particles = config.readName (self.containerName)
767 alg.inScaleFactors = sfList
768 alg.outScaleFactor =
'effSF' + postfix +
'_%SYS%'
769 config.addOutputVar (self.containerName, alg.outScaleFactor,
'effSF' + postfix)