ATLAS Offline Software
Loading...
Searching...
No Matches
yearwise_luminosity.py
Go to the documentation of this file.
1#!/usr/bin/env python
2# Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
3
4"""
5Plot comparisons of Zee/Zmumu and Z/ATLAS over entire data-periods.
6This can be done as a function of time or pileup
7"""
8
9import numpy as np
10import ROOT as R
11import python_tools as pt
12import math
13from array import array
14import argparse
15import csv
16
17parser = argparse.ArgumentParser()
18parser.add_argument('--year', type=str, help='15,16,17,18,22,23,24,25 or run3 or year1_year2_...')
19parser.add_argument('--channel', type=str, help='Zee or Zmumu')
20parser.add_argument('--comp', action='store_true', help='Compare Zee and Zmumu?')
21parser.add_argument('--absolute', action='store_true', help='Compare absolute luminosity')
22parser.add_argument('--indir', type=str, help='Input directory for CSV files')
23parser.add_argument('--outdir', type=str, help='Output directory for plots')
24parser.add_argument('--outcsv', action='store_true', help='Create short CSV with plot content')
25
26args = parser.parse_args()
27year = args.year
28channel = args.channel
29absolute = args.absolute
30indir = args.indir
31outdir = args.outdir
32outcsv = args.outcsv
33
34# Do all of the ugly plot stlying here
35yval = 0.85
36xval = 0.2
37if args.absolute: ymin, ymax = 0.91, 1.09
38else: ymin, ymax = 0.93, 1.07
39
40if year == "run3":
41 years = ["22", "23", "24", "25"]
42 out_tag = "run3"
43 time_format = "%m/%y"
44 xtitle = 'Month / Year'
45 date_tag = "Run 3,#kern[-0.5]{ }#sqrt{s} = 13.6 TeV"
46 labelsize = 44
47 norm_type = "Run3"
48 multiyear = True
49elif len(year.split("_")) > 1:
50 years = year.split("_")
51 out_tag = "data"+year
52 time_format = "%m/%y"
53 xtitle = 'Month / Year'
54 date_tag = "Run 3,#kern[-0.5]{ }#sqrt{s} = 13.6 TeV"
55 labelsize = 44
56 norm_type = "Run3"
57 multiyear = True
58else:
59 years = [year]
60 out_tag = "data"+year
61 time_format = "%d/%m"
62 xtitle = 'Date in 20' + year
63 date_tag = "Data 20" + year + ",#kern[-0.5]{ }#sqrt{s} = 13.6 TeV"
64 norm_type = "year"
65 labelsize = 22
66 multiyear = False
67
68def main():
69 if args.comp:
71 else:
72 zcounting_vs_atlas(channel, years)
73
75
76 print("Lumi Channel Comparison vs Time for years: ", years)
77
78 dict_zlumi = {}
79 for year in years:
80
81 grl = pt.get_grl(year)
82
83 for channel in ["Zee", "Zmumu"]:
84
85 for run in grl:
86 livetime, zlumi, zerr, olumi, timestamp, dfz_small = pt.get_dfz(args.indir, year, run, channel)
87 # Cut out short runs
88 if livetime < pt.runlivetimecut:
89 if livetime >= 0.: print(f"Skip Run {run} because of live time {livetime/60:.1f} min")
90 continue
91
92 dict_zlumi[channel, run] = (zlumi, zerr, timestamp)
93
94 vec_times = array('d')
95 vec_ratio = array('d')
96 vec_ratio_err = array('d')
97 keys = [key[1] for key in dict_zlumi if "Zee" in key]
98
99 # If plotting vs. date simply calculate integrated lumi per run and fill array
100 for key in sorted(keys):
101 try:
102 ratio = dict_zlumi["Zee", key][0]/dict_zlumi["Zmumu", key][0]
103 error = ratio * math.sqrt( pow(dict_zlumi["Zee", key][1]/dict_zlumi["Zee", key][0], 2) + pow(dict_zlumi["Zmumu", key][1]/dict_zlumi["Zmumu", key][0], 2) )
104 date = dict_zlumi["Zee", key][2]
105
106 if ratio < ymin or ratio > ymax:
107 print("WARNING: Run", key, "has Zee/Zmumu ratio %.2f +- %.2f, outside of y-axis range" % (ratio, error))
108 else:
109 vec_times.append(date)
110 vec_ratio.append(ratio)
111 vec_ratio_err.append(error)
112 except KeyError:
113 print("Cannot do ratio for", key)
114
115 tg = R.TGraphErrors(len(vec_times), vec_times, vec_ratio, R.nullptr, vec_ratio_err)
116 leg = R.TLegend(0.645, 0.72, 0.805, 0.91)
117 leg.SetFillStyle(0)
118
119 # Depending if we're plotting over whole Run-3, change canvas size
120 if multiyear:
121 c1 = R.TCanvas("c1", "c1", 2000, 1000)
122 else:
123 c1 = R.TCanvas()
124
125 tg.Draw('ap')
126 tg.GetYaxis().SetTitle(pt.Leemumuratiolabel)
127 tg.Fit('pol0', '0q')
128 tg.GetFunction('pol0').SetLineColor(R.kRed)
129
130 mean = tg.GetFunction('pol0').GetParameter(0)
131
132 # Plot 68% percentile band
133 stdev = np.percentile(abs(vec_ratio - np.median(vec_ratio)), 68)
134 line1 = pt.make_bands(vec_times, stdev, mean)
135 line1.Draw("same 3")
136 tg.GetFunction('pol0').Draw("same l")
137 tg.Draw('same e0p')
138
139 print("Pol0 fit mean +- 68% percentile = ", round(mean,3), " +- ", round(stdev, 3))
140
141 leg.SetBorderSize(0)
142 leg.SetTextSize(0.05)
143 leg.AddEntry(tg, pt.Leemumuratiolabel, "ep")
144 leg.AddEntry(tg.GetFunction("pol0"), "Mean = %.3f" % mean, "l")
145 leg.AddEntry(line1, "68%% band (#pm %.3f)" % stdev, "f")
146 leg.Draw()
147
148 pt.drawAtlasLabel(xval, 0.88, "Internal")
149 pt.drawText(xval, 0.82, date_tag, size=labelsize)
150
151 R.gPad.Update()
152
153 tg.GetYaxis().SetRangeUser(ymin, ymax)
154 tg.GetXaxis().SetTitle(xtitle)
155 tg.GetXaxis().SetTimeDisplay(2)
156 tg.GetXaxis().SetNdivisions(9,R.kFALSE)
157 tg.GetXaxis().SetTimeFormat(time_format)
158 tg.GetXaxis().SetTimeOffset(0,"gmt")
159
160 if years == ["22", "23", "24", "25"]:
161 plot_title = "Ratio of Electron and Muon channel Z-counting Luminosities across Run 3"
162 else:
163 plot_title = "Ratio of Electron and Muon channel Z-counting Luminosities across 20" + years[0]
164
165 tg.SetTitle(plot_title)
166 c1.Update()
167 c1.SaveAs(outdir + "ZeeZmm_ratio_vs_time_"+out_tag+".pdf")
168
169
170def zcounting_vs_atlas(channel, years):
171 """
172 Plot normalised comparison of Z-counting luminosity to ATLAS luminosity.
173 This can be done as a function of time and pileup.
174 """
175
176 zstring = pt.plotlabel[channel]+" counting"
177 ytitle = "L_{"+pt.plotlabel[channel]+"}/L_{ATLAS}"
178 if args.absolute:
179 leg_entry = "L_{"+pt.plotlabel[channel]+"}"
180 else:
181 leg_entry = "L_{"+pt.plotlabel[channel]+"}^{"+norm_type+"-normalised}/L_{ATLAS}"
182
183 print("Channel", channel, "comparison to ATLAS vs time for years: ", years)
184
185 arr_date = []
186 arr_olumi = []
187 arr_zlumi = []
188 arr_zerr = []
189 run_num = []
190 fill_num = []
191
192 for year in years:
193
194 grl = pt.get_grl(year)
195
196 for run in grl:
197 livetime, zlumi, zerr, olumi, timestamp, dfz_small = pt.get_dfz(args.indir, year, run, channel)
198
199 # Cut out short runs
200 if livetime < pt.runlivetimecut:
201 if livetime >= 0.: print(f"Skip Run {run} because of live time {livetime/60:.1f} min")
202 continue
203
204 prelratio = zlumi/olumi
205 if prelratio < ymin or prelratio > ymax:
206 print("WARNING: Run", run, "has", channel, "/ATLAS ratio %.2f +- %.2f, outside of y-axis range" % (prelratio, zerr/olumi) )
207
208 # If plotting vs. date simply fill the arrays here
209 arr_date.append(timestamp)
210 arr_olumi.append(olumi)
211 arr_zlumi.append(zlumi)
212 arr_zerr.append(zerr)
213 run_num.append(run)
214 fill = dfz_small['FillNum'].median()
215 fill_num.append(int(fill))
216
217
218 # for ROOT plotting we need Python arrays
219 arr_date = array('d', arr_date)
220 # convert lists to numpy arrays
221 arr_olumi = np.array(arr_olumi)
222 arr_zlumi = np.array(arr_zlumi)
223 arr_zerr = np.array(arr_zerr)
224 total_lumi = arr_olumi.sum()/1000000
225 total_lumi_string = "Official DQ "
226 if year == "25": total_lumi_string = "Preliminary DQ "
227 total_lumi_string += str(round(total_lumi, 1)) + " fb^{-1}"
228
229 #-----------Normalisation------------
230
231 # Calculate and apply overall normalisation
232 if args.absolute:
233 normalisation = 1.0
234 else:
235 normalisation = np.sum(arr_zlumi) / np.sum(arr_olumi)
236
237 # do normalisation to period integral
238 arr_zlumi /= normalisation
239 arr_zerr /= normalisation
240
241 # calculate ratio to ATLAS preferred lumi
242 arr_zlumi_ratio = arr_zlumi/arr_olumi
243 arr_zerr_ratio = arr_zerr/arr_olumi
244
245#-----------Normalisation------------
246
247 tg = R.TGraphErrors(len(arr_date), arr_date, array('d',arr_zlumi_ratio), R.nullptr, array('d',arr_zerr_ratio))
248
249 # Depending if we're plotting over whole Run-3, change canvas size
250 if multiyear:
251 c1 = R.TCanvas("c1", "c1", 2000, 1000)
252 leg = R.TLegend(0.5, 0.18, 0.65, 0.4)
253 else:
254 c1 = R.TCanvas()
255 leg = R.TLegend(0.6, 0.18, 0.75, 0.4)
256
257 tg.Draw('ap e0')
258 tg.GetYaxis().SetRangeUser(ymin, ymax)
259 if args.absolute:
260 plot_title = "Absolute L_{"+ zstring +"} to ATLAS across " + norm_type
261 else:
262 plot_title = "Normalised L_{"+ zstring +"} to ATLAS across " + norm_type
263
264 # Plot 68% percentile band
265 stdev = np.percentile(abs(arr_zlumi_ratio - np.median(arr_zlumi_ratio)), 68)
266 print("68% band =", stdev)
267 tg.Fit('pol0', '0q')
268 mean = tg.GetFunction('pol0').GetParameter(0)
269 print("const of pol0 fit", mean)
270 print("median", np.median(arr_zlumi_ratio))
271 print("mean", np.mean(arr_zlumi_ratio))
272
273 line1 = pt.make_bands(arr_date, stdev, np.median(arr_zlumi_ratio))
274 line1.Draw("same 3")
275 tg.Draw('same ep')
276
277 leg.SetFillStyle(0)
278 leg.SetBorderSize(0)
279 leg.SetTextSize(0.05)
280 leg.AddEntry(tg, leg_entry, "ep")
281 leg.AddEntry(line1, "68%% band (#pm %.3f)" % stdev, "f")
282 leg.Draw()
283
284 pt.drawAtlasLabel(xval, yval-0.47, "Internal")
285 pt.drawText(xval, yval-0.53, date_tag, size=labelsize)
286 pt.drawText(xval, yval-0.59, zstring, size=labelsize)
287 pt.drawText(xval, yval-0.65, "OflLumi-Run3-006", size=labelsize)
288 pt.drawText(xval, yval-0.04, total_lumi_string, size=labelsize)
289
290 pt.drawText(xval, 0.88, plot_title, size=labelsize)
291
292 tg.GetXaxis().SetTitle(xtitle)
293 tg.GetYaxis().SetTitle(ytitle)
294 tg.GetYaxis().SetRangeUser(ymin, ymax)
295 tg.GetXaxis().SetTimeDisplay(2)
296 tg.GetXaxis().SetNdivisions(9,R.kFALSE)
297 tg.GetXaxis().SetTimeFormat(time_format)
298 tg.GetXaxis().SetTimeOffset(0,"gmt")
299
300 c1.Update()
301 c1.Modified()
302
303 filename = outdir + channel + "ATLAS_ratio_vs_time_"+out_tag
304 if args.absolute:
305 c1.SaveAs(filename+"_abs.pdf")
306 else:
307 c1.SaveAs(filename+".pdf")
308
309 if outcsv:
310 if args.absolute:
311 csvfile = open(filename+"_abs.csv", 'w')
312 else:
313 csvfile = open(filename+".csv", 'w')
314 csvwriter = csv.writer(csvfile, delimiter=',')
315 csvwriter.writerow(['FillNum','RunNum','Time','OffLumi','ZLumi','ZLumiErr','OffZlumi','OffZlumiErr'])
316 for i in range(len(run_num)):
317 csvwriter.writerow([fill_num[i], run_num[i], arr_date[i], arr_olumi[i], arr_zlumi[i], arr_zerr[i], arr_zlumi_ratio[i], arr_zerr_ratio[i]])
318 csvfile.close()
319
320
321
322if __name__ == "__main__":
323 pt.setAtlasStyle()
324 R.gROOT.SetBatch(R.kTRUE)
325 main()
void print(char *figname, TCanvas *c1)
constexpr int pow(int base, int exp) noexcept
STL class.