def initialize(self,config_name): """ Initialize the selection (before looping the events) @param config_name Name of the configuration to use (e.g., 'miniSL') """ self.cfg_name = config_name logging.getLogger('share/{0}.log'.format(self.cfg_name)) self.loggingLEVEL = logging.getLogger().getEffectiveLevel() # DEBUG, INFO, ERROR, etc. logging.info("") logging.critical(" -- In file 'SelectionBase.py'\n") logging.info(" ------------ ") parser_cutsfile_name = self.parser.get(self.cfg_name,'cutsfile') # default if not self.cutsfile: self.cutsfile = parser_cutsfile_name self.cuts = config.processCuts(self.cutsfile) ## We only need to load the cuts files once ## (the cuts don't change from event-to-event!) self.object_level_cuts = ObjectCuts(self.parser,self.cuts) self.event_level_cuts = EventCuts(self.parser,self.cuts) self.build_custom_objects = (self.parser.get(self.cfg_name,'new_objects')) self.btag_WP = info.btagging_WP(self.parser.get(self.cfg_name,'btag_wkpt')) return
class SelectionBase(object): """ Class for applying a custom selection on an existing set of ntuples. This is meant to be the base class that applies cuts on our objects and event-level quantities in a modular way. You can use this file with different cuts files, or inherit from it to build your own selection. """ def __init__(self,cfg_file,_cutsfile=''): """ Initialize the class. @param cfg_file configuration file @param _cutsfile cuts file to use for selection (empty by default) """ self.parser = cfg_file # settings self.cutsfile = _cutsfile # to pass a different cuts file (e.g., from pyDataMC) self.build_lepton = lepBase # setup the lepton self.build_met = metBase # setup the MET self.build_nu = nuBase # setup the neutrino self.build_jets = jetBase # setup the jets self.build_tjets = tjetBase # setup the track jets self.build_ljets = fjBase # setup the Fat Jets return #----------------------------------------------------------------------------------------# def initialize(self,config_name): """ Initialize the selection (before looping the events) @param config_name Name of the configuration to use (e.g., 'miniSL') """ self.cfg_name = config_name logging.getLogger('share/{0}.log'.format(self.cfg_name)) self.loggingLEVEL = logging.getLogger().getEffectiveLevel() # DEBUG, INFO, ERROR, etc. logging.info("") logging.critical(" -- In file 'SelectionBase.py'\n") logging.info(" ------------ ") parser_cutsfile_name = self.parser.get(self.cfg_name,'cutsfile') # default if not self.cutsfile: self.cutsfile = parser_cutsfile_name self.cuts = config.processCuts(self.cutsfile) ## We only need to load the cuts files once ## (the cuts don't change from event-to-event!) self.object_level_cuts = ObjectCuts(self.parser,self.cuts) self.event_level_cuts = EventCuts(self.parser,self.cuts) self.build_custom_objects = (self.parser.get(self.cfg_name,'new_objects')) self.btag_WP = info.btagging_WP(self.parser.get(self.cfg_name,'btag_wkpt')) return #----------------------------------------------------------------------------------------# def execute(self,T,F): """ Function to setup each file. @param T Root Ttree @param f Root file object (not just the string) """ self.t = T # root ttree self.file = F # root file (not used...) return #----------------------------------------------------------------------------------------# def event_loop(self,Entry): """Running the event selection for a single event.""" self.entry = Entry self.t.GetEntry(self.entry) if not self.entry%1000: print " -> Entry {0}".format(self.entry) ## Initialize these in case stuff fails we can return empty values passedSelection = False savedEventVariables = {'fatjets': [],\ 'rcjets': [],\ 'resjets': [],\ 'bjets': [],\ 'jets': [],\ 'lepton': False,\ 'nu': False,\ 'MET': 0,\ 'HT': 0,\ 'MC': {},\ 'eventinfo': {}} # ---------------- # # Physics Objects # # ---------------- # self.initializeObjects() # ---------------- # # Cuts # # ---------------- # ## Cutting on truth objects? DeltaR matching? if any('truth' in i for i in self.cuts): truth_objs = truthBase(self.t) for truth_obj in truth_objs.keys(): self.objects[truth_obj] = truth_objs[truth_obj] ## -- cuts on objects c_objlevel = self.objectLevelCuts() # function below if not c_objlevel['result']: return {'objects':savedEventVariables,'result':passedSelection} self.objects = c_objlevel['objects'] # update our dictionary of objects ## -- cuts on event-level quantities (HT, DeltaR(x,y), etc.) c_evtlevel = self.eventLevelCuts() # function below if not c_evtlevel['result']: return {'objects':savedEventVariables,'result':passedSelection} self.objects = c_evtlevel['objects'] ## -- record HT if it hasn't been saved or used if not self.objects.get('ht'): self.objects['ht'] = vlq.calcHT(self.objects) # ---------------- # # Event Info # # ---------------- # signal_dsids = info.dsids()['signal']['TTS'].keys() ttbar_dsids = info.dsids()['background']['ttbar'].keys() if self.t.mcChannelNumber in signal_dsids+ttbar_dsids: # setup the truth (after cuts and only for signal & ttbar) self.MC = truthBase(self.t) else: self.MC = {} boostedEvent = (len(self.objects['fatjets'])>0) resolvedEvent = (not boostedEvent) eventInfo = {'mcWeight': self.t.weight_mc,\ 'pileupWeight': self.t.weight_pileup,\ 'eventNumber': self.t.eventNumber,\ 'runNumber': self.t.runNumber,\ 'mcChannelNumber': self.t.mcChannelNumber,\ 'mu': self.t.mu,\ 'weight_btag_track_70': self.t.weight_btag_track_70,\ 'weight_btag_60': self.t.weight_btag_60,\ 'weight_btag_70': self.t.weight_btag_70,\ 'weight_btag_77': self.t.weight_btag_77,\ 'weight_btag_85': self.t.weight_btag_85,\ 'weight_lep_eff': self.t.weight_lept_eff,\ 'nAMI': self.t.AMI,\ 'isBoosted': boostedEvent,\ 'isResolved': resolvedEvent,\ 'mujets': self.t.mujets,\ 'ejets': self.t.ejets,\ 'nbtags': self.t.btags_n,\ 'KFactor': self.t.KFactor,\ 'XSection': self.t.XSection,\ 'vlq_tag': self.t.vlq_evtype,\ 'FilterEff': self.t.FilterEff} # ------------------------------------------------------------- # # Log the results for inspection later # # ------------------------------------------------------------- # if self.loggingLEVEL >= 20: self.logResults() # ------------------------------------------------------------- # # Return the results to miniSL.py # # ------------------------------------------------------------- # passedSelection = True savedEventVariables = {'fatjets': self.objects['fatjets'],\ 'rcjets': self.objects['rcjets'],\ 'resjets': self.objects['resjets'],\ 'lepton': self.objects['lepton'],\ 'nu': self.objects['nu'],\ 'jets': self.objects['jets'],\ 'tjets': self.objects['tjets'],\ 'met': self.objects['met'],\ 'HT': self.objects['ht'],\ 'MC': self.MC,\ 'eventinfo': eventInfo} ## Analysis-specific custom object :: need to remove this hard-coding... if self.objects.get('TTbar'): savedEventVariables['TTbar'] = self.objects['TTbar'] else: savedEventVariables['TTbar'] = [] return {'objects':savedEventVariables,'result':passedSelection} #----------------------------------------------------------------------------------------# def initializeObjects(self): """ One function to group other function calls to setup the physics objects. The truth information is called at the end of the event (to make sure that all of the cuts were passed, first.) Physics Objects (or 'Reconstructed Objets') in the analysis: - Lepton - MET (Neutrino) - small-radius jets :: light jets (can be used to form 'resolved' W candidate) :: b-jets (category for the two jets with highest mv2c20 weights) - large-radius jets :: nominal large-R jets :: re-clustered large-R jets (not the default) """ lep = self.build_lepton(self.t) # setup the lepton met = self.build_met(self.t) # setup the MET nu = self.build_nu(self.t) # setup the neutrino jets = self.build_jets(self.t) # setup the jets tjets = self.build_tjets(self.t) # setup the track jets fatjets = self.build_ljets(self.t) # setup the Fat Jets rcjets = [] #rcBase(self.t) # setup the re-clustered jets NOT THIS RELEASE resjets = [] #resBase(jets) # setup the resolved jets NOT ANYMORE num_btags = len([q for q in jets if q.mv2c20>self.btag_WP]) jets.sort( key=lambda x: x.Pt(), reverse=True) # [0] has the highest pT fatjets.sort(key=lambda x: x.Pt(), reverse=True) #rcjets.sort( key=lambda x: x.Pt(), reverse=True) # no RC jets in this production resjets.sort(key=lambda x: x.Pt(), reverse=True) tjets.sort( key=lambda x: x.Pt(), reverse=True) self.objects = {'lepton': lep,\ 'met': met,\ 'nu': nu,\ 'jets': jets,\ 'fatjets':fatjets,\ 'rcjets': rcjets,\ 'resjets':resjets,\ 'tjets': tjets,\ 'nbtags': num_btags} self.objects['vlq_evtype'] = -1 if self.t.mcChannelNumber<=0 else self.t.vlq_evtype if self.build_custom_objects: self.custom_objects() # build TTbar return #----------------------------------------------------------------------------------------# def objectLevelCuts(self): """ Apply cuts to the physics objects in the event. Pass the dict of objects and dict of cuts to ObjectCuts. """ obj_level_cuts = self.object_level_cuts.applyCuts(self.objects) # object_level_cuts looks like: {'result':True/False,'objects':objects} # in case the objects were redefined (e.g., less fat jets after cuts) return obj_level_cuts #----------------------------------------------------------------------------------------# def eventLevelCuts(self): """Apply cuts to the event-level quantities. Pass the dict of objects and dict of cuts to EventCuts. """ evt_level_cuts = self.event_level_cuts.applyCuts(self.objects) # evt_level_cuts looks like: return {'result':True/False,'objects':objects} # for things that were removed return evt_level_cuts #----------------------------------------------------------------------------------------# def custom_objects(self): """ Generate custom objects for the analysis. Assumes that the class you've written has the same name as the file, just capitalized, e.g., file: 'customObjectsBase.py' & class: 'CustomObjectsBase'. """ cfg_new_objects = self.parser.get(self.cfg_name,'new_objects').split() obj_file = cfg_new_objects[0] if len(cfg_new_objects)>1: new_objects = cfg_new_objects[1] # new objects separated by commas else: new_objects = 'custom' obj_path,obj_filename = obj_file.split('/') obj_filename = obj_filename.split('.')[0] obj_classname = obj_filename[0].upper()+obj_filename[1:] # import the class that defines and makes your custom object(s) pyObjectsFile = importlib.import_module(obj_path+"."+obj_filename) pyObjects = getattr(pyObjectsFile,obj_classname) for new_object in new_objects.split(','): if '(' in new_object: # the ttree name is different from the object's name new_object,ttree_object = config.parentheses(new_object) else: # the ttree name is the same as the object's name ttree_object = new_object newObjects = pyObjects(new_object) newObjects.ttree_name = ttree_object self.objects = newObjects.getObject(event_objects=self.objects,ttree=self.t) # return the objects because we have added a new one, # and may have modified others return #----------------------------------------------------------------------------------------# def PrintCutflow(self): """Print the cutflow.""" return #----------------------------------------------------------------------------------------# def logResults(self): logging.info(" ------------------- \n") logging.info(" ++ Event {0} Summary ++ \n".format(self.entry)) objects_keys = self.objects.keys() if self.objects.get('fatjets'): for Wcand in self.objects['fatjets']: logging.info("\n BOOSTED ") logging.info(" - Hadronic W : pT = {0}".format(Wcand.Pt())) logging.info(" : M = {0}".format(Wcand.M())) logging.info(" : eta = {0}".format(Wcand.Eta())) logging.info(" : phi = {0}".format(Wcand.Phi())) if self.objects.get('resjets'): for rWcand in self.objects['resjets']: logging.info("\n RESOLVED ") logging.info(" - Hadronic W : pT = {0}".format(rWcand.Pt())) logging.info(" : M = {0}".format(rWcand.M())) logging.info(" : eta = {0}".format(rWcand.Eta())) logging.info(" : phi = {0}".format(rWcand.Phi())) if self.objects.get('rcjets'): for rcjet in self.objects['rcjets']: logging.info(" - Re-clustered Jet W {0}: pT = {1}".format(1,rcjet.Pt())) logging.info(" : M = {0}".format(rcjet.M())) logging.info(" : eta = {0}".format(rcjet.Eta())) logging.info(" : phi = {0}".format(rcjet.Phi())) if self.objects.get('bjets'): for i,bjet in enumerate(self.objects['bjets']): logging.info(" - b-jet {0} : pT = {1}".format(i+1,bjet.Pt())) logging.info(" : M = {0}".format(bjet.M())) logging.info(" : eta = {0}".format(bjet.Eta())) logging.info(" : phi = {0}".format(bjet.Phi())) logging.info(" : Mv2c20 = {0}".format(bjet.mv2c20)) if self.objects.get('lepton'): logging.info(" - Lepton : pT = {0}".format(self.objects['lepton'].Pt())) logging.info(" : E = {0}".format(self.objects['lepton'].E())) logging.info(" : eta = {0}".format(self.objects['lepton'].Eta())) logging.info(" : phi = {0}\n".format(self.objects['lepton'].Phi())) if self.objects.get('nu'): logging.info(" - Neutrino : phi = {0}".format(self.objects['nu'].Phi())) logging.info(" : eta = {0}".format(self.objects['nu'].Eta())) logging.info(" : pT = {0}".format(self.objects['nu'].Pt())) logging.info(" : E = {0}\n".format(self.objects['nu'].E())) if self.objects.get('met'): logging.info(" - MET : met = {0}".format(self.objects['met'].met)) logging.info(" : phi = {0}".format(self.objects['met'].phi)) logging.info(" : mtw = {0}\n".format(self.objects['met'].mtw)) if self.objects.get('ht'): logging.info(" - HT : HT = {0}\n".format(self.objects['ht'])) try: logging.info(" - Event Info : mcWeight = {0}".format(self.t.weight_mc)) logging.info(" : pileupWeight = {0}".format(self.t.weight_pileup)) logging.info(" : AMI = {0}\n".format(self.t.AMI)) except AttributeError: logging.info(" - Event Info : mcWeight = 1.0") logging.info(" : pileupWeight = 1.0") logging.info(" : AMI = Not readily available\n") logging.info(" : eventNumber = {0}".format(self.t.eventNumber)) logging.info(" : runNumber = {0}".format(self.t.runNumber)) logging.info(" : mcChannelNumber = {0}".format(self.t.mcChannelNumber)) logging.info(" : mu = {0}".format(self.t.mu)) return