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