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