File indexing completed on 2026-06-01 07:51:25
0001 import numpy as np
0002 import matplotlib.pyplot as plt
0003 import mplhep as hep
0004 import uproot
0005 import pandas as pd
0006 from scipy.optimize import curve_fit
0007 from matplotlib.backends.backend_pdf import PdfPages
0008 import os
0009 import awkward as ak
0010
0011 plt.figure()
0012 hep.set_style(hep.style.CMS)
0013 hep.set_style("CMS")
0014
0015 def gaussian(x, amp, mean, sigma):
0016 return amp * np.exp( -(x - mean)**2 / (2*sigma**2) )
0017
0018 def rotateY(xdata, zdata, angle):
0019 s = np.sin(angle)
0020 c = np.cos(angle)
0021 rotatedz = c*zdata - s*xdata
0022 rotatedx = s*zdata + c*xdata
0023 return rotatedx, rotatedz
0024
0025 Energy = [0.005, 0.01, 0.05, 0.1, 0.5, 1.0]
0026
0027
0028 df = pd.DataFrame({})
0029 for eng in Energy:
0030 tree = uproot.open(f'sim_output/zdc_lyso/{os.environ["DETECTOR_CONFIG"]}_gamma_{eng}GeV_theta_0deg_thru_0.3deg.eicrecon.edm4eic.root')['events']
0031 ecal_reco_energy = ak.sum(tree['EcalFarForwardZDCClusters/EcalFarForwardZDCClusters.energy'].array(), axis=-1)
0032 hcal_reco_energy = ak.sum(tree['HcalFarForwardZDCClusters/HcalFarForwardZDCClusters.energy'].array(), axis=-1)
0033 ecal_rec_energy = ak.sum(tree['EcalFarForwardZDCRecHits/EcalFarForwardZDCRecHits.energy'].array(), axis=-1)
0034 hcal_rec_energy = ak.sum(tree['HcalFarForwardZDCRecHits/HcalFarForwardZDCRecHits.energy'].array(), axis=-1)
0035 ecal_reco_clusters = [len(row) if len(row)>=1 else 0 for row in tree['EcalFarForwardZDCClusters/EcalFarForwardZDCClusters.nhits'].array()]
0036 ecal_reco_nhits = [row[0] if len(row)>=1 else 0 for row in tree['EcalFarForwardZDCClusters/EcalFarForwardZDCClusters.nhits'].array()]
0037
0038 tree = uproot.open(f'sim_output/zdc_lyso/{os.environ["DETECTOR_CONFIG"]}_gamma_{eng}GeV_theta_0deg_thru_0.3deg.edm4hep.root')['events']
0039 ecal_sim_energy = ak.sum(tree['EcalFarForwardZDCHits/EcalFarForwardZDCHits.energy'].array(), axis=-1)
0040 hcal_sim_energy = ak.sum(tree['HcalFarForwardZDCHits/HcalFarForwardZDCHits.energy'].array(), axis=-1)
0041
0042 par_x = tree['MCParticles/MCParticles.momentum.x'].array()[:,2]
0043 par_y = tree['MCParticles/MCParticles.momentum.y'].array()[:,2]
0044 par_z = tree['MCParticles/MCParticles.momentum.z'].array()[:,2]
0045
0046 eng = int(eng*1000)
0047
0048 ecal_reco_energy = pd.DataFrame({f'ecal_reco_energy_{eng}': np.array(ecal_reco_energy, dtype=object)})
0049 hcal_reco_energy = pd.DataFrame({f'hcal_reco_energy_{eng}': np.array(hcal_reco_energy, dtype=object)})
0050 ecal_rec_energy = pd.DataFrame({f'ecal_rec_energy_{eng}': np.array(ecal_rec_energy, dtype=object)})
0051 hcal_rec_energy = pd.DataFrame({f'hcal_rec_energy_{eng}': np.array(hcal_rec_energy, dtype=object)})
0052 ecal_sim_energy = pd.DataFrame({f'ecal_sim_energy_{eng}': np.array(ecal_sim_energy, dtype=object)})
0053 hcal_sim_energy = pd.DataFrame({f'hcal_sim_energy_{eng}': np.array(hcal_sim_energy, dtype=object)})
0054 ecal_reco_nhits = pd.DataFrame({f'ecal_reco_nhits_{eng}': np.array(ecal_reco_nhits, dtype=object)})
0055 ecal_reco_clusters = pd.DataFrame({f'ecal_reco_clusters_{eng}': np.array(ecal_reco_clusters, dtype=object)})
0056 par_x = pd.DataFrame({f'par_x_{eng}': np.array(par_x.tolist(), dtype=object)})
0057 par_y = pd.DataFrame({f'par_y_{eng}': np.array(par_y.tolist(), dtype=object)})
0058 par_z = pd.DataFrame({f'par_z_{eng}': np.array(par_z.tolist(), dtype=object)})
0059
0060
0061 df = pd.concat([df,ecal_reco_energy,ecal_rec_energy,ecal_sim_energy,hcal_reco_energy,hcal_rec_energy,hcal_sim_energy,ecal_reco_clusters,ecal_reco_nhits,par_x,par_y,par_z],axis=1)
0062
0063
0064 mu = []
0065 sigma = []
0066 fig1, ax = plt.subplots(3,2,figsize=(20,10))
0067 fig1.suptitle('ZDC ECal Cluster Energy Reconstruction')
0068
0069 plt.tight_layout()
0070 for i in range(6):
0071 x = df[f'par_x_{eng}'].astype(float).to_numpy()
0072 y = df[f'par_y_{eng}'].astype(float).to_numpy()
0073 z = df[f'par_z_{eng}'].astype(float).to_numpy()
0074 x, z = rotateY(x,z, 0.025)
0075 theta = np.arccos(z/np.sqrt((x**2+y**2+z**2)))*1000
0076 condition = theta <= 3.5
0077
0078 plt.sca(ax[i%3,i//3])
0079 eng = int(Energy[i]*1000)
0080 plt.title(f'Gamma Energy: {eng} MeV')
0081 temp = np.array(df[f'ecal_reco_energy_{eng}'].astype(float).to_numpy()[condition])*1000
0082 hist, x = np.histogram(temp,bins=np.linspace(min(temp),max(temp)+np.std(abs(temp)),2*int(np.sqrt(len(temp)))))
0083 x = x[1:]/2 + x[:-1]/2
0084 plt.errorbar(x,hist,yerr=np.sqrt(hist),fmt='-o',label='Cluster')
0085 try:
0086 coeff, covar = curve_fit(gaussian,x[1:],hist[1:],p0=(max(hist[x>=np.std(abs(temp))]),np.mean(temp[temp!=0]),np.std(temp[temp!=0])),maxfev=10000)
0087
0088 mu.append(coeff[1])
0089 sigma.append(coeff[2])
0090 except RuntimeError:
0091 print("fit failed")
0092 mu.append(np.nan)
0093 sigma.append(np.nan)
0094
0095 temp = np.array(df[f'ecal_rec_energy_{eng}'].astype(float).to_numpy()[condition])*1000
0096 hist, x = np.histogram(temp,bins=np.linspace(min(temp),max(temp)+np.std(abs(temp)),2*int(np.sqrt(len(temp)))))
0097 x = x[1:]/2 + x[:-1]/2
0098 plt.errorbar(x,hist,yerr=np.sqrt(hist),fmt='-o',label='Digitization')
0099 try:
0100 coeff, covar = curve_fit(gaussian,x[1:],hist[1:],p0=(max(hist[x>=np.std(abs(temp))]),np.mean(temp[temp!=0]),np.std(temp[temp!=0])),maxfev=10000)
0101
0102 mu.append(coeff[1])
0103 sigma.append(coeff[2])
0104 except RuntimeError:
0105 print("fit failed")
0106 mu.append(np.nan)
0107 sigma.append(np.nan)
0108
0109 temp = np.array(df[f'ecal_sim_energy_{eng}'].astype(float).to_numpy()[condition])*1000
0110 hist, x = np.histogram(temp,bins=np.linspace(min(temp),max(temp)+np.std(abs(temp)),2*int(np.sqrt(len(temp)))))
0111 x = x[1:]/2 + x[:-1]/2
0112 plt.errorbar(x,hist,yerr=np.sqrt(hist),fmt='-o',label='Simulation')
0113 try:
0114 coeff, covar = curve_fit(gaussian,x[1:],hist[1:],p0=(max(hist[x>=np.std(abs(temp))]),np.mean(temp[temp!=0]),np.std(temp[temp!=0])),maxfev=10000)
0115
0116 mu.append(coeff[1])
0117 sigma.append(coeff[2])
0118 except RuntimeError:
0119 print("fit failed")
0120 mu.append(np.nan)
0121 sigma.append(np.nan)
0122
0123 plt.xlabel('Energy (MeV)')
0124 plt.legend()
0125
0126
0127
0128 mu = np.array(mu)
0129 sigma = np.array(sigma)
0130
0131 plt.show()
0132
0133 fig2, (ax1,ax2) = plt.subplots(2,1,figsize=(15,10),sharex=True)
0134
0135 plt.tight_layout()
0136
0137 ax1.scatter(np.array(Energy)*1000, mu[::3], label='cluster')
0138 ax1.scatter(np.array(Energy)*1000, mu[1::3], label='digitization')
0139 ax1.scatter(np.array(Energy)*1000, mu[2::3], label='simulation')
0140
0141 ax1.plot([4.5,1000],[4.5,1000],c='black',label='x=y')
0142 ax1.set_ylabel('Reconstructed Energy (MeV)')
0143 ax1.set_yscale('log')
0144 ax1.legend()
0145 ax1.set_title('ECal Craterlake Cluster Energy Reconstruction')
0146
0147 ax2.errorbar(np.array(Energy)*1000, abs(sigma[::3]/mu[::3])*100, fmt='-o', label='cluster')
0148 ax2.errorbar(np.array(Energy)*1000, abs(sigma[1::3]/mu[1::3])*100, fmt='-o', label='digitization')
0149 ax2.errorbar(np.array(Energy)*1000, abs(sigma[2::3]/mu[2::3])*100, fmt='-o', label='simulation')
0150
0151 ax2.set_ylabel('Resolution (%)')
0152 ax2.set_xlabel('Gamma Energy (MeV)')
0153 ax2.set_xscale('log')
0154 ax2.legend()
0155
0156
0157
0158 plt.show()
0159
0160
0161 htower = []
0162 herr = []
0163 hmean = []
0164 hhits = []
0165 hhits_cut = []
0166 emean = []
0167 ehits = []
0168 etower = []
0169 eerr = []
0170 ehits_cut = []
0171
0172 fig3, ax = plt.subplots(2,3,figsize=(20,10))
0173 fig3.suptitle('ZDC Simulation Energy Reconstruction')
0174 for i in range(6):
0175 plt.sca(ax[i//3,i%3])
0176 eng = int(Energy[i]*1000)
0177
0178 x = df[f'par_x_{eng}'].astype(float).to_numpy()
0179 y = df[f'par_y_{eng}'].astype(float).to_numpy()
0180 z = df[f'par_z_{eng}'].astype(float).to_numpy()
0181 x, z = rotateY(x,z, 0.025)
0182 theta = np.arccos(z/np.sqrt((x**2+y**2+z**2)))*1000
0183 condition = theta <= 3.5
0184
0185 plt.title(f'Gamma Energy: {eng} MeV')
0186 energy1 = df[f'hcal_sim_energy_{eng}'].astype(float).to_numpy()
0187 hist, x = np.histogram(energy1*1000,bins=np.logspace(0,3,200))
0188 x = x[1:]/2 + x[:-1]/2
0189 plt.plot(x,hist,marker='o',label="HCal")
0190 hhits.append(len(energy1[energy1!=0]))
0191 condition1 = energy1!=0
0192 hhits_cut.append(len(energy1[condition & condition1])/len(condition[condition==True]))
0193 energy = df[f'ecal_sim_energy_{eng}'].astype(float).to_numpy()
0194 hist, x = np.histogram(energy*1000,bins=np.logspace(0,3,200))
0195 x = x[1:]/2 + x[:-1]/2
0196 plt.plot(x,hist,marker='o',label="ECal")
0197 emean.append(sum(energy[energy!=0])*1000/len(energy[energy!=0]))
0198 hmean.append(sum(energy1[energy!=0])*1000/len(energy[energy!=0]))
0199 condition1 = energy!=0
0200 ehits_cut.append(len(energy[condition & condition1])/len(condition[condition==True]))
0201 ehits.append(len(energy[energy!=0]))
0202 plt.legend()
0203 plt.xscale('log')
0204 plt.xlim(1e0,1e3)
0205
0206
0207
0208
0209
0210 plt.xlabel('Energy (MeV)')
0211
0212
0213 plt.show()
0214
0215 fig4, ax = plt.subplots(2,1,sharex=True,gridspec_kw={'height_ratios': [2,1]})
0216 plt.sca(ax[0])
0217 plt.errorbar(np.array(Energy)*1000,np.array(hmean)*47.619+np.array(emean),label='HCal/sf+ECal',fmt='-o')
0218 plt.errorbar(np.array(Energy)*1000,emean,label='ECal',fmt='-o')
0219 plt.legend()
0220 plt.yscale('log')
0221 plt.xscale('log')
0222 plt.ylabel('Simulation Energy (MeV)')
0223 plt.sca(ax[1])
0224 plt.errorbar(np.array(Energy)*1000,(1 - np.array(emean)/(np.array(hmean)*47.619+np.array(emean)))*100,label='Total/ECal',fmt='-o')
0225 plt.legend()
0226 plt.ylabel('Fraction of energy\n deposited in Hcal (%)')
0227 plt.xlabel('Truth Energy (MeV)')
0228
0229 plt.tight_layout()
0230 plt.show()
0231
0232 fig5 = plt.figure()
0233 plt.errorbar(np.array(Energy)*1000,np.array(hhits)/1000*100,label='HCal Hits',fmt='-o')
0234 plt.errorbar(np.array(Energy)*1000,np.array(ehits)/1000*100,label='ECal Hits',fmt='-o')
0235
0236
0237 plt.errorbar(np.array(Energy)*1000,np.array(hhits_cut)*100,label='HCal Hits with 3.5 mRad cut',fmt='-^')
0238 plt.errorbar(np.array(Energy)*1000,np.array(ehits_cut)*100,label='ECal Hits with 3.5 mRad cut',fmt='-^')
0239
0240
0241
0242 plt.legend()
0243 plt.xlabel('Simulation Truth Gamma Energy (MeV)')
0244 plt.ylabel('Fraction of Events with non-zero energy (%)')
0245
0246 plt.xscale('log')
0247 plt.show()
0248
0249 fig6, ax = plt.subplots(2,3,figsize=(20,10))
0250 fig6.suptitle('ZDC Clustering')
0251 fig6.tight_layout(pad=1.8)
0252 for i in range(6):
0253 plt.sca(ax[i//3,i%3])
0254 eng = int(Energy[i]*1000)
0255
0256 x = df[f'par_x_{eng}'].astype(float).to_numpy()
0257 y = df[f'par_y_{eng}'].astype(float).to_numpy()
0258 z = df[f'par_z_{eng}'].astype(float).to_numpy()
0259 x, z = rotateY(x,z, 0.025)
0260 theta = np.arccos(z/np.sqrt((x**2+y**2+z**2)))*1000
0261 condition = theta <= 3.5
0262
0263 plt.hist(df[f'ecal_reco_clusters_{eng}'][condition],bins=np.linspace(0,5,6))
0264 plt.xlabel('Number of Clusters')
0265 plt.title(f'Gamma Energy: {eng} MeV')
0266 plt.show()
0267
0268 fig7, ax = plt.subplots(2,3,figsize=(20,10))
0269 fig7.suptitle('ZDC Towering in Clusters')
0270 fig7.tight_layout(pad=1.8)
0271 for i in range(6):
0272 plt.sca(ax[i//3,i%3])
0273 eng = int(Energy[i]*1000)
0274
0275 x = df[f'par_x_{eng}'].astype(float).to_numpy()
0276 y = df[f'par_y_{eng}'].astype(float).to_numpy()
0277 z = df[f'par_z_{eng}'].astype(float).to_numpy()
0278 x, z = rotateY(x,z, 0.025)
0279 theta = np.arccos(z/np.sqrt((x**2+y**2+z**2)))*1000
0280 condition = theta <= 3.5
0281
0282 plt.hist(df[f'ecal_reco_nhits_{eng}'][condition],bins=np.linspace(0,max(df[f'ecal_reco_nhits_{eng}'][condition]),max(df[f'ecal_reco_nhits_{eng}'][condition])+1))
0283 plt.xlabel('Number of tower in Clusters')
0284 plt.title(f'Gamma Energy: {eng} MeV')
0285 plt.show()
0286
0287
0288
0289 with PdfPages(f'results/{os.environ["DETECTOR_CONFIG"]}/zdc_lyso/plots.pdf') as pdf:
0290 pdf.savefig(fig1)
0291 pdf.savefig(fig2)
0292 pdf.savefig(fig3)
0293 pdf.savefig(fig4)
0294 pdf.savefig(fig5)
0295 pdf.savefig(fig6)
0296 pdf.savefig(fig7)