Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
TPC_Cluster_Drift_Animator_beam.py
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file TPC_Cluster_Drift_Animator_beam.py
1 import numpy as np
2 import json
3 import matplotlib.pyplot as plt
4 import pandas as pd
5 import uproot
6 import awkward as ak
7 import mpl_toolkits.mplot3d.art3d as art3d
8 from matplotlib.patches import Wedge
9 import matplotlib.animation as animation
10 import array
11 #import time
12 #from timeit import default_timer as timer
13 
14 #/*************************************************************/
15 #/* TPC Cluster Drift Animator */
16 #/* Aditya Prasad Dash, Thomas Marshall */
17 #/* aditya55@physics.ucla.edu, rosstom@ucla.edu */
18 #/*************************************************************/
19 #Code in python
20 #Input:
21 # root or json file containing TPC cluster positions
22 #Output:
23 # Animation of drifting of TPC clusters with user defined speed and option to save in .mp4 format
24 
25 def TPC_surface(inner_radius,outer_radius, length_z):
26  ngridpoints=30
27  z = np.linspace(-length_z, length_z, ngridpoints)
28  phi = np.linspace(0, 2*np.pi, ngridpoints)
29  phi_grid, z_grid=np.meshgrid(phi, z)
30  x_grid_inner = inner_radius*np.cos(phi_grid)
31  y_grid_inner = inner_radius*np.sin(phi_grid)
32  x_grid_outer = outer_radius*np.cos(phi_grid)
33  y_grid_outer = outer_radius*np.sin(phi_grid)
34 
35  return x_grid_inner,y_grid_inner,x_grid_outer,y_grid_outer,z_grid
36 
37 def TPC_endcap(inner_radius,outer_radius, length_z):
38  ngridpoints=30
39  radius = np.linspace(inner_radius, outer_radius, 5)
40  phi = np.linspace(0, 2*np.pi, ngridpoints)
41  phi_grid, r_grid=np.meshgrid(phi, radius)
42  x_grid=[]
43  y_grid=[]
44  for r in radius:
45  x_grid_radius = r*np.cos(phi_grid)
46  y_grid_radius = r*np.sin(phi_grid)
47  x_grid=np.append(x_grid,x_grid_radius)
48  y_grid=np.append(y_grid,y_grid_radius)
49 
50  z_grid=x_grid
51  return x_grid,y_grid,z_grid
52 
53 
54 def raddist_cluster(cluster_pos):
55  radius=np.sqrt(cluster_pos[0]*cluster_pos[0]+cluster_pos[1]*cluster_pos[1])
56  return radius
57 
58 def theLoop(iteration,dataPoint,scatter):
59  #effective_time=iteration-dataPoint[4]
60  effective_time=iteration
61  if(effective_time>=0):
62  effective_z = 0
63  if(dataPoint[2]>0):
64  effective_z=dataPoint[2]+drift_speed_posz[2]*effective_time
65  if(dataPoint[2]<0):
66  effective_z=dataPoint[2]+drift_speed_negz[2]*effective_time
67  if(abs(effective_z)<len_TPC+drift_speed_posz[2]):
68  scatter._offsets3d = (dataPoint[0:1], dataPoint[1:2], [effective_z])
69  color=['r','g','b','c','m','y']
70 
71  if(abs(effective_z)<len_TPC):
72  #scatter.set_color(color[int(dataPoint[3]%6)])
73  scatter.set_color('r')
74  scatter.set_sizes([0.1])
75  if(abs(effective_z)>=len_TPC):
76  scatter.set_color('white')
77  scatter.set(alpha=1.0)
78  scatter.set_sizes([0.1])
79 
80  elif(abs(effective_z)<len_TPC+2*drift_speed_posz[2]):
81  scatter._offsets3d = ([100], [-100], [100]) #to plot all points outside TPC at one point
82  scatter.set_color('black')
83  scatter.set_sizes([0.1])
84  return 0
85 
86 #Parallel processing
87 def animate_scatters(iteration, data,
88  scatters,fig_text,time_scale,iteration_time,start_iteration):
89  iteration=iteration+start_iteration
90  if(iteration%1==0):
91  print("iteration=")
92  print(iteration)
93  iter_array=[iteration]*len(data)
94  time=(iteration)*iteration_time/time_scale*(10**3)
95  theLoop_return = [theLoop(iteration,da,scat) for da,scat in zip(data,scatters)]
96  fig_text.set_text(str(round(time,1))+"$\mu$s")
97 
98  return scatters,fig_text
99 
100 #Normal Processing
101 #def animate_scatters(iteration, data, scatters,fig_text,time_scale,iteration_time,start_iteration):
102 # print("iteration=")
103 # print(iteration)
104 # for i in range(data.shape[0]):
105 # time=(iteration+start_iteration)*iteration_time/time_scale*(10**3)
106 # effective_time=iteration-data[i,4]
107 # if(effective_time>=0):
108 # if(data[i,2]>0):
109 # effective_z=data[i,2]+drift_speed_posz[2]*effective_time
110 # if(data[i,2]<0):
111 # effective_z=data[i,2]+drift_speed_negz[2]*effective_time
112 # if(abs(effective_z)<len_TPC+drift_speed_posz[2]):
113 # scatters[i]._offsets3d = (data[i,0:1], data[i,1:2], [effective_z])
114 # color=['r','g','b','c','m','y']
115 #
116 # if(abs(effective_z)<len_TPC):
117 # scatters[i].set_color(color[int(data[i,3]%6)])
118 #
119 # if(abs(effective_z)>=len_TPC):
120 # scatters[i].set_color('white')
121 # scatters[i].set(alpha=1.0)
122 #
123 # if(abs(effective_z)>=len_TPC+2*drift_speed_posz[2]):
124 # scatters[i]._offsets3d = ([100], [-100], [100]) #to plot all points outside TPC at one point
125 # scatters[i].set_color('black')
126 # scatters[i].set_sizes([0.01])
127 # else:
128 # scatters[i]._offsets3d = ([100], [-100], [100]) #clusters from event not yet taken place
129 # scatters[i].set_color('black')
130 #
131 # fig_text.set_text(str(round(time,2))+"$\mu$s")
132 #
133 # return scatters,fig_text
134 
135 def animate_clusters(data,animation_name="Animated_clusters_TPC.mp4",save=False,start_iteration=0,no_iterations=1):
136 
137  # Attaching 3D axis to the figure
138  fig = plt.figure(figsize=[7.5,7.5],layout='constrained')
139  ax = fig.add_subplot(111, projection='3d',facecolor='black',alpha=0.0)
140  ax.grid(False)
141  ax.margins(0.0)
142  ax.xaxis.set_pane_color((1,1,1,0))
143  ax.xaxis.line.set_color('w')
144  ax.spines['top'].set_color('w')
145  ax.spines['bottom'].set_color('w')
146  ax.spines['left'].set_color('w')
147  ax.spines['right'].set_color('w')
148 
149  ax.yaxis.set_pane_color((1,1,1,0))
150  ax.yaxis.line.set_color('w')
151  ax.zaxis.set_pane_color((1,1,1,0))
152  ax.zaxis.line.set_color('w')
153 
154  #Drawing TPC
155  endcap1=Wedge((0, 0), 80, 0, 360, color='blue',alpha=0.7)
156  endcap2=Wedge((0, 0), 80, 0, 360, color='blue',alpha=0.7)
157  endcap1.set_width(60)
158  endcap2.set_width(60)
159 
160  ax.add_artist(endcap1)
161  ax.add_artist(endcap2)
162 
163  art3d.pathpatch_2d_to_3d(endcap1, z=len_TPC, zdir="z")
164  art3d.pathpatch_2d_to_3d(endcap2, z=-len_TPC, zdir="z")
165 
166  Xc_in,Yc_in,Xc_out,Yc_out,Zc = TPC_surface(20,80,len_TPC)
167  ax.plot_surface(Xc_in, Yc_in, Zc, alpha=0.5)
168  ax.plot_surface(Xc_out, Yc_out, Zc, alpha=0.5)
169 
170  # Setting the axes properties
171  ax.set_xlim3d([-100, 100])
172  ax.set_xlabel('X (cm)',color='white',fontsize=7)
173  ax.xaxis.set_tick_params(colors='white',labelsize=7)
174 
175  ax.set_ylim3d([-100, 100])
176  ax.set_ylabel('Y (cm)',color='white',fontsize=7)
177  ax.yaxis.set_tick_params(colors='white',labelsize=7)
178 
179  ax.set_zlim3d([-120, 120])
180  ax.set_zlabel('Z (cm)',color='white',fontsize=7)
181  ax.zaxis.set_tick_params(colors='white',labelsize=7)
182 
183  ax.set_title('Clusters drifting in TPC') #(time scaled by $2*10^{5}$)')
184  fig_text=ax.text(-100,80,100, 'time', size=10,color='w',alpha=0.9)
185  fig_text_sPhenix=ax.text(-100,130,100, 'sPHENIX', size=10,fontweight='bold',style='italic',color='w',alpha=0.9)
186  fig_text_TPC=ax.text(-60,134,70, 'Time Projection Chamber', size=10,color='w',alpha=0.9)
187  fig_text_collisions=ax.text(-130,110,90, 'Diffused laser with TPC HV on and Laser 98% ALL ON Trigger modebit@2', size=10,color='w',alpha=0.9)
188  fig_text_frame=ax.text(-130,95,90, '2023-07-06, Run 11011', size=10,color='w',alpha=0.9)
189 
190  #fig_text_sPhenix=ax.text(-130,100,90, 'Au+Au, $\sqrt{s_{NN}}$ = 200 GeV', size=10,color='w',alpha=0.9)
191 
192 
193  # Providing the starting angle for the view
194  ax.view_init(10,70,0,'y')
195 
196  # Initializing scatters
197  scatters = [ ax.scatter([100],[-100],[100]) for i in range(data.shape[0])]
198  for i in range(data.shape[0]): scatters[i].set_color('black')
199  print("Plot initialized")
200  #start = timer()
201  ani = animation.FuncAnimation(fig, animate_scatters, iterations, fargs=(data, scatters,fig_text,time_scale,iteration_time,start_iteration),
202  interval=iteration_time, blit=False, repeat=False) #interval is in milliseconds and is the time between each frame
203  if save:
204  print("Saving animation as"+animation_name)
205  ani.save(animation_name,writer='ffmpeg')
206  print("Animation saved")
207  #end = timer()
208  #print("Time for process=")
209  #print(end-start)
210  plt.show()
211 
212 def read_cluster_pos(inFile):
213  if(inFile.lower().endswith('.json')):
214  print("Reading data from json file")
215  file=open(inFile)
216  data_json=json.load(file)
217  file.close()
218  dict_json = data_json.items()
219  numpy_array_json = np.array(list(dict_json))
220  #print(numpy_array_json[2][1]['TRACKHITS'])
221  mom_dict=numpy_array_json[2][1] #only works for this format the 3rd row is TRACKS and and 1 refers to INNERTRACKER
222  #print(mom_dict)
223  innertracker_arr=mom_dict['TRACKHITS'] #stores the tracks information as an array
224  #print(innertracker_arr[0])
225  #print(innertracker_arr)
226  print("Reading clusters")
227  #Getting the cluster positions of a track
228  x_y_z_clusters=np.array([])
229  #print(x_y_z_clusters)
230  no_hits=len(innertracker_arr) #set no_tracks=10 for testing
231  print(no_hits)
232  #no_hits=1000
233  for hit_no in range(no_hits):
234  x_y_z_dict_track=innertracker_arr[hit_no].copy() #innertracker_arr[i] reads the ith track
235  #print(list(x_y_z_dict_track.values()))
236  x_y_z_clusters_track=np.array(list(x_y_z_dict_track.values())) #2D array the x,y,z positions corresponding to each cluster e.g. x_y_z[0][0] is the x coordinate of the 1st cluster
237  x_y_z_clusters_track=x_y_z_clusters_track[:3]
238  #print(x_y_z_clusters_track)
239  #hit_no=0
240  if(raddist_cluster(x_y_z_clusters_track)<30 or x_y_z_clusters_track[2]==0.0):
241  continue
242  else:
243  if(hit_no==0):
244  x_y_z_clusters=[np.copy(x_y_z_clusters_track)]
245 
246  else:
247  #if(hit_no==1):
248  #x_y_z_clusters=np.append(x_y_z_clusters,x_y_z_clusters_track,axis=0)
249  #x_y_z_clusters=np.append(x_y_z_clusters,x_y_z_clusters_track)
250  x_y_z_clusters=np.vstack([x_y_z_clusters,x_y_z_clusters_track])
251  #np.vstack([a,l])
252  #print(x_y_z_clusters)
253  return x_y_z_clusters
254 
255  if(inFile.lower().endswith('.root')):
256  print("Reading data from root file")
257  file = uproot.open(inFile)
258  ntp_cluster_tree=file['hitTree']
259  branches=ntp_cluster_tree.arrays(["x","y","z"])
260  #branches=branches[~np.isnan(branches.gvt)]
261  branches=branches[((branches.x)**2+(branches.y)**2)>900]
262  #branches=branches[branches.layer>6]
263  #branches=branches[branches.layer<55]
264  #branches=branches[branches.event>=75]
265  #branches=branches[branches.event<55]
266 
267  print("Reading clusters")
268  x_y_z_clusters_run=np.array([])
269  len_events=len(np.unique(branches.event))
270  len_events=200
271  event_times=[0]
272  event_times=np.append(event_times,np.random.poisson(mean_eventtime*1000,len_events-1))
273  event_times=np.cumsum(event_times,dtype=float)
274  for cluster in range(len(branches)):
275  #gvt_event=event_times[int(branches[cluster]['event'])]
276  gvt_event=1
277  gvt_event=gvt_event*(10**(-6))*time_scale #Time in milliseconds scaled for animation
278  gvt_event=gvt_event/(iteration_time) #this is the time in terms of iterations
279  x_y_z_clusters_track=np.array([[branches[cluster]['x'], branches[cluster]['y'], branches[cluster]['z'],branches[cluster]['event'],gvt_event]])
280  if(cluster==0):
281  x_y_z_clusters_run=np.copy(x_y_z_clusters_track)
282  else:
283  x_y_z_clusters_run=np.append(x_y_z_clusters_run,x_y_z_clusters_track,axis=0)
284  return x_y_z_clusters_run
285 
286 print("Generating data for animation")
287 
288 
289 # Main Program starts from here
290 np.random.seed(3)
291 #User defined values
292 time_scale=4.0*(10.0**5) #inverse of speed scale
293 collision_rate=4.0 #MHz #Set fig_text_sPhenix in line 188
294 mean_eventtime=1/collision_rate
295 print("Mean event time (microseconds)=")
296 print(mean_eventtime)
297 TPC_drift_speed=8.0*(10.0**3) #Actual TPC drift speed =8cm/microsecond=8*10^3cm/millisecond
298 iteration_time=100.0 #actual duration between frames in FuncAnimation
299 len_TPC=105.0
300 
301 drift_speed_posz=np.array([0.0,0.0,TPC_drift_speed/time_scale*iteration_time,0.0,0.0])
302 drift_speed_negz=np.array([0.0,0.0,-TPC_drift_speed/time_scale*iteration_time,0.0,0.0])
303 print("Drift_speed_posz(cm/iteration)=")
304 print(drift_speed_posz)
305 print("Drift_speed_negz(cm/iteration)=")
306 print(drift_speed_negz)
307 
308 #ANIMATION
309 #data=read_cluster_pos("Data_files/G4sPHENIX_g4svtx_eval.root")
310 data=read_cluster_pos("Data_files/TPCEventDisplay_11011.json")
311 print(data)
312 # Number of iterations
313 #no_events=np.max(data[:,3])+1
314 no_events=1
315 print("no_events=")
316 print(no_events)
317 
318 iterations = int((no_events-1)*mean_eventtime*time_scale/(iteration_time*1000)+len_TPC/drift_speed_posz[2])+5
319 #iterations=10
320 
321 print("number of iterations=")
322 print(iterations)
323 print("Animation starting!")
324 
325 #Saving takes a long time so use Save=True only when necessary
326 #increase drift_speed_posz and drift_speed_negz if desired
327 animate_clusters(data,"Animated_clusters_TPC_beam.mp4",save=True,start_iteration=50,no_iterations=iterations)
328 
329 
330 #Merge mp4 files using ffmpeg -f concat -safe 0 -i filelist.txt -c copy mergedVideo.mp4