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