Skip to content

Commit 2d7d317

Browse files
authored
Merge pull request #49195 from zhiyuan011/zhhuang-plot
Add a plotting script for BS offline
2 parents 709eee2 + 4ab1412 commit 2d7d317

File tree

3 files changed

+235
-0
lines changed

3 files changed

+235
-0
lines changed
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
import matplotlib.pyplot as plt
2+
import numpy as np
3+
import json
4+
import subprocess
5+
import argparse
6+
import mplhep as hep
7+
import os
8+
from matplotlib.ticker import ScalarFormatter
9+
def getOrigRun(newRun, origX):
10+
for _newRun,run in origX:
11+
if newRun == _newRun:
12+
return run
13+
def unpack(i):
14+
"""unpack 64bit unsigned long long into 2 32bit unsigned int, return tuple (high,low)
15+
"""
16+
high=i>>32
17+
low=i&0xFFFFFFFF
18+
return(high,low)
19+
20+
def plot(json_files, labels, output_file, f_ymin, f_ymax):
21+
CMS_Text_Size = 20
22+
CMS_Text = "Preliminary"
23+
lumitext = "pp collisions (13.6TeV)"
24+
xlabel = "Lumi Section Number"
25+
26+
xlabel_font_size = 20
27+
ylabel_font_size = 20
28+
xlabel_location = "right"
29+
ylabel_location = "top"
30+
colors = ['blue', 'red', 'green','black', 'magenta', 'gray' , 'Cyan', 'darkorange', 'brown', 'purple']
31+
yvar = json_files[0].split("_")[1]
32+
ylabels = {"X":"X", "Y":"Y", "Z":"Z", "SigmaX":r"$\sigma_{X}$", "SigmaY":r"$\sigma_{Y}$", "SigmaZ":r"$\sigma_{Z}$", "dXdY":"dXdY", "dXdZ":"dXdZ", "dYdZ":"dYdZ"}
33+
ylabel = f"{ylabels[yvar]} [cm]"
34+
35+
36+
fig, ax = plt.subplots(figsize = (10, 10), dpi = 150)
37+
38+
hep.cms.text(f"{CMS_Text}", exp = 'CMS',fontsize = CMS_Text_Size,ax=ax)
39+
hep.cms.lumitext(f"{lumitext}", fontsize = CMS_Text_Size,ax=ax)
40+
41+
plt.style.use(hep.style.CMS)
42+
43+
glob_y = []
44+
glob_x = []
45+
x_store = []
46+
y_store = []
47+
y_err_store = []
48+
for i,json_file in enumerate(json_files):
49+
# Load data
50+
with open(json_file, 'r') as f:
51+
input_data = json.load(f)
52+
53+
# Extracting data
54+
x = [point['x'] for point in input_data['data']]
55+
y = [point['y'] for point in input_data['data']]
56+
glob_x += x
57+
glob_y += y
58+
y_err = [point['y_err'] for point in input_data['data']]
59+
60+
# Store for further processing
61+
x_store.append(x)
62+
y_store.append(y)
63+
y_err_store.append(y_err)
64+
65+
# find common x-axis
66+
minX = min(glob_x)
67+
maxX = max(glob_x)
68+
trueL = maxX-minX+1
69+
newX = []
70+
origX = []
71+
penalty = 0
72+
for run in range(minX,minX+trueL):
73+
x_not_in_all = True
74+
for x in x_store:
75+
if run in x: x_not_in_all = False
76+
if not x_not_in_all:
77+
newX.append(run-penalty)
78+
origX.append((run-penalty,run))
79+
else:
80+
penalty += 1
81+
82+
# restore original y-values
83+
new_y_store = []
84+
new_y_err_store = []
85+
for ix,x in enumerate(x_store):
86+
new_y_store.append([])
87+
new_y_err_store.append([])
88+
newXpruned = []
89+
for run in newX:
90+
origRun = getOrigRun(run,origX)
91+
if origRun in x:
92+
newXpruned.append(run)
93+
new_y_store[ix].append(y_store[ix][x.index(origRun)])
94+
new_y_err_store[ix].append(y_err_store[ix][x.index(origRun)])
95+
# Prep the data
96+
mean_y = np.mean(new_y_store[ix])
97+
std_dev_y = np.std(new_y_store[ix])
98+
_newXpruned = [x_val for x_val, y_val in zip(newXpruned, new_y_store[ix]) if abs(y_val - mean_y) <= 2 * std_dev_y]
99+
_newYpruned = [y_val for y_val in new_y_store[ix] if abs(y_val - mean_y) <= 2 * std_dev_y]
100+
_newYerrpruned = [y_err for y_val, y_err in zip(new_y_store[ix], new_y_err_store[ix]) if abs(y_val - mean_y) <= 2 * std_dev_y]
101+
newXpruned = _newXpruned
102+
new_y_store[ix] = _newYpruned
103+
new_y_err_store[ix] = _newYerrpruned
104+
105+
# Prep linear fit
106+
coef = np.polyfit(newXpruned,new_y_store[ix],10)
107+
print(coef)
108+
newXpruned_new = [unpack(i)[1] for i in newXpruned]
109+
110+
poly1d_fn = np.poly1d(coef)
111+
plt.tight_layout()
112+
fig.subplots_adjust(left=0.2, right=0.9, top=0.9, bottom=0.2)
113+
# Plot
114+
plt.errorbar(newXpruned_new, new_y_store[ix], yerr=new_y_err_store[ix], color = colors[ix], fmt='o', capsize=5, label=labels[ix], zorder=1)
115+
plt.plot(newXpruned_new, poly1d_fn(newXpruned), '--', color = colors[ix], label="lin. "+labels[ix])
116+
117+
118+
# Extract the annotations from the first file (assuming all files have similar annotations)
119+
title = input_data['annotations']['title']
120+
x_label = input_data['annotations']['x_label']
121+
y_label = input_data['annotations']['y_label']
122+
runNumber = unpack(newXpruned[0])[0]
123+
plt.xlim(min(newXpruned_new), max(newXpruned_new))
124+
if f_ymin is not None and f_ymax is None:
125+
plt.ylim(f_ymin, max(glob_y))
126+
elif f_ymin is None and f_ymax is not None:
127+
plt.ylim(min(glob_y), f_ymax)
128+
else:
129+
plt.ylim(min(glob_y), max(glob_y))
130+
131+
ax.set_xlabel(xlabel,fontsize = xlabel_font_size, loc=xlabel_location) #, labelpad=25
132+
133+
ax.set_ylabel(ylabel,fontsize = ylabel_font_size, loc=ylabel_location)
134+
135+
136+
leg = ax.legend(facecolor='white',edgecolor="black",frameon=True, title=f"Run {runNumber}",fontsize=8) #,ncol=2 , title="Expected"
137+
leg.get_title().set_fontsize(8)
138+
plt.savefig(output_file + ".pdf")
139+
140+
if __name__ == "__main__":
141+
parser = argparse.ArgumentParser(description="Plot data from multiple JSON files.")
142+
parser.add_argument('-o',"--outputName", type=str, help="name for the output plot image.", default="out")
143+
parser.add_argument("--db", type=str, help="db object", nargs='+', default="")
144+
parser.add_argument('-t',"--tags", type=str, help="tags for db object", nargs='+', default=None)
145+
parser.add_argument('-l',"--labels", type=str, help="labels for db object", nargs='+', default=None)
146+
parser.add_argument('-p',"--plugins", type=str, help="plugin", nargs='+', default=None)
147+
parser.add_argument('-tp',"--time_types", type=str, help="time_type", nargs='+', default=None)
148+
parser.add_argument('--unitTest', action="store_true", help="Enable if you want to do the unit test")
149+
parser.add_argument('--test', action="store_false", help="Enable if you want to do the test")
150+
parser.add_argument('--setRangeYMax', type=float, help='Enforce max for y-axis.', default=None)
151+
parser.add_argument('--setRangeYMin', type=float, help='Enforce min for y-axis.', default=None)
152+
args = parser.parse_args()
153+
154+
input_files = args.db
155+
plugins = args.plugins
156+
labels = args.labels
157+
test = args.test
158+
outputName = args.outputName
159+
if args.unitTest:
160+
cmd = "conddb_import -c sqlite_file:BeamSpotObjects_FTV_GT_DigiMorphing_HLT_v0.db -f frontier://FrontierProd/CMS_CONDITIONS -i BeamSpotObjects_FTV_GT_DigiMorphing_HLT_v0 -b 1706957442384272"
161+
os.system(cmd)
162+
input_files = ["BeamSpotObjects_FTV_GT_DigiMorphing_HLT_v0.db"]
163+
outputName = "BeamSpotObjects_FTV_GT_DigiMorphing_HLT_v0"
164+
if not (args.tags is None):
165+
tags = args.tags
166+
else:
167+
tags = []
168+
for file in input_files:
169+
tags.append(file.split('.')[0])
170+
171+
172+
if not (args.time_types is None):
173+
time_types = args.time_types
174+
else:
175+
time_types = []
176+
for i in range(len(input_files)):
177+
time_types.append("Lumi")
178+
if not (args.plugins is None):
179+
plugins = args.plugins
180+
else:
181+
plugins = []
182+
for i in range(len(input_files)):
183+
plugins.append("pluginBeamSpot_PayloadInspector")
184+
if not (args.labels is None):
185+
labels = args.labels
186+
else:
187+
labels = []
188+
for file in input_files:
189+
label = file.split('.')[0].split('FTV_')[-1]
190+
labels.append(label)
191+
cmd = "eval $(scram ru -sh)"
192+
os.system(cmd)
193+
iovs = []
194+
for file in input_files:
195+
name = file.split('.')[0]
196+
cmd = f"conddb --db {file} list {name}"
197+
output = subprocess.check_output([cmd], text=True, shell=True)
198+
lines = output.splitlines()
199+
count = 0
200+
for line in lines:
201+
if ("BeamSpotObjects" in line):
202+
if count == 0:
203+
start_iov = line.split('(')[1].split(')')[0]
204+
count += 1
205+
end_iov = line.split('(')[1].split(')')[0]
206+
iov = "{" + f'\"start_iov\": \"{start_iov}\", \"end_iov\": \"{end_iov}\"' + "}"
207+
iovs.append(iov)
208+
209+
ylabels = ["X", "Y", "Z", "SigmaX", "SigmaY", "SigmaZ", "dXdZ", "dYdZ"]
210+
211+
if ((len(input_files) == len(iovs)) and ((len(input_files) == len(tags))) and (len(input_files) == len(plugins)) and (len(input_files) == len(time_types))) and (len(input_files) == len(labels)):
212+
for ylabel in ylabels:
213+
for file, iv, tag, plugin, time_type in zip(input_files, iovs, tags, plugins, time_types):
214+
if (test):
215+
216+
cmd = f"getPayloadData.py --plugin {plugin} --plot plot_BeamSpot_History{ylabel} --tag {tag} --time_type {time_type} --iovs \'{iv}\' --db sqlite:{file} --test > toPlot_{ylabel}_{tag}.txt 2>&1"
217+
else:
218+
cmd = f"getPayloadData.py --plugin {plugin} --plot plot_BeamSpot_History{ylabel} --tag {tag} --time_type {time_type} --iovs \'{iv}\' --db sqlite:{file} > toPlot_{ylabel}_{tag}.txt 2>&1"
219+
os.system(cmd)
220+
else:
221+
raise ValueError(f"the number of input files: {len(input_files)}, tags: {len(tags)}, plugins: {len(plugins)}, time_types: {len(time_types)}, iovs: {len(iovs)}, labels: {len(labels)} are not the same")
222+
223+
224+
for ylabel in ylabels:
225+
files = []
226+
for tag in tags:
227+
files.append(f"toPlot_{ylabel}_{tag}.txt")
228+
plot(files, labels, f"plot{ylabel}_{outputName}", args.setRangeYMin, args.setRangeYMax)

Alignment/OfflineValidation/test/BuildFile.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
<test name="Miscellanea" command="testingScripts/test_unitMiscellanea.sh"/>
6363
<test name="ShortTrackValidation" command="testingScripts/test_unitShortTrackValidation.sh"/>
6464
<test name="SagittaBiasNtuplizer" command="testingScripts/test_unitSagittaBiasNtuplizer.sh"/>
65+
<test name="BeamSpotPlotting" command="testingScripts/test_unitBeamSpotPlotting.sh"/>
6566
<bin file="testanalyzeDiMuonBiases.cpp" name="testDiMuonBiasesPlotting">
6667
<flags PRE_TEST="SagittaBiasNtuplizer"/>
6768
<use name="rootmath"/>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#! /bin/bash
2+
function die { echo $1: status $2 ; exit $2; }
3+
4+
echo -e "\n\nTESTING BeamSpot Plotting Script"
5+
6+
python3 ${CMSSW_BASE}/src/Alignment/OfflineValidation/scripts/plotBeamSpotFromOfflineTag.py --unitTest || die "Failure running BeamSpot Plotting Script" $?

0 commit comments

Comments
 (0)