class Layout: def __init__(self, main): self.main = main self.input_path = self.main.lastjob_name self.cutflow = CutFlow(self.main) self.plotflow = PlotFlow(self.main) self.merging = MergingPlots(self.main) def Initialize(self): # Calculating cut efficiencies self.cutflow.Initialize() # Creating histograms self.plotflow.Initialize() self.merging.Initialize() @staticmethod def DisplayInteger(value): if type(value) is not int: return "" if value < 0: return "-" + Layout.DisplayInteger(-value) elif value < 1000: return str(value) else: return Layout.DisplayInteger(value / 1000) +\ "," + '%03d' % (value % 1000) @staticmethod def Round_to_Ndigits(x, N): if N < 1: return "" if x < (10**(N - 1)): convert = '%.' + str(N) + 'G' return '%s' % float(convert % x) else: tmp = '%s' % float('%.12G' % int(x)) if len(tmp) >= 3 and tmp.endswith('.0'): tmp = tmp[:-2] return tmp @staticmethod def DisplayXsection(xsection, xerror): # xsection and xerror are null if xsection == 0. and xerror == 0.: return "0.0 @ 0.0%" # xsection is not null but xerror is # keep the 3 significative digit elif xerror == 0: return Layout.Round_to_Ndigits(xsection, 3) # error greater than xsection ? else: string1 = Layout.Round_to_Ndigits(xsection, 3) string2 = Layout.Round_to_Ndigits(100. * xerror / xsection, 2) return string1 + " @ " + string2 + '%' @staticmethod def DisplayXsecCut(xsection, xerror): # xsection and xerror are null if xsection == 0. and xerror == 0.: return "0.0 +/- 0.0" # xsection is not null but xerror is # keep the 3 significative digit elif xerror == 0: return Layout.Round_to_Ndigits(xsection, 3) # error greater than xsection ? elif xsection > xerror: string1 = Layout.Round_to_Ndigits(xerror, 3) if 'e' in string1 or 'E' in string1: string2 = '%.2e' % xsection string1 = '%.2e' % xerror elif '.' in string1: convert = '%.' + str(len(string1.split('.')[1])) + 'f' string2 = convert % xsection else: string2 = str(int(xsection)) return string2 + " +/- " + string1 else: string1 = Layout.Round_to_Ndigits(xsection, 3) if 'e' in string1 or 'E' in string1: string2 = '%.2e' % xerror string1 = '%.2e' % xsection elif '.' in string1: convert = '%.' + str(len(string1.split('.')[1])) + 'f' string2 = convert % xerror else: string2 = str(int(xerror)) return string1 + " +/- " + string2 def DoPlots(self, mode, output_path): if self.main.merging.enable: self.merging.DrawAll(mode, output_path) if self.main.selection.Nhistos == 0: return True self.plotflow.DrawAll(mode, output_path) return True def CopyLogo(self, mode, output_path): # Filename filename = self.main.ma5dir+"/madanalysis/input/" + \ "logo." + \ ReportFormatType.convert2filetype(mode) # Checking file presence if not os.path.isfile(filename): logging.error("the image '" + \ filename + \ "' is not found.") return False # Copy file try: shutil.copy(filename, output_path) return True except: logging.error("Errors have occured during the copy of the file ") logging.error(" " + filename) return False def WriteDatasetTable(self, report, dataset): # Datatype datatype = "signal" if dataset.background: datatype = "background" report.OpenBullet() text = TextReport() # Must we specify the sample folder ? specify = False for ind in range(0, len(dataset.filenames)): samplename = dataset.filenames[ind].replace( self.main.currentdir, '') if samplename[0] != '/' or samplename == dataset.filenames[ind]: specify = True break # Displaying the folder if specify: text.Add('Samples stored in the directory: ') text.SetColor(ColorType.BLUE) text.Add(self.main.currentdir) text.SetColor(ColorType.BLACK) text.Add('.\n') report.WriteText(text) text.Reset() # Signal or background sample text.Add('Sample consisting of: ') text.SetColor(ColorType.BLUE) text.Add(datatype) text.SetColor(ColorType.BLACK) text.Add(' events.\n') report.WriteText(text) text.Reset() # Number of generated events text.Add('Generated events: ') text.SetColor(ColorType.BLUE) ngen = int(dataset.measured_global.nevents) text.Add(str(ngen) + " ") text.SetColor(ColorType.BLACK) text.Add(' events.\n') report.WriteText(text) text.Reset() # Cross section imposed by the user if dataset.xsection != 0.0: text.Add('* Cross section imposed by the user: '******' pb.\n') report.WriteText(text) text.Reset() # Weight of the events, if different from one if dataset.weight != 1.0: text.Add('* Event weight imposed by the user: '******'.\n') report.WriteText(text) text.Reset() # Number of events after normalization, with the error text.Add('Normalization to the luminosity: ') text.SetColor(ColorType.BLUE) if dataset.xsection != 0.0: nlumi = int(dataset.xsection * 1000 * self.main.lumi) else: nlumi = int(dataset.measured_global.xsection * \ 1000*self.main.lumi * \ dataset.weight) text.Add(str(nlumi)) text.Add(' +/- ') if dataset.xsection != 0.0: elumi = 0.0 else: elumi = ceil(dataset.measured_global.xerror * 1000 * \ self.main.lumi * dataset.weight) text.Add(str(int(elumi)) + " ") text.SetColor(ColorType.BLACK) text.Add(' events.\n') report.WriteText(text) text.Reset() # Statistical significance of the sample evw = 0. if ngen != 0: evw = float(nlumi) / float(ngen) * dataset.weight if evw > 1: text.SetColor(ColorType.RED) text.Add('Ratio (event weight): ') if evw < 1: text.SetColor(ColorType.BLUE) text.Add(str(Layout.Round_to_Ndigits(evw, 2)) + " ") if evw < 1: text.SetColor(ColorType.BLACK) text.Add('.') if evw > 1: text.Add( ' - warning: please generate more events (weight larger than 1)!\n' ) text.SetColor(ColorType.BLACK) else: text.Add(' \n') report.WriteText(text) text.Reset() report.CloseBullet() # table with information # titles of the columns report.CreateTable([6.5, 3, 3.5, 3.5], text) report.NewCell(ColorType.YELLOW) if len(dataset.filenames) >= 2: text.Add(" Paths to the event files") else: text.Add(" Path to the event file") report.WriteText(text) text.Reset() report.NewCell(ColorType.YELLOW) text.Add(" Nr. of events") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add(" Cross section (pb)") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add(" Negative wgts (%)") report.WriteText(text) text.Reset() report.NewLine() # information for ind in range(0, len(dataset.filenames)): color = ColorType.BLACK # path to the file report.NewCell() text.Add(' ') text.SetColor(color) samplename = str(dataset.filenames[ind]).replace( self.main.currentdir, '') if samplename[0] == '/' and samplename != dataset.filenames[ind]: samplename = samplename[1:] text.Add(samplename) report.WriteText(text) text.Reset() # number of events report.NewCell() text.Add(' ') text.SetColor(color) text.Add(str(int(dataset.measured_detail[ind].nevents))) report.WriteText(text) text.Reset() # cross section report.NewCell() text.Add(' ') text.SetColor(color) if dataset.xsection != 0.0: text.Add(str(dataset.xsection)) else: value = dataset.measured_detail[ind].xsection * dataset.weight error = dataset.measured_detail[ind].xerror * dataset.weight text.Add(Layout.DisplayXsection(value, error)) report.WriteText(text) text.Reset() # Negative weigths report.NewCell() text.Add(' ') text.SetColor(color) if (dataset.measured_detail[ind].sumw_positive +\ dataset.measured_detail[ind].sumw_negative)==0: text.Add(str(0.0)) else: text.Add(Layout.Round_to_Ndigits( 100 * \ dataset.measured_detail[ind].sumw_negative / \ (dataset.measured_detail[ind].sumw_positive + \ dataset.measured_detail[ind].sumw_negative),2 )) report.WriteText(text) text.Reset() # end of the line if len(dataset.filenames) == 1: report.EndLine() else: report.NewLine() # sum if many datasets if len(dataset.filenames) != 1: color = ColorType.BLUE report.NewCell() text.Add(' ') text.SetColor(color) text.Add("Sum") report.WriteText(text) text.Reset() # number of events report.NewCell() text.Add(' ') text.SetColor(color) text.Add(str(int(dataset.measured_global.nevents))) report.WriteText(text) text.Reset() # cross section report.NewCell() text.Add(' ') text.SetColor(color) if dataset.xsection != 0.0: text.Add(str(dataset.xsection)) else: value = dataset.measured_global.xsection * dataset.weight error = dataset.measured_global.xerror * dataset.weight text.Add(Layout.DisplayXsection(value, error)) report.WriteText(text) text.Reset() # Negative weigths report.NewCell() text.Add(' ') text.SetColor(color) if (dataset.measured_detail[ind].sumw_positive +\ dataset.measured_detail[ind].sumw_negative)==0: text.Add(str(0.0)) else: text.Add(Layout.Round_to_Ndigits( 100 * \ dataset.measured_global.sumw_negative / \ (dataset.measured_global.sumw_positive + \ dataset.measured_global.sumw_negative),2 )) report.WriteText(text) text.Reset() ## The end of line report.EndLine() report.EndTable() text.Reset() # Writing Final Table def WriteFinalTable(self, report): # Information report.OpenBullet() text = TextReport() text.Reset() text.Add("How to compare signal (S) and background (B): ") text.SetColor(ColorType.BLUE) text.Add(self.main.SBratio) text.SetColor(ColorType.BLACK) text.Add('.\n') report.WriteText(text) text.Reset() text.Add("Associated uncertainty: ") text.SetColor(ColorType.BLUE) text.Add(self.main.SBerror) text.SetColor(ColorType.BLACK) text.Add('.\n') report.WriteText(text) text.Reset() report.CloseBullet() # Caption text.Add("Signal and Background comparison") report.CreateTable([2.6, 2.5, 3.6, 3.6, 2.1], text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add(" Cuts") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add(" Signal (S)") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add(" Background (B)") # (+/- err)") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add(" S vs B") report.WriteText(text) report.NewLine() text.Reset() # Initial report.NewCell() text.Reset() text.Add(" Initial (no cut)") report.WriteText(text) report.NewCell() text.Reset() if self.cutflow.isSignal: text.Add(" " +\ Layout.DisplayXsecCut(self.cutflow.signal.Ntotal.mean, self.cutflow.signal.Ntotal.error)) else: text.Add(" ") report.WriteText(text) report.NewCell() text.Reset() if self.cutflow.isBackground: text.Add(" " +\ Layout.DisplayXsecCut(self.cutflow.background.Ntotal.mean,\ self.cutflow.background.Ntotal.error)) else: text.Add(" ") report.WriteText(text) report.NewCell() text.Reset() if self.cutflow.isSignal and self.cutflow.isBackground: value = self.cutflow.calculateBSratio(\ self.cutflow.background.Ntotal.mean,\ self.cutflow.background.Ntotal.error,\ self.cutflow.signal.Ntotal.mean,\ self.cutflow.signal.Ntotal.error) text.Add(" " + Layout.DisplayXsecCut(value.mean, value.error)) else: text.Add(" ") report.WriteText(text) report.NewLine() text.Reset() # Loop for ind in range(0, len(self.cutflow.detail[0].Nselected)): report.NewCell() text.Reset() text.Add(" Cut " + str(ind + 1)) report.WriteText(text) report.NewCell() text.Reset() if self.cutflow.isSignal: text.Add(" " + \ Layout.DisplayXsecCut(\ self.cutflow.signal.Nselected[ind].mean,\ self.cutflow.signal.Nselected[ind].error) ) else: text.Add(" ") report.WriteText(text) report.NewCell() text.Reset() if self.cutflow.isBackground: text.Add(" " + \ Layout.DisplayXsecCut(\ self.cutflow.background.Nselected[ind].mean,\ self.cutflow.background.Nselected[ind].error) ) else: text.Add(" ") report.WriteText(text) report.NewCell() text.Reset() if self.cutflow.isSignal and self.cutflow.isBackground: value = self.cutflow.calculateBSratio(\ self.cutflow.background.Nselected[ind].mean,\ self.cutflow.background.Nselected[ind].error,\ self.cutflow.signal.Nselected[ind].mean,\ self.cutflow.signal.Nselected[ind].error) text.Add(" " + Layout.DisplayXsecCut(value.mean, value.error)) else: text.Add(" ") report.WriteText(text) if ind == (len(self.cutflow.detail[0].Nselected) - 1): report.EndLine() else: report.NewLine() text.Reset() report.EndTable() # Writing Efficiency Table def WriteEfficiencyTable(self, index, report): text = TextReport() report.CreateTable([2.1, 2.8, 2.8, 3.4, 3.3], text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add(" Dataset") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add(" Events kept:\n") text.Add(" K") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add(" Rejected events:\n") text.Add(" R") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add(" Efficiency:\n") text.Add(" K / (K + R)") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add(" Cumul. efficiency:\n") text.Add(" K / Initial") report.WriteText(text) text.Reset() report.NewLine() for i in range(0, len(self.main.datasets)): # DatasetName report.NewCell() text.Reset() text.Add(' ' + self.main.datasets[i].name) report.WriteText(text) # SelectedEvents report.NewCell() text.Reset() text.Add(' ' + Layout.DisplayXsecCut(self.cutflow.detail[i].Nselected[index].mean,\ self.cutflow.detail[i].Nselected[index].error)) report.WriteText(text) # RejectedEvents report.NewCell() text.Reset() text.Add(' ' + Layout.DisplayXsecCut(self.cutflow.detail[i].Nrejected[index].mean,\ self.cutflow.detail[i].Nrejected[index].error)) report.WriteText(text) # Efficiency Events report.NewCell() text.Reset() text.Add(' ' + Layout.DisplayXsecCut(self.cutflow.detail[i].eff[index].mean,\ self.cutflow.detail[i].eff[index].error)) report.WriteText(text) # Cumulative efficiency events report.NewCell() text.Reset() text.Add(' ' + Layout.DisplayXsecCut(self.cutflow.detail[i].effcumu[index].mean,\ self.cutflow.detail[i].effcumu[index].error)) report.WriteText(text) if i == (len(self.main.datasets) - 1): report.EndLine() else: report.NewLine() text.Reset() report.EndTable() text.Reset() # Checking if warnings (due to negative weights) warning_test = False for i in range(0, len(self.main.datasets)): if len(self.cutflow.detail[i].warnings[index]) != 0: warning_test = True break # Displaying warnings if warning_test: report.CreateTable([13], text) report.NewCell() text.SetColor(ColorType.RED) text.Add("Warnings related to negative event-weights:") report.WriteText(text) report.NewLine() for item in range(0, len(self.main.datasets)): for line in self.cutflow.detail[i].warnings[index]: report.NewCell() text.Reset() text.SetColor(ColorType.RED) text.Add(line) report.WriteText(text) report.NewLine() report.EndTable() # Writing Statistics Table def WriteStatisticsTable(self, index, report): text = TextReport() text.Add("Statistics table") report.CreateTable([2.6, 4.1, 3.6, 2.8, 2.8, 2.1, 2.1], text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add(" Dataset") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add(' Integral') report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add(" Entries / events") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add(" Mean") # (+/- err)") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add(" RMS") # (+/- err)") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add(" %Underflow") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add(" %Overflow") report.WriteText(text) report.NewLine() # Looping over dataset warning_test = False for iset in range(0, len(self.plotflow.detail)): # Is there warning ? if len(self.plotflow.detail[iset][index].warnings) != 0: warning_test = True # Getting the number of entries integral = self.plotflow.detail[iset][index].summary.integral # Getting underflow and overflow uflow = self.plotflow.detail[iset][index].summary.underflow oflow = self.plotflow.detail[iset][index].summary.overflow # Computing underflow and overflow ratio / integral uflow_percent = 0 oflow_percent = 0 if integral != 0: uflow_percent = uflow * 100 / integral oflow_percent = oflow * 100 / integral # mean value mean = self.plotflow.detail[iset][index].summary.GetMean() # root mean square + error rms = self.plotflow.detail[iset][index].summary.GetRMS() # writing the table report.NewCell() text.Reset() text.Add(' ' + self.main.datasets[iset].name) report.WriteText(text) report.NewCell() # Nentries text.Reset() text.Add(' ' + Layout.DisplayXsecCut( integral * self.plotflow.detail[iset][index].scale, 0)) report.WriteText(text) report.NewCell() # Getting the number of events and number of entries nentries = self.plotflow.detail[iset][index].summary.nentries nevents = self.plotflow.detail[iset][index].summary.nevents # Nentries / Nevents text.Reset() if nevents != 0.: text.Add(' ' + \ str(Layout.Round_to_Ndigits(float(nentries)/float(nevents),3))) else: text.Add(" 0.") report.WriteText(text) report.NewCell() # Mean value text.Reset() text.Add(' ' + str(Layout.Round_to_Ndigits(mean, 6))) # +"(+/- "+str(Layout.Round_to_Ndigits(mean_error,3))+")") report.WriteText(text) report.NewCell() text.Reset() text.Add(' ' + str(Layout.Round_to_Ndigits(rms, 4))) # +"(+/- "+str(Layout.Round_to_Ndigits(rms_error,3))+")") report.WriteText(text) if uflow_percent + oflow_percent <= 5: report.NewCell(ColorType.GREEN) if uflow_percent + oflow_percent > 5 and uflow_percent + oflow_percent < 15: report.NewCell(ColorType.ORANGE) if uflow_percent + oflow_percent > 15: report.NewCell(ColorType.RED) text.Reset() text.Add(' ' + str(Layout.Round_to_Ndigits(uflow_percent, 4))) report.WriteText(text) if uflow_percent + oflow_percent <= 5: report.NewCell(ColorType.GREEN) if uflow_percent + oflow_percent > 5 and uflow_percent + oflow_percent < 15: report.NewCell(ColorType.ORANGE) if uflow_percent + oflow_percent > 15: report.NewCell(ColorType.RED) text.Reset() text.Add(' ' + str(Layout.Round_to_Ndigits(oflow_percent, 4))) report.WriteText(text) if iset == (len(self.plotflow.detail) - 1): report.EndLine() else: report.NewLine() text.Reset() report.EndTable() text.Reset() # Displaying warnings if warning_test: report.CreateTable([13], text) report.NewCell() text.SetColor(ColorType.RED) text.Add("Warnings related to negative event-weights:") report.WriteText(text) report.NewLine() for iset in range(0, len(self.plotflow.detail)): for line in self.plotflow.detail[iset][index].warnings: report.NewCell() text.Reset() text.SetColor(ColorType.RED) text.Add(line) report.WriteText(text) report.NewLine() report.EndTable() def GenerateReport(self, history, output_path, mode): # Creating production directory if not FolderWriter.CreateDirectory(output_path, True): return False if not self.CopyLogo(mode, output_path): return False # Draw plots if not self.DoPlots(mode, output_path): return # logging.info(" ** Computing cut efficiencies...") #if not layout.DoEfficiencies(): # return # Find a name for PDF file if self.main.pdflatex: self.pdffile = self.main.lastjob_name + '/PDF/main.pdf' elif self.main.latex and self.main.dvipdf: self.pdffile = self.main.lastjob_name + '/DVI/main.pdf' else: self.pdffile = '' # Defining report writing if mode == ReportFormatType.HTML: report = HTMLReportWriter(output_path + "/index.html", self.pdffile) elif mode == ReportFormatType.LATEX: report = LATEXReportWriter(output_path+"/main.tex",\ self.main.ma5dir+"/madanalysis/input",False) else: report = LATEXReportWriter(output_path+"/main.tex",\ self.main.ma5dir+"/madanalysis/input",True) # Opening if not report.Open(): return False # Create text text = TextReport() # Header report.WriteHeader() report.WriteTitle('MadAnalysis 5 report') # History of commands report.WriteSubTitle('Setup') report.WriteSubSubTitle('Command history') text.Reset() text.SetFont(FontType.TT) for item in history: text.Add('ma5>' + item + '\n\n') report.WriteText(text) # Configuration report.WriteSubSubTitle('Configuration') # Integrated luminosity report.OpenBullet() text.Reset() text.Add('MadAnalysis version ' + self.main.version + \ ' (' + self.main.date + ').\n') report.WriteText(text) # Integrated luminosity text.Reset() # Normalization if self.main.normalize == NormalizeType.LUMI or \ self.main.normalize == NormalizeType.LUMI_WEIGHT: text.Add('Histograms given for an integrated luminosity of ') text.SetColor(ColorType.BLUE) text.Add(str(self.main.lumi)) text.Add(' fb') text.SetScript(ScriptType.SUP) text.Add('-1') text.SetScript(ScriptType.none) text.Add('.\n') elif self.main.normalize == NormalizeType.NONE: text.Add('Histograms are not scaled.\n') report.WriteText(text) report.CloseBullet() # Datasets report.WriteSubTitle('Datasets') for ind in range(0, len(self.main.datasets)): report.WriteSubSubTitle(self.main.datasets[ind].name) self.WriteDatasetTable(report,\ self.main.datasets[ind]) # Merging plots if self.main.merging.enable: # Title : merging plots report.WriteSubTitle('Merging plots') # Getting all plot names allnames = self.merging.GetPlotNames(mode,\ output_path) # Loop over datasets for i in range(0, len(allnames)): # Subtitle : dataset names report.WriteSubSubTitle(self.main.datasets[i].name) # Loop over DJR plots for j in range(0, len(allnames[i])): text.Reset() title = "DJR" + str(j + 1) + " : " + str(j) if j > 1: title += " jets -> " else: title += " jet -> " title += str(j + 1) if j > 0: title += " jets" else: title += " jet" text.Add(title) report.WriteFigure(text, allnames[i][j]) # Plots display if len(self.main.selection) != 0: report.WriteSubTitle('Histos and cuts') # Plots ihisto = 0 icut = 0 for ind in range(0, len(self.main.selection)): if self.main.selection[ind].__class__.__name__ == "Histogram": report.WriteSubSubTitle("Histogram " + str(ihisto + 1)) text.Reset() text.Add(' ') text.SetFont(FontType.BF) text.Add(self.main.selection[ind].GetStringDisplay() + '\n') report.WriteText(text) text.Reset() if self.main.selection[ind].observable.name not in [ 'NPID', 'NAPID' ]: self.WriteStatisticsTable(ihisto, report) report.WriteFigure(text, output_path + '/selection_' + str(ihisto)) text.Add('\n\n') report.WriteText(text) text.Reset() ihisto += 1 if self.main.selection[ind].__class__.__name__ == "Cut": report.WriteSubSubTitle("Cut " + str(icut + 1)) text.Reset() text.Add(' ') text.SetFont(FontType.BF) text.Add(self.main.selection[ind].GetStringDisplay() + '\n') report.WriteText(text) text.Reset() self.WriteEfficiencyTable(icut, report) text.Add('\n\n') report.WriteText(text) text.Reset() icut += 1 # Final table if self.main.selection.Ncuts != 0: report.WriteSubTitle('Summary') report.WriteSubSubTitle('Cut-flow chart') self.WriteFinalTable(report) # Foot report.WriteFoot() # Closing report.Close() return True @staticmethod def CheckLatexLog(file): if not os.path.isfile(file): return False for line in file: if line.startswith('!'): return False return True def CompileReport(self, mode, output_path): # ---- LATEX MODE ---- if mode == ReportFormatType.LATEX: # Launching latex and producing DVI file os.system( 'cd ' + output_path + '; latex -interaction=nonstopmode main.tex > latex.log 2>&1; latex -interaction=nonstopmode main.tex >> latex.log 2>&1' ) name = os.path.normpath(output_path + '/main.dvi') if not os.path.isfile(name): logging.error('DVI file cannot be produced') logging.error('Please have a look to the log file ' + output_path + '/latex.log') return False # Checking latex log : are there errors if not Layout.CheckLatexLog(output_path + '/latex.log'): logging.error('some errors occured during LATEX compilation') logging.error( 'for more details, have a look to the log file : ' + output_path + '/latex.log') return False # Converting DVI file to PDF file if self.main.dvipdf: logging.info( " -> Converting the DVI report to a PDF report.") os.system('cd ' + output_path + '; dvipdf main.dvi > dvipdf.log 2>&1') name = os.path.normpath(output_path + '/main.pdf') # Checking PDF file presence if not os.path.isfile(name): logging.error('PDF file cannot be produced') logging.error('Please have a look to the log file ' + output_path + '/dvipdf.log') return False # ---- PDFLATEX MODE ---- elif mode == ReportFormatType.PDFLATEX: # Launching latex and producing PDF file os.system( 'cd ' + output_path + '; pdflatex -interaction=nonstopmode main.tex > latex.log 2>&1; pdflatex -interaction=nonstopmode main.tex >> latex.log 2>&1' ) # Checking latex log : are there errors if not Layout.CheckLatexLog(output_path + '/latex.log'): logging.error('some errors occured during LATEX compilation') logging.error( 'for more details, have a look to the log file : ' + output_path + '/latex.log') return False # Checking PDF file presence name = os.path.normpath(output_path + '/main.pdf') if not os.path.isfile(name): logging.error('PDF file cannot be produced') logging.error('Please have a look to the log file ' + output_path + '/latex2.log') return False
class Layout: def __init__(self, main, path, mode): self.main = main self.input_path = self.main.lastjob_name self.output_path = path self.files = [] self.plots = [] self.mode = mode self.color = 1 self.cutflow = [] self.plotflow = PlotFlow(self.main, self.files, self.output_path, self.mode) self.isSignal = False self.isBackground = False @staticmethod def DisplayInteger(value): if type(value) is not int: return "" if value < 0: return "-" + Layout.DisplayInteger(-value) elif value < 1000: return str(value) else: return Layout.DisplayInteger(value / 1000) +\ "," + '%03d' % (value % 1000) @staticmethod def Round_to_Ndigits(x, N): if N < 1: return "" if x < (10**(N - 1)): convert = '%.' + str(N) + 'G' return '%s' % float(convert % x) else: tmp = '%s' % float('%.12G' % int(x)) if len(tmp) >= 3 and tmp.endswith('.0'): tmp = tmp[:-2] return tmp @staticmethod def DisplayXsection(xsection, xerror): # xsection and xerror are null if xsection == 0. and xerror == 0.: return "0.0 +/- 0.0" # xsection is not null but xerror is # keep the 3 significative digit elif xerror == 0: return Layout.Round_to_Ndigits(xsection, 3) # error greater than xsection ? elif xsection > xerror: string1 = Layout.Round_to_Ndigits(xerror, 3) if 'e' in string1 or 'E' in string1: string2 = '%e' % xsection elif '.' in string1: convert = '%.' + str(len(string1.split('.')[1])) + 'f' string2 = convert % xsection else: string2 = str(int(xsection)) return string2 + " +/- " + string1 else: string1 = Layout.Round_to_Ndigits(xsection, 3) if 'e' in string1 or 'E' in string1: string2 = '%e' % xerror elif '.' in string1: convert = '%.' + str(len(string1.split('.')[1])) + 'f' string2 = convert % xerror else: string2 = str(int(xerror)) return string1 + " +/- " + string2 def Open(self): # Checking input dir if not os.path.isdir(self.input_path): logging.error("no directory denoted by '" + self.input_path + "' found.") return False # Creating list of ROOT files for ind in range(0, len(self.main.datasets)): name = InstanceName.Get(self.main.datasets[ind].name) self.files.append( RootFileReader( os.path.normpath(self.input_path + "/root/" + name + ".root"))) # Trying to open each ROOT files for ind in range(0, len(self.files)): if not self.files[ind].Open(): for i in range(0, ind): self.files[i].Close() return False # Creating production directory if not FolderWriter.CreateDirectory(self.output_path, True): return False # Creating cut flow for each file for ind in range(0, len(self.files)): self.cutflow.append(CutFlow(self.main.datasets[ind],\ self.main.selection,\ self.main.lumi, self.main)) # Good end return True def DoEfficiencies(self): if self.main.selection.Ncuts == 0: return True for i in range(0, len(self.cutflow)): if not self.cutflow[i].initializeFromFile(self.files[i]): return False self.cutflow[i].calculate() self.signal = CutFlow(self.main.datasets[0],\ self.main.selection,\ self.main.lumi,\ self.main) self.background = CutFlow(self.main.datasets[0],\ self.main.selection,\ self.main.lumi,\ self.main) signalvect = [] backgroundvect = [] for i in range(0, len(self.main.datasets)): if not self.main.datasets[i].background: signalvect.append(self.cutflow[i]) else: backgroundvect.append(self.cutflow[i]) if len(signalvect) != 0: self.signal.initializeFromCutflow(signalvect) self.isSignal = True if len(backgroundvect) != 0: self.background.initializeFromCutflow(backgroundvect) self.isBackground = True return True def DoPlots(self): if self.main.selection.Nhistos == 0: return True if not self.plotflow.initialize(): return False self.plotflow.DrawAll() return True def CopyLogo(self): # Filename filename = self.main.ma5dir+"/madanalysis/input/" + \ "logo." + \ ReportFormatType.convert2filetype(self.mode) # Checking file presence if not os.path.isfile(filename): logging.error("the image '" + \ filename + \ "' is not found.") return False # Copy file try: shutil.copy(filename, self.output_path) return True except: logging.error("Errors have occured during the copy of the file ") logging.error(" " + filename) return False def WriteDatasetTable(self, rootfile, report, xsection, weight): filenames = rootfile.Get("general/filenames", "TClonesArray") if filenames is None: return False xsections = rootfile.Get("general/xsections", "TVectorT<float>") if xsections is None: return False xerrors = rootfile.Get("general/xerrors", "TVectorT<float>") if xerrors is None: return False nevents = rootfile.Get("general/nevents", "TVectorT<float>") if nevents is None: return False if filenames.GetEntries()!=xsections.GetNoElements() or \ xsections.GetNoElements()!=xerrors.GetNoElements() or \ xerrors.GetNoElements()!=nevents.GetNoElements() or \ nevents.GetNoElements()!=filenames.GetEntries() : logging.error("the 'general' branches have different size "\ "in the file '"+filename+"'") return text = TextReport() text.Add('* Generation: ') text.SetColor(ColorType.BLUE) ngen = int(nevents[filenames.GetEntries() - 1]) text.Add(str(ngen) + " ") text.SetColor(ColorType.BLACK) text.Add(' events.\n') if xsection != 0.0: text.Add('* Cross section imposed by the user: '******' pb.\n') if weight != 1.0: text.Add('* Event weight imposed by the user: '******'.\n') text.Add('* Normalization to ') text.Add(str(self.main.lumi)) text.Add(' fb') text.SetScript(ScriptType.SUP) text.Add('-1') text.SetScript(ScriptType.none) text.Add(': ') text.SetColor(ColorType.BLUE) if xsection != 0.0: nlumi = int(xsection * 1000 * self.main.lumi) else: nlumi = int(xsections[filenames.GetEntries() - 1] * 1000 * self.main.lumi * weight) text.Add(str(nlumi)) text.Add(' +/- ') # round to the smallest integer greater than error if xsection != 0.0: elumi = 0.0 else: elumi = ceil(xerrors[filenames.GetEntries() - 1] * 1000 * self.main.lumi * weight) text.Add(str(int(elumi)) + " ") text.SetColor(ColorType.BLACK) text.Add(' events.\n') evw = float(nlumi) / float(ngen) * weight if evw > 1: text.SetColor(ColorType.RED) text.Add('* Ratio (event weight) = ') if evw < 1: text.SetColor(ColorType.BLUE) text.Add(str(Layout.Round_to_Ndigits(evw, 2)) + " ") if evw < 1: text.SetColor(ColorType.BLACK) text.Add('.') if evw > 1: text.Add( ' - warning: please generate more events (weight larger than 1)!\n' ) text.SetColor(ColorType.BLACK) else: text.Add(' \n') if self.mode is ReportFormatType.HTML: text.Add(' \n') report.WriteText(text) report.CreateTable([11.5, 2, 3]) report.NewCell(ColorType.YELLOW) text.Reset() if filenames.GetEntries() > 1: text.Add("Event files") else: text.Add("Event file") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add("Number of events") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add("Cross section (pb)") report.WriteText(text) report.NewLine() for ind in range(0, filenames.GetEntries()): color = ColorType.BLACK if ind is filenames.GetEntries() - 1: color = ColorType.BLUE report.NewCell() text.Reset() text.SetColor(color) text.Add(str(filenames[ind])) report.WriteText(text) report.NewCell() text.Reset() text.SetColor(color) text.Add(str(int(nevents[ind]))) report.WriteText(text) report.NewCell() text.Reset() text.SetColor(color) if xsection != 0.0: text.Add(str(xsection)) else: text.Add( Layout.DisplayXsection(xsections[ind] * weight, xerrors[ind] * weight)) report.WriteText(text) report.NewLine() text.Reset() report.EndTable(text) text.Reset() # Writing Final Table def WriteFinalTable(self, report): # Caption text = TextReport() text.Reset() text.Add("Formula for signal(S)-background(B) comparison : ") text.SetColor(ColorType.BLUE) text.Add(self.main.SBratio + '\n') text.SetColor(ColorType.BLACK) report.WriteText(text) text.Reset() text.Add( "Formula for uncertainty on signal(S)-background(B) comparison : ") text.SetColor(ColorType.BLUE) text.Add(self.main.SBerror + '\n') text.SetColor(ColorType.BLACK) report.WriteText(text) text.Reset() text.Add(' \n') report.WriteText(text) # Caption report.CreateTable([2.6, 2.5, 3.6, 3.6, 2.1]) report.NewCell(ColorType.YELLOW) text.Reset() text.Add("Cuts") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add("Signal (S)") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add("Background (B)") # (+/- err)") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add(" S vs B") report.WriteText(text) report.NewLine() text.Reset() # Initial report.NewCell() text.Reset() text.Add("Initial (no cut)") report.WriteText(text) report.NewCell() text.Reset() if self.isSignal: text.Add(Layout.DisplayXsection(self.signal.Ntotal.mean,\ self.signal.Ntotal.error)) else: text.Add("") report.WriteText(text) report.NewCell() text.Reset() if self.isBackground: text.Add(Layout.DisplayXsection(self.background.Ntotal.mean,\ self.background.Ntotal.error)) else: text.Add("") report.WriteText(text) report.NewCell() text.Reset() if self.isSignal and self.isBackground: value = self.signal.calculateBSratio(\ self.background.Ntotal.mean,\ self.background.Ntotal.error,\ self.signal.Ntotal.mean,\ self.signal.Ntotal.error) text.Add(Layout.DisplayXsection(value.mean, value.error)) else: text.Add("") report.WriteText(text) report.NewLine() text.Reset() # Loop for ind in range(0, len(self.cutflow[0].Nselected)): report.NewCell() text.Reset() text.Add("cut " + str(ind + 1)) report.WriteText(text) report.NewCell() text.Reset() if self.isSignal: text.Add(Layout.DisplayXsection(self.signal.Nselected[ind].mean,\ self.signal.Nselected[ind].error)) else: text.Add("") report.WriteText(text) report.NewCell() text.Reset() if self.isBackground: text.Add(Layout.DisplayXsection(self.background.Nselected[ind].mean,\ self.background.Nselected[ind].error)) else: text.Add("") report.WriteText(text) report.NewCell() text.Reset() if self.isSignal and self.isBackground: value = self.cutflow[0].calculateBSratio(\ self.background.Nselected[ind].mean,\ self.background.Nselected[ind].error,\ self.signal.Nselected[ind].mean,\ self.signal.Nselected[ind].error) text.Add(Layout.DisplayXsection(value.mean, value.error)) else: text.Add("") report.WriteText(text) report.NewLine() text.Reset() text.Add("Signal and Background comparison") report.EndTable(text) # Writing Efficiency Table def WriteEfficiencyTable(self, index, report): text = TextReport() report.CreateTable([2.6, 2.5, 3.6, 3.6, 2.1]) report.NewCell(ColorType.YELLOW) text.Reset() text.Add("Dataset") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add("Selected events (S)") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add("Rejected events (R)") # (+/- err)") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add("S / (S + R)") # (+/- err)") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add("S / Initial") report.WriteText(text) report.NewLine() text.Reset() report.NewLine() for i in range(0, len(self.main.datasets)): # DatasetName report.NewCell() text.Reset() text.Add(self.main.datasets[i].name) report.WriteText(text) # SelectedEvents report.NewCell() text.Reset() text.Add( Layout.DisplayXsection(self.cutflow[i].Nselected[index].mean, self.cutflow[i].Nselected[index].error)) report.WriteText(text) # RejectedEvents report.NewCell() text.Reset() text.Add( Layout.DisplayXsection(self.cutflow[i].Nrejected[index].mean, self.cutflow[i].Nrejected[index].error)) report.WriteText(text) # Efficiency Events report.NewCell() text.Reset() text.Add( Layout.DisplayXsection(self.cutflow[i].eff[index].mean, self.cutflow[i].eff[index].error)) report.WriteText(text) # Cumulative efficiency events report.NewCell() text.Reset() text.Add( Layout.DisplayXsection(self.cutflow[i].effcumu[index].mean, self.cutflow[i].effcumu[index].error)) report.WriteText(text) report.NewLine() text.Reset() report.EndTable(text) # Writing Statistics Table def WriteStatisticsTable(self, index, report): text = TextReport() report.CreateTable([2.6, 2.5, 3.6, 3.6, 3.6, 2.1, 2.1]) report.NewCell(ColorType.YELLOW) text.Reset() text.Add("Dataset") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add("Integral") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add("Entries / events") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add("Mean") # (+/- err)") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add("RMS") # (+/- err)") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add("Underflow") report.WriteText(text) report.NewCell(ColorType.YELLOW) text.Reset() text.Add("Overflow") report.WriteText(text) report.NewLine() histos = [] nevents = [] nentries = [] for iset in range(0, len(self.plotflow.plots)): histos.append(self.plotflow.plots[iset][index]) nevents.append(self.plotflow.nevents[iset][index]) nentries.append(self.plotflow.nentries[iset][index]) # Looping on each histogram for item in range(0, len(histos)): # Getting the number of entries integral = 0. nbinx = histos[item].GetNbinsX() + 2 for bin in range(0, nbinx): integral += histos[item].GetBinContent(bin) # Getting the number of events nevent = nevents[item] # Getting underflow and overflow - percentage uflow = histos[item].GetBinContent(0) oflow = histos[item].GetBinContent(nbinx) uflow_percent = 0 oflow_percent = 0 if integral != 0: uflow_percent = uflow * 100 / integral oflow_percent = oflow * 100 / integral # mean value + error mean = histos[item].GetMean(1) mean_error = histos[item].GetMeanError(1) # root mean square + error rms = histos[item].GetRMS() rms_error = histos[item].GetRMSError() # writing the table report.NewCell() text.Reset() text.Add(self.main.datasets[item].name) report.WriteText(text) report.NewCell() # Nentries text.Reset() text.Add(Layout.DisplayXsection(integral, 0)) report.WriteText(text) report.NewCell() # Nentries / Nevents text.Reset() if nevents[item] != 0.: text.Add( str( Layout.Round_to_Ndigits( float(nentries[item]) / float(nevents[item]), 3))) else: text.Add("0.") report.WriteText(text) report.NewCell() # Mean value text.Reset() text.Add(str(Layout.Round_to_Ndigits( mean, 6))) #+"(+/- "+str(Layout.Round_to_Ndigits(mean_error,3))+")") report.WriteText(text) report.NewCell() text.Reset() text.Add(str(Layout.Round_to_Ndigits( rms, 4))) #+"(+/- "+str(Layout.Round_to_Ndigits(rms_error,3))+")") report.WriteText(text) if uflow_percent + oflow_percent <= 5: report.NewCell(ColorType.GREEN) if uflow_percent + oflow_percent > 5 and uflow_percent + oflow_percent < 15: report.NewCell(ColorType.ORANGE) if uflow_percent + oflow_percent > 15: report.NewCell(ColorType.RED) text.Reset() text.Add(str(Layout.Round_to_Ndigits(uflow_percent, 4))) report.WriteText(text) if uflow_percent + oflow_percent <= 5: report.NewCell(ColorType.GREEN) if uflow_percent + oflow_percent > 5 and uflow_percent + oflow_percent < 15: report.NewCell(ColorType.ORANGE) if uflow_percent + oflow_percent > 15: report.NewCell(ColorType.RED) text.Reset() text.Add(str(Layout.Round_to_Ndigits(oflow_percent, 4))) report.WriteText(text) report.NewLine() text.Reset() text.Add("Histogram number " + str(index + 1) + " - Statistics") report.EndTable(text) def GenerateReport(self, history): # Defining report writing if self.mode is ReportFormatType.HTML: report = HTMLReportWriter(self.output_path + "/index.html") elif self.mode is ReportFormatType.LATEX: report = LATEXReportWriter(self.output_path + "/main.tex", False) else: report = LATEXReportWriter(self.output_path + "/main.tex", True) # Opening if not report.Open(): return False # Create text text = TextReport() # Header report.WriteHeader() report.WriteTitle('MadAnalysis 5 report') report.WriteSpacor() # History of commands report.WriteSubTitle('Command history') text.Reset() text.SetFont(FontType.TT) for item in history: text.Add('ma5>' + item + '\n') report.WriteText(text) # Configuration report.WriteSubTitle('Configuration') # Integrated luminosity text.Reset() text.Add('MadAnalysis version ' + self.main.version + \ ' (' + self.main.date + ').\n') report.WriteText(text) # Integrated luminosity text.Reset() # Normalization if self.main.normalize == NormalizeType.LUMI or \ self.main.normalize == NormalizeType.LUMI_WEIGHT: text.Add('Histograms correspond to an integrated luminosity of ') text.SetColor(ColorType.BLUE) text.Add(str(self.main.lumi)) text.SetColor(ColorType.BLUE) text.Add(' fb') text.SetScript(ScriptType.SUP) text.Add('-1') text.SetScript(ScriptType.none) text.Add('.\n') elif self.main.normalize == NormalizeType.NONE: text.Add('Histograms are not scaled.\n') report.WriteText(text) # Datasets report.WriteSubTitle('Datasets used') for ind in range(0, len(self.main.datasets)): datatype = "signal" if self.main.datasets[ind].background: datatype = "background" report.WriteSubSubTitle( self.main.datasets[ind].name + \ ' (' + datatype + ')' ) self.WriteDatasetTable(self.files[ind],report,self.main.datasets[ind].xsection,\ self.main.datasets[ind].weight) # Plots display report.WriteSubTitle('Histograms / Cuts') # Plots ihisto = 0 icut = 0 for ind in range(0, len(self.main.selection)): if self.main.selection[ind].__class__.__name__ == "Histogram": report.WriteSubSubTitle("Histogram number " + str(ihisto + 1)) if self.main.selection[ind].observable.name not in [ 'NPID', 'NAPID' ]: self.WriteStatisticsTable(ihisto, report) text.Reset() text.Add("Histogram number " + str(ihisto + 1)) report.WriteFigure(text,self.output_path +"/"+ 'selection_'+str(ihisto),\ 1.0) ihisto += 1 if self.main.selection[ind].__class__.__name__ == "Cut": report.WriteSubSubTitle("Cut number " + str(icut + 1)) text.Reset() text.Add(self.main.selection[ind].GetStringDisplay() + '\n') report.WriteText(text) self.WriteEfficiencyTable(icut, report) icut += 1 # Final table if self.main.selection.Ncuts != 0: report.WriteSubTitle('Signal and Background comparison') self.WriteFinalTable(report) # Foot report.WriteFoot() # Closing report.Close() return True def Close(self): for item in self.files: item.Close() @staticmethod def CheckLatexLog(file): if not os.path.isfile(file): return False for line in file: if line.startswith('!'): return False return True def CompileReport(self): # ---- LATEX MODE ---- if self.mode == ReportFormatType.LATEX: # Launching latex and producing DVI file os.system( 'cd ' + self.output_path + '; latex main.tex -interaction=nonstopmode > latex.log 2>&1') name = os.path.normpath(self.output_path + '/main.dvi') if not os.path.isfile(name): logging.error('DVI file cannot be produced') logging.error('Please have a look to the log file ' + self.output_path + '/latex.log') return False # Checking latex log : are there errors if not Layout.CheckLatexLog(self.output_path + '/latex.log'): logging.error('some errors occured during LATEX compilation') logging.error( 'for more details, have a look to the log file : ' + self.output_path + '/latex.log') return False # Converting DVI file to PDF file os.system('cd ' + self.output_path + '; dvipdf main.dvi > dvipdf.log 2>&1') name = os.path.normpath(self.output_path + '/main.pdf') # Checking PDF file presence if not os.path.isfile(name): logging.error('PDF file cannot be produced') logging.error('Please have a look to the log file ' + self.output_path + '/dvipdf.log') return False # ---- PDFLATEX MODE ---- elif self.mode == ReportFormatType.PDFLATEX: # Launching latex and producing PDF file os.system( 'cd ' + self.output_path + '; pdflatex -interaction=nonstopmode main.tex > latex.log 2>&1' ) # Checking latex log : are there errors if not Layout.CheckLatexLog(self.output_path + '/latex.log'): logging.error('some errors occured during LATEX compilation') logging.error( 'for more details, have a look to the log file : ' + self.output_path + '/latex.log') return False # Checking PDF file presence name = os.path.normpath(self.output_path + '/main.pdf') if not os.path.isfile(name): logging.error('PDF file cannot be produced') logging.error('Please have a look to the log file ' + self.output_path + '/latex2.log') return False