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