SND@LHC Software
Loading...
Searching...
No Matches
2dEventDisplay.py
Go to the documentation of this file.
1import ROOT
2import os,sys,subprocess,atexit
3from array import array
4import shipunit as u
5import SndlhcMuonReco
6import rootUtils as ut
7from decorators import *
8from rootpyPickler import Unpickler
9import time
10from XRootD import client
11
12import numpy as np
13
14from datetime import datetime
15from pathlib import Path
16
17import logging
18logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.WARNING)
19logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.FATAL)
20
21ROOT.gStyle.SetPalette(ROOT.kViridis)
22ROOT.gInterpreter.ProcessLine('#include "'+os.environ['SNDSW_ROOT']+'/analysis/tools/sndSciFiTools.h"')
23
24def pyExit():
25 "unfortunately need as bypassing an issue related to use xrootd"
26 os.system('kill '+str(os.getpid()))
27atexit.register(pyExit)
28
29
30A,B = ROOT.TVector3(),ROOT.TVector3()
31
32eventComment = {} # possibility to add an event comment before moving to next event
33
34h={}
35from argparse import ArgumentParser
36parser = ArgumentParser()
37parser.add_argument("-r", "--runNumber", dest="runNumber", help="run number", type=int,required=False)
38parser.add_argument("-p", "--path", dest="path", help="path to data file",required=False,default=os.environ["EOSSHIP"]+"/eos/experiment/sndlhc/convertedData/physics/2022/")
39parser.add_argument("-f", "--inputFile", dest="inputFile", help="input file data and MC",default="",required=False)
40parser.add_argument("-g", "--geoFile", dest="geoFile", help="geofile", default=os.environ["EOSSHIP"]+"/eos/experiment/sndlhc/convertedData/physics/2022/geofile_sndlhc_TI18_V0_2022.root")
41parser.add_argument("-P", "--partition", dest="partition", help="partition of data", type=int,required=False,default=-1)
42parser.add_argument("--server", dest="server", help="xrootd server",default=os.environ["EOSSHIP"])
43parser.add_argument("--no-extraInfo", dest="extraInfo", action="store_false", help="Do not print extra information on the event.")
44
45parser.add_argument("--extension", help="Extension of file to save. E.g. png, pdf, root, etc.", default="png")
46parser.add_argument("--rootbatch", help="Run ROOT in batch mode.", action="store_true")
47
48parser.add_argument("--collision_axis", dest="drawCollAxis", help="Draw collision axis", action="store_true")
49
50parser.add_argument("-par", "--parFile", dest="parFile", help="parameter file", default=os.environ['SNDSW_ROOT']+"/python/TrackingParams.xml")
51parser.add_argument("-hf", "--HoughSpaceFormat", dest="HspaceFormat", help="Hough space representation. Should match the 'Hough_space_format' name in parFile, use quotes", default='linearSlopeIntercept')
52
53parser.add_argument("--shower_dir", dest="drawShowerDir", help="Draw shower direction if a shower is found", action="store_true")
54
55options = parser.parse_args()
56
57resolution_factor = 1
58if options.rootbatch:
59 ROOT.gROOT.SetBatch()
60 # Produce figures larger than the screen resolution. E.g., for printing.
61 resolution_factor = 2
62
63options.storePic = ''
64trans2local = False
65runInfo = False
66try:
67 fg = ROOT.TFile.Open(options.server+options.p+"RunInfodict.root")
68 pkl = Unpickler(fg)
69 runInfo = pkl.load('runInfo')
70 fg.Close()
71except: pass
72
73import SndlhcGeo
74geo = SndlhcGeo.GeoInterface(options.geoFile)
75
76lsOfGlobals = ROOT.gROOT.GetListOfGlobals()
77lsOfGlobals.Add(geo.modules['Scifi'])
78lsOfGlobals.Add(geo.modules['MuFilter'])
79
80detSize = {}
81em = geo.snd_geo.EmulsionDet
82si = geo.snd_geo.Scifi
83detSize[0] =[si.channel_width, si.channel_width, si.scifimat_z ]
84mi = geo.snd_geo.MuFilter
85if hasattr(mi, "Veto3BarX"): vetoXdim = mi.Veto3BarX/2
86else: vetoXdim = mi.VetoBarX/2
87detSize[1] =[vetoXdim, mi.VetoBarY/2, mi.VetoBarZ/2]
88detSize[2] =[mi.UpstreamBarX/2, mi.UpstreamBarY/2, mi.UpstreamBarZ/2]
89detSize[3] =[mi.DownstreamBarX_ver/2,mi.DownstreamBarY/2,mi.DownstreamBarZ/2]
90withDetector = True # False is useful when using zoom
91with2Points = False # plot start and end point of straw/bar
92mc = False
93
94firstScifi_z = 300 * u.cm
95# Initialize FairLogger: set severity and verbosity
96logger = ROOT.FairLogger.GetLogger()
97logger.SetColoredLog(True)
98logger.SetLogVerbosityLevel('low')
99logger.SetLogScreenLevel('WARNING')
100logger.SetLogToScreen(True)
101
102run = ROOT.FairRunAna()
103ioman = ROOT.FairRootManager.Instance()
104
105if options.inputFile=="":
106 f=ROOT.TFile.Open(options.path+'sndsw_raw_'+str(options.runNumber).zfill(6)+'.root')
107else:
108 f=ROOT.TFile.Open(options.path+options.inputFile)
109
110if f.FindKey('cbmsim'):
111 eventTree = f.Get("cbmsim")
112 runId = 'sim'
113 if eventTree.GetBranch('ScifiPoint'): mc = True
114else:
115 eventTree = f.Get("rawConv")
116 ioman.SetTreeName('rawConv')
117
118outFile = ROOT.TMemFile('dummy','CREATE')
119source = ROOT.FairFileSource(f)
120run.SetSource(source)
121sink = ROOT.FairRootFileSink(outFile)
122run.SetSink(sink)
123
124HT_tasks = {'muon_reco_task_Sf':SndlhcMuonReco.MuonReco(),
125 'muon_reco_task_DS':SndlhcMuonReco.MuonReco(),
126 'muon_reco_task_nuInt':SndlhcMuonReco.MuonReco()}
127for ht_task in HT_tasks.values():
128 run.AddTask(ht_task)
129
130import SndlhcTracking
132trackTask.SetName('simpleTracking')
133run.AddTask(trackTask)
134
135#avoiding some error messages
136xrdb = ROOT.FairRuntimeDb.instance()
137xrdb.getContainer("FairBaseParSet").setStatic()
138xrdb.getContainer("FairGeoParSet").setStatic()
139
140for ht_task in HT_tasks.values():
141 ht_task.SetParFile(options.parFile)
142 ht_task.SetHoughSpaceFormat(options.HspaceFormat)
143 # force the output of reco task to genfit::Track
144 # as the display code looks for such output
145 ht_task.ForceGenfitTrackFormat()
146HT_tasks['muon_reco_task_Sf'].SetTrackingCase('passing_mu_Sf')
147HT_tasks['muon_reco_task_DS'].SetTrackingCase('passing_mu_DS')
148HT_tasks['muon_reco_task_nuInt'].SetTrackingCase('nu_interaction_products')
149
150run.Init()
151OT = sink.GetOutTree()
152eventTree = ioman.GetInTree()
153eventTree.GetEvent(0)
154if eventTree.EventHeader.ClassName() == 'SNDLHCEventHeader':
155 geo.modules['Scifi'].InitEvent(eventTree.EventHeader)
156 geo.modules['MuFilter'].InitEvent(eventTree.EventHeader)
157# if faireventheader, rely on user to select correct geofile.
158
159if eventTree.GetBranch('Digi_MuFilterHit'):
160# backward compatbility for early converted events
161 eventTree.GetEvent(0)
162 eventTree.Digi_MuFilterHits = eventTree.Digi_MuFilterHit
163
164nav = ROOT.gGeoManager.GetCurrentNavigator()
165
166# get filling scheme
167try:
168 runNumber = eventTree.EventHeader.GetRunId()
169 fg = ROOT.TFile.Open(os.environ['EOSSHIP']+options.p+'FSdict.root')
170 pkl = Unpickler(fg)
171 FSdict = pkl.load('FSdict')
172 fg.Close()
173 if runNumber in FSdict: fsdict = FSdict[runNumber]
174 else: fsdict = False
175except:
176 print('continue without knowing filling scheme')
177 fsdict = False
178
179Nlimit = 4
180onlyScifi = False
181
182def goodEvent(event):
183# can be replaced by any user selection
184 stations = {'Scifi':{},'Mufi':{}}
185 if event.Digi_ScifiHits.GetEntries()>25: return False
186 for d in event.Digi_ScifiHits:
187 stations['Scifi'][d.GetDetectorID()//1000000] = 1
188 for d in event.Digi_MuFilterHits:
189 plane = d.GetDetectorID()//1000
190 stations['Mufi'][plane] = 1
191 totalN = len(stations['Mufi'])+len(stations['Scifi'])
192 if len(stations['Scifi'])>4 and len(stations['Mufi'])>6: return True
193 else: False
194 if onlyScifi and len(stations['Scifi'])>Nlimit: return True
195 elif not onlyScifi and totalN > Nlimit: return True
196 else: return False
197
198def userProcessing(event):
199 '''User hook to add action after event is plotted.
200
201 Useful for adding special objects to the display for example.
202 An example for display of 3-track events with external reco:
203
204 ```python
205 trackTask.multipleTrackCandidates(
206 nMaxCl=8, dGap=0.2, dMax=0.8, dMax3=0.8, ovMax=1, doublet=True, debug=False
207 )
208 n3D = [0, 0]
209 for p in range(2):
210 tc = h['simpleDisplay'].cd(-p + 2)
211 for trackId in trackTask.multipleTrackStore['trackCand'][p]:
212 if trackId < 100000 and not trackTask.multipleTrackStore['doublet']:
213 continue
214 if trackId in trackTask.multipleTrackStore['cloneCand'][p]:
215 continue
216 n3D[p] += 1
217 rc = trackTask.multipleTrackStore['trackCand'][p][trackId].Fit('pol1', 'SQ')
218 trackTask.multipleTrackStore['trackCand'][p][trackId].Draw('same')
219 tc.Update()
220 print('Number of full tracks', n3D)
221 return True
222 ```
223 '''
224 return
225
227# check for b1,b2,IP1,IP2
228 xing = {'all':True,'B1only':False,'B2noB1':False,'noBeam':False}
229 if fsdict:
230 T = eventTree.EventHeader.GetEventTime()
231 bunchNumber = int(T%(4*3564)/4+0.5)
232 nb1 = (3564 + bunchNumber - fsdict['phaseShift1'])%3564
233 nb2 = (3564 + bunchNumber - fsdict['phaseShift1']- fsdict['phaseShift2'])%3564
234 b1 = nb1 in fsdict['B1']
235 b2 = nb2 in fsdict['B2']
236 IP1 = False
237 IP2 = False
238 if b1:
239 IP1 = fsdict['B1'][nb1]['IP1']
240 if b2:
241 IP2 = fsdict['B2'][nb2]['IP2']
242 if b2 and not b1:
243 xing['B2noB1'] = True
244 if b1 and not b2 and not IP1:
245 xing['B1only'] = True
246 if not b1 and not b2: xing['noBeam'] = True
247 return xing
248
249def getSciFiHitDensity(g, x_range=0.5):
250 """Takes ROOT TGraph g and returns array with number of hits within x_range cm of each hit."""
251 ret = []
252 for i in range(g.GetN()):
253 x_i = g.GetPointX(i)
254 y_i = g.GetPointY(i)
255 density = 0
256 for j in range(g.GetN()):
257 x_j = g.GetPointX(j)
258 y_j = g.GetPointY(j)
259 if ((x_i - x_j)**2 + (y_i - y_j)**2) <= x_range**2:
260 density += 1
261 ret.append(density)
262 return ret
263
264def drawLegend(max_density, max_QDC, n_legend_points, darkMode=False):
265 """Draws legend for hit colour"""
266 h['simpleDisplay'].cd(1)
267 n_legend_points = 5
268 padLegScifi = ROOT.TPad("legend","legend",0.4,0.15,0.4+0.27, 0.15+0.25)
269 padLegScifi.SetFillStyle(4000)
270 padLegScifi.Draw()
271 padLegScifi.cd()
272 text_scifi_legend = ROOT.TLatex()
273 text_scifi_legend.SetTextAlign(11)
274 text_scifi_legend.SetTextFont(42)
275 text_scifi_legend.SetTextSize(.15)
276 if darkMode: text_scifi_legend.SetTextColor(ROOT.kWhite)
277 for i in range(n_legend_points) :
278 if i < (n_legend_points - 1) :
279 text_scifi_legend.DrawLatex((i+0.3)*(1./(n_legend_points+2)), 0.2, "{:d}".format(int(i*max_density/(n_legend_points-1))))
280 text_scifi_legend.DrawLatex((i+0.3)*(1./(n_legend_points+2)), 0., "{:.0f}".format(int(i*max_QDC/(n_legend_points-1))))
281 else :
282 text_scifi_legend.DrawLatex((i+0.3)*(1./(n_legend_points+2)), 0.2, "{:d} SciFi hits/cm".format(int(i*max_density/(n_legend_points-1))))
283 text_scifi_legend.DrawLatex((i+0.3)*(1./(n_legend_points+2)), 0., "{:.0f} QDC units".format(int(i*max_QDC/(n_legend_points-1))))
284
285 h["markerCollection"].append(ROOT.TEllipse((i+0.15)*(1./(n_legend_points+2)), 0.26, 0.05/4, 0.05))
286 if not darkMode:
287 h["markerCollection"][-1].SetFillColor(ROOT.TColor.GetPalette()[int(float(i*max_density/(n_legend_points-1))/max_density*(len(ROOT.TColor.GetPalette())-1))])
288 else:
289 h["markerCollection"][-1].SetFillColor(ROOT.TColor.GetPalette()[int((1/4+3/4*float(i*max_density/(n_legend_points-1))/max_density)*(len(ROOT.TColor.GetPalette())-1))])
290 h["markerCollection"][-1].Draw("SAME")
291
292 h["markerCollection"].append(ROOT.TBox((i+0.15)*(1./(n_legend_points+2))-0.05/4 , 0.06 - 0.05, (i+0.15)*(1./(n_legend_points+2))+0.05/4, 0.06 + 0.05))
293 if not darkMode:
294 h["markerCollection"][-1].SetFillColor(ROOT.TColor.GetPalette()[int(float(i*max_QDC/(n_legend_points-1))/max_QDC*(len(ROOT.TColor.GetPalette())-1))])
295 else:
296 h["markerCollection"][-1].SetFillColor(ROOT.TColor.GetPalette()[int((1/4+3/4*float(i*max_QDC/(n_legend_points-1))/max_QDC)*(len(ROOT.TColor.GetPalette())-1))])
297 h["markerCollection"][-1].Draw("SAME")
298
299def drawSciFiHits(g, colour):
300 """Takes TGraph g and draws the graphs markers with the TColor given in list colour."""
301 n = g.GetN()
302 # Draw highest density points last
303 sorted_indices = np.argsort(colour)
304 for i_unsorted in range(0, n):
305 i = int(sorted_indices[i_unsorted])
306
307 x = g.GetPointX(i)
308 y = g.GetPointY(i)
309
310 h["markerCollection"].append(ROOT.TEllipse(x, y, 1.5, 1.5))
311 h["markerCollection"][-1].SetLineWidth(0)
312 h["markerCollection"][-1].SetFillColor(colour[i])
313 h["markerCollection"][-1].Draw("SAME")
314
315def loopEvents(
316 start=0,
317 save=False,
318 goodEvents=False,
319 withTrack=-1,
320 withHoughTrack=-1,
321 nTracks=0,
322 minSipmMult=1,
323 withTiming=False,
324 option=None,
325 Setup='TI18',
326 verbose=0,
327 auto=False,
328 hitColour=None,
329 FilterScifiHits=None,
330 darkMode=False
331 ):
332 if darkMode:
333 ROOT.gStyle.SetCanvasColor(ROOT.kBlack)
334
335 # check the format of FilterScifiHits if set
336 if FilterScifiHits:
337 important_keys = {"bins_x", "min_x", "max_x", "time_lower_range", "time_upper_range"}
338 all_keys = important_keys.copy()
339 all_keys.add("method")
340 filter_parameters = {"bins_x":52., "min_x":0., "max_x":26.,
341 "time_lower_range":1E9/(2*u.snd_freq/u.hertz),
342 "time_upper_range":2E9/(u.snd_freq/u.hertz),
343 "method":0}
344 if FilterScifiHits!="default" and not important_keys.issubset(FilterScifiHits):
345 logging.fatal("Invalid FilterScifiHits format. Two options are supported:\n"
346 "#1 FilterScifiHits = 'default'\nwhich sets the default parameters:\n"+
347 str(filter_parameters)+" or\n"
348 "#2 FilterScifiHits = filter_dictionary \nwhere filter_dictionary has all of the following keys\n"+
349 str(important_keys)+"\nAn additional key 'method' exists: its single supported value, also default, is 0.")
350 return
351 if FilterScifiHits!="default" and any(k not in all_keys for k in FilterScifiHits):
352 logging.warning("Ignoring provided keys other than "+str(all_keys))
353
354 if 'simpleDisplay' not in h:
355 ut.bookCanvas(h,key='simpleDisplay',title='simple event display',nx=1200,ny=1016,cx=1,cy=2)
356
357 h['simpleDisplay'].cd(1)
358 # TI18 coordinate system
359 zStart = 250.
360 zEnd = 600.
361 xStart = -100.
362 yStart = -30.
363 if Setup == 'H6': zStart = 60.
364 if Setup == 'TP': zStart = -50. # old coordinate system with origin in middle of target
365 if Setup == 'H4':
366 xStart = -110.
367 yStart = -10.
368 zStart = 300.
369 zEnd = 430.
370 if 'xz' in h:
371 h.pop('xz').Delete()
372 h.pop('yz').Delete()
373 else:
374 h['xmin'],h['xmax'] = xStart,xStart+110.
375 h['ymin'],h['ymax'] = yStart,yStart+110.
376 h['zmin'],h['zmax'] = zStart,zEnd
377 for d in ['xmin','xmax','ymin','ymax','zmin','zmax']: h['c'+d]=h[d]
378 ut.bookHist(h,'xz','; z [cm]; x [cm]',500,h['czmin'],h['czmax'],100,h['cxmin'],h['cxmax'])
379 ut.bookHist(h,'yz','; z [cm]; y [cm]',500,h['czmin'],h['czmax'],100,h['cymin'],h['cymax'])
380 if darkMode:
381 for hist in [h['xz'], h['yz']]:
382 # set axis lines, labels, titles to white in dark mode
383 hist.GetXaxis().SetAxisColor(ROOT.kWhite)
384 hist.GetYaxis().SetAxisColor(ROOT.kWhite)
385 hist.GetXaxis().SetLabelColor(ROOT.kWhite)
386 hist.GetYaxis().SetLabelColor(ROOT.kWhite)
387 hist.GetXaxis().SetTitleColor(ROOT.kWhite)
388 hist.GetYaxis().SetTitleColor(ROOT.kWhite)
389
390 proj = {1:'xz',2:'yz'}
391 h['xz'].SetStats(0)
392 h['yz'].SetStats(0)
393
394 N = -1
395 Tprev = -1
396 A,B = ROOT.TVector3(),ROOT.TVector3()
397 ptext={0:' Y projection',1:' X projection'}
398 text = ROOT.TLatex()
399 event = eventTree
400 OT = sink.GetOutTree()
401 if withTrack==0 or withHoughTrack==0: OT = eventTree
402 if type(start) == type(1):
403 s = start
404 e = event.GetEntries()
405 else:
406 s = 0
407 e = len(start)
408 for N in range(s,e):
409 if type(start) == type(1): rc = event.GetEvent(N)
410 else: rc = event.GetEvent(start[N])
411 if goodEvents and not goodEvent(event): continue
412 nHoughtracks = 0
413 OT.Reco_MuonTracks = ROOT.TObjArray(10)
414 if withHoughTrack > 0:
415 rc = source.GetInTree().GetEvent(N)
416 # Delete SndlhcMuonReco kalman tracks container
417 for ht_task in HT_tasks.values():
418 ht_task.kalman_tracks.Delete()
419 if withHoughTrack==1:
420 HT_tasks['muon_reco_task_Sf'].Exec(0)
421 HT_tasks['muon_reco_task_DS'].Exec(0)
422 elif withHoughTrack==2:
423 HT_tasks['muon_reco_task_Sf'].Exec(0)
424 elif withHoughTrack==3:
425 HT_tasks['muon_reco_task_DS'].Exec(0)
426 elif withHoughTrack==4:
427 HT_tasks['muon_reco_task_nuInt'].Exec(0)
428 # Save the tracks in OT.Reco_MuonTracks object
429 for ht_task in HT_tasks.values():
430 for trk in ht_task.kalman_tracks:
431 OT.Reco_MuonTracks.Add(trk)
432 nHoughtracks = OT.Reco_MuonTracks.GetEntries()
433 if nHoughtracks>0: print('number of tracks by HT:', nHoughtracks)
434
435 if withTrack > 0:
436 # Delete SndlhcTracking fitted tracks container
437 trackTask.fittedTracks.Delete()
438 if withTrack==1:
439 trackTask.ExecuteTask("ScifiDS")
440 elif withTrack==2:
441 trackTask.ExecuteTask("Scifi")
442 elif withTrack==3:
443 trackTask.ExecuteTask("DS")
444 # Save found tracks
445 for trk in trackTask.fittedTracks:
446 OT.Reco_MuonTracks.Add(trk)
447 ntracks = len(OT.Reco_MuonTracks) - nHoughtracks
448 if ntracks>0: print('number of tracks by ST:', ntracks)
449 nAlltracks = len(OT.Reco_MuonTracks)
450 if nAlltracks<nTracks: continue
451
452 if verbose>0:
453 for aTrack in OT.Reco_MuonTracks:
454 print(aTrack.__repr__())
455 mom = aTrack.getFittedState().getMom()
456 pos = aTrack.getFittedState().getPos()
457 mom.Print()
458 pos.Print()
459 T,dT = 0,0
460 T = event.EventHeader.GetEventTime()
461 runId = eventTree.EventHeader.GetRunId()
462 if Tprev >0: dT = T-Tprev
463 Tprev = T
464 if nAlltracks > 0: print('total number of tracks: ', nAlltracks)
465
466 digis = []
467 if event.FindBranch("Digi_ScifiHits"):
468 scifi_digis = event.Digi_ScifiHits
469 method = 0
470 if FilterScifiHits!=None and FilterScifiHits!="default":
471 filter_parameters = {k: FilterScifiHits[k] for k in important_keys if k in FilterScifiHits}
472 method = FilterScifiHits.get("method", 0) # set to the default 0, if item is not provided
473 if FilterScifiHits and (Setup=="TI18" or Setup=="H8" or Setup=="H4"):
474 setup = Setup
475 # Only H8 is explicitly supported in the SciFi tools. However, the same baby SciFi
476 # system was reused in H4. It is then safe to use the SciFi tools for H4 as well.
477 if Setup =="H4":
478 setup ="H8"
479 # Convert the filter_parameters to the needed std.map format
480 selection_parameters = ROOT.std.map('string', 'float')()
481 selection_parameters["bins_x"] = float(filter_parameters["bins_x"])
482 selection_parameters["min_x"] = float(filter_parameters["min_x"])
483 selection_parameters["max_x"] = float(filter_parameters["max_x"])
484 selection_parameters["time_lower_range"] = float(filter_parameters["time_lower_range"])
485 selection_parameters["time_upper_range"] = float(filter_parameters["time_upper_range"])
486 scifi_digis = ROOT.snd.analysis_tools.filterScifiHits(event.Digi_ScifiHits,selection_parameters,method,setup,mc)
487 else:
488 if FilterScifiHits:
489 logging.warning(Setup+" is not supported for the time-filtering of SciFi hits, using all hits instead.")
490 digis.append(scifi_digis)
491 run_conf = ROOT.snd.Configuration.Option.ti18_2022_2023
492 if Setup=='TI18' or Setup=="H6":
493 run_conf = ROOT.snd.Configuration.Option.ti18_2022_2023
494 elif Setup == "H8":
495 ROOT.snd.Configuration.Option.test_beam_2023
496 elif Setup == "H4":
497 ROOT.snd.Configuration.Option.test_beam_2024
498 else:
499 print("Going for default setup TI18")
500 configuration = ROOT.snd.Configuration(run_conf, geo.modules['Scifi'], geo.modules['MuFilter'])
501 scifi_planes = ROOT.snd.analysis_tools.FillScifi(configuration, scifi_digis, geo.modules['Scifi'])
502 us_planes = ROOT.snd.analysis_tools.FillUS(configuration, event.Digi_MuFilterHits, geo.modules['MuFilter'], mc)
503 if event.FindBranch("Digi_MuFilterHits"): digis.append(event.Digi_MuFilterHits)
504 if event.FindBranch("Digi_MuFilterHit"): digis.append(event.Digi_MuFilterHit)
505 empty = True
506 for x in digis:
507 if x.GetEntries()>0:
508 if empty: print( "event -> %i"%N)
509 empty = False
510 if empty: continue
511 h['hitCollectionX']= {'Veto':[0,ROOT.TGraphErrors()],'Scifi':[0,ROOT.TGraphErrors()],'DS':[0,ROOT.TGraphErrors()]}
512 h['hitCollectionY']= {'Veto':[0,ROOT.TGraphErrors()],'Scifi':[0,ROOT.TGraphErrors()],'US':[0,ROOT.TGraphErrors()],'DS':[0,ROOT.TGraphErrors()]}
513 if hitColour:
514 h['hitColourX'] = {'Veto': [], 'Scifi': [], 'DS' : []}
515 h['hitColourY'] = {'Veto': [], 'Scifi' : [], 'US' : [], 'DS' : []}
516 h["markerCollection"] = []
517
518 h['firedChannelsX']= {'Veto':[0,0,0,0],'Scifi':[0,0,0],'DS':[0,0,0]}
519 h['firedChannelsY']= {'Veto':[0,0,0,0],'Scifi':[0,0,0],'US':[0,0,0,0],'DS':[0,0,0,0]}
520 systems = {1:'Veto',2:'US',3:'DS',0:'Scifi'}
521 for collection in ['hitCollectionX','hitCollectionY']:
522 for c in h[collection]:
523 rc=h[collection][c][1].SetName(c)
524 rc=h[collection][c][1].Set(0)
525
526 if hitColour:
527 h["markerCollection"] = []
528
529 #Do we still use these lines? Seems no.
530 #And for events having all negative QDCs minT[1] is returned empty and the display crashes.
531 #dTs = "%5.2Fns"%(dT/u.snd_freq*1E9)
532 # find detector which triggered
533 #minT = firstTimeStamp(event)
534 #dTs+= " " + str(minT[1].GetDetectorID())
535 for p in proj:
536 rc = h[ 'simpleDisplay'].cd(p)
537 h[proj[p]].Draw('b')
538
539 if options.drawCollAxis:
540 for k in proj:
541 drawCollisionAxis(h['simpleDisplay'], k)
542
543 if withDetector:
544 drawDetectors(darkMode=darkMode)
545 for D in digis:
546 for digi in D:
547 detID = digi.GetDetectorID()
548 sipmMult = 1
549 if digi.GetName() == 'MuFilterHit':
550 system = digi.GetSystem()
551 geo.modules['MuFilter'].GetPosition(detID,A,B)
552 sipmMult = len(digi.GetAllSignals(False,False))
553 if sipmMult<minSipmMult and (system==1 or system==2): continue
554 else:
555 geo.modules['Scifi'].GetSiPMPosition(detID,A,B)
556 system = 0
557 curPath = nav.GetPath()
558 tmp = curPath.rfind('/')
559 nav.cd(curPath[:tmp])
560 first = True
561 for X in [A, B]:
562 if not first and not with2Points:
563 continue
564 first = False
565 globA, locA = array('d', [X[0], X[1], X[2]]), array('d', [X[0], X[1], X[2]])
566 if trans2local:
567 nav.MasterToLocal(globA, locA)
568 Z = X[2]
569 if digi.isVertical():
570 # only using hits with positive qdc for centroids, so only show such
571 if options.drawShowerDir and system==0 and digi.GetSignal(0)<0:
572 continue
573 collection = 'hitCollectionX'
574 Y = locA[0]
575 sY = detSize[system][0]
576 else:
577 # only using hits with positive qdc for centroids, so only show such
578 if options.drawShowerDir and system==0 and digi.GetSignal(0)<0:
579 continue
580 collection = 'hitCollectionY'
581 Y = locA[1]
582 sY = detSize[system][1]
583 c = h[collection][systems[system]]
584 rc = c[1].SetPoint(c[0], Z, Y)
585 rc = c[1].SetPointError(c[0], detSize[system][2], sY)
586 c[0] += 1
587 if hitColour == "q" :
588 max_QDC = 200 * 16
589 this_qdc = 0
590 ns = max(1,digi.GetnSides())
591 for side in range(ns):
592 for m in range(digi.GetnSiPMs()):
593 qdc = digi.GetSignal(m+side*digi.GetnSiPMs())
594 if not qdc < 0 :
595 this_qdc += qdc
596 if this_qdc > max_QDC :
597 this_qdc = max_QDC
598 if not darkMode:
599 fillNode(curPath, ROOT.TColor.GetPalette()[int(this_qdc/max_QDC*(len(ROOT.TColor.GetPalette())-1))])
600 else: # for dark mode, only use the lighter part of the colormap; the dark hues have bad contrast
601 fillNode(curPath, ROOT.TColor.GetPalette()[int((1/4 + 3/4*this_qdc/max_QDC) * (len(ROOT.TColor.GetPalette())-1))])
602 else :
603 fillNode(curPath, darkMode=darkMode)
604
605 if digi.isVertical(): F = 'firedChannelsX'
606 else: F = 'firedChannelsY'
607 ns = max(1,digi.GetnSides())
608 for side in range(ns):
609 for m in range(digi.GetnSiPMs()):
610 qdc = digi.GetSignal(m+side*digi.GetnSiPMs())
611 if qdc < 0 and qdc > -900: h[F][systems[system]][1]+=1
612 elif not qdc<0:
613 h[F][systems[system]][0]+=1
614 if len(h[F][systems[system]]) < 2+side: continue
615 h[F][systems[system]][2+side]+=qdc
616 h['hitCollectionY']['Scifi'][1].SetMarkerColor(ROOT.kBlue+2 if not darkMode else ROOT.kBlue-4)
617 h['hitCollectionX']['Scifi'][1].SetMarkerColor(ROOT.kBlue+2 if not darkMode else ROOT.kBlue-4)
618
619 if hitColour == "q" :
620 for orientation in ['X', 'Y']:
621 max_density = 40
622 density = np.clip(0, max_density, getSciFiHitDensity(h['hitCollection'+orientation]['Scifi'][1]))
623 for i in range(h['hitCollection'+orientation]['Scifi'][1].GetN()) :
624 if not darkMode:
625 h['hitColour'+orientation]['Scifi'].append(ROOT.TColor.GetPalette()[int(float(density[i])/max_density*(len(ROOT.TColor.GetPalette())-1))])
626 else: # for dark mode, only use the lighter part of the colormap; the dark hues have bad contrast
627 h['hitColour'+orientation]['Scifi'].append(ROOT.TColor.GetPalette()[int((1/4 + 3/4*float(density[i])/max_density) * (len(ROOT.TColor.GetPalette())-1))])
628
629 drawLegend(max_density, max_QDC, 5, darkMode=darkMode)
630
631 k = 1
632 moreEventInfo = []
633
634
635 for collection in ['hitCollectionX','hitCollectionY']:
636 h['simpleDisplay'].cd(k)
637 drawInfo(h['simpleDisplay'], k, runId, N, T, darkMode=darkMode)
638 k+=1
639 for c in h[collection]:
640 F = collection.replace('hitCollection','firedChannels')
641 pj = collection.split('ion')[1]
642 if pj =="X" or c=="Scifi":
643 atext = "%1s %5s %3i +:%3i -:%3i qdc :%5.1F"%(pj,c,h[collection][c][1].GetN(),h[F][c][0],h[F][c][1],h[F][c][2])
644 else:
645 atext = "%1s %5s %3i +:%3i -:%3i qdcL:%5.1F qdcR:%5.1F"%(pj,c,h[collection][c][1].GetN(),h[F][c][0],h[F][c][1],h[F][c][2],h[F][c][3])
646 moreEventInfo.append(atext)
647 print(atext)
648 if h[collection][c][1].GetN()<1: continue
649 if c=='Scifi':
650 if hitColour not in ["q"] :
651 h[collection][c][1].SetMarkerStyle(20)
652 h[collection][c][1].SetMarkerSize(1.5)
653 rc=h[collection][c][1].Draw('sameP')
654 h['display:'+c]=h[collection][c][1]
655 elif hitColour == "q" :
656 drawSciFiHits(h[collection][c][1], h['hitColour'+collection[-1]][c])
657
658 T0 = eventTree.EventHeader.GetEventTime()
659 if type(start) == type(1): rc = event.GetEvent(N-1)
660 else: rc = event.GetEvent(start[N]-1)
661 delTM1 = eventTree.EventHeader.GetEventTime() - T0
662 if type(start) == type(1): rc = event.GetEvent(N+1)
663 else: rc = event.GetEvent(start[N]+1)
664 delTP1 = eventTree.EventHeader.GetEventTime() - T0
665 atext = "timing info, prev event: %6i cc next event: %6i cc"%(delTM1,delTP1)
666 moreEventInfo.append(atext)
667 if type(start) == type(1): rc = event.GetEvent(N)
668 else: rc = event.GetEvent(start[N])
669
670 k = 1
671 for collection in ['hitCollectionX','hitCollectionY']:
672 h['simpleDisplay'].cd(k)
673 drawInfo(h['simpleDisplay'], k, runId, N, T,moreEventInfo, darkMode=darkMode)
674 k+=1
675
676 h['simpleDisplay'].Update()
677 if withTiming: timingOfEvent()
678 addTrack(OT, darkMode=darkMode)
679
680 # try finding shower direction and intercept
681 if options.drawShowerDir:
682 sh_scifi_planes, sh_us_planes = ROOT.snd.analysis_tools.GetShoweringPlanes(scifi_planes, us_planes)
683 ref_point, shower_direction = ROOT.snd.analysis_tools.GetShowerInterceptAndDirection(configuration, sh_scifi_planes, sh_us_planes)
684 is_nan = np.isnan([ref_point.X(), ref_point.Y(), ref_point.Z(), shower_direction.X(), shower_direction.Y(), shower_direction.Z()]).any()
685 if is_nan:
686 print("Found shower direction and/or intercept contain NaN value")
687 # try finding the shower origin
688 else:
689 shower_start = 10*ROOT.snd.analysis_tools.GetScifiShowerStart(scifi_planes)
690 if shower_start<0:
691 print("Could not find shower start in SciFi, going for US")
692 shower_start = 100*ROOT.snd.analysis_tools.GetUSShowerStart(us_planes)
693 if shower_start<0:
694 print("Could not find shower start in SciFi or in US")
695 else:
696 for k in proj:
697 if k==1:
698 drawShowerAxis(h['simpleDisplay'], k, shower_start, ref_point.X(),
699 shower_direction.X()/shower_direction.Z())
700 else:
701 drawShowerAxis(h['simpleDisplay'], k, shower_start, ref_point.Y(),
702 shower_direction.Y()/shower_direction.Z())
703 h['simpleDisplay'].Update()
704
705 if option == "2tracks":
706 rc = twoTrackEvent(sMin=10,dClMin=7,minDistance=0.5,sepDistance=0.5, darkMode=darkMode)
707 if not rc: rc = twoTrackEvent(sMin=10,dClMin=7,minDistance=0.5,sepDistance=0.75, darkMode=darkMode)
708 if not rc: rc = twoTrackEvent(sMin=10,dClMin=7,minDistance=0.5,sepDistance=1.0, darkMode=darkMode)
709 if not rc: rc = twoTrackEvent(sMin=10,dClMin=7,minDistance=0.5,sepDistance=1.75, darkMode=darkMode)
710 if not rc: rc = twoTrackEvent(sMin=10,dClMin=7,minDistance=0.5,sepDistance=2.5, darkMode=darkMode)
711 if not rc: rc = twoTrackEvent(sMin=10,dClMin=7,minDistance=0.5,sepDistance=3.0, darkMode=darkMode)
712
713 if verbose>0: dumpChannels()
714 userProcessing(event)
715
716 if save:
717 h['simpleDisplay'].Print('{:0>2d}-event_{:04d}'.format(runId, N) + '.' + options.extension)
718 if auto:
719 h['simpleDisplay'].Print(options.storePic + str(runId) + '-event_' + str(event.EventHeader.GetEventNumber()) + '.' + options.extension)
720 if not auto:
721 rc = input("hit return for next event or p for print or q for quit: ")
722 if rc=='p':
723 h['simpleDisplay'].Print(options.storePic + str(runId) + '-event_' + str(event.EventHeader.GetEventNumber()) + '.' + options.extension)
724 elif rc == 'q':
725 break
726 else:
727 eventComment[f"{runId}-event_{event.EventHeader.GetEventNumber()}"] = rc
728 if save:
729 os.system("convert -delay 60 -loop 0 event*." + options.extension + " animated.gif")
730
731def addTrack(OT,scifi=False, darkMode=False):
732 xax = h['xz'].GetXaxis()
733 nTrack = 0
734 for aTrack in OT.Reco_MuonTracks:
735 trackColor = ROOT.kRed
736 if aTrack.GetUniqueID()==1:
737 trackColor = ROOT.kBlue+2 if not darkMode else ROOT.kBlue-4
738 flightDir = trackTask.trackDir(aTrack)
739 print('flight direction: %5.3F significance: %5.3F'%(flightDir[0],flightDir[1]))
740 if aTrack.GetUniqueID()==3: trackColor = ROOT.kBlack if not darkMode else ROOT.kWhite
741 if aTrack.GetUniqueID()==11: trackColor = ROOT.kAzure-2 if not darkMode else ROOT.kAzure-3 # HT scifi track
742 if aTrack.GetUniqueID()==13: trackColor = ROOT.kGray+2 if not darkMode else ROOT.kGray # HT ds track
743 # HT cross-system track fit
744 if aTrack.GetUniqueID()==15: trackColor = ROOT.kOrange+7
745 S = aTrack.getFitStatus()
746 if not S.isFitConverged() and (scifi or (aTrack.GetUniqueID()==1 or aTrack.GetUniqueID()==11) ):# scifi trk object ids are 1 or 11(Hough tracking)
747 print('not converge')
748 continue
749 for p in [0,1]:
750 h['aLine'+str(nTrack*10+p)] = ROOT.TGraph()
751
752 # draw the track line starting from the most upstream Veto plane
753 zEx = h['veto0_z']
754 mom = aTrack.getFittedState().getMom()
755 pos = aTrack.getFittedState().getPos()
756 lam = (zEx-pos.z())/mom.z()
757 Ex = [pos.x()+lam*mom.x(),pos.y()+lam*mom.y()]
758 for p in [0,1]: h['aLine'+str(nTrack*10+p)].SetPoint(0,zEx,Ex[p])
759
760 for i in range(aTrack.getNumPointsWithMeasurement()):
761 state = aTrack.getFittedState(i)
762 pos = state.getPos()
763 for p in [0,1]:
764 h['aLine'+str(nTrack*10+p)].SetPoint(i+1,pos[2],pos[p])
765
766 zEx = xax.GetBinCenter(xax.GetLast())
767 mom = aTrack.getFittedState().getMom()
768 pos = aTrack.getFittedState().getPos()
769 lam = (zEx-pos.z())/mom.z()
770 Ex = [pos.x()+lam*mom.x(),pos.y()+lam*mom.y()]
771 for p in [0,1]: h['aLine'+str(nTrack*10+p)].SetPoint(i+2,zEx,Ex[p])
772
773 for p in [0,1]:
774 tc = h[ 'simpleDisplay'].cd(p+1)
775 h['aLine'+str(nTrack*10+p)].SetLineColor(trackColor)
776 h['aLine'+str(nTrack*10+p)].SetLineWidth(2)
777 h['aLine'+str(nTrack*10+p)].Draw('same')
778 tc.Update()
779 h[ 'simpleDisplay'].Update()
780 nTrack+=1
781
782def twoTrackEvent(sMin=10,dClMin=7,minDistance=1.5,sepDistance=0.5, darkMode=False):
783 trackTask.clusScifi.Clear()
784 trackTask.scifiCluster()
785 clusters = trackTask.clusScifi
786 sortedClusters={}
787 for aCl in clusters:
788 so = aCl.GetFirst()//100000
789 if not so in sortedClusters: sortedClusters[so]=[]
790 sortedClusters[so].append(aCl)
791 if len(sortedClusters)<sMin: return False
792 M=0
793 for x in sortedClusters:
794 if len(sortedClusters[x]) == 2: M+=1
795 if M < dClMin: return False
796 seeds = {}
797 S = [-1,-1]
798 for o in range(0,2):
799# same procedure for both projections
800# take seeds from from first station with 2 clusters
801 for s in range(1,6):
802 x = 10*s+o
803 if x in sortedClusters:
804 if len(sortedClusters[x])==2:
805 sortedClusters[x][0].GetPosition(A,B)
806 if o%2==1: pos0 = (A[0]+B[0])/2
807 else: pos0 = (A[1]+B[1])/2
808 sortedClusters[x][1].GetPosition(A,B)
809 if o%2==1: pos1 = (A[0]+B[0])/2
810 else: pos1 = (A[1]+B[1])/2
811 if abs(pos0-pos1) > minDistance:
812 S[o] = s
813 break
814 if S[o]<0: break # no seed found
815 seeds[o]={}
816 k = -1
817 for c in sortedClusters[S[o]*10+o]:
818 k += 1
819 c.GetPosition(A,B)
820 if o%2==1: pos = (A[0]+B[0])/2
821 else: pos = (A[1]+B[1])/2
822 seeds[o][k] = [[c,pos]]
823 if k!=1: continue
824 if abs(seeds[o][0][0][1] - seeds[o][1][0][1]) < sepDistance: continue
825 for s in range(1,6):
826 if s==S[o]: continue
827 for c in sortedClusters[s*10+o]:
828 c.GetPosition(A,B)
829 if o%2==1: pos = (A[0]+B[0])/2
830 else: pos = (A[1]+B[1])/2
831 for k in range(2):
832 if abs(seeds[o][k][0][1] - pos) < sepDistance:
833 seeds[o][k].append([c,pos])
834 if S[0]<0 or S[1]<0:
835 passed = False
836 else:
837 passed = True
838 for o in range(0,2):
839 for k in range(2):
840 if len(seeds[o][k])<3:
841 passed = False
842 break
843 print(passed)
844 if passed:
845 tracks = []
846 for k in range(2):
847 # arbitrarly combine X and Y of combination 0
848 n = 0
849 hitlist = {}
850 for o in range(0,2):
851 for X in seeds[o][k]:
852 hitlist[n] = X[0]
853 n+=1
854 theTrack = trackTask.fitTrack(hitlist)
855 if not hasattr(theTrack,"getFittedState"):
856 validTrack = False
857 continue
858 fitStatus = theTrack.getFitStatus()
859 if not fitStatus.isFitConverged():
860 theTrack.Delete()
861 else:
862 tracks.append(theTrack)
863 if len(tracks)==2:
864 OT = sink.GetOutTree()
865 OT.Reco_MuonTracks = tracks
866 addTrack(OT,True, darkMode=darkMode)
867 return passed
868
869def drawDetectors(darkMode=False):
870 nodes = {'volMuFilter_1/volFeBlockEnd_1':ROOT.kGreen-6 if not darkMode else ROOT.kGreen-2}
871 for i in range(mi.NVetoPlanes):
872 nodes['volVeto_1/volVetoPlane_{}_{}'.format(i, i)]=ROOT.kRed
873 for j in range(mi.NVetoBars):
874 if i<2: nodes['volVeto_1/volVetoPlane_{}_{}/volVetoBar_1{}{:0>3d}'.format(i, i, i, j)]=ROOT.kRed
875 if i==2: nodes['volVeto_1/volVetoPlane_{}_{}/volVetoBar_ver_1{}{:0>3d}'.format(i, i, i, j)]=ROOT.kRed
876 if i<2: nodes['volVeto_1/subVetoBox_{}'.format(i)]=ROOT.kGray+1 if not darkMode else ROOT.kGray+2
877 if i==2: nodes['volVeto_1/subVeto3Box_{}'.format(i)]=ROOT.kGray+1 if not darkMode else ROOT.kGray+2
878 for i in range(si.nscifi): # number of scifi stations
879 nodes['volTarget_1/ScifiVolume{}_{}000000'.format(i+1, i+1)]=ROOT.kBlue+1 if not darkMode else ROOT.kCyan-6
880 # iron blocks btw SciFi planes in the testbeam 2023-2024 det layout
881 nodes['volTarget_1/volFeTarget{}_1'.format(i+1)]=ROOT.kGreen-6 if not darkMode else ROOT.kGreen-2
882 for i in range(em.wall): # number of target walls
883 nodes['volTarget_1/volWallborder_{}'.format(i)]=ROOT.kGray if not darkMode else ROOT.kGray+2
884 for i in range(mi.NDownstreamPlanes):
885 nodes['volMuFilter_1/volMuDownstreamDet_{}_{}'.format(i, i+mi.NVetoPlanes+mi.NUpstreamPlanes)]=ROOT.kBlue+1 if not darkMode else ROOT.kCyan-6
886 for j in range(mi.NDownstreamBars):
887 nodes['volMuFilter_1/volMuDownstreamDet_{}_{}/volMuDownstreamBar_ver_3{}{:0>3d}'.format(i, i+mi.NVetoPlanes+mi.NUpstreamPlanes, i, j+mi.NDownstreamBars)]=ROOT.kBlue+1 if not darkMode else ROOT.kCyan-6
888 if i < 3:
889 nodes['volMuFilter_1/volMuDownstreamDet_{}_{}/volMuDownstreamBar_hor_3{}{:0>3d}'.format(i, i+mi.NVetoPlanes+mi.NUpstreamPlanes, i, j)]=ROOT.kBlue+1 if not darkMode else ROOT.kCyan-6
890 for i in range(mi.NDownstreamPlanes):
891 nodes['volMuFilter_1/subDSBox_{}'.format(i+mi.NVetoPlanes+mi.NUpstreamPlanes)]=ROOT.kGray+1 if not darkMode else ROOT.kGray+2
892 for i in range(mi.NUpstreamPlanes):
893 nodes['volMuFilter_1/subUSBox_{}'.format(i+mi.NVetoPlanes)]=ROOT.kGray+1 if not darkMode else ROOT.kGray+2
894 nodes['volMuFilter_1/volMuUpstreamDet_{}_{}'.format(i, i+mi.NVetoPlanes)]=ROOT.kBlue+1 if not darkMode else ROOT.kCyan-6
895 for j in range(mi.NUpstreamBars):
896 nodes['volMuFilter_1/volMuUpstreamDet_{}_{}/volMuUpstreamBar_2{}00{}'.format(i, i+mi.NVetoPlanes, i, j)]=ROOT.kBlue+1 if not darkMode else ROOT.kCyan-6
897 nodes['volMuFilter_1/volFeBlock_{}'.format(i)]=ROOT.kGreen-6 if not darkMode else ROOT.kGreen-2
898 for i in range(mi.NVetoPlanes+mi.NUpstreamPlanes,mi.NVetoPlanes+mi.NUpstreamPlanes+mi.NDownstreamPlanes):
899 nodes['volMuFilter_1/volFeBlock_{}'.format(i)]=ROOT.kGreen-6 if not darkMode else ROOT.kGreen-2
900 passNodes = {'Block', 'Wall', 'FeTarget'}
901 xNodes = {'UpstreamBar', 'VetoBar', 'hor'}
902 proj = {'X':0,'Y':1}
903 for node_ in nodes:
904 node = '/cave_1/Detector_0/'+node_
905 for p in proj:
906 if node+p in h and any(passNode in node for passNode in passNodes):
907 X = h[node+p]
908 c = proj[p]
909 h['simpleDisplay'].cd(c+1)
910 X.Draw('f&&same')
911 X.Draw('same')
912 else:
913 # check if node exists
914 if not nav.CheckPath(node): continue
915 nav.cd(node)
916 N = nav.GetCurrentNode()
917 S = N.GetVolume().GetShape()
918 dx,dy,dz = S.GetDX(),S.GetDY(),S.GetDZ()
919 ox,oy,oz = S.GetOrigin()[0],S.GetOrigin()[1],S.GetOrigin()[2]
920 P = {}
921 M = {}
922 if p=='X' and (not any(xNode in node for xNode in xNodes) or 'VetoBar_ver' in node):
923 P['LeftBottom'] = array('d',[-dx+ox,oy,-dz+oz])
924 P['LeftTop'] = array('d',[dx+ox,oy,-dz+oz])
925 P['RightBottom'] = array('d',[-dx+ox,oy,dz+oz])
926 P['RightTop'] = array('d',[dx+ox,oy,dz+oz])
927 elif p=='Y' and 'ver' not in node:
928 P['LeftBottom'] = array('d',[ox,-dy+oy,-dz+oz])
929 P['LeftTop'] = array('d',[ox,dy+oy,-dz+oz])
930 P['RightBottom'] = array('d',[ox,-dy+oy,dz+oz])
931 P['RightTop'] = array('d',[ox,dy+oy,dz+oz])
932 else: continue
933 for C in P:
934 M[C] = array('d',[0,0,0])
935 nav.LocalToMaster(P[C],M[C])
936 if "volVetoPlane_0" in node:
937 h['veto0_z'] = M['LeftBottom'][2]
938 if "ScifiVolume" in node:
939 for st in range(1,si.nscifi+1):
940 if f"{st}_{st}000000" in node:
941 h[f"scifi{st}_z"] = M['LeftBottom'][2]
942 if "volMuUpstreamDet" in node:
943 for st in range(mi.NUpstreamPlanes):
944 if f"{st}_{st+mi.NVetoPlanes}" in node:
945 h[f"us{st+1}_z"] = M['LeftBottom'][2]
946
947 h[node+p] = ROOT.TPolyLine()
948 X = h[node+p]
949 c = proj[p]
950 X.SetPoint(0,M['LeftBottom'][2],M['LeftBottom'][c])
951 X.SetPoint(1,M['LeftTop'][2],M['LeftTop'][c])
952 X.SetPoint(2,M['RightTop'][2],M['RightTop'][c])
953 X.SetPoint(3,M['RightBottom'][2],M['RightBottom'][c])
954 X.SetPoint(4,M['LeftBottom'][2],M['LeftBottom'][c])
955 X.SetLineColor(nodes[node_])
956 X.SetLineWidth(1)
957 h['simpleDisplay'].cd(c+1)
958 if any(passNode in node for passNode in passNodes):
959 X.SetFillColorAlpha(nodes[node_], 0.5)
960 X.Draw('f&&same')
961 X.Draw('same')
962def zoom(xmin=None,xmax=None,ymin=None,ymax=None,zmin=None,zmax=None):
963# zoom() will reset to default setting
964 for d in ['xmin','xmax','ymin','ymax','zmin','zmax']:
965 if eval(d): h['c'+d]=eval(d)
966 else: h['c'+d]=h[d]
967 h['xz'].GetXaxis().SetRangeUser(h['czmin'],h['czmax'])
968 h['yz'].GetXaxis().SetRangeUser(h['czmin'],h['czmax'])
969 h['xz'].GetYaxis().SetRangeUser(h['cxmin'],h['cxmax'])
970 h['yz'].GetYaxis().SetRangeUser(h['cymin'],h['cymax'])
971 tc = h['simpleDisplay'].cd(1)
972 tc.Update()
973 tc = h['simpleDisplay'].cd(2)
974 tc.Update()
975 h['simpleDisplay'].Update()
976
977def dumpVeto():
978 muHits = {10:[],11:[]}
979 for aHit in eventTree.Digi_MuFilterHits:
980 if not aHit.isValid(): continue
981 s = aHit.GetDetectorID()//10000
982 if s>1: continue
983 p = (aHit.GetDetectorID()//1000)%10
984 bar = (aHit.GetDetectorID()%1000)%60
985 plane = s*10+p
986 muHits[plane].append(aHit)
987 for plane in [10,11]:
988 for aHit in muHits[plane]:
989 S =aHit.GetAllSignals(False,False)
990 txt = ""
991 for x in S:
992 if x[1]>0: txt+=str(x[1])+" "
993 print(plane, (aHit.GetDetectorID()%1000)%60, txt)
994
995# decode MuFilter detID
996def MuFilter_PlaneBars(detID):
997 s = detID//10000
998 l = (detID%10000)//1000 # plane number
999 bar = (detID%1000)
1000 if s>2:
1001 l=2*l
1002 if bar>59:
1003 bar=bar-60
1004 if l<6: l+=1
1005 return {'station':s,'plane':l,'bar':bar}
1006
1007def checkOtherTriggers(event,deadTime = 100,debug=False):
1008 T0 = event.EventHeader.GetEventTime()
1009 N = event.EventHeader.GetEventNumber()
1010 Nprev = 1
1011 rc = event.GetEvent(N-Nprev)
1012 dt = T0 - event.EventHeader.GetEventTime()
1013 otherFastTrigger = False
1014 otherAdvTrigger = False
1015 tightNoiseFilter = False
1016 while dt < deadTime:
1017 otherFastTrigger = False
1018 for x in event.EventHeader.GetFastNoiseFilters():
1019 if debug: print('fast:', x.first, x.second )
1020 if x.second and not x.first == 'Veto_Total': otherFastTrigger = True
1021 otherAdvTrigger = False
1022 for x in event.EventHeader.GetAdvNoiseFilters():
1023 if debug: print('adv:', x.first, x.second )
1024 if x.second and not x.first == 'VETO_Planes': otherAdvTrigger = True
1025 if debug: print('pre event ',Nprev,dt,otherFastTrigger,otherAdvTrigger)
1026 if otherFastTrigger and otherAdvTrigger:
1027 rc = event.GetEvent(N)
1028 return otherFastTrigger, otherAdvTrigger, tightNoiseFilter, Nprev, dt
1029 Nprev+=1
1030 rc = event.GetEvent(N-Nprev)
1031 dt = T0 - event.EventHeader.GetEventTime()
1032 Nprev = 1
1033 rc = event.GetEvent(N-Nprev)
1034 dt = T0 - event.EventHeader.GetEventTime()
1035 while dt < deadTime:
1036 hits = {1:0,0:0}
1037 for aHit in event.Digi_MuFilterHits:
1038 Minfo = MuFilter_PlaneBars(aHit.GetDetectorID())
1039 s,l,bar = Minfo['station'],Minfo['plane'],Minfo['bar']
1040 if s>1: continue
1041 allChannels = aHit.GetAllSignals(False,False)
1042 hits[l]+=len(allChannels)
1043 noiseFilter0 = (hits[0]+hits[1])>4.5
1044 noiseFilter1 = hits[0]>0 and hits[1]>0
1045 if debug: print('veto hits:',hits)
1046 if noiseFilter0 and noiseFilter1:
1047 tightNoiseFilter = True
1048 rc = event.GetEvent(N)
1049 return otherFastTrigger, otherAdvTrigger, tightNoiseFilter, Nprev-1, dt
1050 Nprev+=1
1051 rc = event.GetEvent(N-Nprev)
1052 dt = T0 - event.EventHeader.GetEventTime()
1053 if Nprev>1:
1054 rc = event.GetEvent(N-Nprev+1)
1055 dt = T0 - event.EventHeader.GetEventTime()
1056 rc = event.GetEvent(N)
1057 return otherFastTrigger, otherAdvTrigger, tightNoiseFilter, Nprev-1, dt
1058
1059def cleanTracks():
1060 OT = sink.GetOutTree()
1061 listOfDetIDs = {}
1062 n = 0
1063 for aTrack in OT.Reco_MuonTracks:
1064 listOfDetIDs[n] = []
1065 for i in range(aTrack.getNumPointsWithMeasurement()):
1066 M = aTrack.getPointWithMeasurement(i)
1067 R = M.getRawMeasurement()
1068 listOfDetIDs[n].append(R.getDetId())
1069 if R.getDetId()>0: listOfDetIDs[n].append(R.getDetId()-1)
1070 listOfDetIDs[n].append(R.getDetId()+1)
1071 n+=1
1072 uniqueTracks = []
1073 for n1 in range( len(listOfDetIDs) ):
1074 unique = True
1075 for n2 in range( len(listOfDetIDs) ):
1076 if n1==n2: continue
1077 I = set(listOfDetIDs[n1]).intersection(listOfDetIDs[n2])
1078 if len(I)>0: unique = False
1079 if unique: uniqueTracks.append(n1)
1080 if len(uniqueTracks)>1:
1081 for n1 in range( len(listOfDetIDs) ): print(listOfDetIDs[n1])
1082 return uniqueTracks
1083
1084def timingOfEvent(makeCluster=False,debug=False):
1085 ut.bookHist(h,'evTimeDS','cor time of hits;[ns]',70,-5.,30)
1086 ut.bookHist(h,'evTimeScifi','cor time of hits blue DS red Scifi;[ns]',70,-5.,30)
1087 ut.bookCanvas(h,'tevTime','cor time of hits',1024,768,1,1)
1088 h['evTimeScifi'].SetLineColor(ROOT.kRed)
1089 h['evTimeDS'].SetLineColor(ROOT.kBlue)
1090 h['evTimeScifi'].SetStats(0)
1091 h['evTimeDS'].SetStats(0)
1092 h['evTimeScifi'].SetLineWidth(2)
1093 h['evTimeDS'].SetLineWidth(2)
1094 if makeCluster: trackTask.scifiCluster()
1095 meanXY = {}
1096 for siCl in trackTask.clusScifi:
1097 detID = siCl.GetFirst()
1098 s = detID//1000000
1099 isVertical = detID%1000000//100000
1100 siCl.GetPosition(A,B)
1101 z=(A[2]+B[2])/2.
1102 pos = (A[1]+B[1])/2.
1103 L = abs(A[0]-B[0])/2.
1104 if isVertical:
1105 pos = (A[0]+B[0])/2.
1106 L = abs(A[1]-B[1])/2.
1107 corTime = geo.modules['Scifi'].GetCorrectedTime(detID, siCl.GetTime(), 0) - (z-firstScifi_z)/u.speedOfLight
1108 h['evTimeScifi'].Fill(corTime)
1109 if debug: print(detID,corTime,pos)
1110 for aHit in eventTree.Digi_MuFilterHits:
1111 detID = aHit.GetDetectorID()
1112 if not detID//10000==3: continue
1113 if aHit.isVertical(): nmax = 1
1114 else: nmax=2
1115 geo.modules['MuFilter'].GetPosition(detID,A,B)
1116 z=(A[2]+B[2])/2.
1117 pos = (A[1]+B[1])/2.
1118 L = abs(A[0]-B[0])/2.
1119 if isVertical:
1120 pos = (A[0]+B[0])/2.
1121 L = abs(A[1]-B[1])/2.
1122 for i in range(nmax):
1123 corTime = geo.modules['MuFilter'].GetCorrectedTime(detID, i, aHit.GetTime(i)*u.snd_TDC2ns, 0)- (z-firstScifi_z)/u.speedOfLight
1124 h['evTimeDS'].Fill(corTime)
1125 if debug: print(detID,i,corTime,pos)
1126 tc=h['tevTime'].cd()
1127 h['evTimeScifi'].Draw()
1128 h['evTimeDS'].Draw('same')
1129 tc.Update()
1130def mufiNoise():
1131 for s in range(1,4):
1132 ut.bookHist(h,'mult'+str(s),'hit mult for system '+str(s),100,-0.5,99.5)
1133 ut.bookHist(h,'multb'+str(s),'hit mult per bar for system '+str(s),20,-0.5,19.5)
1134 ut.bookHist(h,'res'+str(s),'residual system '+str(s),20,-10.,10.)
1135 OT = sink.GetOutTree()
1136 N=0
1137 for event in eventTree:
1138 N+=1
1139 if N%1000==0: print(N)
1140 OT.Reco_MuonTracks.Delete()
1141 rc = trackTask.ExecuteTask("Scifi")
1142 for aTrack in OT.Reco_MuonTracks:
1143 mom = aTrack.getFittedState().getMom()
1144 pos = aTrack.getFittedState().getPos()
1145 if not aTrack.getFitStatus().isFitConverged(): continue
1146 mult = {1:0,2:0,3:0}
1147 for aHit in eventTree.Digi_MuFilterHits:
1148 if not aHit.isValid(): continue
1149 s = aHit.GetDetectorID()//10000
1150 S = aHit.GetAllSignals(False,False)
1151 rc = h['multb'+str(s)].Fill(len(S))
1152 mult[s]+=len(S)
1153 if s==2 or s==1:
1154 geo.modules['MuFilter'].GetPosition(aHit.GetDetectorID(),A,B)
1155 y = (A[1]+B[1])/2.
1156 zEx = (A[2]+B[2])/2.
1157 lam = (zEx-pos.z())/mom.z()
1158 Ey = pos.y()+lam*mom.y()
1159 rc = h['res'+str(s)].Fill(Ey-y)
1160 for s in mult: rc = h['mult'+str(s)].Fill(mult[s])
1161 ut.bookCanvas(h,'noise','',1200,1200,2,3)
1162 for s in range(1,4):
1163 tc = h['noise'].cd(s*2-1)
1164 tc.SetLogy(1)
1165 h['mult'+str(s)].Draw()
1166 h['noise'].cd(s*2)
1167 h['multb'+str(s)].Draw()
1168 ut.bookCanvas(h,'res','',600,1200,1,3)
1169 for s in range(1,4):
1170 tc = h['res'].cd(s)
1171 h['res'+str(s)].Draw()
1172
1173def firstTimeStamp(event):
1174 tmin = [1E9,'']
1175 digis = [event.Digi_MuFilterHits,event.Digi_ScifiHits]
1176 for digi in event.Digi_ScifiHits:
1177 dt = digi.GetTime()
1178 if dt<tmin[0]:
1179 tmin[0]=dt
1180 tmin[1]=digi
1181 for digi in event.Digi_MuFilterHits:
1182 for t in digi.GetAllTimes(): # will not give time if QDC<0!
1183 dt = t.second
1184 if dt<tmin[0]:
1185 tmin[0]=dt
1186 tmin[1]=digi
1187 return tmin
1188
1189def dumpChannels(D='Digi_MuFilterHits'):
1190 X = eval("eventTree."+D)
1191 text = {}
1192 for aHit in X:
1193 side = 'L'
1194 txt = "%8i"%(aHit.GetDetectorID())
1195 for k in range(aHit.GetnSiPMs()*aHit.GetnSides()):
1196 qdc = aHit.GetSignal(k)
1197 if qdc < -900: continue
1198 i = k
1199 if not k<aHit.GetnSiPMs():
1200 i = k-aHit.GetnSiPMs()
1201 if side == 'L':
1202 txt += " | "
1203 side = 'R'
1204 txt+= " %2i:%4.1F "%(i,qdc)
1205 text[aHit.GetDetectorID()] = txt
1206 keys = list(text.keys())
1207 keys.sort()
1208 for k in keys: print(text[k])
1209
1210def fillNode(node, color=None, darkMode=False):
1211 xNodes = {'UpstreamBar', 'VetoBar', 'hor'}
1212 proj = {'X':0,'Y':1}
1213 if color == None :
1214 hcal_color = ROOT.kBlack if not darkMode else ROOT.kWhite
1215 veto_color = ROOT.kRed+1 if not darkMode else ROOT.kRed-4
1216 else :
1217 hcal_color = color
1218 veto_color = color
1219 thick = 5
1220 for p in proj:
1221 if node+p in h:
1222 X = h[node+p]
1223 if 'Veto' in node:
1224 color = veto_color
1225 else :
1226 color = hcal_color
1227
1228 if 'Downstream' in node:
1229 thick = 5
1230 c = proj[p]
1231 h[ 'simpleDisplay'].cd(c+1)
1232 X.SetFillColor(color)
1233 X.SetLineColor(color)
1234 X.SetLineWidth(thick)
1235 X.Draw('f&&same')
1236 X.Draw('same')
1237
1238def drawInfo(pad, k, run, event, timestamp,moreEventInfo=[], darkMode=False):
1239 drawLogo = True
1240 drawText = True
1241 if drawLogo:
1242 padLogo = ROOT.TPad("logo","logo",0.1,0.1,0.2,0.3)
1243 padLogo.SetFillStyle(4000)
1244 padLogo.SetFillColorAlpha(0, 0)
1245 if darkMode: padLogo.SetFillColor(ROOT.kBlack)
1246 padLogo.Draw()
1247 logo = ROOT.TImage.Open('$SNDSW_ROOT/shipLHC/Large__SND_Logo_black_cut.png') if not darkMode else ROOT.TImage.Open('$SNDSW_ROOT/shipLHC/Large__SND_Logo_white_cut.png')
1248 logo.SetConstRatio(True)
1249 padLogo.cd()
1250 logo.Draw()
1251 pad.cd(k)
1252
1253 if drawText:
1254 if k==1 or len(moreEventInfo)<5:
1255 runNumber = eventTree.EventHeader.GetRunId()
1256 timestamp_print = False
1257 if not mc and hasattr(eventTree.EventHeader, "GetUTCtimestamp"):
1258 timestamp_print = True
1259 time_event= datetime.utcfromtimestamp(eventTree.EventHeader.GetUTCtimestamp())
1260 padText = ROOT.TPad("info","info",0.19,0.1,0.6,0.3)
1261 padText.SetFillStyle(4000)
1262 padText.Draw()
1263 padText.cd()
1264 textInfo = ROOT.TLatex()
1265 textInfo.SetTextAlign(11)
1266 textInfo.SetTextFont(42)
1267 textInfo.SetTextSize(.15)
1268 if darkMode: textInfo.SetTextColor(ROOT.kWhite)
1269 textInfo.DrawLatex(0, 0.6, 'SND@LHC Experiment, CERN')
1270 if hasattr(eventTree.EventHeader,'GetEventNumber'): N = eventTree.EventHeader.GetEventNumber()
1271 else: N = event
1272 textInfo.DrawLatex(0, 0.4, 'Run / Event: '+str(run)+' / '+str(N))
1273 if timestamp_print:
1274 textInfo.DrawLatex(0, 0.2, 'Time (GMT): {}'.format(time_event))
1275 pad.cd(k)
1276 elif options.extraInfo:
1277 padText = ROOT.TPad("info","info",0.29,0.12,0.9,0.35)
1278 padText.SetFillStyle(4000)
1279 padText.Draw()
1280 padText.cd()
1281 textInfo = ROOT.TLatex()
1282 textInfo.SetTextAlign(11)
1283 textInfo.SetTextFont(42)
1284 textInfo.SetTextSize(.1)
1285 textInfo.SetTextColor((ROOT.kMagenta+2) if not darkMode else (ROOT.kMagenta-4))
1286 dely = 0.12
1287 ystart = 0.85
1288 for i in range(7):
1289 textInfo.DrawLatex(0.4, 0.9-dely*i, moreEventInfo[i])
1290 pad.cd(k)
1291
1292def drawCollisionAxis(pad, k):
1293 line_name = "collision_axis_line_" + str(k)
1294 h[line_name] = ROOT.TLine(h["zmin"], 0, h["zmax"], 0)
1295 h[line_name].SetLineColor(ROOT.kRed)
1296 h[line_name].SetLineStyle(2)
1297
1298 text_name = "collision_axis_text_" + str(k)
1299 h[text_name] = ROOT.TText(h["zmin"] + 8, 0 + 2, "Collision axis")
1300 h[text_name].SetTextAlign(12)
1301 h[text_name].SetTextFont(43)
1302 h[text_name].SetTextSize(13 * resolution_factor)
1303 h[text_name].SetTextColor(ROOT.kRed)
1304
1305 pad.cd(k)
1306 h[line_name].Draw()
1307 h[text_name].Draw()
1308
1309def drawShowerAxis(pad, k, shower_start, ref_point, shower_direction):
1310 line_name= "shower_dir_" + str(k)
1311 if shower_start<100:
1312 zpos_name='scifi'+str(shower_start//10)
1313 else:
1314 zpos_name='us'+str(shower_start//100)
1315 h[line_name] = ROOT.TLine(h[zpos_name+'_z'],
1316 ref_point+shower_direction*h[zpos_name+'_z'],
1317 h['zmax'],
1318 ref_point+shower_direction*h['zmax'])
1319 h[line_name].SetLineColor(ROOT.kPink-6)
1320 h[line_name].SetLineStyle(3)
1321 h[line_name].SetLineWidth(5)
1322 pad.cd(k)
1323 h[line_name].Draw()
drawLegend(max_density, max_QDC, n_legend_points, darkMode=False)
getSciFiHitDensity(g, x_range=0.5)