From 8109a406879932f1cbf9484821fe002b3b3b7fad Mon Sep 17 00:00:00 2001
From: Nils Krumnack <nils.erik.krumnack@cern.ch>
Date: Wed, 7 Feb 2024 08:11:24 -0600
Subject: [PATCH] introduce a mechanism to exclude specific cuts from
 subsequent use

The main usage here is that if OR is run before MET we don't want to use the OR selections with MET.  A similar issue is expected to come up with fakes/fakeable objects, i.e. some specific baseline cuts should not be applied (in fact they are inverted), but only for very specific users.

The mechanism chosen here is to mark the cuts when they are registered and then exclude by name when requesting the selection.
---
 .../python/ConfigAccumulator.py               | 24 ++++++++++++-------
 .../python/OverlapAnalysisConfig.py           | 24 +++++++++----------
 .../python/MetAnalysisConfig.py               |  8 +++----
 3 files changed, 31 insertions(+), 25 deletions(-)

diff --git a/PhysicsAnalysis/Algorithms/AnalysisAlgorithmsConfig/python/ConfigAccumulator.py b/PhysicsAnalysis/Algorithms/AnalysisAlgorithmsConfig/python/ConfigAccumulator.py
index fac5deb24953..f8ef2213acb7 100644
--- a/PhysicsAnalysis/Algorithms/AnalysisAlgorithmsConfig/python/ConfigAccumulator.py
+++ b/PhysicsAnalysis/Algorithms/AnalysisAlgorithmsConfig/python/ConfigAccumulator.py
@@ -29,13 +29,14 @@ class SelectionConfig :
     removed in the future."""
 
     def __init__ (self, selectionName, decoration,
-                  *, bits=0, preselection=None) :
+                  *, bits=0, preselection=None, comesFrom = '') :
         self.name = selectionName
         self.decoration = decoration
         if preselection is not None :
             self.preselection = preselection
         else :
             self.preselection = (selectionName == '')
+        self.comesFrom = comesFrom
 
 
 
@@ -388,7 +389,7 @@ class ConfigAccumulator :
         return self._containerConfig[containerName].isMet
 
 
-    def readNameAndSelection (self, containerName) :
+    def readNameAndSelection (self, containerName, *, excludeFrom = None) :
         """get the name of the "current copy" of the given container, and the
         selection string
 
@@ -405,7 +406,7 @@ class ConfigAccumulator :
             selectionName = split[1]
         else :
             raise Exception ('invalid object selection name: ' + containerName)
-        return self.readName (objectName), self.getFullSelection (objectName, selectionName)
+        return self.readName (objectName), self.getFullSelection (objectName, selectionName, excludeFrom=excludeFrom)
 
 
     def nextPass (self) :
@@ -445,7 +446,7 @@ class ConfigAccumulator :
 
 
     def getFullSelection (self, containerName, selectionName,
-                          *, skipBase = False) :
+                          *, skipBase = False, excludeFrom = None) :
 
         """get the selection string for the given selection on the given
         container
@@ -460,10 +461,16 @@ class ConfigAccumulator :
                           expression based on multiple named selections
         skipBase --- will avoid the base selection, and should normally
                      not be used by the end-user.
-
+        excludeFrom --- a set of string names of selection sources to exclude
+                        e.g. to exclude OR selections from MET
         """
         if containerName not in self._containerConfig :
             return ""
+        
+        if excludeFrom is None :
+            excludeFrom = {}
+        elif not isinstance(excludeFrom, set) :
+            raise ValueError ('invalid excludeFrom argument (need set of strings): ' + str(excludeFrom))
 
         # Check if this is actually a selection expression,
         # e.g. `A||B` and if so translate it into a complex expression
@@ -480,13 +487,13 @@ class ConfigAccumulator :
                     selectionName = selectionName[1:]
                 else :
                     subname = match.group(0)
-                    subresult = self.getFullSelection (containerName, subname, skipBase = True)
+                    subresult = self.getFullSelection (containerName, subname, skipBase = True, excludeFrom=excludeFrom)
                     if subresult != '' :
                         result += '(' + subresult + ')'
                     else :
                         result += 'true'
                     selectionName = selectionName[len(subname):]
-            subresult = self.getFullSelection (containerName, '')
+            subresult = self.getFullSelection (containerName, '', excludeFrom=excludeFrom)
             if subresult != '' :
                 result = subresult + '&&(' + result + ')'
             return result
@@ -494,8 +501,7 @@ class ConfigAccumulator :
         config = self._containerConfig[containerName]
         decorations = []
         for selection in config.selections :
-            if ((selection.name == '' and not skipBase) or
-                selection.name == selectionName) :
+            if ((selection.name == '' and not skipBase) or selection.name == selectionName) and (selection.comesFrom not in excludeFrom) :
                 decorations += [selection.decoration]
         return '&&'.join (decorations)
 
diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/OverlapAnalysisConfig.py b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/OverlapAnalysisConfig.py
index 80c4f9e3ae6c..8a3b5d14a0c2 100644
--- a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/OverlapAnalysisConfig.py
+++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/python/OverlapAnalysisConfig.py
@@ -152,27 +152,27 @@ class OverlapAnalysisConfig (ConfigBlock):
         if electrons :
             alg.electrons = electrons
             alg.electronsDecoration = self.outputLabel + '_%SYS%,as_char'
-            config.addSelection (self.electrons.split('.')[0], electronsSelectionName, alg.electronsDecoration, preselection=False)
+            config.addSelection (self.electrons.split('.')[0], electronsSelectionName, alg.electronsDecoration, preselection=False, comesFrom='or')
         if muons :
             alg.muons = muons
             alg.muonsDecoration = self.outputLabel + '_%SYS%,as_char'
-            config.addSelection (self.muons.split('.')[0], muonsSelectionName, alg.muonsDecoration, preselection=False)
+            config.addSelection (self.muons.split('.')[0], muonsSelectionName, alg.muonsDecoration, preselection=False, comesFrom='or')
         if taus :
             alg.taus = taus
             alg.tausDecoration = self.outputLabel + '_%SYS%,as_char'
-            config.addSelection (self.taus.split('.')[0], tausSelectionName, alg.tausDecoration, preselection=False)
+            config.addSelection (self.taus.split('.')[0], tausSelectionName, alg.tausDecoration, preselection=False, comesFrom='or')
         if jets :
             alg.jets = jets
             alg.jetsDecoration = self.outputLabel + '_%SYS%,as_char'
-            config.addSelection (self.jets.split('.')[0], jetsSelectionName, alg.jetsDecoration, preselection=False)
+            config.addSelection (self.jets.split('.')[0], jetsSelectionName, alg.jetsDecoration, preselection=False, comesFrom='or')
         if photons :
             alg.photons = photons
             alg.photonsDecoration = self.outputLabel + '_%SYS%,as_char'
-            config.addSelection (self.photons.split('.')[0], photonsSelectionName, alg.photonsDecoration, preselection=False)
+            config.addSelection (self.photons.split('.')[0], photonsSelectionName, alg.photonsDecoration, preselection=False, comesFrom='or')
         if fatJets :
             alg.fatJets = fatJets
             alg.fatJetsDecoration = self.outputLabel + '_%SYS%,as_char'
-            config.addSelection (self.fatJets.split('.')[0], fatJetsSelectionName, alg.fatJetsDecoration, preselection=False)
+            config.addSelection (self.fatJets.split('.')[0], fatJetsSelectionName, alg.fatJetsDecoration, preselection=False, comesFrom='or')
 
         # Create its main tool, and set its basic properties:
         config.addPrivateTool( 'overlapTool', 'ORUtils::OverlapRemovalTool' )
@@ -354,42 +354,42 @@ class OverlapAnalysisConfig (ConfigBlock):
                 alg.preselection = '&&'.join (config.getPreselection (self.electrons.split('.')[0], electronsSelectionName, asList=True)
                         + [self.outputLabel + '_%SYS%,as_char'])
                 alg.selectionDecoration = preselectLabel
-                config.addSelection (self.electrons.split('.')[0], electronsSelectionName, alg.selectionDecoration+',as_char', bits=1, preselection=True)
+                config.addSelection (self.electrons.split('.')[0], electronsSelectionName, alg.selectionDecoration+',as_char', bits=1, preselection=True, comesFrom='or')
             if muons :
                 alg = config.createAlgorithm( 'CP::AsgUnionPreselectionAlg','ORMuonsPreselectionAlg' + postfix )
                 alg.particles = muons
                 alg.preselection = '&&'.join (config.getPreselection (self.muons.split('.')[0], muonsSelectionName, asList=True)
                         + [self.outputLabel + '_%SYS%,as_char'])
                 alg.selectionDecoration = preselectLabel
-                config.addSelection (self.muons.split('.')[0], muonsSelectionName, alg.selectionDecoration+',as_char', bits=1, preselection=True)
+                config.addSelection (self.muons.split('.')[0], muonsSelectionName, alg.selectionDecoration+',as_char', bits=1, preselection=True, comesFrom='or')
             if taus :
                 alg = config.createAlgorithm( 'CP::AsgUnionPreselectionAlg','ORTausPreselectionAlg' + postfix )
                 alg.particles = taus
                 alg.preselection = '&&'.join (config.getPreselection (self.taus.split('.')[0], tausSelectionName, asList=True)
                         + [self.outputLabel + '_%SYS%,as_char'])
                 alg.selectionDecoration = preselectLabel
-                config.addSelection (self.taus.split('.')[0], tausSelectionName, alg.selectionDecoration+',as_char', bits=1, preselection=True)
+                config.addSelection (self.taus.split('.')[0], tausSelectionName, alg.selectionDecoration+',as_char', bits=1, preselection=True, comesFrom='or')
             if jets :
                 alg = config.createAlgorithm( 'CP::AsgUnionPreselectionAlg','ORJetsPreselectionAlg' + postfix )
                 alg.particles = jets
                 alg.preselection = '&&'.join (config.getPreselection (self.jets.split('.')[0], jetsSelectionName, asList=True)
                         + [self.outputLabel + '_%SYS%,as_char'])
                 alg.selectionDecoration = preselectLabel
-                config.addSelection (self.jets.split('.')[0], jetsSelectionName, alg.selectionDecoration+',as_char', bits=1, preselection=True)
+                config.addSelection (self.jets.split('.')[0], jetsSelectionName, alg.selectionDecoration+',as_char', bits=1, preselection=True, comesFrom='or')
             if photons :
                 alg = config.createAlgorithm( 'CP::AsgUnionPreselectionAlg','ORPhotonsPreselectionAlg' + postfix )
                 alg.particles = photons
                 alg.preselection = '&&'.join (config.getPreselection (self.photons.split('.')[0], photonsSelectionName, asList=True)
                         + [self.outputLabel + '_%SYS%,as_char'])
                 alg.selectionDecoration = preselectLabel
-                config.addSelection (self.photons.split('.')[0], photonsSelectionName, alg.selectionDecoration+',as_char', bits=1, preselection=True)
+                config.addSelection (self.photons.split('.')[0], photonsSelectionName, alg.selectionDecoration+',as_char', bits=1, preselection=True, comesFrom='or')
             if fatJets :
                 alg = config.createAlgorithm( 'CP::AsgUnionPreselectionAlg','ORFatJetsPreselectionAlg' + postfix )
                 alg.particles = fatJets
                 alg.preselection = '&&'.join (config.getPreselection (self.fatJets.split('.')[0], fatJetsSelectionName, asList=True)
                         + [self.outputLabel + '_%SYS%,as_char'])
                 alg.selectionDecoration = preselectLabel
-                config.addSelection (self.fatJets.split('.')[0], fatJetsSelectionName, alg.selectionDecoration+',as_char', bits=1, preselection=True)
+                config.addSelection (self.fatJets.split('.')[0], fatJetsSelectionName, alg.selectionDecoration+',as_char', bits=1, preselection=True, comesFrom='or')
 
 
 
diff --git a/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/python/MetAnalysisConfig.py b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/python/MetAnalysisConfig.py
index 897ea8a2c2a0..9f677322e874 100644
--- a/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/python/MetAnalysisConfig.py
+++ b/PhysicsAnalysis/Algorithms/MetAnalysisAlgorithms/python/MetAnalysisConfig.py
@@ -57,13 +57,13 @@ class MetAnalysisConfig (ConfigBlock):
         alg.metAssociation = 'METAssoc_' + metSuffix
         alg.jets = config.readName (self.jets)
         if self.muons != "" :
-            alg.muons, alg.muonsSelection = config.readNameAndSelection (self.muons)
+            alg.muons, alg.muonsSelection = config.readNameAndSelection (self.muons, excludeFrom={'or'})
         if self.electrons != "" :
-            alg.electrons, alg.electronsSelection = config.readNameAndSelection (self.electrons)
+            alg.electrons, alg.electronsSelection = config.readNameAndSelection (self.electrons, excludeFrom={'or'})
         if self.photons != "" :
-            alg.photons, alg.photonsSelection = config.readNameAndSelection (self.photons)
+            alg.photons, alg.photonsSelection = config.readNameAndSelection (self.photons, excludeFrom={'or'})
         if self.taus != "" :
-            alg.taus, alg.tausSelection = config.readNameAndSelection (self.taus)
+            alg.taus, alg.tausSelection = config.readNameAndSelection (self.taus, excludeFrom={'or'})
         if self.invisible != "" :
             alg.invisible = config.readName (self.invisible)
         alg.met = config.writeName (self.containerName, isMet = True)
-- 
GitLab