Belle II Software development
ecms.py
1# disable doxygen check for this file
2# @cond
3
4
11
12"""
13Validation of the Invariant Collision Energy calibration
14"""
15
16
17from prompt import ValidationSettings
18
19import ROOT
20import sys
21import subprocess
22import json
23
24import numpy as np
25import pandas as pd
26import matplotlib.pyplot as plt
27from matplotlib import rcParams
28
29import re
30import os
31from glob import glob
32
33from datetime import datetime
34
35from collections import OrderedDict
36from shutil import copyfile, rmtree
37
38
39
40settings = ValidationSettings(name='eCMS Calibrations',
41 description=__doc__,
42 download_files=['stdout'],
43 expert_config={})
44
45
46def setPlotRange(df, tag):
47 """
48 Function which adjusts the y-axis range of the plot according to tag
49 """
50
51 if tag == '4S':
52 df4S = df[df['Ecms'] > 10560]['Ecms']
53 if len(df4S) > 0:
54 yMin = df4S.min()
55 plt.ylim(bottom=yMin-3)
56
57 dfHigh = df[df['Ecms'] < 10600]['Ecms']
58 if len(dfHigh) > 0:
59 yMax = dfHigh.max()
60 plt.ylim(top=yMax+3)
61
62 elif tag == 'Off':
63 dfOff = df[df['Ecms'] < 10560]['Ecms']
64 if len(dfOff) > 0:
65 yMax = dfOff.max()
66 plt.ylim(top=yMax+3)
67
68
69def toJST(times):
70 """
71 Converts time from UTC to the JST
72 """
73 return np.vectorize(lambda t: datetime.utcfromtimestamp((t + 9) * 3600))(times)
74
75
76def plotSplitLines(dfC):
77 """
78 Plot vertical lines in places where run energy type changes
79 """
80 indx = np.where(np.diff(dfC['pull'] == 0))[0]
81 for i in indx:
82 tt1 = toJST(dfC['t2'].loc[i]).item()
83 tt2 = toJST(dfC['t1'].loc[i+1]).item()
84 tt = tt1 + (tt2 - tt1) / 2
85 plt.axvline(x=tt, color='g', linestyle='--')
86
87
88def get_Ecms_values(path):
89 """
90 Load the values of the Ecms properties from the payloads into the list
91 """
92
93 runDict = {}
94 with open(path + '/database.txt') as fDB:
95 for ll in fDB:
96 ll = ll.strip()
97 ll = ll.split()
98
99 # deriving file name
100 fN = ll[0].replace('/', '_') + '_rev_' + ll[1] + '.root'
101
102 r = ll[2].split(',')
103 runDict[fN] = ((int(r[0]), int(r[1])), (int(r[2]), int(r[3])))
104
105 arr = []
106
107 fList = glob(path + '/*.root')
108 for fName in fList:
109 bName = os.path.basename(fName)
110 runExp = runDict[bName]
111
112 f = ROOT.TFile.Open(fName)
113 ecmsObj = f.Get("CollisionInvariantMass")
114 assert(ecmsObj.ClassName() == "Belle2::CollisionInvariantMass")
115
116 eCMS = ecmsObj.getMass()
117 eCMSe = ecmsObj.getMassError()
118 eCMSs = ecmsObj.getMassSpread()
119
120 arr.append((runExp, (eCMS, eCMSe, eCMSs)))
121
122 f.Close()
123
124 arr = sorted(arr, key=lambda x: x[0])
125
126 return arr
127
128
129class Plotter():
130 def __init__(self, location):
131 """
132 Data are loaded from text files to pandas
133 """
134
135 # read B-only calibration
136 self.dfB = pd.read_csv(f'{location}/BonlyEcmsCalib.txt', delim_whitespace=True)
137
138 # read combined calibration
139 self.dfC = pd.read_csv(f'{location}/finalEcmsCalib.txt', delim_whitespace=True)
140
141 # read mumu calibration
142 self.dfM = pd.read_csv(f'{location}/mumuEcalib.txt', delim_whitespace=True)
143
144 # add the state
145 dfM = self.dfM
146 dfC = self.dfC
147 state = -1
148 dfM['type'] = 1
149 dfC['type'] = 1
150 for i in range(len(dfM)):
151 if (dfM['id'][i]) == 0:
152 state *= -1
153
154 dfM.at[i, 'type'] = state
155 dfC.at[i, 'type'] = state
156
157 @staticmethod
158 def plotLine(df, var, color, label):
159 """
160 Plot single line with error band
161 """
162
163 varUnc = var + 'Unc'
164 nan = np.full(len(df), np.nan)
165 avg = (df['t1']+df['t2']).to_numpy()/2
166 times = np.c_[df[['t1', 't2']].to_numpy(), avg].ravel()
167 eCMS = np.c_[df[[var, var]].to_numpy(), nan].ravel()
168 eCMSu = np.c_[df[[varUnc, varUnc]].to_numpy(), nan].ravel()
169
170 timesg = np.c_[df['t1'].to_numpy(), avg, df['t2'].to_numpy()].ravel()
171 eCMSg = np.c_[df[var].to_numpy(), nan, df[var].to_numpy()].ravel()
172
173 times = toJST(times)
174 timesg = toJST(timesg)
175
176 plt.fill_between(times, eCMS-eCMSu, eCMS+eCMSu, alpha=0.2, color=color)
177 plt.plot(times, eCMS, linewidth=2, color=color, label=label)
178 plt.plot(timesg, eCMSg, linewidth=2, color=color, alpha=0.35)
179
180 def plotEcmsComparison(self, limits=None, tag=''):
181 """
182 Plot Ecms obtained from combined, hadB and mumu method
183 """
184
185 Plotter.plotLine(self.dfB, 'Ecms', 'green', label='B decay method')
186 Plotter.plotLine(self.dfC, 'Ecms', 'red', label='Combined method')
187
188 d = np.nanmedian(self.dfC['Ecms']-self.dfM['Ecms'])
189
190 dfMc = self.dfM.copy()
191 dfMc['Ecms'] += d
192 Plotter.plotLine(dfMc, 'Ecms', 'blue', label=f'mumu method (+{round(d,1)} MeV)')
193
194 plotSplitLines(self.dfC)
195 setPlotRange(self.dfC, tag)
196
197 plt.xlabel('time')
198 plt.ylabel('Ecms [MeV]')
199
200 plt.legend()
201
202 loc = 'plots/allData'
203 if limits is not None:
204 plt.xlim(datetime.strptime(limits[0], '%Y-%m-%d'), datetime.strptime(limits[1], '%Y-%m-%d'))
205 loc = 'plots/' + limits[0] + 'to' + limits[1]
206
207 os.makedirs(loc, exist_ok=True)
208 plt.savefig(f'{loc}/EcmsComparison{tag}.png')
209 plt.clf()
210
211 def plotSpreadComparison(self, limits=None):
212 """
213 Plot Ecms spread estimated from the combined method and B-decay method
214 """
215
216 Plotter.plotLine(self.dfB, 'spread', 'green', label='B decay method')
217 Plotter.plotLine(self.dfC, 'spread', 'red', label='Combined method')
218
219 plotSplitLines(self.dfC)
220
221 plt.legend()
222
223 plt.xlabel('time')
224 plt.ylabel('spread [MeV]')
225
226 loc = 'plots/allData'
227 if limits is not None:
228 plt.xlim(datetime.strptime(limits[0], '%Y-%m-%d'), datetime.strptime(limits[1], '%Y-%m-%d'))
229 loc = 'plots/' + limits[0] + 'to' + limits[1]
230
231 os.makedirs(loc, exist_ok=True)
232 plt.savefig(f'{loc}/SpreadComparison.png')
233 plt.clf()
234
235 @staticmethod
236 def plotCurve(df, var, label, withBand=True, withCurve=True):
237 """
238 Plot curve with possible error band where large intervals are distinguished by color
239 """
240
241 def plotBands(df, var, color, withBand=True, withCurve=True):
242 varUnc = var + 'Unc'
243 nan = np.full(len(df), np.nan)
244 avg = (df['t1']+df['t2']).to_numpy()/2
245 times = np.c_[df[['t1', 't2']].to_numpy(), avg].ravel()
246 eCMS = np.c_[df[[var, var]].to_numpy(), nan].ravel()
247 times = toJST(times)
248
249 if withBand:
250 eCMSu = np.c_[df[[varUnc, varUnc]].to_numpy(), nan].ravel()
251 plt.fill_between(times, eCMS-eCMSu, eCMS+eCMSu, alpha=0.15, color=color)
252 if withCurve:
253 plt.plot(times, eCMS, linewidth=2, color=color)
254
255 nan = np.full(len(df), np.nan)
256 avg = (df['t1']+df['t2']).to_numpy()/2
257 timesg = np.c_[df['t1'].to_numpy(), avg, df['t2'].to_numpy()].ravel()
258 eCMSg = np.c_[df[var].to_numpy(), nan, df[var].to_numpy()].ravel()
259 timesg = toJST(timesg)
260
261 if withCurve:
262 plt.plot(timesg, eCMSg, linewidth=2, color='gray', alpha=0.35)
263
264 plotBands(df[df['type'] == 1], var, 'red', withBand, withCurve)
265 plotBands(df[df['type'] == -1], var, 'blue', withBand, withCurve)
266
267 def plotShift(self, limits=None):
268 """
269 Plot shift between Bhad and mumu method
270 """
271
272 Plotter.plotCurve(self.dfC, 'shift', label='Combined method')
273
274 plotSplitLines(self.dfC)
275
276 plt.xlabel('time')
277 plt.ylabel('shift [MeV]')
278
279 loc = 'plots/allData'
280 if limits is not None:
281 plt.xlim(datetime.strptime(limits[0], '%Y-%m-%d'), datetime.strptime(limits[1], '%Y-%m-%d'))
282 loc = 'plots/' + limits[0] + 'to' + limits[1]
283
284 os.makedirs(loc, exist_ok=True)
285 plt.savefig(f'{loc}/Shift.png')
286
287 plt.clf()
288
289 def plotEcms(self, limits=None, tag=''):
290 """
291 Plot Ecms of the combined fit, 'tag' can be '4S' means that y-axis is zoomed to 4S mass region, 'Off' to off-resonance
292 """
293
294 Plotter.plotCurve(self.dfC, 'Ecms', label='Combined method')
295
296 plotSplitLines(self.dfC)
297
298 plt.xlabel('time')
299 plt.ylabel('Ecms [MeV]')
300
301 setPlotRange(self.dfC, tag)
302
303 loc = 'plots/allData'
304 if limits is not None:
305 plt.xlim(datetime.strptime(limits[0], '%Y-%m-%d'), datetime.strptime(limits[1], '%Y-%m-%d'))
306 loc = 'plots/' + limits[0] + 'to' + limits[1]
307
308 os.makedirs(loc, exist_ok=True)
309 plt.savefig(f'{loc}/Ecms{tag}.png')
310 plt.clf()
311
312 def plotPulls(self, limits=None):
313 """
314 Plot pulls of the combined fit.
315 """
316
317 dfC = self.dfC.copy()
318 dfC['ref'] = 0
319 dfC['refUnc'] = 1
320 Plotter.plotCurve(dfC, 'pull', label='Combined method', withBand=False)
321 Plotter.plotCurve(dfC, 'ref', label='Combined method', withCurve=False)
322
323 plotSplitLines(self.dfC)
324
325 plt.ylim(-1.5, 1.5)
326 plt.xlabel('time')
327 plt.ylabel('pull')
328
329 loc = 'plots/allData'
330 if limits is not None:
331 plt.xlim(datetime.strptime(limits[0], '%Y-%m-%d'), datetime.strptime(limits[1], '%Y-%m-%d'))
332 loc = 'plots/' + limits[0] + 'to' + limits[1]
333
334 os.makedirs(loc, exist_ok=True)
335 plt.savefig(f'{loc}/Pull.png')
336 plt.clf()
337
338
339# Get the results from the combined calibration
340def read_Combined_data(outputDir):
341 """
342 It reads the calibration table from the text file produced by the CAF calibration.
343 This text file includes the results from the final calibration.
344 (combined or mumu-only)
345 """
346
347 arr = []
348 with open(outputDir + '/finalEcmsCalib.txt') as text_file:
349 for i, ll in enumerate(text_file):
350 if i == 0:
351 continue
352 ll = ll.strip().split()
353 arr.append(
354 ((float(
355 ll[0]), float(
356 ll[1])), (int(ll[2]), int(ll[3]), int(ll[4]), int(ll[5])), int(
357 ll[2+4]), (float(
358 ll[3+4]), float(
359 ll[4+4])), float(
360 ll[5+4]), (float(
361 ll[6+4]), float(
362 ll[7+4])), (float(
363 ll[8+4]), float(
364 ll[9+4]))))
365 return arr
366
367
368# Get the results from the combined calibration
369def read_Bonly_data(outputDir):
370 """
371 It reads the calibration table from the text file produced by the CAF calibration.
372 This text file includes the results from the B-only calibration.
373 """
374
375 arr = []
376 with open(outputDir + '/BonlyEcmsCalib.txt') as text_file:
377 for i, ll in enumerate(text_file):
378 if i == 0:
379 continue
380 ll = ll.strip().split()
381 arr.append(
382 ((float(
383 ll[0]), float(
384 ll[1])), (int(ll[2]), int(ll[3]), int(ll[4]), int(ll[5])),
385 (float(ll[2+4]), float(ll[3+4])), (float(ll[4+4]), float(ll[5+4]))))
386 return arr
387
388
389def read_mumu_data(outputDir):
390 """
391 It reads the calibration table from the text file produced by the CAF calibration.
392 This text file includes the results from the mumu-based calibration.
393 """
394
395 arr = []
396 with open(outputDir + '/mumuEcalib.txt') as text_file:
397 for i, ll in enumerate(text_file):
398 if i == 0:
399 continue
400 ll = ll.strip().split()
401 arr.append(
402 ((float(
403 ll[2+0]), float(
404 ll[2+1])), (int(ll[2+2]), int(ll[2+3]), int(ll[2+4]), int(ll[2+5])),
405 (float(ll[2+6]), float(ll[2+7])), (int(ll[0]), int(ll[1]))))
406 return arr
407
408
409# Create multi-page pdf file with the fit plots
410def create_hadB_fit_plots(outputDir, pdflatex):
411 """
412 Create multi-page pdf file with the fit plots for hadronic B decays (with mumu constraint).
413 The file is created using pdflatex
414 """
415
416 arr = read_Combined_data(outputDir)
417
418 dName = 'plotsHadB'
419
420 files = glob(outputDir+'/'+dName + '/*.pdf')
421 files = list(map(os.path.basename, files))
422
423 items = OrderedDict()
424
425 for f in files:
426 res = re.search('B[p0]_([0-9]*)_([0-9]*)\\.pdf', f)
427 t = int(res.group(1))
428 i = int(res.group(2))
429 if t not in items:
430 items[t] = 1
431 else:
432 items[t] = max(items[t], i + 1)
433
434 times = sorted(items)
435
436 header = """\\documentclass[aspectratio=169]{beamer}
437 \\usepackage{graphicx}
438
439 \\begin{document}
440 """
441
442 body = ""
443 for t in times:
444
445 i0 = None
446 for i0Temp in range(len(arr)):
447 if int(round(arr[i0Temp][0][0], 0)) == t:
448 i0 = i0Temp
449 break
450 assert(i0 is not None)
451
452 frac = 1. / (items[t] + 0.2)
453 if items[t] >= 6:
454 frac = 1. / (items[t] + 0.3)
455
456 body += '\\begin{frame}[t]\n'
457 shift, shiftE = str(round(arr[i0][5][0], 1)), str(round(arr[i0][5][1], 1))
458 spread, spreadE = str(round(arr[i0][6][0], 1)), str(round(arr[i0][6][1], 1))
459
460 body += '$E_\\mathrm{shift} = (' + shift + '\\pm' + shiftE + ')$~MeV \\hspace{2cm} \n'
461 body += '$E_\\mathrm{spread} = (' + spread + '\\pm' + spreadE + ')$~MeV \\\\ \\vspace{0.5cm}\n'
462 for iShort in range(items[t]):
463 i = i0 + iShort
464 tStart, tEnd = arr[i][0][0], arr[i][0][1]
465
466 exp1, run1, exp2, run2 = map(str, arr[i][1])
467
468 eCMS, eCMSe = str(round(arr[i][3][0], 1)), str(round(arr[i][3][1], 1))
469 pull = str(round(arr[i][4], 2))
470
471 t1 = datetime.utcfromtimestamp((tStart + 9) * 3600).strftime('%y-%m-%d %H:%M')
472 t2 = datetime.utcfromtimestamp((tEnd + 9) * 3600).strftime('%y-%m-%d %H:%M')
473
474 body += '\\begin{minipage}{' + str(frac) + '\\textwidth}\n'
475 body += '\\begin{center}\n'
476 body += '\\scriptsize ' + exp1 + ' '+run1 + ' \\\\\n'
477 body += '\\scriptsize ' + exp2 + ' '+run2 + ' \\\\\n'
478 body += '\\scriptsize ' + t1 + ' \\\\\n'
479 body += '\\scriptsize ' + t2 + ' \\\\\n'
480 body += '\\scriptsize ' + str(round(tEnd - tStart, 1)) + ' hours \\\\ \\vspace{0.3cm}\n'
481 body += '$(' + eCMS + '\\pm' + eCMSe + ')$~MeV \\\\\n'
482 body += '$\\mathrm{pull} = ' + pull + '$ \\\\\n'
483 body += '\\includegraphics[width=1.0\\textwidth]{' + outputDir + \
484 '/' + dName + '/B0_' + str(t) + '_' + str(iShort) + '.pdf}\\\\\n'
485 body += '\\includegraphics[width=1.0\\textwidth]{' + outputDir + \
486 '/' + dName + '/Bp_' + str(t) + '_' + str(iShort) + '.pdf}\n'
487 body += '\\end{center}\n'
488 body += '\\end{minipage}\n'
489
490 body += '\\end{frame}\n\n'
491
492 tail = '\n\\end{document}'
493
494 whole = header + body + tail
495
496 os.makedirs('tmp', exist_ok=True)
497
498 with open("tmp/hadBfits.tex", "w") as text_file:
499 text_file.write(whole)
500
501 subprocess.call(f'{pdflatex} tmp/hadBfits.tex', shell=True)
502
503 os.makedirs('plots', exist_ok=True)
504 copyfile('hadBfits.pdf', 'plots/hadBfits.pdf')
505
506 ext = ['aux', 'log', 'nav', 'out', 'pdf', 'snm', 'toc']
507 for e in ext:
508 if os.path.exists(f'hadBfits.{e}'):
509 os.remove(f'hadBfits.{e}')
510
511 rmtree('tmp')
512
513
514# Create multi-page pdf file with the fit plots
515def create_hadBonly_fit_plots(outputDir, pdflatex):
516 """
517 Create multi-page pdf file with the fit plots for hadronic B decays (without using mumu constraint).
518 The file is created using pdflatex
519 """
520
521 arr = read_Bonly_data(outputDir)
522
523 dName = 'plotsHadBonly'
524
525 files = glob(outputDir+'/'+dName + '/*.pdf')
526 files = list(map(os.path.basename, files))
527
528 items = set()
529
530 for f in files:
531 res = re.search('B[p0]Single_([0-9]*)\\.pdf', f)
532 t = int(res.group(1))
533 items.add(t) # [t] = 1
534
535 items = sorted(items)
536
537 header = """\\documentclass[aspectratio=169]{beamer}
538 \\usepackage{graphicx}
539
540 \\begin{document}
541 """
542
543 body = ""
544 for i, t in enumerate(items):
545
546 body += '\\begin{frame}[t]\n'
547
548 exp1, run1, exp2, run2 = map(str, arr[i][1])
549 eCMS, eCMSe = str(round(arr[i][2][0], 1)), str(round(arr[i][2][1], 1))
550 spread, spreadE = str(round(arr[i][3][0], 1)), str(round(arr[i][3][1], 1))
551
552 body += '$E_\\mathrm{cms} = (' + eCMS + '\\pm' + eCMSe + ')$~MeV \\\\\n'
553 body += '$E_\\mathrm{spread} = (' + spread + '\\pm' + spreadE + ')$~MeV \\\\ \\vspace{0.5cm}\n'
554
555 tStart, tEnd = arr[i][0][0], arr[i][0][1]
556
557 t1 = datetime.utcfromtimestamp((tStart + 9) * 3600).strftime('%y-%m-%d %H:%M')
558 t2 = datetime.utcfromtimestamp((tEnd + 9) * 3600).strftime('%y-%m-%d %H:%M')
559
560 body += '\\begin{minipage}{' + str(0.99) + '\\textwidth}\n'
561 body += '\\begin{center}\n'
562 body += '\\scriptsize ' + exp1 + ' ' + run1 + '\\hspace{1cm} ' + t1 + ' \\\\\n'
563 body += '\\scriptsize ' + exp2 + ' ' + run2 + '\\hspace{1cm} ' + t2 + ' \\\\\n'
564 body += '\\scriptsize ' + str(round(tEnd - tStart, 1)) + ' hours \\\\ \\vspace{0.3cm}\n'
565 body += '\\includegraphics[width=0.48\\textwidth]{'+outputDir + '/' + dName + '/B0Single_' + str(t) + '.pdf}\n'
566 body += '\\includegraphics[width=0.48\\textwidth]{'+outputDir + '/' + dName + '/BpSingle_' + str(t) + '.pdf}\n'
567 body += '\\end{center}\n'
568 body += '\\end{minipage}\n'
569
570 body += '\\end{frame}\n\n'
571
572 tail = '\n\\end{document}'
573
574 whole = header + body + tail
575
576 os.makedirs('tmp', exist_ok=True)
577
578 with open("tmp/hadBonlyFits.tex", "w") as text_file:
579 text_file.write(whole)
580
581 subprocess.call(f'{pdflatex} tmp/hadBonlyFits.tex', shell=True)
582
583 os.makedirs('plots', exist_ok=True)
584 copyfile('hadBonlyFits.pdf', 'plots/hadBonlyFits.pdf')
585
586 ext = ['aux', 'log', 'nav', 'out', 'pdf', 'snm', 'toc']
587 for e in ext:
588 if os.path.exists(f'hadBonlyFits.{e}'):
589 os.remove(f'hadBonlyFits.{e}')
590
591 if os.path.exists("tmp/hadBonlyFits.tex"):
592 os.remove("tmp/hadBonlyFits.tex")
593 rmtree('tmp')
594
595
596def create_mumu_fit_plots(outputDir, pdflatex):
597 """
598 Create multi-page pdf file with the fit plots for mumu method.
599 The file is created using pdflatex
600 """
601
602 arr = read_mumu_data(outputDir)
603
604 limits = []
605 for i, a in enumerate(arr):
606 if a[3][1] == a[3][0] - 1:
607 limits.append((i - a[3][1], a[3][0]))
608
609 dName = 'plotsMuMu'
610
611 files = glob(outputDir+'/'+dName + '/*.pdf')
612 files = list(map(os.path.basename, files))
613
614 items = set()
615
616 for f in files:
617 res = re.search('mumu_([0-9]*)\\.pdf', f)
618 t = int(res.group(1))
619 items.add(t)
620
621 items = sorted(items)
622
623 header = """\\documentclass[aspectratio=169]{beamer}
624 \\usepackage{graphicx}
625
626 \\begin{document}
627 """
628
629 body = ""
630 for k, n in limits:
631
632 frac = None
633 if n >= 11:
634 frac = 0.159
635 elif n >= 9:
636 frac = 0.193
637 elif n >= 7:
638 frac = 0.24
639 elif n >= 5:
640 frac = 0.3
641 elif n >= 3:
642 frac = 0.33
643 elif n >= 2:
644 frac = 0.48
645 elif n >= 1:
646 frac = 0.75
647
648 body += '\\begin{frame}[t]\n'
649
650 for i in range(k, k+n):
651
652 body += '\\begin{minipage}{' + str(frac) + '\\textwidth}\n'
653
654 exp1, run1, exp2, run2 = map(str, arr[i][1])
655 eCMS, eCMSe = str(round(arr[i][2][0], 1)), str(round(arr[i][2][1], 1))
656
657 tStart, tEnd = arr[i][0][0], arr[i][0][1]
658
659 t1 = datetime.utcfromtimestamp((tStart + 9) * 3600).strftime('%y-%m-%d %H:%M')
660 t2 = datetime.utcfromtimestamp((tEnd + 9) * 3600).strftime('%y-%m-%d %H:%M')
661
662 body += '\\begin{center}\n'
663 body += '\\tiny $E_\\mathrm{cms} = (' + eCMS + '\\pm' + eCMSe + ')$~MeV \\\\\n'
664 body += '\\tiny ' + exp1 + ' ' + run1 + '\\hspace{0.3cm} ' + t1 + ' \\\\\n'
665 body += '\\tiny ' + exp2 + ' ' + run2 + '\\hspace{0.3cm} ' + t2 + ' \\\\\n'
666 body += '\\tiny ' + str(round(tEnd - tStart, 1)) + ' hours \\vspace{0.3cm}\n'
667 body += '\\includegraphics[trim=0.3cm 0.0cm 1.3cm 0.7cm,clip=true,width=0.99\\textwidth]{' + \
668 outputDir + '/' + dName + '/mumu_' + str(items[i]) + '.pdf}\n'
669 body += '\\end{center}\n'
670 body += '\\end{minipage}\n'
671
672 body += '\\end{frame}\n\n'
673
674 tail = '\n\\end{document}'
675
676 whole = header + body + tail
677
678 os.makedirs('tmp', exist_ok=True)
679
680 with open("tmp/mumuFits.tex", "w") as text_file:
681 text_file.write(whole)
682
683 subprocess.call(f'{pdflatex} tmp/mumuFits.tex', shell=True)
684
685 os.makedirs('plots', exist_ok=True)
686 copyfile('mumuFits.pdf', 'plots/mumuFits.pdf')
687
688 ext = ['aux', 'log', 'nav', 'out', 'pdf', 'snm', 'toc']
689 for e in ext:
690 if os.path.exists(f'mumuFits.{e}'):
691 os.remove(f'mumuFits.{e}')
692
693 if os.path.exists("tmp/mumuFits.tex"):
694 os.remove("tmp/mumuFits.tex")
695 rmtree('tmp')
696
697
698def run_validation(job_path, input_data_path, requested_iov, expert_config):
699 """
700 Create validation plots related to the Ecms calibration
701 """
702
703 # get location of pdflatex
704 result = subprocess.run('locate pdflatex | grep "/pdflatex$"', stdout=subprocess.PIPE, shell=True)
705 pdflatex = result.stdout.strip().decode('utf-8')
706
707 # Expert config can contain the time ranges of the plots
708 if expert_config != '':
709 expert_config = json.loads(expert_config)
710
711 allLimits = [None]
712 if expert_config is not None and 'plotsRanges' in expert_config:
713 allLimits += expert_config['plotsRanges']
714
715 plt.figure(figsize=(18, 9))
716 rcParams['axes.formatter.useoffset'] = False
717
718 location = f'{job_path}/eCMS/0/algorithm_output'
719
720 plotter = Plotter(location)
721 # plot the results
722 for limits in allLimits:
723 plotter.plotEcmsComparison(limits, tag='4S')
724 plotter.plotEcmsComparison(limits, tag='Off')
725 plotter.plotEcmsComparison(limits)
726 plotter.plotSpreadComparison(limits)
727
728 plotter.plotEcms(limits, tag='4S')
729 plotter.plotEcms(limits, tag='Off')
730 plotter.plotEcms(limits, tag='')
731
732 plotter.plotShift(limits)
733 plotter.plotPulls(limits)
734
735 # create pdf with plots of fits
736 create_hadB_fit_plots(location, pdflatex)
737 create_hadBonly_fit_plots(location, pdflatex)
738 create_mumu_fit_plots(location, pdflatex)
739
740 # copy csv files to validation directory
741 copyfile(f'{location}/BonlyEcmsCalib.txt', 'plots/BonlyEcmsCalib.txt')
742 copyfile(f'{location}/finalEcmsCalib.txt', 'plots/finalEcmsCalib.txt')
743 copyfile(f'{location}/mumuEcalib.txt', 'plots/mumuEcalib.txt')
744
745
746if __name__ == "__main__":
747 run_validation(*sys.argv[1:])
748
749# @endcond