Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
TPC_Cluster_Drift_Animator.py
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file TPC_Cluster_Drift_Animator.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  if(effective_time>=0):
61  effective_z = 0
62  if(dataPoint[2]>0):
63  effective_z=dataPoint[2]+drift_speed_posz[2]*effective_time
64  if(dataPoint[2]<0):
65  effective_z=dataPoint[2]+drift_speed_negz[2]*effective_time
66  if(abs(effective_z)<len_TPC+drift_speed_posz[2]):
67  scatter._offsets3d = (dataPoint[0:1], dataPoint[1:2], [effective_z])
68  color=['r','g','b','c','m','y']
69 
70  if(abs(effective_z)<len_TPC):
71  scatter.set_color(color[int(dataPoint[3]%6)])
72 
73  if(abs(effective_z)>=len_TPC):
74  scatter.set_color('white')
75  scatter.set(alpha=1.0)
76 
77  elif(abs(effective_z)<len_TPC+2*drift_speed_posz[2]):
78  scatter._offsets3d = ([100], [-100], [100]) #to plot all points outside TPC at one point
79  scatter.set_color('black')
80  scatter.set_sizes([10])
81  return 0
82 
83 #Parallel processing
84 def animate_scatters(iteration, data,
85  scatters,fig_text,time_scale,iteration_time,start_iteration):
86  iteration=iteration+start_iteration
87  if(iteration%1==0):
88  print("iteration=")
89  print(iteration)
90  iter_array=[iteration]*len(data)
91  time=(iteration)*iteration_time/time_scale*(10**3)
92  theLoop_return = [theLoop(iteration,da,scat) for da,scat in zip(data,scatters)]
93  fig_text.set_text(str(round(time,1))+"$\mu$s")
94 
95  return scatters,fig_text
96 
97 #Normal Processing
98 #def animate_scatters(iteration, data, scatters,fig_text,time_scale,iteration_time,start_iteration):
99 # print("iteration=")
100 # print(iteration)
101 # for i in range(data.shape[0]):
102 # time=(iteration+start_iteration)*iteration_time/time_scale*(10**3)
103 # effective_time=iteration-data[i,4]
104 # if(effective_time>=0):
105 # if(data[i,2]>0):
106 # effective_z=data[i,2]+drift_speed_posz[2]*effective_time
107 # if(data[i,2]<0):
108 # effective_z=data[i,2]+drift_speed_negz[2]*effective_time
109 # if(abs(effective_z)<len_TPC+drift_speed_posz[2]):
110 # scatters[i]._offsets3d = (data[i,0:1], data[i,1:2], [effective_z])
111 # color=['r','g','b','c','m','y']
112 #
113 # if(abs(effective_z)<len_TPC):
114 # scatters[i].set_color(color[int(data[i,3]%6)])
115 #
116 # if(abs(effective_z)>=len_TPC):
117 # scatters[i].set_color('white')
118 # scatters[i].set(alpha=1.0)
119 #
120 # if(abs(effective_z)>=len_TPC+2*drift_speed_posz[2]):
121 # scatters[i]._offsets3d = ([100], [-100], [100]) #to plot all points outside TPC at one point
122 # scatters[i].set_color('black')
123 # scatters[i].set_sizes([10])
124 # else:
125 # scatters[i]._offsets3d = ([100], [-100], [100]) #clusters from event not yet taken place
126 # scatters[i].set_color('black')
127 #
128 # fig_text.set_text(str(round(time,2))+"$\mu$s")
129 #
130 # return scatters,fig_text
131 
132 def animate_clusters(data,animation_name="Animated_clusters_TPC.mp4",save=False,start_iteration=0,no_iterations=1):
133 
134  # Attaching 3D axis to the figure
135  fig = plt.figure(figsize=[7.5,7.5],layout='constrained')
136  ax = fig.add_subplot(111, projection='3d',facecolor='black',alpha=0.0)
137  ax.grid(False)
138  ax.margins(0.0)
139  ax.xaxis.set_pane_color((1,1,1,0))
140  ax.xaxis.line.set_color('w')
141  ax.spines['top'].set_color('w')
142  ax.spines['bottom'].set_color('w')
143  ax.spines['left'].set_color('w')
144  ax.spines['right'].set_color('w')
145 
146  ax.yaxis.set_pane_color((1,1,1,0))
147  ax.yaxis.line.set_color('w')
148  ax.zaxis.set_pane_color((1,1,1,0))
149  ax.zaxis.line.set_color('w')
150 
151  #Drawing TPC
152  endcap1=Wedge((0, 0), 80, 0, 360, color='blue',alpha=0.7)
153  endcap2=Wedge((0, 0), 80, 0, 360, color='blue',alpha=0.7)
154  endcap1.set_width(60)
155  endcap2.set_width(60)
156 
157  ax.add_artist(endcap1)
158  ax.add_artist(endcap2)
159 
160  art3d.pathpatch_2d_to_3d(endcap1, z=len_TPC, zdir="z")
161  art3d.pathpatch_2d_to_3d(endcap2, z=-len_TPC, zdir="z")
162 
163  Xc_in,Yc_in,Xc_out,Yc_out,Zc = TPC_surface(20,80,len_TPC)
164  ax.plot_surface(Xc_in, Yc_in, Zc, alpha=0.5)
165  ax.plot_surface(Xc_out, Yc_out, Zc, alpha=0.5)
166 
167  # Setting the axes properties
168  ax.set_xlim3d([-100, 100])
169  ax.set_xlabel('X (cm)',color='white',fontsize=7)
170  ax.xaxis.set_tick_params(colors='white',labelsize=7)
171 
172  ax.set_ylim3d([-100, 100])
173  ax.set_ylabel('Y (cm)',color='white',fontsize=7)
174  ax.yaxis.set_tick_params(colors='white',labelsize=7)
175 
176  ax.set_zlim3d([-120, 120])
177  ax.set_zlabel('Z (cm)',color='white',fontsize=7)
178  ax.zaxis.set_tick_params(colors='white',labelsize=7)
179 
180  ax.set_title('Clusters drifting in TPC') #(time scaled by $2*10^{5}$)')
181  fig_text=ax.text(-100,90,100, 'time', size=10,color='w',alpha=0.9)
182  fig_text_sPhenix=ax.text(-100,130,100, 'sPHENIX', size=10,fontweight='bold',style='italic',color='w',alpha=0.9)
183  fig_text_TPC=ax.text(-60,135,70, 'TPC simulation', size=10,color='w',alpha=0.9)
184 
185  fig_text_sPhenix=ax.text(-100,110,100, 'p+p, $\sqrt{s_{NN}}$ = 200 GeV 4MHz', size=10,color='w',alpha=0.9)
186 
187 
188  # Providing the starting angle for the view
189  ax.view_init(10,70,0,'y')
190 
191  # Initializing scatters
192  scatters = [ ax.scatter([100],[-100],[100]) for i in range(data.shape[0])]
193  for i in range(data.shape[0]): scatters[i].set_color('black')
194  print("Plot initialized")
195  #start = timer()
196  ani = animation.FuncAnimation(fig, animate_scatters, iterations, fargs=(data, scatters,fig_text,time_scale,iteration_time,start_iteration),
197  interval=iteration_time, blit=False, repeat=False) #interval is in milliseconds and is the time between each frame
198  if save:
199  print("Saving animation as"+animation_name)
200  ani.save(animation_name,writer='ffmpeg')
201  print("Animation saved")
202  #end = timer()
203  #print("Time for process=")
204  #print(end-start)
205  #plt.show()
206 
207 def read_cluster_pos(inFile):
208  if(inFile.lower().endswith('.json')):
209  print("Reading data from json file")
210  file=open(inFile)
211  data_json=json.load(file)
212  file.close()
213  dict_json = data_json.items()
214  numpy_array_json = np.array(list(dict_json))
215  mom_dict=numpy_array_json[3][1] #only works for this format the 3rd row is TRACKS and and 1 refers to INNERTRACKER
216  innertracker_arr=mom_dict['INNERTRACKER'] #stores the tracks information as an array
217 
218  print("Reading clusters")
219  #Getting the cluster positions of a track
220  x_y_z_clusters=np.array([])
221  no_tracks=len(innertracker_arr) #set no_tracks=10 for testing
222 
223  for track_no in range(no_tracks):
224  x_y_z_dict_track=innertracker_arr[track_no].copy() #innertracker_arr[i] reads the ith track
225  x_y_z_clusters_track=np.array(x_y_z_dict_track['pts']) #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
226  event_and_times=np.zeros((len(x_y_z_clusters_track),1))
227  x_y_z_clusters_track=np.append(x_y_z_clusters_track,event_and_times,axis=1)
228  x_y_z_clusters_track=np.append(x_y_z_clusters_track,event_and_times,axis=1)
229  x_y_z_clusters_track=x_y_z_clusters_track[raddist_cluster(x_y_z_clusters_track[:,:2])>30]
230 
231  if(track_no==0):
232  x_y_z_clusters=np.copy(x_y_z_clusters_track)
233 
234  else:
235  x_y_z_clusters=np.append(x_y_z_clusters,x_y_z_clusters_track,axis=0)
236  return x_y_z_clusters
237 
238  if(inFile.lower().endswith('.root')):
239  print("Reading data from root file")
240  file = uproot.open(inFile)
241  ntp_cluster_tree=file['ntp_cluster']
242  branches=ntp_cluster_tree.arrays(["x","y","z","event","gvt","layer"])
243  branches=branches[~np.isnan(branches.gvt)]
244  branches=branches[((branches.x)**2+(branches.y)**2)>900]
245  branches=branches[branches.layer>6] #layers corresponding to TPC
246  branches=branches[branches.layer<55] #layers corresponding to TPC
247  #branches=branches[branches.event>=75]
248  #branches=branches[branches.event<55]
249 
250  print("Reading clusters")
251  x_y_z_clusters_run=np.array([])
252  len_events=len(np.unique(branches.event))
253  len_events=200
254  event_times=[0]
255  event_times=np.append(event_times,np.random.poisson(mean_eventtime*1000,len_events-1))
256  event_times=np.cumsum(event_times,dtype=float)
257  for cluster in range(len(branches)):
258  gvt_event=event_times[int(branches[cluster]['event'])]
259  gvt_event=gvt_event*(10**(-6))*time_scale #Time in milliseconds scaled for animation
260  gvt_event=gvt_event/(iteration_time) #this is the time in terms of iterations
261  x_y_z_clusters_track=np.array([[branches[cluster]['x'], branches[cluster]['y'], branches[cluster]['z'],branches[cluster]['event'],gvt_event]])
262  if(cluster==0):
263  x_y_z_clusters_run=np.copy(x_y_z_clusters_track)
264  else:
265  x_y_z_clusters_run=np.append(x_y_z_clusters_run,x_y_z_clusters_track,axis=0)
266  return x_y_z_clusters_run
267 
268 print("Generating data for animation")
269 
270 
271 # Main Program starts from here
272 np.random.seed(3)
273 #User defined values
274 time_scale=4.0*(10.0**5) #inverse of speed scale
275 collision_rate=4.0 #MHz #Set fig_text_sPhenix in line 188
276 mean_eventtime=1/collision_rate
277 print("Mean event time (microseconds)=")
278 print(mean_eventtime)
279 TPC_drift_speed=8.0*(10.0**3) #Actual TPC drift speed =8cm/microsecond=8*10^3cm/millisecond
280 iteration_time=100.0 #actual duration between frames in FuncAnimation
281 len_TPC=105.0
282 
283 drift_speed_posz=np.array([0.0,0.0,TPC_drift_speed/time_scale*iteration_time,0.0,0.0])
284 drift_speed_negz=np.array([0.0,0.0,-TPC_drift_speed/time_scale*iteration_time,0.0,0.0])
285 print("Drift_speed_posz(cm/iteration)=")
286 print(drift_speed_posz)
287 print("Drift_speed_negz(cm/iteration)=")
288 print(drift_speed_negz)
289 
290 #ANIMATION
291 data=read_cluster_pos("G4sPHENIX_g4svtx_eval.root")
292 
293 # Number of iterations
294 no_events=np.max(data[:,3])+1
295 print("no_events=")
296 print(no_events)
297 
298 iterations = int((no_events-1)*mean_eventtime*time_scale/(iteration_time*1000)+len_TPC/drift_speed_posz[2])+5
299 #iterations=20
300 
301 print("number of iterations=")
302 print(iterations)
303 print("Animation starting!")
304 
305 #Saving takes a long time so use Save=True only when necessary
306 #increase drift_speed_posz and drift_speed_negz if desired
307 animate_clusters(data,"Animated_clusters_TPC.mp4",save=True,start_iteration=0,no_iterations=iterations)
308 
309 
310 #Merge mp4 files using ffmpeg -f concat -safe 0 -i filelist.txt -c copy mergedVideo.mp4