329 FilterScifiHits=None,
333 ROOT.gStyle.SetCanvasColor(ROOT.kBlack)
335 # check the format of FilterScifiHits if set
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),
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.")
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))
354 if 'simpleDisplay' not in h:
355 ut.bookCanvas(h,key='simpleDisplay',title='simple event display',nx=1200,ny=1016,cx=1,cy=2)
357 h['simpleDisplay'].cd(1)
358 # TI18 coordinate system
363 if Setup == 'H6': zStart = 60.
364 if Setup == 'TP': zStart = -50. # old coordinate system with origin in middle of target
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'])
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)
390 proj = {1:'xz',2:'yz'}
396 A,B = ROOT.TVector3(),ROOT.TVector3()
397 ptext={0:' Y projection',1:' X projection'}
400 OT = sink.GetOutTree()
401 if withTrack==0 or withHoughTrack==0: OT = eventTree
402 if type(start) == type(1):
404 e = event.GetEntries()
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
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)
436 # Delete SndlhcTracking fitted tracks container
437 trackTask.fittedTracks.Delete()
439 trackTask.ExecuteTask("ScifiDS")
441 trackTask.ExecuteTask("Scifi")
443 trackTask.ExecuteTask("DS")
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
453 for aTrack in OT.Reco_MuonTracks:
454 print(aTrack.__repr__())
455 mom = aTrack.getFittedState().getMom()
456 pos = aTrack.getFittedState().getPos()
460 T = event.EventHeader.GetEventTime()
461 runId = eventTree.EventHeader.GetRunId()
462 if Tprev >0: dT = T-Tprev
464 if nAlltracks > 0: print('total number of tracks: ', nAlltracks)
467 if event.FindBranch("Digi_ScifiHits"):
468 scifi_digis = event.Digi_ScifiHits
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"):
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.
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)
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
495 ROOT.snd.Configuration.Option.test_beam_2023
497 ROOT.snd.Configuration.Option.test_beam_2024
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)
508 if empty: print( "event -> %i"%N)
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()]}
514 h['hitColourX'] = {'Veto': [], 'Scifi': [], 'DS' : []}
515 h['hitColourY'] = {'Veto': [], 'Scifi' : [], 'US' : [], 'DS' : []}
516 h["markerCollection"] = []
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)
527 h["markerCollection"] = []
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())
536 rc = h[ 'simpleDisplay'].cd(p)
539 if options.drawCollAxis:
541 drawCollisionAxis(h['simpleDisplay'], k)
544 drawDetectors(darkMode=darkMode)
547 detID = digi.GetDetectorID()
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
555 geo.modules['Scifi'].GetSiPMPosition(detID,A,B)
557 curPath = nav.GetPath()
558 tmp = curPath.rfind('/')
559 nav.cd(curPath[:tmp])
562 if not first and not with2Points:
565 globA, locA = array('d', [X[0], X[1], X[2]]), array('d', [X[0], X[1], X[2]])
567 nav.MasterToLocal(globA, locA)
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:
573 collection = 'hitCollectionX'
575 sY = detSize[system][0]
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:
580 collection = 'hitCollectionY'
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)
587 if hitColour == "q" :
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())
596 if this_qdc > max_QDC :
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))])
603 fillNode(curPath, darkMode=darkMode)
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
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)
619 if hitColour == "q" :
620 for orientation in ['X', 'Y']:
622 density = np.clip(0, max_density, getSciFiHitDensity(h['hitCollection'+orientation]['Scifi'][1]))
623 for i in range(h['hitCollection'+orientation]['Scifi'][1].GetN()) :
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))])
629 drawLegend(max_density, max_QDC, 5, darkMode=darkMode)
635 for collection in ['hitCollectionX','hitCollectionY']:
636 h['simpleDisplay'].cd(k)
637 drawInfo(h['simpleDisplay'], k, runId, N, T, darkMode=darkMode)
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])
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)
648 if h[collection][c][1].GetN()<1: continue
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])
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])
671 for collection in ['hitCollectionX','hitCollectionY']:
672 h['simpleDisplay'].cd(k)
673 drawInfo(h['simpleDisplay'], k, runId, N, T,moreEventInfo, darkMode=darkMode)
676 h['simpleDisplay'].Update()
677 if withTiming: timingOfEvent()
678 addTrack(OT, darkMode=darkMode)
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()
686 print("Found shower direction and/or intercept contain NaN value")
687 # try finding the shower origin
689 shower_start = 10*ROOT.snd.analysis_tools.GetScifiShowerStart(scifi_planes)
691 print("Could not find shower start in SciFi, going for US")
692 shower_start = 100*ROOT.snd.analysis_tools.GetUSShowerStart(us_planes)
694 print("Could not find shower start in SciFi or in US")
698 drawShowerAxis(h['simpleDisplay'], k, shower_start, ref_point.X(),
699 shower_direction.X()/shower_direction.Z())
701 drawShowerAxis(h['simpleDisplay'], k, shower_start, ref_point.Y(),
702 shower_direction.Y()/shower_direction.Z())
703 h['simpleDisplay'].Update()
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)
713 if verbose>0: dumpChannels()
714 userProcessing(event)
717 h['simpleDisplay'].Print('{:0>2d}-event_{:04d}'.format(runId, N) + '.' + options.extension)
719 h['simpleDisplay'].Print(options.storePic + str(runId) + '-event_' + str(event.EventHeader.GetEventNumber()) + '.' + options.extension)
721 rc = input("hit return for next event or p for print or q for quit: ")
723 h['simpleDisplay'].Print(options.storePic + str(runId) + '-event_' + str(event.EventHeader.GetEventNumber()) + '.' + options.extension)
727 eventComment[f"{runId}-event_{event.EventHeader.GetEventNumber()}"] = rc
729 os.system("convert -delay 60 -loop 0 event*." + options.extension + " animated.gif")
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
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'}
904 node = '/cave_1/Detector_0/'+node_
906 if node+p in h and any(passNode in node for passNode in passNodes):
909 h['simpleDisplay'].cd(c+1)
913 # check if node exists
914 if not nav.CheckPath(node): continue
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]
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])
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]
947 h[node+p] = ROOT.TPolyLine()
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_])
957 h['simpleDisplay'].cd(c+1)
958 if any(passNode in node for passNode in passNodes):
959 X.SetFillColorAlpha(nodes[node_], 0.5)