def __init__(self, name, full_filename): if self.__class__.__name__ == "PhysicalEvents": AdvPrint.cerr_exit( "Internal error: Instance of abstract base class 'PhysicalEvents' created!" ) Events.__init__(self, name) self.full_filename = full_filename.strip()
def line_from_data(self, columns): """ Returns the information stated in the list 'columns' as a single line """ line = "" for c in columns: c = c.lower().strip() if c.lower() in ['name', 'id', 'identifier']: line += str(self.name)+" " elif c.lower() in ['analysis', 'ana']: line += str(self.analysis)+" " elif c.lower() in ['sr', 'signalregion']: line += str(self.sr)+" " elif c.lower() in ['totalmc', 'totalmcevents','total_mcevents']: line += str(self.total_mcevents)+" " elif c.lower() in ['totalnorm', 'totalnormevents','total_normevents']: line += str(self.total_normevents)+" " elif c.lower() in ['totalsw', 'totalsumofweights','total_sumofweights']: line += str(self.total_sumofweights)+" " elif c.lower() in ['totalsw2', 'totalsumofweights2','total_sumofweights2']: line += str(self.total_sumofweights2)+" " elif c.lower() in ['signalsw', 'signalsumofweights','signal_sumofweights']: line += str(self.signal_sumofweights)+" " elif c.lower() in ['signalsw2', 'signalsumofweights2','signal_sumofweights2']: line += str(self.signal_sumofweights2)+" " elif c.lower() in ['signalnorm', 'signalnormevents', "signal_normevents", "s"]: line += str(self.signal_normevents)+" " elif c.lower() in ['signalerrstat', 'signal_err_stat', 'signalerrstat', 'sig_err_stat']: line += str(self.signal_err_stat)+" " elif c.lower() in ['signalerrsys', 'signal_err_sys', 'signalerrsys', 'sig_err_sys']: line += str(self.signal_err_sys)+" " elif c.lower() in ['signalerrtot', 'signal_err_tot', "ds", 'signalerrtot', 'sig_err_tot']: line += str(self.signal_err_tot)+" " else: AdvPrint.cerr_exit("resultcollector::line_from_data - column "+c+" unknown!") return line
def print_result(self): AdvPrint.set_cout_file(Info.files["output_result"], True) if self.cls_obs != -1: AdvPrint.cout("Test: Calculation of CLs(S, dS, B, dB, O) using profile likelihood") elif self.likelihood != -1: AdvPrint.cout("Test: Calculation of approximate (fast) likelihood given in results folder") elif self.r_obs != -1: AdvPrint.cout("Test: Calculation of r = signal/(95%CL limit on signal)") else: AdvPrint.cerr_exit("evaluator::printResult(): No result has been evaluated!") for w in self.warnings: AdvPrint.cout("\033[33mWarning: "+w+"\033[0m") result = "\033[31mExcluded\033[0m" if self.allowed(): result = "\033[32mAllowed\033[0m" AdvPrint.cout("Result: "+result) if self.cls_obs != -1: AdvPrint.cout("Result for CLs: "+str(self.cls_obs)) #if self.likelihood != -1: # AdvPrint.cout("Result for likelihood: "+str(self.likelihood)) elif self.r_obs != -1: AdvPrint.cout("Result for r: "+str(self.r_obs_cons)) else: AdvPrint.cerr_exit("evaluator::printResult(): No result has been evaluated!") AdvPrint.cout("Analysis: "+self.resultCollector.analysis) AdvPrint.cout("SR: "+self.resultCollector.sr) AdvPrint.set_cout_file("#None")
def find_strongest_evaluators(list_of_evaluators, n_best): """ Finds the n_best strongest of all evaluators in given list or dict """ def flat(d, out=[]): """ Transforms a dict of dicts of dicts ... into a single list """ for val in d.values(): if isinstance(val, dict): flat(val, out) else: out += [val,] return out list_of_evaluators = flat(list_of_evaluators) if n_best != 0 and len(list_of_evaluators) == 0: AdvPrint.cerr_exit("evaluator::find_strongest_evaluator \n Cannot find strongest out of 0 evaluators!") # Sort evaluators by cls_exp first best_evaluators = sorted(list_of_evaluators, key=operator.attrgetter('cls_exp')) # If the best evaluator has cls_exp which is not -1, it means that indeed # all considered evaluators had a properly evaluated cls_exp if best_evaluators[0].cls_exp != -1: if n_best == 1: return best_evaluators[0] else: return best_evaluators[:n_best] # otherwise, sort again but now with respect to r_exp_cons best_evaluators = sorted(list_of_evaluators, key=operator.attrgetter('r_exp_cons')) best_evaluators.reverse() # largest r first if n_best == 1: return best_evaluators[0] else: return best_evaluators[:n_best]
def combine_processes(self, other): """ In case of an "add" run, the user might add new events to a given process """ if self.name != other.name: AdvPrint.cerr_exit("'add' feature tried to combine incompatible processes (different names)") for other_ev in other.eventsList: self.eventsList.append(other_ev)
def line_from_data(self, columns): """ Returns the information stated in the list 'columns' as a single line """ line = "" for c in columns: c = c.lower().strip() if c.lower() in ['name', 'id', 'identifier']: line += str(self.name) + " " elif c.lower() in ['analysis', 'ana']: line += str(self.analysis) + " " elif c.lower() in ['sr', 'signalregion']: line += str(self.sr) + " " elif c.lower() in ['totalmc', 'totalmcevents', 'total_mcevents']: line += str(self.total_mcevents) + " " elif c.lower() in [ 'totalnorm', 'totalnormevents', 'total_normevents' ]: line += str(self.total_normevents) + " " elif c.lower() in [ 'totalsw', 'totalsumofweights', 'total_sumofweights' ]: line += str(self.total_sumofweights) + " " elif c.lower() in [ 'totalsw2', 'totalsumofweights2', 'total_sumofweights2' ]: line += str(self.total_sumofweights2) + " " elif c.lower() in [ 'signalsw', 'signalsumofweights', 'signal_sumofweights' ]: line += str(self.signal_sumofweights) + " " elif c.lower() in [ 'signalsw2', 'signalsumofweights2', 'signal_sumofweights2' ]: line += str(self.signal_sumofweights2) + " " elif c.lower() in [ 'signalnorm', 'signalnormevents', "signal_normevents", "s" ]: line += str(self.signal_normevents) + " " elif c.lower() in [ 'signalerrstat', 'signal_err_stat', 'signalerrstat', 'sig_err_stat' ]: line += str(self.signal_err_stat) + " " elif c.lower() in [ 'signalerrsys', 'signal_err_sys', 'signalerrsys', 'sig_err_sys' ]: line += str(self.signal_err_sys) + " " elif c.lower() in [ 'signalerrtot', 'signal_err_tot', "ds", 'signalerrtot', 'sig_err_tot' ]: line += str(self.signal_err_tot) + " " else: AdvPrint.cerr_exit( "resultcollector::line_from_data - column " + c + " unknown!") return line
def combine_processes(self, other): """ In case of an "add" run, the user might add new events to a given process """ if self.name != other.name: AdvPrint.cerr_exit( "'add' feature tried to combine incompatible processes (different names)" ) for other_ev in other.eventsList: self.eventsList.append(other_ev)
def add_and_sum(self, other): """ Used to add process by process: event numbers are added and errors are added independently """ # only compatible results should be added if self.analysis != other.analysis or self.sr != other.sr: AdvPrint.cerr_exit( "\t resultCollector::add_and_sum() \n \t Only results of the same analysis and same signal region can be added!" ) # When summing the weights, each subsample has to be properly reweighted: The overall contribtuion should only be proportional to the cross section i.e. totalnormevents. # It should not depend on the MC size of the sample which hence should be divided out. # If 'self' is 0, then the weights are trivially just 'other': if self.total_normevents == 0: weight_self = 0 weight_other = 1 # If 'other' is 0, then the weights are trivially just 'self': elif other.total_normevents == 0: weight_self = 1 weight_other = 0 # otherwise, evaluate weights properly else: weight_self = self.total_normevents / ( other.total_normevents + self.total_normevents) * ( self.total_sumofweights + other.total_sumofweights) / self.total_sumofweights weight_other = other.total_normevents / ( other.total_normevents + self.total_normevents) * ( self.total_sumofweights + other.total_sumofweights) / other.total_sumofweights self.total_mcevents = self.total_mcevents + other.total_mcevents self.total_normevents = self.total_normevents + other.total_normevents # Note thatw weight_self and weight_other are normalised such that self.total_sow * weight_self + other.total_sow * weight_other = self.total_sow + other.total_sow self.total_sumofweights = self.total_sumofweights + other.total_sumofweights self.total_sumofweights2 = self.total_sumofweights2 * weight_self**2 + other.total_sumofweights2 * weight_other**2 self.signal_sumofweights = self.signal_sumofweights * weight_self + other.signal_sumofweights * weight_other self.signal_sumofweights2 = self.signal_sumofweights2 * weight_self**2 + other.signal_sumofweights2 * weight_other**2 self.signal_normevents = self.signal_normevents + other.signal_normevents # note that doing signal_sumofweights/total_sumofweights * total_normevents yields the same! if Info.flags['no_mc_stat_err']: self.signal_err_stat = 0.0 else: self.signal_err_stat = sqrt( self.signal_err_stat**2 + other.signal_err_stat**2 ) # Added in quadrature. Using signal_sumogweights2 yields almost same result (except for the N=0 staterror which comes for each process) self.signal_err_sys = sqrt( self.signal_err_sys**2 + other.signal_err_sys**2) # Added in quadrature self.signal_err_tot = sqrt(self.signal_err_stat**2 + self.signal_err_sys**2)
def allowed(self): """ Determines from the given result if the corresponding signal is excluded or not""" # If we have CLs, use CLs if self.cls_obs != -1: return self.cls_obs > 0.05 # otherwise, use r-value elif self.r_obs_cons != -1: return self.r_obs_cons < 1 else: AdvPrint.cerr_exit("evaluator::allowed() Cannot determine allowed/excluded without calculating cls or r-value!")
def allowed(self): """ Determines from the given result if the corresponding signal is excluded or not""" # If we have CLs, use CLs if self.cls_obs != -1: return self.cls_obs > 0.05 # otherwise, use r-value elif self.r_obs_cons != -1: return self.r_obs_cons < 1 else: AdvPrint.cerr_exit( "evaluator::allowed() Cannot determine allowed/excluded without calculating cls or r-value!" )
def setup_pythia8_for_lhe_showering(self): out_path = Info.paths['output_pythia'] procnum = len(Info.files['pythia_cards']) filename = self.name + "card_" + str(procnum) + ".in" fpath = os.path.join(out_path, filename) with open(fpath, 'w') as f: # generate LHE showering card with hepmc output with open(Info.files['pythia_lhe_template'], 'r') as default: for line in default: f.write(line) if len(Info.files["slha"]) > 0: f.write('SLHA:file = ') f.write(Info.files["slha"] + '\n') iRun = 0 run_str = list() for lhe_filename in self.full_filenames: # Check file exists, else skip file if not os.path.isfile(lhe_filename): AdvPrint.cerr( "\t Process:genPy8card():: File not readable:" + lhe_filename) continue iRun += 1 run_str.append("Main:subrun = " + str(iRun) + "\n") if iRun == 1: run_str.append("Beams:frameType = 4\n") else: run_str.append("Beams:newLHEFsameInit = on\n") run_str.append("Beams:LHEF = " + lhe_filename + "\n\n") # Exit if no valid files if iRun < 1: AdvPrint.cerr_exit( "\t Process:genPy8card():: No valid LHE files found") # Write out all subruns if iRun > 1: subrun_str = "Main:numberOfSubruns = " + str(iRun) f.write(subrun_str + '\n\n') for line in run_str: f.write(line) else: f.write("Beams:frameType = 4\n") f.write("Beams:LHEF = " + lhe_filename + "\n\n") default.close() Info.files['pythia_cards'].append(fpath) self.py8_infile = fpath
def __init__(self, name): # Note that the below condition should fail if this __ini__ condition is called from a derived class, as self.__class__.__name__ should be the daughter class name! if self.__class__.__name__ == "Events": AdvPrint.cerr_exit("Internal error: Instance of abstract base class 'Events' created!") self.name = name Info.book_events(self) # sets identifier self.processed = False self.analysis_result_files = dict() self.analysis_signal_files = dict() self.analysis_cutflow_files = dict() self.maxEvents = -1 self.result_output_file = "" self.fritz_config_file = ""
def setup_pythia8_for_lhe_showering(self): out_path = Info.paths['output_pythia'] procnum = len(Info.files['pythia_cards']) filename = self.name+"card_"+str(procnum)+".in" fpath = os.path.join(out_path,filename) with open(fpath,'w') as f: # generate LHE showering card with hepmc output with open(Info.files['pythia_lhe_template'],'r') as default: for line in default: f.write(line) if len(Info.files["slha"]) > 0 : f.write('SLHA:file = ') f.write(Info.files["slha"] + '\n') iRun = 0 run_str = list() for lhe_filename in self.full_filenames: # Check file exists, else skip file if not os.path.isfile(lhe_filename): AdvPrint.cerr("\t Process:genPy8card():: File not readable:"+lhe_filename) continue iRun+=1 run_str.append("Main:subrun = "+str(iRun)+"\n") if iRun == 1: run_str.append("Beams:frameType = 4\n") else: run_str.append("Beams:newLHEFsameInit = on\n") run_str.append("Beams:LHEF = "+lhe_filename+"\n\n") # Exit if no valid files if iRun < 1: AdvPrint.cerr_exit("\t Process:genPy8card():: No valid LHE files found") # Write out all subruns if iRun > 1: subrun_str = "Main:numberOfSubruns = "+str(iRun) f.write( subrun_str + '\n\n' ) for line in run_str: f.write(line) else: f.write("Beams:frameType = 4\n") f.write("Beams:LHEF = "+lhe_filename+"\n\n") default.close() Info.files['pythia_cards'].append(fpath) self.py8_infile = fpath
def __init__(self, name): # Note that the below condition should fail if this __ini__ condition is called from a derived class, as self.__class__.__name__ should be the daughter class name! if self.__class__.__name__ == "Events": AdvPrint.cerr_exit( "Internal error: Instance of abstract base class 'Events' created!" ) self.name = name Info.book_events(self) # sets identifier self.processed = False self.analysis_result_files = dict() self.analysis_signal_files = dict() self.analysis_cutflow_files = dict() self.maxEvents = -1 self.result_output_file = "" self.fritz_config_file = ""
def add_and_sum(self, other): """ Used to add process by process: event numbers are added and errors are added independently """ # only compatible results should be added if self.analysis != other.analysis or self.sr != other.sr: AdvPrint.cerr_exit("\t resultCollector::add_and_sum() \n \t Only results of the same analysis and same signal region can be added!") # When summing the weights, each subsample has to be properly reweighted: The overall contribtuion should only be proportional to the cross section i.e. totalnormevents. # It should not depend on the MC size of the sample which hence should be divided out. # If 'self' is 0, then the weights are trivially just 'other': if self.total_normevents == 0: weight_self = 0 weight_other = 1 # If 'other' is 0, then the weights are trivially just 'self': elif other.total_normevents == 0: weight_self = 1 weight_other = 0 # otherwise, evaluate weights properly else: weight_self = self.total_normevents / (other.total_normevents + self.total_normevents) * (self.total_sumofweights + other.total_sumofweights)/self.total_sumofweights weight_other = other.total_normevents / (other.total_normevents + self.total_normevents) * (self.total_sumofweights + other.total_sumofweights)/other.total_sumofweights self.total_mcevents = self.total_mcevents + other.total_mcevents self.total_normevents = self.total_normevents + other.total_normevents # Note thatw weight_self and weight_other are normalised such that self.total_sow * weight_self + other.total_sow * weight_other = self.total_sow + other.total_sow self.total_sumofweights = self.total_sumofweights + other.total_sumofweights self.total_sumofweights2 = self.total_sumofweights2 * weight_self**2 + other.total_sumofweights2 * weight_other**2 self.signal_sumofweights = self.signal_sumofweights * weight_self + other.signal_sumofweights * weight_other self.signal_sumofweights2 = self.signal_sumofweights2 * weight_self**2 + other.signal_sumofweights2 * weight_other**2 self.signal_normevents = self.signal_normevents + other.signal_normevents # note that doing signal_sumofweights/total_sumofweights * total_normevents yields the same! if Info.flags['no_mc_stat_err']: self.signal_err_stat = 0.0 else: self.signal_err_stat = sqrt(self.signal_err_stat**2 + other.signal_err_stat**2) # Added in quadrature. Using signal_sumogweights2 yields almost same result (except for the N=0 staterror which comes for each process) self.signal_err_sys = sqrt(self.signal_err_sys**2 + other.signal_err_sys**2) # Added in quadrature self.signal_err_tot = sqrt(self.signal_err_stat**2+self.signal_err_sys**2)
def checkInputConsistency(self): """ Checks if the amount of input information given is sufficient to run CheckMATE2 """ # Case 1: no process, no eventfile, no setting if len(self.eventsList) < 1: self.printInfo() AdvPrint.cerr_exit( "\t " + self.name + "::checkInputConsistency():: \n \t" "There are no events to generate and/or analyse") # Case 3: Both k-factor and cross section are non-zero if self.xsec != 0.0 and self.kfac != 0.0: AdvPrint.cerr_exit( "\t " + self.name + ":checkInputConsistency():: \n \t " "Either enter Kfactor or total cross section (which might be Kfactor * LO-cross section)!" ) for e in self.eventsList: from events import DelphesEvents if isinstance(e, DelphesEvents) and len(Info.used_experiments) > 1: AdvPrint.cerr_exit( "\t " + self.name + "::checkInputConsistency():: \n \t" "If you provide a .root file you cannot run analyses that need different detector settings." "Please only run analyses of the experiment the .root file was simulated with " "in Delphes!") for events in self.eventsList: events.check()
def load(self, filename): """ Loads contents for current instance from a valid file """ with open(filename, "r") as f: contents = json.load(f) try: # processes = old processes plus added new ones of current run newProcList = self.procList self.procList = pickle.loads(contents["procList"]) for new_p in newProcList: combined = False for old_p in self.procList: if old_p.name == new_p.name: old_p.combine_processes(new_p) combined = True break if not combined: self.procList.append(new_p) except KeyError: AdvPrint.cerr_exit("Problem loading info file "+inputfile) return self
def __init__(self): #global Info, AdvPrint """ Initialisation of a CheckMATE object leads to an entire run of the CheckMATE procedure""" # Initialisation steps #Info.init() Info.fill_standard_paths_and_files() if len(sys.argv) == 1: self.printUsage() self.printLogo() if len(sys.argv) == 2 and sys.argv[-1] != "-h": Info.fill_info_from_file(sys.argv[1]) self.procList = Info.fill_processes_from_file(sys.argv[1]) else: Info.fill_info_from_parameters() self.procList = Info.fill_processes_from_parameters() if Info.parameters["outputexists"] == "add": self.load(Info.files['internal_processes']) for p in self.procList: p.checkInputConsistency() self.user_param_check() self.prepare_run() # Running the event-based part if self.procList == []: AdvPrint.cerr_exit("No processes are loaded!") for p in self.procList: p.prepare() p.run() AdvPrint.cout("\n") # Evaluate if not Info.flags['skipevaluation']: self.evaluate() # Store internal status Info.save(Info.files['internal_info']) self.save(Info.files['internal_processes'])
def find_strongest_evaluators(list_of_evaluators, n_best): """ Finds the n_best strongest of all evaluators in given list or dict """ def flat(d, out=[]): """ Transforms a dict of dicts of dicts ... into a single list """ for val in d.values(): if isinstance(val, dict): flat(val, out) else: out += [ val, ] return out list_of_evaluators = flat(list_of_evaluators) if n_best != 0 and len(list_of_evaluators) == 0: AdvPrint.cerr_exit( "evaluator::find_strongest_evaluator \n Cannot find strongest out of 0 evaluators!" ) # Sort evaluators by cls_exp first best_evaluators = sorted(list_of_evaluators, key=operator.attrgetter('cls_exp')) # If the best evaluator has cls_exp which is not -1, it means that indeed # all considered evaluators had a properly evaluated cls_exp if best_evaluators[0].cls_exp != -1: if n_best == 1: return best_evaluators[0] else: return best_evaluators[:n_best] # otherwise, sort again but now with respect to r_exp_cons best_evaluators = sorted(list_of_evaluators, key=operator.attrgetter('r_exp_cons')) best_evaluators.reverse() # largest r first if n_best == 1: return best_evaluators[0] else: return best_evaluators[:n_best]
def print_result(self): AdvPrint.set_cout_file(Info.files["output_result"], True) if self.cls_obs != -1: AdvPrint.cout( "Test: Calculation of CLs(S, dS, B, dB, O) using profile likelihood" ) elif self.likelihood != -1: AdvPrint.cout( "Test: Calculation of approximate (fast) likelihood given in results folder" ) elif self.r_obs != -1: AdvPrint.cout( "Test: Calculation of r = signal/(95%CL limit on signal)") else: AdvPrint.cerr_exit( "evaluator::printResult(): No result has been evaluated!") for w in self.warnings: AdvPrint.cout("\033[33mWarning: " + w + "\033[0m") result = "\033[31mExcluded\033[0m" if self.allowed(): result = "\033[32mAllowed\033[0m" AdvPrint.cout("Result: " + result) if self.cls_obs != -1: AdvPrint.cout("Result for CLs: " + str(self.cls_obs)) #if self.likelihood != -1: # AdvPrint.cout("Result for likelihood: "+str(self.likelihood)) elif self.r_obs != -1: AdvPrint.cout("Result for r: " + str(self.r_obs_cons)) else: AdvPrint.cerr_exit( "evaluator::printResult(): No result has been evaluated!") AdvPrint.cout("Analysis: " + self.resultCollector.analysis) AdvPrint.cout("SR: " + self.resultCollector.sr) AdvPrint.set_cout_file("#None")
def load_expdata(self): analysis = self.resultCollector.analysis if analysis == "": return # for pseudo-evaluators sr = self.resultCollector.sr parameters = Info.get_analysis_parameters(analysis) # S95 comparison: r value compares lower s95 limit on s (which is almost 2 sigma) to the model independent limit if sr not in parameters["reference_data"]: AdvPrint.cerr_exit("evaluator::load_expdata unknown signal region "+analysis+" - "+sr) if "S95_obs" not in parameters["reference_data"][sr]: AdvPrint.cerr_exit("evaluator::load_expdata S95_obs value is unknown for "+analysis+" - "+sr) self.s95_obs = float(parameters["reference_data"][sr]["S95_obs"]) # If no expected upper limit is given, observed limit is used if "S95_exp" not in parameters["reference_data"][sr]: self.s95_exp = self.s95_obs self.warnings.append("No expected limit could be found in reference data. Using expected = observed.") else: self.s95_exp = float(parameters["reference_data"][sr]["S95_exp"]) if "obs" not in parameters["reference_data"][sr]: AdvPrint.cerr_exit("evaluator::load_expdata 'obs' is unknown for "+analysis+" - "+sr) self.obs = float(parameters["reference_data"][sr]["obs"]) if "bkg" not in parameters["reference_data"][sr]: AdvPrint.cerr_exit("evaluator::load_expdata 'bkg' is unknown for "+analysis+" - "+sr) self.bkg = float(parameters["reference_data"][sr]["bkg"]) self.bkg_err = -1 if "bkg_err" in parameters["reference_data"][sr]: self.bkg_err = float(parameters["reference_data"][sr]["bkg_err"]) elif "bkg_errp" in parameters["reference_data"][sr]: # Asymmetric error: as a rough approximation, use the mean of the squares self.bkg_err = sqrt(float(parameters["reference_data"][sr]["bkg_errp"])**2 + float(parameters["reference_data"][sr]["bkg_errm"])**2)/sqrt(2.) elif "bkg_err_sys" in parameters["reference_data"][sr]: # Total error = independent quadratic sum of statistical and systematical component self.bkg_err = sqrt(float(parameters["reference_data"][sr]["bkg_err_stat"])**2 + float(parameters["reference_data"][sr]["bkg_err_sys"])**2 ) elif "bkg_err_sysp" in parameters["reference_data"][sr]: self.bkg_err = sqrt(float(parameters["reference_data"][sr]["bkg_err_stat"])**2 + (float(parameters["reference_data"][sr]["bkg_err_sysp"])**2)/2. + (float(parameters["reference_data"][sr]["bkg_err_sysp"])**2)/2.)
def checkInputConsistency(self): """ Checks if the amount of input information given is sufficient to run CheckMATE2 """ # Case 1: no process, no eventfile, no setting if len(self.eventsList) < 1: self.printInfo() AdvPrint.cerr_exit("\t "+self.name+"::checkInputConsistency():: \n \t" "There are no events to generate and/or analyse") # Case 3: Both k-factor and cross section are non-zero if self.xsec != 0.0 and self.kfac != 0.0: AdvPrint.cerr_exit("\t "+self.name+":checkInputConsistency():: \n \t " "Either enter Kfactor or total cross section (which might be Kfactor * LO-cross section)!") for e in self.eventsList: from events import DelphesEvents if isinstance(e, DelphesEvents) and len(Info.used_experiments) > 1: AdvPrint.cerr_exit("\t "+self.name+"::checkInputConsistency():: \n \t" "If you provide a .root file you cannot run analyses that need different detector settings." "Please only run analyses of the experiment the .root file was simulated with " "in Delphes!") for events in self.eventsList: events.check()
def get_resultCollectors(self): resultCollectors = dict() # list of all collectors of all analyses and all signal regions resultCollector = ResultCollector(self.identifier, "", "") # base collector object which we will just edit and copy for analysis in Info.analyses: # check if results file exists if not os.path.isfile(self.analysis_signal_files[analysis]): AdvPrint.cerr_exit("\t events::get_resultCollector() \n" "\t Required analysis result file does not exist: \n " "\t\t"+self.analysis_signal_files[analysis]+"\n" "\t It is very likely that something went wrong in the delphes and/or the analysis step. \n" "\t Please check \n " "\t \t "+Info.files['delphes_log']+" \n " "\t \t "+Info.files['analysis_log']+"* \n " "\t for error messages and, should you not be able to fix them yourself, contact the authors under \n" "\t \t [email protected]") # setup resultCollector object resultCollector.analysis = analysis resultCollectors[analysis] = dict() signal_regions = Info.get_analysis_parameters(analysis)["signal_regions"] # Read result file f = open(self.analysis_signal_files[analysis], "r") for line in f: # Ignore empty or commented lines line = line.rstrip() if line == "" or line[0] == "#": continue # Read file: line = AdvPrint.remove_extra_spaces(line) tokens = [t for t in line.split(" ") if t != ""] # First, read information on total events number if tokens[0] == "MCEvents:": resultCollector.total_mcevents = float(tokens[1]) elif tokens[0] == " SumOfWeights:": resultCollector.total_sumofweights = float(tokens[1]) elif tokens[0] == " SumOfWeights2:": resultCollector.total_sumofweights2 = float(tokens[1]) elif tokens[0] == " NormEvents:": resultCollector.total_normevents = float(tokens[1]) elif tokens[0] == "XSect:": xsect = float(tokens[1].split(" ")[0]) elif tokens[0] == " Error:": xsecterr = float(tokens[1].split(" ")[0]) else: # SR Sum_W Sum_W2 Acc N_Norm for sr in signal_regions: if tokens[0].startswith(sr): resultCollector.sr = sr # Read number of events resultCollector.signal_sumofweights = float(tokens[1]) resultCollector.signal_sumofweights2 = float(tokens[2]) resultCollector.signal_normevents = float(tokens[4]) # Calculate errors if resultCollector.signal_sumofweights > 0: resultCollector.signal_err_stat = resultCollector.signal_normevents*sqrt(resultCollector.signal_sumofweights2)/resultCollector.signal_sumofweights resultCollector.signal_err_sys = resultCollector.signal_normevents*xsecterr/xsect resultCollector.signal_err_tot = sqrt(resultCollector.signal_err_stat**2+resultCollector.signal_err_sys**2) else: resultCollector.signal_err_stat = 0 resultCollector.signal_err_sys = 0 resultCollector.signal_err_tot = 0 # put copy of resultCollector in collector dict resultCollectors[analysis][sr] = deepcopy(resultCollector) f.close() # Write events file, if wanted if Info.parameters["EventResultFileColumns"] != []: AdvPrint.mute() AdvPrint.set_cout_file(self.result_output_file, True) for col in Info.parameters["EventResultFileColumns"]: AdvPrint.cout(col+" ", "nlb") AdvPrint.cout("") for a in sorted(resultCollectors.keys()): for sr in sorted(resultCollectors[a].keys()): AdvPrint.cout(resultCollectors[a][sr].line_from_data(Info.parameters["EventResultFileColumns"])) AdvPrint.format_columnated_file(self.result_output_file) AdvPrint.set_cout_file("#None") AdvPrint.unmute() return resultCollectors
def runFritz(self): self.prepareFritz() """ Runs Fritz """ from events import MG5Events for event in self.eventsList: if event.processed: continue fritz_command = Info.files["fritz_bin"] + " " + event.configFile result = subprocess.Popen(fritz_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) maxlen = 0 try: for line in iter(result.stdout.readline, b''): # Print to logfile. If it does not start with the Fritz::Global prefix, it comes from MG5 and should be redirected AdvPrint.mute() if not line.startswith("|~| ") and isinstance( event, MG5Events): AdvPrint.set_cout_file( os.path.join( Info.paths["output_mg5"], "mg5amcatnlo_" + event.identifier + ".log")) elif "PYTHIA Rndm::dumpState" in line: AdvPrint.set_cout_file( os.path.join(Info.paths["output_pythia"], "pythia_" + event.identifier + ".log")) else: line = line.replace("|~| ", "") AdvPrint.set_cout_file( os.path.join(Info.paths["output_fritz"], "fritz_" + event.identifier + ".log")) AdvPrint.cout(line.rstrip()) AdvPrint.set_cout_file("#None") AdvPrint.unmute() # We should not exceed the terminal terminal_width: terminal_width = AdvPrint.get_terminal_width() print_line = " |-> " + str(line.strip()) print_line.replace("\t", " ") while "\r" in print_line: print_line = print_line[print_line.index("\r"):] len_of_print_line = len(print_line) maxlen = max(maxlen, len(print_line)) # As we print line by line in the same terminal row, we have to add spaces if the curr line is shorter than a line before fill_spaces = "" if len(print_line) < maxlen: fill_spaces = " " * (maxlen - len(print_line)) # if line is too long, make it shorter by appropriate amoung if len(print_line + fill_spaces) >= terminal_width and len( print_line) <= terminal_width: fill_spaces = " " * (terminal_width - len(print_line) - 1) elif len(print_line) > terminal_width: fill_spaces = "" print_line = print_line[:terminal_width - 4] + "..." AdvPrint.cout( "\r" + print_line + fill_spaces + "\x1b[0m\r", "nlb") except KeyboardInterrupt: AdvPrint.cout("Caught Keyboard Signal. Aborting Fritz") result.send_signal(signal.SIGTERM) for line in iter(result.stderr.readline, b''): AdvPrint.unmute() AdvPrint.set_cout_file(Info.files['fritz_log']) # remove nasty ROOT6-CLING warnings from on-screen output if "cling::AutoloadingVisitor::InsertIntoAutoloadingState:" in line: AdvPrint.mute() elif "Missing FileEntry for ExRootAnalysis" in line: AdvPrint.mute() elif "requested to autoload type" in line: AdvPrint.mute() AdvPrint.cout(line.rstrip() + "") AdvPrint.set_cout_file("#None") AdvPrint.unmute() AdvPrint.cout("") # Abort if there was an error result.wait() if result.returncode != 0: AdvPrint.cerr_exit( "Fritz returned with error. Check logfiles in result folder for more information!" ) # Remove all empty analysisstdout files for f in [ x for x in os.listdir(Info.paths['output_analysis']) if x.startswith("analysisstdout") ]: if os.stat(os.path.join(Info.paths['output_analysis'], f)).st_size == 0: os.remove(os.path.join(Info.paths['output_analysis'], f)) # Associate result files to event for a in Info.analyses: event.analysis_signal_files[a] = os.path.join( Info.paths['output_analysis'], event.identifier + '_' + a + '_signal.dat') if os.path.isfile(event.analysis_signal_files[a]): AdvPrint.format_columnated_file( event.analysis_signal_files[a]) event.analysis_cutflow_files[a] = os.path.join( Info.paths['output_analysis'], event.identifier + '_' + a + '_cutflow.dat') if os.path.isfile(event.analysis_cutflow_files[a]): AdvPrint.format_columnated_file( event.analysis_cutflow_files[a]) # finish event.processed = True
def load_expdata(self): analysis = self.resultCollector.analysis if analysis == "": return # for pseudo-evaluators sr = self.resultCollector.sr parameters = Info.get_analysis_parameters(analysis) # S95 comparison: r value compares lower s95 limit on s (which is almost 2 sigma) to the model independent limit if sr not in parameters["reference_data"]: AdvPrint.cerr_exit( "evaluator::load_expdata unknown signal region " + analysis + " - " + sr) if "S95_obs" not in parameters["reference_data"][sr]: AdvPrint.cerr_exit( "evaluator::load_expdata S95_obs value is unknown for " + analysis + " - " + sr) self.s95_obs = float(parameters["reference_data"][sr]["S95_obs"]) # If no expected upper limit is given, observed limit is used if "S95_exp" not in parameters["reference_data"][sr]: self.s95_exp = self.s95_obs self.warnings.append( "No expected limit could be found in reference data. Using expected = observed." ) else: self.s95_exp = float(parameters["reference_data"][sr]["S95_exp"]) if "obs" not in parameters["reference_data"][sr]: AdvPrint.cerr_exit( "evaluator::load_expdata 'obs' is unknown for " + analysis + " - " + sr) self.obs = float(parameters["reference_data"][sr]["obs"]) if "bkg" not in parameters["reference_data"][sr]: AdvPrint.cerr_exit( "evaluator::load_expdata 'bkg' is unknown for " + analysis + " - " + sr) self.bkg = float(parameters["reference_data"][sr]["bkg"]) self.bkg_err = -1 if "bkg_err" in parameters["reference_data"][sr]: self.bkg_err = float(parameters["reference_data"][sr]["bkg_err"]) elif "bkg_errp" in parameters["reference_data"][sr]: # Asymmetric error: as a rough approximation, use the mean of the squares self.bkg_err = sqrt( float(parameters["reference_data"][sr]["bkg_errp"])**2 + float(parameters["reference_data"][sr]["bkg_errm"])**2) / sqrt( 2.) elif "bkg_err_sys" in parameters["reference_data"][sr]: # Total error = independent quadratic sum of statistical and systematical component self.bkg_err = sqrt( float(parameters["reference_data"][sr]["bkg_err_stat"])**2 + float(parameters["reference_data"][sr]["bkg_err_sys"])**2) elif "bkg_err_sysp" in parameters["reference_data"][sr]: self.bkg_err = sqrt( float(parameters["reference_data"][sr]["bkg_err_stat"])**2 + (float(parameters["reference_data"][sr]["bkg_err_sysp"])**2) / 2. + (float(parameters["reference_data"][sr]["bkg_err_sysp"])**2) / 2.)
def get_resultCollectors(self): resultCollectors = dict( ) # list of all collectors of all analyses and all signal regions resultCollector = ResultCollector( self.identifier, "", "") # base collector object which we will just edit and copy for analysis in Info.analyses: # check if results file exists if not os.path.isfile(self.analysis_signal_files[analysis]): AdvPrint.cerr_exit( "\t events::get_resultCollector() \n" "\t Required analysis result file does not exist: \n " "\t\t" + self.analysis_signal_files[analysis] + "\n" "\t It is very likely that something went wrong in the delphes and/or the analysis step. \n" "\t Please check \n " "\t \t " + Info.files['delphes_log'] + " \n " "\t \t " + Info.files['analysis_log'] + "* \n " "\t for error messages and, should you not be able to fix them yourself, contact the authors under \n" "\t \t [email protected]") # setup resultCollector object resultCollector.analysis = analysis resultCollectors[analysis] = dict() signal_regions = Info.get_analysis_parameters( analysis)["signal_regions"] # Read result file f = open(self.analysis_signal_files[analysis], "r") for line in f: # Ignore empty or commented lines line = line.rstrip() if line == "" or line[0] == "#": continue # Read file: line = AdvPrint.remove_extra_spaces(line) tokens = [t for t in line.split(" ") if t != ""] # First, read information on total events number if tokens[0] == "MCEvents:": resultCollector.total_mcevents = float(tokens[1]) elif tokens[0] == " SumOfWeights:": resultCollector.total_sumofweights = float(tokens[1]) elif tokens[0] == " SumOfWeights2:": resultCollector.total_sumofweights2 = float(tokens[1]) elif tokens[0] == " NormEvents:": resultCollector.total_normevents = float(tokens[1]) elif tokens[0] == "XSect:": xsect = float(tokens[1].split(" ")[0]) elif tokens[0] == " Error:": xsecterr = float(tokens[1].split(" ")[0]) else: # SR Sum_W Sum_W2 Acc N_Norm for sr in signal_regions: if tokens[0].startswith(sr): resultCollector.sr = sr # Read number of events resultCollector.signal_sumofweights = float( tokens[1]) resultCollector.signal_sumofweights2 = float( tokens[2]) resultCollector.signal_normevents = float( tokens[4]) # Calculate errors if resultCollector.signal_sumofweights > 0: resultCollector.signal_err_stat = resultCollector.signal_normevents * sqrt( resultCollector.signal_sumofweights2 ) / resultCollector.signal_sumofweights resultCollector.signal_err_sys = resultCollector.signal_normevents * xsecterr / xsect resultCollector.signal_err_tot = sqrt( resultCollector.signal_err_stat**2 + resultCollector.signal_err_sys**2) else: resultCollector.signal_err_stat = 0 resultCollector.signal_err_sys = 0 resultCollector.signal_err_tot = 0 # put copy of resultCollector in collector dict resultCollectors[analysis][sr] = deepcopy( resultCollector) f.close() # Write events file, if wanted if Info.parameters["EventResultFileColumns"] != []: AdvPrint.mute() AdvPrint.set_cout_file(self.result_output_file, True) for col in Info.parameters["EventResultFileColumns"]: AdvPrint.cout(col + " ", "nlb") AdvPrint.cout("") for a in sorted(resultCollectors.keys()): for sr in sorted(resultCollectors[a].keys()): AdvPrint.cout(resultCollectors[a][sr].line_from_data( Info.parameters["EventResultFileColumns"])) AdvPrint.format_columnated_file(self.result_output_file) AdvPrint.set_cout_file("#None") AdvPrint.unmute() return resultCollectors
def check(self): if self.mg5_cards["proc"] == "" and self.commandstring == "": AdvPrint.cerr_exit( "Error in MG5Events object " + self.name + ":\n\t Neither a proc card nor a command string was provided!")
def runFritz(self): self.prepareFritz() """ Runs Fritz """ from events import MG5Events for event in self.eventsList: if event.processed: continue fritz_command = Info.files["fritz_bin"]+" "+event.configFile result = subprocess.Popen(fritz_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) maxlen = 0 try: for line in iter(result.stdout.readline, b''): # Print to logfile. If it does not start with the Fritz::Global prefix, it comes from MG5 and should be redirected AdvPrint.mute() if not line.startswith("|~| ") and isinstance(event, MG5Events): AdvPrint.set_cout_file(os.path.join(Info.paths["output_mg5"], "mg5amcatnlo_"+event.identifier+".log")) elif "PYTHIA Rndm::dumpState" in line: AdvPrint.set_cout_file(os.path.join(Info.paths["output_pythia"], "pythia_"+event.identifier+".log")) else: line = line.replace("|~| ", "") AdvPrint.set_cout_file(os.path.join(Info.paths["output_fritz"], "fritz_"+event.identifier+".log")) AdvPrint.cout(line.rstrip()) AdvPrint.set_cout_file("#None") AdvPrint.unmute() # We should not exceed the terminal terminal_width: terminal_width = AdvPrint.get_terminal_width() print_line = " |-> "+str(line.strip()) print_line.replace("\t", " ") while "\r" in print_line: print_line = print_line[print_line.index("\r"):] len_of_print_line = len(print_line) maxlen = max(maxlen, len(print_line)) # As we print line by line in the same terminal row, we have to add spaces if the curr line is shorter than a line before fill_spaces = "" if len(print_line) < maxlen: fill_spaces = " "*(maxlen-len(print_line)) # if line is too long, make it shorter by appropriate amoung if len(print_line+fill_spaces) >= terminal_width and len(print_line) <= terminal_width: fill_spaces = " "*(terminal_width - len(print_line)-1) elif len(print_line) > terminal_width: fill_spaces = "" print_line = print_line[:terminal_width-4]+"..." AdvPrint.cout("\r"+print_line+fill_spaces+"\x1b[0m\r", "nlb") except KeyboardInterrupt: AdvPrint.cout("Caught Keyboard Signal. Aborting Fritz") result.send_signal(signal.SIGTERM) for line in iter(result.stderr.readline, b''): AdvPrint.unmute() AdvPrint.set_cout_file(Info.files['fritz_log']) # remove nasty ROOT6-CLING warnings from on-screen output if "cling::AutoloadingVisitor::InsertIntoAutoloadingState:" in line: AdvPrint.mute() elif "Missing FileEntry for ExRootAnalysis" in line: AdvPrint.mute() elif "requested to autoload type" in line: AdvPrint.mute() AdvPrint.cout(line.rstrip()+"") AdvPrint.set_cout_file("#None") AdvPrint.unmute() AdvPrint.cout("") # Abort if there was an error result.wait() if result.returncode != 0: AdvPrint.cerr_exit("Fritz returned with error. Check logfiles in result folder for more information!") # Remove all empty analysisstdout files for f in [x for x in os.listdir(Info.paths['output_analysis']) if x.startswith("analysisstdout")]: if os.stat(os.path.join(Info.paths['output_analysis'], f)).st_size == 0: os.remove(os.path.join(Info.paths['output_analysis'], f)) # Associate result files to event for a in Info.analyses: event.analysis_signal_files[a] = os.path.join(Info.paths['output_analysis'], event.identifier+'_'+a+'_signal.dat') if os.path.isfile(event.analysis_signal_files[a]): AdvPrint.format_columnated_file(event.analysis_signal_files[a]) event.analysis_cutflow_files[a] = os.path.join(Info.paths['output_analysis'], event.identifier+'_'+a+'_cutflow.dat') if os.path.isfile(event.analysis_cutflow_files[a]): AdvPrint.format_columnated_file(event.analysis_cutflow_files[a]) # finish event.processed = True
def line_from_data(self, columns): """ Returns the information stated in the list 'columns' as a single line """ line = "" for c in columns: c = c.lower().strip() if c.lower() in ['name', 'id', 'identifier']: line += str(self.resultCollector.name) + " " elif c.lower() in ['analysis', 'ana']: line += str(self.resultCollector.analysis) + " " elif c.lower() in ['sr', 'signalregion']: line += str(self.resultCollector.sr) + " " elif c.lower() in ['totalmc', 'totalmcevents']: line += str(self.resultCollector.total_mcevents) + " " elif c.lower() in ['totalnorm', 'totalnormevents']: line += str(self.resultCollector.total_normevents) + " " elif c.lower() in ['totalsw', 'totalsumofweights']: line += str(self.resultCollector.total_sumofweights) + " " elif c.lower() in ['totalsw2', 'totalsumofweights2']: line += str(self.resultCollector.total_sumofweights2) + " " elif c.lower() in ['signalsw', 'signalsumofweights']: line += str(self.resultCollector.signal_sumofweights) + " " elif c.lower() in ['signalsw2', 'signalsumofweights2']: line += str(self.resultCollector.signal_sumofweights2) + " " elif c.lower() in ['signalnorm', 'signalnormevents', 's']: line += str(self.resultCollector.signal_normevents) + " " elif c.lower() in ['signalerrstat', 'signal_err_stat']: line += str(self.resultCollector.signal_err_stat) + " " elif c.lower() in ['signalerrsys', 'signal_err_sys']: line += str(self.resultCollector.signal_err_sys) + " " elif c.lower() in ['signalerrtot', 'ds', 'signal_err_tot']: line += str(self.resultCollector.signal_err_tot) + " " elif c.lower() in ['obs', 'o']: line += str(self.obs) + " " elif c.lower() in ['b', 'bkg']: line += str(self.bkg) + " " elif c.lower() in ['db', 'bkgerr']: line += str(self.bkg_err) + " " elif c.lower() in ['signaleff', 'eff']: line += str(self.signal_eff) + " " elif c.lower() in ['signaleff_err_stat', 'eff_err_stat']: line += str(self.signal_eff_error_stat) + " " elif c.lower() in ['signaleff_err_sys', 'eff_err_sys']: line += str(self.signal_eff_error_sys) + " " elif c.lower() in ['signaleff_err_tot', 'eff_err_tot']: line += str(self.signal_eff_error_tot) + " " elif c.lower() in ['s95obs']: line += str(self.s95_obs) + " " elif c.lower() in ['s95exp']: line += str(self.s95_exp) + " " elif c.lower() in ['robs', 'r_obs']: line += str(self.r_obs) + " " elif c.lower() in ['robscons', 'r_obs_cons']: line += str(self.r_obs_cons) + " " elif c.lower() in ['robsconssysonly', 'r_obs_cons_sysonly']: line += str(self.r_obs_cons_sysonly) + " " elif c.lower() in ['rexp', 'r_exp']: line += str(self.r_exp) + " " elif c.lower() in ['rexpcons', 'r_exp_cons']: line += str(self.r_exp_cons) + " " elif c.lower() in ['rexpconssysonly', 'r_exp_cons_sysonly']: line += str(self.r_exp_cons_sysonly) + " " elif c.lower() in ['clsobs', 'cls_obs']: line += str(self.cls_obs) + " " elif c.lower() in ['clsobs_err', 'cls_obs_err']: line += str(self.cls_obs_err) + " " elif c.lower() in ['clsexp', 'cls_exp']: line += str(self.cls_exp) + " " elif c.lower() in ['clsexp_err', 'cls_exp_err']: line += str(self.cls_exp_err) + " " elif c.lower() in ['likelihood']: line += str(self.likelihood) + " " #todoelif c.lower() in ['likelihood']: # line += str(self.likelihood)+" " else: AdvPrint.cerr_exit("evaluator::line_from_data - column " + c + " unknown!") return line
def __init__(self, name, full_filename): if self.__class__.__name__ == "PhysicalEvents": AdvPrint.cerr_exit("Internal error: Instance of abstract base class 'PhysicalEvents' created!") Events.__init__(self, name) self.full_filename = full_filename.strip()
def add_and_average(self, other): """ Used to add event samples within the same process: weights are added and event numbers are redetermined """ if self.analysis != other.analysis or self.sr != other.sr: AdvPrint.cerr_exit("\t resultCollector::add_and_average() \n \t Only results of the same analysis and same signal region can be added!") # This number must be equal for all averaged resultCollector (or 0, if this 'other' is the first item that is added) if self.total_normevents == 0: self.total_normevents = other.total_normevents else: weight1 = self.total_mcevents / (self.total_mcevents + other.total_mcevents) weight2 = other.total_mcevents / (self.total_mcevents + other.total_mcevents) self.total_normevents = (self.total_normevents * weight1 + other.total_normevents * weight2)/(weight1 + weight2) """ the below test is not correct anymore as combined runs of the same process with event gneration via Pythia8 or MadGraph can lead to different values for the respective cross sections. In that case, total_normevents should be given as the weighted mean of the respective total_normevents-numbers, using total_mcevents as the weight #elif (self.total_normevents - other.total_normevents)/float(self.total_normevents + other.total_normevents) > 0.001: # This number must be universal for averaging, can slightly differ due to rounding! # AdvPrint.cerr_exit("\t resultCollector::add_and_average() \n \t Event files that should be averaged ("+self.name+" "+other.name+") have different total normevents in "+self.analysis+" - "+self.sr+". Something must have gone wrong!") """ self.total_mcevents = self.total_mcevents + other.total_mcevents # add mc events self.total_sumofweights = self.total_sumofweights + other.total_sumofweights # add sum of weights self.total_sumofweights2 = self.total_sumofweights2 + other.total_sumofweights2 # add sum of weights^2 # store old relative syserror (beware the case N = 0 and that self and other should be consistent!) rel_sys_error = 0 if self.signal_err_sys != 0: rel_sys_error = self.signal_err_sys/self.signal_normevents if other.signal_err_sys != 0: rel_sys_error = other.signal_err_sys/other.signal_normevents # ds1/s1 must equal ds2/s2, or in other words ds1*s2 = ds2*s1, which avoids rounding problems if self.signal_err_sys != 0 and other.signal_err_sys != 0 and abs(self.signal_err_sys*other.signal_normevents - other.signal_err_sys*self.signal_normevents)>1E-5: print self.signal_err_sys # debug print self.signal_normevents # debug print other.signal_err_sys # debug print other.signal_normevents # debug print self.signal_err_sys*other.signal_normevents # debug print other.signal_err_sys*self.signal_normevents #debug print self.signal_err_sys*other.signal_normevents - other.signal_err_sys*self.signal_normevents #debug AdvPrint.cerr_exit("\t resultCollector::add_and_average() \n \t Event files that should be averaged have different syserrors. Something must have gone wrong!") # weights can be added self.signal_sumofweights = self.signal_sumofweights + other.signal_sumofweights self.signal_sumofweights2 = self.signal_sumofweights2 + other.signal_sumofweights2 # redetermine event numbers and stat error if self.total_sumofweights != 0: self.signal_normevents = self.total_normevents * self.signal_sumofweights / self.total_sumofweights if self.signal_sumofweights <= 0: # If there are no events in the signal region, ... if Info.flags['no_mc_stat_err']: self.signal_err_stat = 0.0 else: self.signal_err_stat = 1.*self.total_normevents/self.total_sumofweights # ... set staterror to error on 1 Monte Carlo Event under the assumption that all events have equal weight self.signal_err_sys = 0 # ... set syserror to 0 else: if Info.flags['no_mc_stat_err']: self.signal_err_stat = 0.0 else: self.signal_err_stat = self.total_normevents*sqrt(self.signal_sumofweights2)/(self.total_sumofweights) self.signal_err_sys = self.signal_normevents * rel_sys_error # multiply new normevents by old rel_sys_error else: # if there are no events, everything is 0 self.signal_normevents = 0 self.signal_err_stat = 0 self.signal_err_sys = 0 self.signal_err_tot = sqrt(self.signal_err_stat**2+self.signal_err_sys**2)
def add_and_average(self, other): """ Used to add event samples within the same process: weights are added and event numbers are redetermined """ if self.analysis != other.analysis or self.sr != other.sr: AdvPrint.cerr_exit( "\t resultCollector::add_and_average() \n \t Only results of the same analysis and same signal region can be added!" ) # This number must be equal for all averaged resultCollector (or 0, if this 'other' is the first item that is added) if self.total_normevents == 0: self.total_normevents = other.total_normevents else: weight1 = self.total_mcevents / (self.total_mcevents + other.total_mcevents) weight2 = other.total_mcevents / (self.total_mcevents + other.total_mcevents) self.total_normevents = (self.total_normevents * weight1 + other.total_normevents * weight2) / ( weight1 + weight2) """ the below test is not correct anymore as combined runs of the same process with event gneration via Pythia8 or MadGraph can lead to different values for the respective cross sections. In that case, total_normevents should be given as the weighted mean of the respective total_normevents-numbers, using total_mcevents as the weight #elif (self.total_normevents - other.total_normevents)/float(self.total_normevents + other.total_normevents) > 0.001: # This number must be universal for averaging, can slightly differ due to rounding! # AdvPrint.cerr_exit("\t resultCollector::add_and_average() \n \t Event files that should be averaged ("+self.name+" "+other.name+") have different total normevents in "+self.analysis+" - "+self.sr+". Something must have gone wrong!") """ self.total_mcevents = self.total_mcevents + other.total_mcevents # add mc events self.total_sumofweights = self.total_sumofweights + other.total_sumofweights # add sum of weights self.total_sumofweights2 = self.total_sumofweights2 + other.total_sumofweights2 # add sum of weights^2 # store old relative syserror (beware the case N = 0 and that self and other should be consistent!) rel_sys_error = 0 if self.signal_err_sys != 0: rel_sys_error = self.signal_err_sys / self.signal_normevents if other.signal_err_sys != 0: rel_sys_error = other.signal_err_sys / other.signal_normevents # ds1/s1 must equal ds2/s2, or in other words ds1*s2 = ds2*s1, which avoids rounding problems if self.signal_err_sys != 0 and other.signal_err_sys != 0 and abs( self.signal_err_sys * other.signal_normevents - other.signal_err_sys * self.signal_normevents) > 1E-5: print self.signal_err_sys # debug print self.signal_normevents # debug print other.signal_err_sys # debug print other.signal_normevents # debug print self.signal_err_sys * other.signal_normevents # debug print other.signal_err_sys * self.signal_normevents #debug print self.signal_err_sys * other.signal_normevents - other.signal_err_sys * self.signal_normevents #debug AdvPrint.cerr_exit( "\t resultCollector::add_and_average() \n \t Event files that should be averaged have different syserrors. Something must have gone wrong!" ) # weights can be added self.signal_sumofweights = self.signal_sumofweights + other.signal_sumofweights self.signal_sumofweights2 = self.signal_sumofweights2 + other.signal_sumofweights2 # redetermine event numbers and stat error if self.total_sumofweights != 0: self.signal_normevents = self.total_normevents * self.signal_sumofweights / self.total_sumofweights if self.signal_sumofweights <= 0: # If there are no events in the signal region, ... if Info.flags['no_mc_stat_err']: self.signal_err_stat = 0.0 else: self.signal_err_stat = 1. * self.total_normevents / self.total_sumofweights # ... set staterror to error on 1 Monte Carlo Event under the assumption that all events have equal weight self.signal_err_sys = 0 # ... set syserror to 0 else: if Info.flags['no_mc_stat_err']: self.signal_err_stat = 0.0 else: self.signal_err_stat = self.total_normevents * sqrt( self.signal_sumofweights2) / (self.total_sumofweights) self.signal_err_sys = self.signal_normevents * rel_sys_error # multiply new normevents by old rel_sys_error else: # if there are no events, everything is 0 self.signal_normevents = 0 self.signal_err_stat = 0 self.signal_err_sys = 0 self.signal_err_tot = sqrt(self.signal_err_stat**2 + self.signal_err_sys**2)
def check(self): if self.py8_infile == '' and self.processString == '': AdvPrint.cerr_exit("ERROR in Pythia8Events-object '"+self.name+"':\n\t Neither a .in file nor a process string was given!")
def setup_pythia8_from_process_string(self): """ Generates Pythia8 input card """ out_path = Info.paths['output_pythia'] procnum = len(Info.files['pythia_cards']) filename = self.name + "card_" + str(procnum) + ".in" fpath = os.path.join(out_path, filename) f = open(fpath, 'w') # Part 2: Generate cards based on process string part = self.processString.split('>')[1].strip() proclist = list() if part == 'go go': proclist.append('SUSY:gg2gluinogluino = on\n') proclist.append('SUSY:qqbar2gluinogluino = on\n') elif part == 'go sq': proclist.append('SUSY:qg2squarkgluino = on\n') proclist.append( 'SUSY:idVecA = 1000001,1000002,1000003,1000004,2000001,2000002,2000003,2000004' ) elif part == 'sq sq~': proclist.append('SUSY:gg2squarkantisquark = on\n') proclist.append('SUSY:qqbar2squarkantisquark = on\n') proclist.append( 'SUSY:idVecA = 1000001,1000002,1000003,1000004,2000001,2000002,2000003,2000004' ) elif part == 't1 t1~': proclist.append('SUSY:gg2squarkantisquark = on\n') proclist.append('SUSY:qqbar2squarkantisquark = on\n') proclist.append('SUSY:idA = 1000006\n') elif part == '3gen': proclist.append('SUSY:gg2squarkantisquark = on\n') proclist.append('SUSY:qqbar2squarkantisquark = on\n') proclist.append('SUSY:qq2squarksquark = on\n') proclist.append('SUSY:idVecA = 1000005,100006,2000005,2000006\n') elif part == 'sq sq': proclist.append('SUSY:qq2squarksquark = on\n') proclist.append( 'SUSY:idVecA = 1000001,1000002,1000003,1000004,2000001,2000002,2000003,2000004\n' ) elif part.lower() == "ewsusy": proclist.append('SUSY:qqbar2chi0chi0 = on\n') proclist.append('SUSY:qqbar2chi+-chi0 = on\n') proclist.append('SUSY:qqbar2chi+chi- = on\n') elif part.lower() == "colsusy": proclist.append('SUSY:gg2gluinogluino = on\n') proclist.append('SUSY:qqbar2gluinogluino = on\n') proclist.append('SUSY:qg2squarkgluino = on\n') proclist.append('SUSY:gg2squarkantisquark = on\n') proclist.append('SUSY:qqbar2squarkantisquark = on\n') proclist.append('SUSY:qq2squarksquark = on\n') proclist.append( 'SUSY:idVecA = 1000001,1000002,1000003,1000004,1000005,100006,2000001,2000002,2000003,2000004,2000005,2000006\n' ) elif part.lower() == "allsusy": proclist.append('SUSY:all = on\n') else: AdvPrint.cerr_exit( "\t Process:genPy8card():: Cannot understand process " + part) # Write Pythia cards ecm_str = "Beams:eCM = 8000.\n" if (float(Info.parameters["ecm"]) == 7.0): ecm_str = "Beams:eCM = 7000.\n" elif (float(Info.parameters["ecm"]) == 13.0): ecm_str = "Beams:eCM = 13000.\n" elif (float(Info.parameters["ecm"]) == 14.0): ecm_str = "Beams:eCM = 14000.\n" if len(proclist) == 0: AdvPrint.cerr_exit("No processes found") default = open(Info.files['pythia_settings_template'], 'r') slhafile = Info.files['slha'] for line in default: f.write(line) f.write(ecm_str + '\n') if len(slhafile) > 0: f.write('SLHA:file = ') f.write(slhafile + '\n') else: AdvPrint.cerr_exit("\t Process:genPy8card():: No SLHA file found") for item in proclist: f.write(item) default.close() # if no maximal number set, set number of generated events to 5000 if self.maxEvents == -1: AdvPrint.cout( "\t " + self.name + ":genPy8card(): Setting number of to-be-generated events to 5000. Use --maxevents parameter to change this behaviour." ) self.maxEvents = 5000 f.close() Info.files['pythia_cards'].append(fpath) self.py8_infile = fpath
def check(self): if self.py8_infile == '' and self.processString == '': AdvPrint.cerr_exit( "ERROR in Pythia8Events-object '" + self.name + "':\n\t Neither a .in file nor a process string was given!")
def setup_pythia8_from_process_string(self): """ Generates Pythia8 input card """ out_path = Info.paths['output_pythia'] procnum = len(Info.files['pythia_cards']) filename = self.name+"card_"+str(procnum)+".in" fpath = os.path.join(out_path,filename) f = open(fpath,'w') # Part 2: Generate cards based on process string part = self.processString.split('>')[1].strip() proclist = list() if part == 'go go': proclist.append('SUSY:gg2gluinogluino = on\n') proclist.append('SUSY:qqbar2gluinogluino = on\n') elif part == 'go sq': proclist.append('SUSY:qg2squarkgluino = on\n') proclist.append('SUSY:idVecA = 1000001,1000002,1000003,1000004,2000001,2000002,2000003,2000004') elif part == 'sq sq~': proclist.append('SUSY:gg2squarkantisquark = on\n') proclist.append('SUSY:qqbar2squarkantisquark = on\n') proclist.append('SUSY:idVecA = 1000001,1000002,1000003,1000004,2000001,2000002,2000003,2000004') elif part == 't1 t1~': proclist.append('SUSY:gg2squarkantisquark = on\n') proclist.append('SUSY:qqbar2squarkantisquark = on\n') proclist.append('SUSY:idA = 1000006\n') elif part == '3gen': proclist.append('SUSY:gg2squarkantisquark = on\n') proclist.append('SUSY:qqbar2squarkantisquark = on\n') proclist.append('SUSY:qq2squarksquark = on\n') proclist.append('SUSY:idVecA = 1000005,100006,2000005,2000006\n') elif part == 'sq sq': proclist.append('SUSY:qq2squarksquark = on\n') proclist.append('SUSY:idVecA = 1000001,1000002,1000003,1000004,2000001,2000002,2000003,2000004\n') elif part.lower() == "ewsusy": proclist.append('SUSY:qqbar2chi0chi0 = on\n') proclist.append('SUSY:qqbar2chi+-chi0 = on\n') proclist.append('SUSY:qqbar2chi+chi- = on\n') elif part.lower() == "colsusy": proclist.append('SUSY:gg2gluinogluino = on\n') proclist.append('SUSY:qqbar2gluinogluino = on\n') proclist.append('SUSY:qg2squarkgluino = on\n') proclist.append('SUSY:gg2squarkantisquark = on\n') proclist.append('SUSY:qqbar2squarkantisquark = on\n') proclist.append('SUSY:qq2squarksquark = on\n') proclist.append('SUSY:idVecA = 1000001,1000002,1000003,1000004,1000005,100006,2000001,2000002,2000003,2000004,2000005,2000006\n') elif part.lower() == "allsusy": proclist.append('SUSY:all = on\n') else: AdvPrint.cerr_exit("\t Process:genPy8card():: Cannot understand process " + part) # Write Pythia cards ecm_str = "Beams:eCM = 8000.\n" if(Info.parameters["ecm"] == 7.0): ecm_str = "Beams:eCM = 7000.\n" elif (Info.parameters["ecm"] == 13.0): ecm_str = "Beams:eCM = 13000\n." elif (Info.parameters["ecm"] == 14.0): ecm_str = "Beams:eCM = 14000.\n" if len(proclist) == 0: AdvPrint.cerr_exit("No processes found") default = open(Info.files['pythia_settings_template'],'r') slhafile = Info.files['slha'] for line in default: f.write(line) f.write(ecm_str + '\n' ) if len(slhafile) > 0 : f.write('SLHA:file = ') f.write(slhafile + '\n') else: AdvPrint.cerr_exit("\t Process:genPy8card():: No SLHA file found") for item in proclist: f.write(item) default.close() # if no maximal number set, set number of generated events to 5000 if self.maxEvents == -1: AdvPrint.cout("\t "+self.name+":genPy8card(): Setting number of to-be-generated events to 5000. Use --maxevents parameter to change this behaviour.") self.maxEvents = 5000 f.close() Info.files['pythia_cards'].append(fpath) self.py8_infile = fpath
def line_from_data(self, columns): """ Returns the information stated in the list 'columns' as a single line """ line = "" for c in columns: c = c.lower().strip() if c.lower() in ['name', 'id', 'identifier']: line += str(self.resultCollector.name)+" " elif c.lower() in ['analysis', 'ana']: line += str(self.resultCollector.analysis)+" " elif c.lower() in ['sr', 'signalregion']: line += str(self.resultCollector.sr)+" " elif c.lower() in ['totalmc', 'totalmcevents']: line += str(self.resultCollector.total_mcevents)+" " elif c.lower() in ['totalnorm', 'totalnormevents']: line += str(self.resultCollector.total_normevents)+" " elif c.lower() in ['totalsw', 'totalsumofweights']: line += str(self.resultCollector.total_sumofweights)+" " elif c.lower() in ['totalsw2', 'totalsumofweights2']: line += str(self.resultCollector.total_sumofweights2)+" " elif c.lower() in ['signalsw', 'signalsumofweights']: line += str(self.resultCollector.signal_sumofweights)+" " elif c.lower() in ['signalsw2', 'signalsumofweights2']: line += str(self.resultCollector.signal_sumofweights2)+" " elif c.lower() in ['signalnorm', 'signalnormevents', 's']: line += str(self.resultCollector.signal_normevents)+" " elif c.lower() in ['signalerrstat','signal_err_stat']: line += str(self.resultCollector.signal_err_stat)+" " elif c.lower() in ['signalerrsys','signal_err_sys']: line += str(self.resultCollector.signal_err_sys)+" " elif c.lower() in ['signalerrtot', 'ds','signal_err_tot']: line += str(self.resultCollector.signal_err_tot)+" " elif c.lower() in ['obs', 'o']: line += str(self.obs)+" " elif c.lower() in ['b', 'bkg']: line += str(self.bkg)+" " elif c.lower() in ['db', 'bkgerr']: line += str(self.bkg_err)+" " elif c.lower() in ['signaleff', 'eff']: line += str(self.signal_eff)+" " elif c.lower() in ['signaleff_err_stat', 'eff_err_stat']: line += str(self.signal_eff_error_stat)+" " elif c.lower() in ['signaleff_err_sys', 'eff_err_sys']: line += str(self.signal_eff_error_sys)+" " elif c.lower() in ['signaleff_err_tot', 'eff_err_tot']: line += str(self.signal_eff_error_tot)+" " elif c.lower() in ['s95obs']: line += str(self.s95_obs)+" " elif c.lower() in ['s95exp']: line += str(self.s95_exp)+" " elif c.lower() in ['robs','r_obs']: line += str(self.r_obs)+" " elif c.lower() in ['robscons','r_obs_cons']: line += str(self.r_obs_cons)+" " elif c.lower() in ['robsconssysonly','r_obs_cons_sysonly']: line += str(self.r_obs_cons_sysonly)+" " elif c.lower() in ['rexp','r_exp']: line += str(self.r_exp)+" " elif c.lower() in ['rexpcons','r_exp_cons']: line += str(self.r_exp_cons)+" " elif c.lower() in ['rexpconssysonly','r_exp_cons_sysonly']: line += str(self.r_exp_cons_sysonly)+" " elif c.lower() in ['clsobs','cls_obs']: line += str(self.cls_obs)+" " elif c.lower() in ['clsobs_err','cls_obs_err']: line += str(self.cls_obs_err)+" " elif c.lower() in ['clsexp','cls_exp']: line += str(self.cls_exp)+" " elif c.lower() in ['clsexp_err','cls_exp_err']: line += str(self.cls_exp_err)+" " elif c.lower() in ['likelihood']: line += str(self.likelihood)+" " #todoelif c.lower() in ['likelihood']: # line += str(self.likelihood)+" " else: AdvPrint.cerr_exit("evaluator::line_from_data - column "+c+" unknown!") return line
def check(self): if self.mg5_cards["proc"] == "" and self.commandstring == "": AdvPrint.cerr_exit("Error in MG5Events object "+self.name+":\n\t Neither a proc card nor a command string was provided!")