#! /usr/bin/python ################# PlotMyNicePlots.py ############################ # # # Author: Felicia Volle # # Date: 21/06/2025 # # Description: For Joseph's Dark Photon internship # # Usage: python PlotMyNicePlots.py -f /path/to/my/file.root # # -t Rho_Tuple_Prompt/DecayTree # # # ##################################################################### ################# IMPORT ############################################ import os, sys from ROOT import TFile, TLatex, TH1F, TPaveText, TCanvas, TLegend, gROOT, gStyle, kBlue, kOrange, TMath gROOT.SetBatch(True) import argparse ################# VARIABLES AND DICTIONNARIES ####################### # Example selection requirement, to be adapted for Dark Photons VetoDict = {'PhiVeto':'TMath::Abs(Phi_M -1020) > 12', } ################# FUNCTION DEFINITION ############################### def get_args(): parser = argparse.ArgumentParser(description='Plotting Options') parser.add_argument('-f', '--filename', type=str, help='''What is the name of the file ?''', required=True) parser.add_argument('-t', '--tree', type=str, help='''What is the name of the tree ?''', required=True) parser.add_argument('-p', '--path', type=str, help='''What is the path to VetoPlots ?''', required=True) parser.add_argument('-a', '--angle', type=str, help='''Plot which angle ?''', required=True) parser.add_argument('-s', '--selectionFile', type=str, help='''Where is selection file ?''', required=False, default='1==1') return parser.parse_args() def getCuts(cutsFile): """ Read cuts from a .txt file and return a string Cuts on different lines in the file are concatenated with an && string. :cutsFile: type str Name of the file that contains the cuts """ with open(cutsFile, 'r') as f: cuts = f.readlines() cuts = [c.strip() for c in cuts] cut_string = " && ".join(cuts) return cut_string def get_correctExpression(x): # TLatex label has better quality, but no #ell, # therefore use TMathText for cosTheta_lepton AngleDict={'Phi' : ['Phi', '#phi', 40, -TMath.Pi(), TMath.Pi()], 'cosTl':['cosTheta_lepton', '\\cos(\\theta_{\\ell})', 40, -1, 1], 'cosTp':['cosTheta_Lst', 'cos(#theta_{p})', 40, -1, 1]} return AngleDict[x] def get_correctExpression_UCAS(x): # TLatex label has better quality, but no #ell, # therefore use TMathText for cosTheta_lepton AngleDict={'Phi' : ['phi', '#phi', 40, -TMath.Pi(), TMath.Pi()], 'cosTl':['cos_theta_l', '\\cos(\\theta_{\\ell})', 40, -1, 1], 'cosTp':['cos_theta_Lz', 'cos(#theta_{p})', 40, -1, 1]} return AngleDict[x] def drawAngles( a, inputTree, TriggerSel, year, name_of_bin, Path, pK, NicePK, nice_bin_name, qsq_bin_min, qsq_bin_max, mpKMin, mpKMax, GeV2ToMeV2, L1520): cName = TCanvas('c', 'c', 0, 0, 600, 450) xName, xAxis, xBins, xmin, xmax = get_correctExpression(a) x2Name, x2Axis, x2Bins, x2min, x2max = get_correctExpression_UCAS(a) BinWidth = (xmax - xmin)/xBins yAxis = 'Normalized entries / (%.2f)' % (BinWidth) qsq = "Jpsi_M*Jpsi_M" q2_cut = "({} >= {} && {} <= {})".format(qsq, qsq_bin_min*GeV2ToMeV2, qsq, qsq_bin_max*GeV2ToMeV2) MCperBin = 'B_BKGCAT <= 10 && '+q2_cut OnlyTrigger = MCperBin + " && " + TriggerSel Sel = OnlyTrigger # Apply JPsi Veto in rare mode if (name_of_bin != 'JPsi' and name_of_bin != 'Psi2S'): Sel = OnlyTrigger+' && TMath::Abs(LtoK_M - 3097)>35' pK, NicePK = '', '' if L1520 == True: Lst_Cut = f"(X_M >= {mpKMin} && X_M <= {mpKMax})" Sel = Sel + " && " + Lst_Cut h1 = TH1F("h1", "Angle def Orsay", xBins, xmin, xmax) inputTree.Project("h1", xName, "( "+Sel+")*PIDweight") h2 = TH1F("h2", "Angle def UCAS", xBins, xmin, xmax) inputTree.Project("h2", x2Name, "( "+Sel+")*PIDweight") # Draw Normalized h1.Scale(1.0/h1.Integral()) h2.Scale(1.0/h2.Integral()) h1.GetXaxis().SetTitle( xAxis ) h1.GetYaxis().SetTitle( yAxis ) h1.SetTitle("") maxi = h1.GetBinContent(h1.GetMaximumBin()) if h2.GetBinContent(h2.GetMaximumBin()) > maxi : maxi = h2.GetBinContent(h2.GetMaximumBin()) mini = h1.GetBinContent(h1.GetMinimumBin()) if h2.GetBinContent(h2.GetMinimumBin()) < mini : mini = h2.GetBinContent(h2.GetMinimumBin()) h1.SetMaximum(maxi+(maxi-mini)*0.5) h1.SetMinimum(mini-(maxi-mini)*0.7) h1.SetLineColor(kOrange-3) h1.SetMarkerColor(kOrange-3) h2.SetLineColor(kBlue+2) h2.SetMarkerColor(kBlue+2) #h1.Draw("AXIS") h1.DrawCopy("L") h2.DrawCopy("L SAME") # LHCb label lhcbName = TPaveText(gStyle.GetPadLeftMargin() + 0.05, 0.87 - gStyle.GetPadTopMargin(), gStyle.GetPadLeftMargin() + 0.20, 0.95 - gStyle.GetPadTopMargin(), "BRNDC") lhcbName.AddText("LHCb unofficial") lhcbName.SetFillColor(0) lhcbName.SetTextAlign(12) lhcbName.SetTextSize(0.06) lhcbName.SetBorderSize(0) lhcbName.Draw() lhcbText = TLatex() lhcbText.SetTextFont(132) lhcbText.SetTextSize(0.06) lhcbText.SetTextAlign(12) lhcbText.DrawLatexNDC(0.6, 0.91-gStyle.GetPadTopMargin(), nice_bin_name+NicePK) #0.85 cName.Update() legend = TLegend(0.55, 0.17, 0.92, 0.35) legend.AddEntry(h1, "Angle def Orsay", "lep") legend.AddEntry(h2, "Angle def UCAS", "lep") legend.Draw() cName.Update() if not os.path.isdir(Path+'/VetoPlots/AngleComparison'): os.mkdir(Path+'/VetoPlots/AngleComparison') PlotName = Path+'/VetoPlots/AngleComparison/'+a+'_'+year+'_'+pK+'_'+name_of_bin+'.png' cName.SaveAs(PlotName) del h1 del h2 cName.Close() return PlotName def getVariableList(a): aName = get_correctExpression(a)[0] uName = get_correctExpression_UCAS(a)[0] l = ['X_M', 'Jpsi_M', 'B_M'] l.append(aName) l.append(uName) return l ################# MAIN ############################################## if __name__ == '__main__': args = get_args() fName = args.filename tree = args.tree path = args.path a = args.angle sF = args.selectionFile year = fName.split("_")[-2] Scale = 1 sys.path.insert(1, p) #sys.path.append(path) from setup import q2list, q2Dict, getMCcutstring, mpKMax, mpKMin, GeV2ToMeV2 iF = TFile(fName, "READ") iT = iF.Get(tree) BranchList = getVariableList(a) for activeBranchName in BranchList: iT.SetBranchStatus(activeBranchName, 1) gROOT.LoadMacro(path+"/lhcbStyle.C") # Trigger and loose PID selection specified in FinalSelection.txt TrigSel = "1==1" if sF != 'FinalSelection': TrigSel = getCuts(path+sF) print(" <<< START PLOTTING ANGLE {} >>> ".format(a)) print(" <<< TRIGGER SEL = {} >>> ".format(TrigSel)) for name_of_bin in q2list: #loop over the bins print ('----------------------------------------------') qsq_bin_min, qsq_bin_max, nice_bin_name = q2Dict[name_of_bin] print (f'---- qsq_bin_min {qsq_bin_min} GeV2') print (f'---- qsq_bin_max {qsq_bin_max} GeV2') tot_cut, pK, NicePK = getMCcutstring(name_of_bin, qsq_bin_min, qsq_bin_max, L1520=False, Truth=False, PID=True) # Once around L1520 #c2 = drawAngles(a, iT, TrigSel, year, name_of_bin, path, pK, NicePK, nice_bin_name, qsq_bin_min, qsq_bin_max, mpKMin, mpKMax, GeV2ToMeV2, L1520=True) # Once all pK region c1 = drawAngles(a, iT, TrigSel, year, name_of_bin, path, pK, NicePK, nice_bin_name, qsq_bin_min, qsq_bin_max, mpKMin, mpKMax, GeV2ToMeV2, L1520=False) iF.Close() #EOF