325 # check the format of FilterScifiHits if set
327 important_keys = {"bins_x", "min_x", "max_x", "time_lower_range", "time_upper_range"}
328 all_keys = important_keys.copy()
329 all_keys.add("method")
330 filter_parameters = {"bins_x":52., "min_x":0., "max_x":26.,
331 "time_lower_range":1E9/(2*u.snd_freq/u.hertz),
332 "time_upper_range":2E9/(u.snd_freq/u.hertz),
334 if FilterScifiHits!="default" and not important_keys.issubset(FilterScifiHits):
335 logging.fatal("Invalid FilterScifiHits format. Two options are supported:\n"
336 "#1 FilterScifiHits = 'default'\nwhich sets the default parameters:\n"+
337 str(filter_parameters)+" or\n"
338 "#2 FilterScifiHits = filter_dictionary \nwhere filter_dictionary has all of the following keys\n"+
339 str(important_keys)+"\nAn additional key 'method' exists: its single supported value, also default, is 0.")
341 if FilterScifiHits!="default" and any(k not in all_keys for k in FilterScifiHits):
342 logging.warning("Ignoring provided keys other than "+str(all_keys))
344 if 'simpleDisplay' not in h:
345 ut.bookCanvas(h,key='simpleDisplay',title='simple event display',nx=1200,ny=1016,cx=1,cy=2)
347 h['simpleDisplay'].cd(1)
348 # TI18 coordinate system
353 if Setup == 'H6': zStart = 60.
354 if Setup == 'TP': zStart = -50. # old coordinate system with origin in middle of target
364 h['xmin'],h['xmax'] = xStart,xStart+110.
365 h['ymin'],h['ymax'] = yStart,yStart+110.
366 h['zmin'],h['zmax'] = zStart,zEnd
367 for d in ['xmin','xmax','ymin','ymax','zmin','zmax']: h['c'+d]=h[d]
368 ut.bookHist(h,'xz','; z [cm]; x [cm]',500,h['czmin'],h['czmax'],100,h['cxmin'],h['cxmax'])
369 ut.bookHist(h,'yz','; z [cm]; y [cm]',500,h['czmin'],h['czmax'],100,h['cymin'],h['cymax'])
371 proj = {1:'xz',2:'yz'}
377 A,B = ROOT.TVector3(),ROOT.TVector3()
378 ptext={0:' Y projection',1:' X projection'}
381 OT = sink.GetOutTree()
382 if withTrack==0 or withHoughTrack==0: OT = eventTree
383 if type(start) == type(1):
385 e = event.GetEntries()
390 if type(start) == type(1): rc = event.GetEvent(N)
391 else: rc = event.GetEvent(start[N])
392 if goodEvents and not goodEvent(event): continue
394 OT.Reco_MuonTracks = ROOT.TObjArray(10)
395 if withHoughTrack > 0:
396 rc = source.GetInTree().GetEvent(N)
397 # Delete SndlhcMuonReco kalman tracks container
398 for ht_task in HT_tasks.values():
399 ht_task.kalman_tracks.Delete()
400 if withHoughTrack==1:
401 HT_tasks['muon_reco_task_Sf'].Exec(0)
402 HT_tasks['muon_reco_task_DS'].Exec(0)
403 elif withHoughTrack==2:
404 HT_tasks['muon_reco_task_Sf'].Exec(0)
405 elif withHoughTrack==3:
406 HT_tasks['muon_reco_task_DS'].Exec(0)
407 elif withHoughTrack==4:
408 HT_tasks['muon_reco_task_nuInt'].Exec(0)
409 # Save the tracks in OT.Reco_MuonTracks object
410 for ht_task in HT_tasks.values():
411 for trk in ht_task.kalman_tracks:
412 OT.Reco_MuonTracks.Add(trk)
413 nHoughtracks = OT.Reco_MuonTracks.GetEntries()
414 if nHoughtracks>0: print('number of tracks by HT:', nHoughtracks)
417 # Delete SndlhcTracking fitted tracks container
418 trackTask.fittedTracks.Delete()
420 trackTask.ExecuteTask("ScifiDS")
422 trackTask.ExecuteTask("Scifi")
424 trackTask.ExecuteTask("DS")
426 for trk in trackTask.fittedTracks:
427 OT.Reco_MuonTracks.Add(trk)
428 ntracks = len(OT.Reco_MuonTracks) - nHoughtracks
429 if ntracks>0: print('number of tracks by ST:', ntracks)
430 nAlltracks = len(OT.Reco_MuonTracks)
431 if nAlltracks<nTracks: continue
434 for aTrack in OT.Reco_MuonTracks:
435 print(aTrack.__repr__())
436 mom = aTrack.getFittedState().getMom()
437 pos = aTrack.getFittedState().getPos()
441 T = event.EventHeader.GetEventTime()
442 runId = eventTree.EventHeader.GetRunId()
443 if Tprev >0: dT = T-Tprev
445 if nAlltracks > 0: print('total number of tracks: ', nAlltracks)
448 if event.FindBranch("Digi_ScifiHits"):
449 scifi_digis = event.Digi_ScifiHits
451 if FilterScifiHits!=None and FilterScifiHits!="default":
452 filter_parameters = {k: FilterScifiHits[k] for k in important_keys if k in FilterScifiHits}
453 method = FilterScifiHits.get("method", 0) # set to the default 0, if item is not provided
454 if FilterScifiHits and (Setup=="TI18" or Setup=="H8" or Setup=="H4"):
456 # Only H8 is explicitly supported in the SciFi tools. However, the same baby SciFi
457 # system was reused in H4. It is then safe to use the SciFi tools for H4 as well.
460 # Convert the filter_parameters to the needed std.map format
461 selection_parameters = ROOT.std.map('string', 'float')()
462 selection_parameters["bins_x"] = float(filter_parameters["bins_x"])
463 selection_parameters["min_x"] = float(filter_parameters["min_x"])
464 selection_parameters["max_x"] = float(filter_parameters["max_x"])
465 selection_parameters["time_lower_range"] = float(filter_parameters["time_lower_range"])
466 selection_parameters["time_upper_range"] = float(filter_parameters["time_upper_range"])
467 scifi_digis = ROOT.snd.analysis_tools.filterScifiHits(event.Digi_ScifiHits,selection_parameters,method,setup,mc)
470 logging.warning(Setup+" is not supported for the time-filtering of SciFi hits, using all hits instead.")
471 digis.append(scifi_digis)
472 run_conf = ROOT.snd.Configuration.Option.ti18_2022_2023
473 if Setup=='TI18' or Setup=="H6":
474 run_conf = ROOT.snd.Configuration.Option.ti18_2022_2023
476 ROOT.snd.Configuration.Option.test_beam_2023
478 ROOT.snd.Configuration.Option.test_beam_2024
480 print("Going for default setup TI18")
481 configuration = ROOT.snd.Configuration(run_conf, geo.modules['Scifi'], geo.modules['MuFilter'])
482 scifi_planes = ROOT.snd.analysis_tools.FillScifi(configuration, scifi_digis, geo.modules['Scifi'])
483 us_planes = ROOT.snd.analysis_tools.FillUS(configuration, event.Digi_MuFilterHits, geo.modules['MuFilter'], mc)
484 if event.FindBranch("Digi_MuFilterHits"): digis.append(event.Digi_MuFilterHits)
485 if event.FindBranch("Digi_MuFilterHit"): digis.append(event.Digi_MuFilterHit)
489 if empty: print( "event -> %i"%N)
492 h['hitCollectionX']= {'Veto':[0,ROOT.TGraphErrors()],'Scifi':[0,ROOT.TGraphErrors()],'DS':[0,ROOT.TGraphErrors()]}
493 h['hitCollectionY']= {'Veto':[0,ROOT.TGraphErrors()],'Scifi':[0,ROOT.TGraphErrors()],'US':[0,ROOT.TGraphErrors()],'DS':[0,ROOT.TGraphErrors()]}
495 h['hitColourX'] = {'Veto': [], 'Scifi': [], 'DS' : []}
496 h['hitColourY'] = {'Veto': [], 'Scifi' : [], 'US' : [], 'DS' : []}
497 h["markerCollection"] = []
499 h['firedChannelsX']= {'Veto':[0,0,0,0],'Scifi':[0,0,0],'DS':[0,0,0]}
500 h['firedChannelsY']= {'Veto':[0,0,0,0],'Scifi':[0,0,0],'US':[0,0,0,0],'DS':[0,0,0,0]}
501 systems = {1:'Veto',2:'US',3:'DS',0:'Scifi'}
502 for collection in ['hitCollectionX','hitCollectionY']:
503 for c in h[collection]:
504 rc=h[collection][c][1].SetName(c)
505 rc=h[collection][c][1].Set(0)
508 h["markerCollection"] = []
510 #Do we still use these lines? Seems no.
511 #And for events having all negative QDCs minT[1] is returned empty and the display crashes.
512 #dTs = "%5.2Fns"%(dT/u.snd_freq*1E9)
513 # find detector which triggered
514 #minT = firstTimeStamp(event)
515 #dTs+= " " + str(minT[1].GetDetectorID())
517 rc = h[ 'simpleDisplay'].cd(p)
520 if options.drawCollAxis:
522 drawCollisionAxis(h['simpleDisplay'], k)
528 detID = digi.GetDetectorID()
530 if digi.GetName() == 'MuFilterHit':
531 system = digi.GetSystem()
532 geo.modules['MuFilter'].GetPosition(detID,A,B)
533 sipmMult = len(digi.GetAllSignals(False,False))
534 if sipmMult<minSipmMult and (system==1 or system==2): continue
536 geo.modules['Scifi'].GetSiPMPosition(detID,A,B)
538 curPath = nav.GetPath()
539 tmp = curPath.rfind('/')
540 nav.cd(curPath[:tmp])
543 if not first and not with2Points:
546 globA, locA = array('d', [X[0], X[1], X[2]]), array('d', [X[0], X[1], X[2]])
548 nav.MasterToLocal(globA, locA)
550 if digi.isVertical():
551 # only using hits with positive qdc for centroids, so only show such
552 if options.drawShowerDir and system==0 and digi.GetSignal(0)<0:
554 collection = 'hitCollectionX'
556 sY = detSize[system][0]
558 # only using hits with positive qdc for centroids, so only show such
559 if options.drawShowerDir and system==0 and digi.GetSignal(0)<0:
561 collection = 'hitCollectionY'
563 sY = detSize[system][1]
564 c = h[collection][systems[system]]
565 rc = c[1].SetPoint(c[0], Z, Y)
566 rc = c[1].SetPointError(c[0], detSize[system][2], sY)
568 if hitColour == "q" :
571 ns = max(1,digi.GetnSides())
572 for side in range(ns):
573 for m in range(digi.GetnSiPMs()):
574 qdc = digi.GetSignal(m+side*digi.GetnSiPMs())
577 if this_qdc > max_QDC :
579 fillNode(curPath, ROOT.TColor.GetPalette()[int(this_qdc/max_QDC*(len(ROOT.TColor.GetPalette())-1))])
583 if digi.isVertical(): F = 'firedChannelsX'
584 else: F = 'firedChannelsY'
585 ns = max(1,digi.GetnSides())
586 for side in range(ns):
587 for m in range(digi.GetnSiPMs()):
588 qdc = digi.GetSignal(m+side*digi.GetnSiPMs())
589 if qdc < 0 and qdc > -900: h[F][systems[system]][1]+=1
591 h[F][systems[system]][0]+=1
592 if len(h[F][systems[system]]) < 2+side: continue
593 h[F][systems[system]][2+side]+=qdc
594 h['hitCollectionY']['Scifi'][1].SetMarkerColor(ROOT.kBlue+2)
595 h['hitCollectionX']['Scifi'][1].SetMarkerColor(ROOT.kBlue+2)
597 if hitColour == "q" :
598 for orientation in ['X', 'Y']:
600 density = np.clip(0, max_density, getSciFiHitDensity(h['hitCollection'+orientation]['Scifi'][1]))
601 for i in range(h['hitCollection'+orientation]['Scifi'][1].GetN()) :
602 h['hitColour'+orientation]['Scifi'].append(ROOT.TColor.GetPalette()[int(float(density[i])/max_density*(len(ROOT.TColor.GetPalette())-1))])
604 drawLegend(max_density, max_QDC, 5)
610 for collection in ['hitCollectionX','hitCollectionY']:
611 h['simpleDisplay'].cd(k)
612 drawInfo(h['simpleDisplay'], k, runId, N, T)
614 for c in h[collection]:
615 F = collection.replace('hitCollection','firedChannels')
616 pj = collection.split('ion')[1]
617 if pj =="X" or c=="Scifi":
618 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])
620 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])
621 moreEventInfo.append(atext)
623 if h[collection][c][1].GetN()<1: continue
625 if hitColour not in ["q"] :
626 h[collection][c][1].SetMarkerStyle(20)
627 h[collection][c][1].SetMarkerSize(1.5)
628 rc=h[collection][c][1].Draw('sameP')
629 h['display:'+c]=h[collection][c][1]
630 elif hitColour == "q" :
631 drawSciFiHits(h[collection][c][1], h['hitColour'+collection[-1]][c])
633 T0 = eventTree.EventHeader.GetEventTime()
634 if type(start) == type(1): rc = event.GetEvent(N-1)
635 else: rc = event.GetEvent(start[N]-1)
636 delTM1 = eventTree.EventHeader.GetEventTime() - T0
637 if type(start) == type(1): rc = event.GetEvent(N+1)
638 else: rc = event.GetEvent(start[N]+1)
639 delTP1 = eventTree.EventHeader.GetEventTime() - T0
640 atext = "timing info, prev event: %6i cc next event: %6i cc"%(delTM1,delTP1)
641 moreEventInfo.append(atext)
642 if type(start) == type(1): rc = event.GetEvent(N)
643 else: rc = event.GetEvent(start[N])
646 for collection in ['hitCollectionX','hitCollectionY']:
647 h['simpleDisplay'].cd(k)
648 drawInfo(h['simpleDisplay'], k, runId, N, T,moreEventInfo)
651 h['simpleDisplay'].Update()
652 if withTiming: timingOfEvent()
655 # try finding shower direction and intercept
656 if options.drawShowerDir:
657 sh_scifi_planes, sh_us_planes = ROOT.snd.analysis_tools.GetShoweringPlanes(scifi_planes, us_planes)
658 ref_point, shower_direction = ROOT.snd.analysis_tools.GetShowerInterceptAndDirection(configuration, sh_scifi_planes, sh_us_planes)
659 is_nan = np.isnan([ref_point.X(), ref_point.Y(), ref_point.Z(), shower_direction.X(), shower_direction.Y(), shower_direction.Z()]).any()
661 print("Found shower direction and/or intercept contain NaN value")
662 # try finding the shower origin
664 shower_start = 10*ROOT.snd.analysis_tools.GetScifiShowerStart(scifi_planes)
666 print("Could not find shower start in SciFi, going for US")
667 shower_start = 100*ROOT.snd.analysis_tools.GetUSShowerStart(us_planes)
669 print("Could not find shower start in SciFi or in US")
673 drawShowerAxis(h['simpleDisplay'], k, shower_start, ref_point.X(),
674 shower_direction.X()/shower_direction.Z())
676 drawShowerAxis(h['simpleDisplay'], k, shower_start, ref_point.Y(),
677 shower_direction.Y()/shower_direction.Z())
678 h['simpleDisplay'].Update()
680 if option == "2tracks":
681 rc = twoTrackEvent(sMin=10,dClMin=7,minDistance=0.5,sepDistance=0.5)
682 if not rc: rc = twoTrackEvent(sMin=10,dClMin=7,minDistance=0.5,sepDistance=0.75)
683 if not rc: rc = twoTrackEvent(sMin=10,dClMin=7,minDistance=0.5,sepDistance=1.0)
684 if not rc: rc = twoTrackEvent(sMin=10,dClMin=7,minDistance=0.5,sepDistance=1.75)
685 if not rc: rc = twoTrackEvent(sMin=10,dClMin=7,minDistance=0.5,sepDistance=2.5)
686 if not rc: rc = twoTrackEvent(sMin=10,dClMin=7,minDistance=0.5,sepDistance=3.0)
688 if verbose>0: dumpChannels()
689 userProcessing(event)
692 h['simpleDisplay'].Print('{:0>2d}-event_{:04d}'.format(runId, N) + '.' + options.extension)
694 h['simpleDisplay'].Print(options.storePic + str(runId) + '-event_' + str(event.EventHeader.GetEventNumber()) + '.' + options.extension)
696 rc = input("hit return for next event or p for print or q for quit: ")
698 h['simpleDisplay'].Print(options.storePic + str(runId) + '-event_' + str(event.EventHeader.GetEventNumber()) + '.' + options.extension)
702 eventComment[f"{runId}-event_{event.EventHeader.GetEventNumber()}"] = rc
704 os.system("convert -delay 60 -loop 0 event*." + options.extension + " animated.gif")
845 nodes = {'volMuFilter_1/volFeBlockEnd_1':ROOT.kGreen-6}
846 for i in range(mi.NVetoPlanes):
847 nodes['volVeto_1/volVetoPlane_{}_{}'.format(i, i)]=ROOT.kRed
848 for j in range(mi.NVetoBars):
849 if i<2: nodes['volVeto_1/volVetoPlane_{}_{}/volVetoBar_1{}{:0>3d}'.format(i, i, i, j)]=ROOT.kRed
850 if i==2: nodes['volVeto_1/volVetoPlane_{}_{}/volVetoBar_ver_1{}{:0>3d}'.format(i, i, i, j)]=ROOT.kRed
851 if i<2: nodes['volVeto_1/subVetoBox_{}'.format(i)]=ROOT.kGray+1
852 if i==2: nodes['volVeto_1/subVeto3Box_{}'.format(i)]=ROOT.kGray+1
853 for i in range(si.nscifi): # number of scifi stations
854 nodes['volTarget_1/ScifiVolume{}_{}000000'.format(i+1, i+1)]=ROOT.kBlue+1
855 # iron blocks btw SciFi planes in the testbeam 2023-2024 det layout
856 nodes['volTarget_1/volFeTarget{}_1'.format(i+1)]=ROOT.kGreen-6
857 for i in range(em.wall): # number of target walls
858 nodes['volTarget_1/volWallborder_{}'.format(i)]=ROOT.kGray
859 for i in range(mi.NDownstreamPlanes):
860 nodes['volMuFilter_1/volMuDownstreamDet_{}_{}'.format(i, i+mi.NVetoPlanes+mi.NUpstreamPlanes)]=ROOT.kBlue+1
861 for j in range(mi.NDownstreamBars):
862 nodes['volMuFilter_1/volMuDownstreamDet_{}_{}/volMuDownstreamBar_ver_3{}{:0>3d}'.format(i, i+mi.NVetoPlanes+mi.NUpstreamPlanes, i, j+mi.NDownstreamBars)]=ROOT.kBlue+1
864 nodes['volMuFilter_1/volMuDownstreamDet_{}_{}/volMuDownstreamBar_hor_3{}{:0>3d}'.format(i, i+mi.NVetoPlanes+mi.NUpstreamPlanes, i, j)]=ROOT.kBlue+1
865 for i in range(mi.NDownstreamPlanes):
866 nodes['volMuFilter_1/subDSBox_{}'.format(i+mi.NVetoPlanes+mi.NUpstreamPlanes)]=ROOT.kGray+1
867 for i in range(mi.NUpstreamPlanes):
868 nodes['volMuFilter_1/subUSBox_{}'.format(i+mi.NVetoPlanes)]=ROOT.kGray+1
869 nodes['volMuFilter_1/volMuUpstreamDet_{}_{}'.format(i, i+mi.NVetoPlanes)]=ROOT.kBlue+1
870 for j in range(mi.NUpstreamBars):
871 nodes['volMuFilter_1/volMuUpstreamDet_{}_{}/volMuUpstreamBar_2{}00{}'.format(i, i+mi.NVetoPlanes, i, j)]=ROOT.kBlue+1
872 nodes['volMuFilter_1/volFeBlock_{}'.format(i)]=ROOT.kGreen-6
873 for i in range(mi.NVetoPlanes+mi.NUpstreamPlanes,mi.NVetoPlanes+mi.NUpstreamPlanes+mi.NDownstreamPlanes):
874 nodes['volMuFilter_1/volFeBlock_{}'.format(i)]=ROOT.kGreen-6
875 passNodes = {'Block', 'Wall', 'FeTarget'}
876 xNodes = {'UpstreamBar', 'VetoBar', 'hor'}
879 node = '/cave_1/Detector_0/'+node_
881 if node+p in h and any(passNode in node for passNode in passNodes):
884 h['simpleDisplay'].cd(c+1)
888 # check if node exists
889 if not nav.CheckPath(node): continue
891 N = nav.GetCurrentNode()
892 S = N.GetVolume().GetShape()
893 dx,dy,dz = S.GetDX(),S.GetDY(),S.GetDZ()
894 ox,oy,oz = S.GetOrigin()[0],S.GetOrigin()[1],S.GetOrigin()[2]
897 if p=='X' and (not any(xNode in node for xNode in xNodes) or 'VetoBar_ver' in node):
898 P['LeftBottom'] = array('d',[-dx+ox,oy,-dz+oz])
899 P['LeftTop'] = array('d',[dx+ox,oy,-dz+oz])
900 P['RightBottom'] = array('d',[-dx+ox,oy,dz+oz])
901 P['RightTop'] = array('d',[dx+ox,oy,dz+oz])
902 elif p=='Y' and 'ver' not in node:
903 P['LeftBottom'] = array('d',[ox,-dy+oy,-dz+oz])
904 P['LeftTop'] = array('d',[ox,dy+oy,-dz+oz])
905 P['RightBottom'] = array('d',[ox,-dy+oy,dz+oz])
906 P['RightTop'] = array('d',[ox,dy+oy,dz+oz])
909 M[C] = array('d',[0,0,0])
910 nav.LocalToMaster(P[C],M[C])
911 if "volVetoPlane_0" in node:
912 h['veto0_z'] = M['LeftBottom'][2]
913 if "ScifiVolume" in node:
914 for st in range(1,si.nscifi+1):
915 if f"{st}_{st}000000" in node:
916 h[f"scifi{st}_z"] = M['LeftBottom'][2]
917 if "volMuUpstreamDet" in node:
918 for st in range(mi.NUpstreamPlanes):
919 if f"{st}_{st+mi.NVetoPlanes}" in node:
920 h[f"us{st+1}_z"] = M['LeftBottom'][2]
922 h[node+p] = ROOT.TPolyLine()
925 X.SetPoint(0,M['LeftBottom'][2],M['LeftBottom'][c])
926 X.SetPoint(1,M['LeftTop'][2],M['LeftTop'][c])
927 X.SetPoint(2,M['RightTop'][2],M['RightTop'][c])
928 X.SetPoint(3,M['RightBottom'][2],M['RightBottom'][c])
929 X.SetPoint(4,M['LeftBottom'][2],M['LeftBottom'][c])
930 X.SetLineColor(nodes[node_])
932 h['simpleDisplay'].cd(c+1)
933 if any(passNode in node for passNode in passNodes):
934 X.SetFillColorAlpha(nodes[node_], 0.5)