SND@LHC Software
Loading...
Searching...
No Matches
Monitor.py
Go to the documentation of this file.
1#!/usr/bin/env python
2import ROOT
3import os,sys,subprocess
4import time
5import ctypes
6from array import array
7import rootUtils as ut
8import shipunit as u
9import SndlhcGeo
10from XRootD import client
11from XRootD.client.flags import DirListFlags, OpenFlags, MkDirFlags, QueryCode
12from rootpyPickler import Unpickler
13
14A,B=ROOT.TVector3(),ROOT.TVector3()
15# for fixing a root bug, will be solved in the forthcoming 6.26 release.
16
17ROOT.gInterpreter.Declare("""
18#include "MuFilterHit.h"
19#include "AbsMeasurement.h"
20#include "TrackPoint.h"
21
22void fixRoot(MuFilterHit& aHit,std::vector<int>& key,std::vector<float>& value, bool mask) {
23 std::map<int,float> m = aHit.GetAllSignals(false);
24 std::map<int, float>::iterator it = m.begin();
25 while (it != m.end())
26 {
27 key.push_back(it->first);
28 value.push_back(it->second);
29 it++;
30 }
31}
32void fixRootT(MuFilterHit& aHit,std::vector<int>& key,std::vector<float>& value, bool mask) {
33 std::map<int,float> m = aHit.GetAllTimes(false);
34 std::map<int, float>::iterator it = m.begin();
35 while (it != m.end())
36 {
37 key.push_back(it->first);
38 value.push_back(it->second);
39 it++;
40 }
41}
42void fixRoot(MuFilterHit& aHit, std::vector<TString>& key,std::vector<float>& value, bool mask) {
43 std::map<TString, float> m = aHit.SumOfSignals();
44 std::map<TString, float>::iterator it = m.begin();
45 while (it != m.end())
46 {
47 key.push_back(it->first);
48 value.push_back(it->second);
49 it++;
50 }
51}
52
53void fixRoot(std::vector<genfit::TrackPoint*>& points, std::vector<int>& d,std::vector<int>& k, bool mask) {
54 for(std::size_t i = 0; i < points.size(); ++i) {
55 genfit::AbsMeasurement* m = points[i]->getRawMeasurement();
56 d.push_back( m->getDetId() );
57 k.push_back( int(m->getHitId()/1000) );
58 }
59}
60""")
61
62Tkey = ROOT.std.vector('TString')()
63Ikey = ROOT.std.vector('int')()
64Value = ROOT.std.vector('float')()
65
66class Monitoring():
67 " set of monitor histograms "
68 def __init__(self,options,FairTasks):
69 self.options = options
70 self.EventNumber = -1
71 self.TStart = -1
72 self.TEnd = -1
73 self.MonteCarlo = False
74 self.Weight = 1
75
76 path = options.path
77 self.myclient = None
78 if path.find('eos')>0:
79 path = options.server+options.path
80 if options.online:
81 path = path.replace("raw_data","convertedData").replace("data/","")
82 self.myclient = client.FileSystem(options.server)
83# setup geometry
84 if (options.geoFile).find('../')<0: self.snd_geo = SndlhcGeo.GeoInterface(path+options.geoFile)
85 else: self.snd_geo = SndlhcGeo.GeoInterface(options.geoFile[3:])
86 self.MuFilter = self.snd_geo.modules['MuFilter']
87 self.Scifi = self.snd_geo.modules['Scifi']
88
89# MuFilter mapping of planes and bars
90 self.systemAndPlanes = {1:self.MuFilter.GetConfParI("MuFilter/NVetoPlanes"),
91 2:self.MuFilter.GetConfParI("MuFilter/NUpstreamPlanes"),
92 3:2*self.MuFilter.GetConfParI("MuFilter/NDownstreamPlanes")-1} # to arrive at 7 DS planes
93 self.systemAndBars = {1:self.MuFilter.GetConfParI("MuFilter/NVetoBars"),
94 2:self.MuFilter.GetConfParI("MuFilter/NUpstreamBars"),
95 3:self.MuFilter.GetConfParI("MuFilter/NDownstreamBars")}
96 self.systemAndChannels = {1:[self.MuFilter.GetConfParI("MuFilter/VetonSiPMs"),0],
97 2:[self.MuFilter.GetConfParI("MuFilter/UpstreamnSiPMs")-2,2],
98 3:[self.MuFilter.GetConfParI("MuFilter/DownstreamnSiPMs"),0]}
99 self.sdict = {0:'Scifi',1:'Veto',2:'US',3:'DS'}
100
101 self.freq = 160.316E6
102 self.TDC2ns = 1E9/self.freq
103
105
106 self.h = {} # histogram storage
107
108 self.runNr = str(options.runNumber).zfill(6)
109# presenter file
110 if hasattr(self, "saveTo") and options.saveTo!="":
111 name = options.saveTo+'run'+self.runNr+'_'+str(options.nStart//1000000)+'.root'
112 else:
113 name = 'run'+self.runNr+'.root'
114 if options.interactive: name = 'I-'+name
115 self.presenterFile = ROOT.TFile(name,'recreate')
116 for role in ['','shifter', 'expert']:
117 self.presenterFile.mkdir('scifi/'+role)
118 self.presenterFile.mkdir('mufilter/'+role)
119 self.presenterFile.mkdir('daq/'+role)
120 if options.interactive: self.presenterFile.mkdir('eventdisplay/'+role)
121 self.FairTasks = {}
122 for x in FairTasks: # keeps extended methods if from python class
123 self.FairTasks[x.GetName()] = x
124
125# setup input
126 if options.online:
127 import ConvRawData
128 options.chi2Max = 2000.
129 options.saturationLimit = 0.95
130 options.stop = False
131 options.withGeoFile = True
133 self.converter.Init(options)
134 self.options.online = self.converter
135 self.fsdict = False
136 self.hasBunchInfo = False
137 self.eventTree = options.online.fSink.GetOutTree()
138 self.Nkeys = 38 # need to find a way to get this number automatically
139 if self.converter.newFormat: self.Nkeys = 1
140 for t in self.FairTasks:
141 T = self.FairTasks[t]
142 self.converter.run.AddTask(T)
143 if t=='simpleTracking':
144 T.Init(self.converter)
145 self.trackTask = self.FairTasks[t]
146 self.Reco_MuonTracks = T.fittedTracks
147 self.clusMufi = T.clusMufi
148 self.clusScifi = T.clusScifi
149 else: T.Init()
150 self.run = self.converter.run
151 if self.eventTree.EventHeader.GetAccMode()==12: # ion runs
152 self.Nbunches = 1782
153 self.div = 8
154 else: # proton runs
155 self.Nbunches = 3564
156 self.div = 4
157 return
158 else:
159 if options.fname:
160 f=ROOT.TFile.Open(options.fname)
161 eventChain = f.Get('rawConv')
162 if not eventChain:
163 eventChain = f.cbmsim
164 if eventChain.GetBranch('MCTrack'): self.MonteCarlo = True
165 partitions = []
166 else:
167 partitions = 0
168 if options.partition < 0:
169 partitions = []
170 if path.find('eos')>0:
171# check for partitions
172 dirlist = str( subprocess.check_output("xrdfs "+options.server+" ls "+options.path+"run_"+self.runNr,shell=True) )
173 for x in dirlist.split('\\n'):
174 ix = x.find('sndsw_raw-')
175 if ix<0: continue
176 partitions.append(x[ix:])
177 else:
178# check for partitions
179 dirlist = os.listdir(options.path+"run_"+self.runNr)
180 for x in dirlist:
181 if not x.find('sndsw_raw-')<0:
182 partitions.append(x)
183 else:
184 partitions = ["sndsw_raw-"+ str(options.partition).zfill(4)+".root"]
185 if options.runNumber>0:
186 eventChain = ROOT.TChain('rawConv')
187 for p in partitions:
188 eventChain.Add(path+'run_'+self.runNr+'/'+p)
189
190 rc = eventChain.GetEvent(0)
191 self.TStart = eventChain.EventHeader.GetEventTime()
192 if options.nEvents <0:
193 rc = eventChain.GetEvent(eventChain.GetEntries()-1)
194 else:
195 rc = eventChain.GetEvent(options.nEvents-1)
196 self.TEnd = eventChain.EventHeader.GetEventTime()
197 rc = eventChain.GetEvent(0)
198# start FairRunAna
199 self.run = ROOT.FairRunAna()
200 ioman = ROOT.FairRootManager.Instance()
201 ioman.SetTreeName(eventChain.GetName())
202 outFile = ROOT.TMemFile('dummy','CREATE')
203 source = ROOT.FairFileSource(eventChain.GetCurrentFile())
204 for i in range(1,len(partitions)):
205 p = partitions[i]
206 source.AddFile(path+'run_'+self.runNr+'/'+p)
207 self.run.SetSource(source)
208 self.sink = ROOT.FairRootFileSink(outFile)
209 self.run.SetSink(self.sink)
210
211 for t in FairTasks:
212 self.run.AddTask(t)
213
214#avoiding some error messages
215 xrdb = ROOT.FairRuntimeDb.instance()
216 xrdb.getContainer("FairBaseParSet").setStatic()
217 xrdb.getContainer("FairGeoParSet").setStatic()
218
219 self.run.Init()
220 if len(partitions)>0: self.eventTree = ioman.GetInChain()
221 else: self.eventTree = ioman.GetInTree()
222#fitted tracks
223 if "simpleTracking" in self.FairTasks:
224 self.trackTask = self.FairTasks["simpleTracking"]
225 self.Reco_MuonTracks = self.trackTask.fittedTracks
226 self.clusMufi = self.trackTask.clusMufi
227 self.clusScifi = self.trackTask.clusScifi
228 self.trackTask.DSnPlanes = 3
229
230# initialize detector class for access to eventheader
231 rc = eventChain.GetEvent(0)
232 if not self.MonteCarlo:
233 self.snd_geo.modules['Scifi'].InitEvent(eventChain.EventHeader)
234 self.snd_geo.modules['MuFilter'].InitEvent(eventChain.EventHeader)
235
236 # define the number of bunches in the LHC
237 if eventChain.EventHeader.GetAccMode()==12: # ion runs
238 self.Nbunches = 1782
239 self.div = 8
240 else: # proton runs
241 self.Nbunches = 3564
242 self.div = 4
243
244 # get filling scheme, only necessary if not encoded in EventHeader, before 2022 reprocessing
245 self.hasBunchInfo = False
246 self.fsdict = False
247 if hasattr(eventChain.EventHeader,"GetBunchType"):
248 if not eventChain.EventHeader.GetBunchType()<0:
249 self.hasBunchInfo = True
250 print('take bunch info from event header')
251 if not self.hasBunchInfo:
252 try:
253 fg = ROOT.TFile.Open(options.server+options.path+'FSdict.root')
254 pkl = Unpickler(fg)
255 FSdict = pkl.load('FSdict')
256 fg.Close()
257 if options.runNumber in FSdict: self.fsdict = FSdict[options.runNumber]
258 except:
259 print('continue without knowing filling scheme',options.server+options.path)
260 if self.fsdict:
261 print('extract bunch info from filling scheme')
262 if self.fsdict or self.hasBunchInfo:
263 for x in ['B1only','B2noB1','noBeam']:
264 for role in ['shifter', 'expert']:
265 self.presenterFile.mkdir('mufilter/'+role+'/'+x)
266 self.presenterFile.mkdir('scifi/'+role+'/'+x)
267
268 def GetEntries(self):
269 if self.options.online:
270 if self.converter.newFormat: return self.converter.fiN.Get('data').GetEntries()
271 else: return self.converter.fiN.event.GetEntries()
272 else:
273 return self.eventTree.GetEntries()
274
275 def updateSource(self,fname):
276 # only needed in auto mode
277 notOK = True
278 nIter = 0
279 while notOK:
280 # self.converter.fiN.Close()
281 if nIter > 100:
282 print('too many attempts to read the file ',fname,' I am exiting.')
283 quit()
284 nIter+=1
285 if self.converter.fiN.GetName() != fname:
286 self.converter.fiN = ROOT.TFile.Open(fname)
287 else:
288 self.converter.fiN = ROOT.TFile.Open(fname)
289 Nkeys = self.converter.fiN.ReadKeys()
290 if Nkeys != self.Nkeys:
291 time.sleep(10)
292 self.converter.fiN = ROOT.TFile.Open(fname)
293 print('wrong number of keys',Nkeys)
294 continue
295
296 listOfKeys = []
297 for b in self.converter.fiN.GetListOfKeys(): listOfKeys.append(b.GetName())
298 for name in listOfKeys:
299 exec("self.converter.fiN."+name+".Refresh()")
300
301 first = -1
302 for name in listOfKeys:
303 nentries = eval("self.converter.fiN."+name+".GetEntries()")
304 if first<0: first = nentries
305 if first != nentries:
306 print('wrong number of entries',first,nentries)
307 first = -1
308 break
309 if first<0: continue
310
311 for name in listOfKeys:
312 if name.find('board')==0:
313 self.converter.boards[name]=eval("self.converter.fiN."+name)
314
315 notOK = False
316
317 def GetEvent(self,n):
318 if not self.options.online: # offline, FairRoot in charge
319
320 if self.eventTree.GetBranchStatus('Reco_MuonTracks'):
321 for aTrack in self.eventTree.Reco_MuonTracks:
322 if aTrack: aTrack.Delete()
323 self.eventTree.Reco_MuonTracks.Delete()
324 if "simpleTracking" in self.FairTasks:
325 self.Reco_MuonTracks.Delete()
326
327 if self.options.online:
328 online = self.options.online
329 online.executeEvent(n)
330 self.Reco_MuonTracks.Delete()
331 if self.options.FairTask_convRaw:
332 self.options.online.sTree.GetEvent(self.options.online.sTree.GetEntries()-1)
333 for t in self.FairTasks: self.FairTasks[t].ExecuteTask()
334 self.eventTree = self.options.online.sTree
335 else:
336 self.eventTree.GetEvent(n)
337 if not self.MonteCarlo:
338 # initialize detector class for access to eventheader
339 self.snd_geo.modules['Scifi'].InitEvent(self.eventTree.EventHeader)
340 self.snd_geo.modules['MuFilter'].InitEvent(self.eventTree.EventHeader)
341 if self.MonteCarlo: self.Weight = self.eventTree.MCTrack[0].GetWeight()
342 for t in self.FairTasks: self.FairTasks[t].ExecuteTask()
343 self.EventNumber = n
344
345# check for bunch xing type
346 self.xing = {'all':True,'B1only':False,'B2noB1':False,'noBeam':False}
347 if self.hasBunchInfo:
348 binfo = self.eventTree.EventHeader
349 self.xing['IP1'] = binfo.isIP1()
350 self.xing['IP2'] = binfo.isIP2()
351 self.xing['B1'] = binfo.isB1()
352 self.xing['B2'] = binfo.isB2()
353 self.xing['B1only'] = binfo.isB1Only()
354 self.xing['B2noB1'] = binfo.isB2noB1()
355 self.xing['noBeam'] = binfo.isNoBeam()
356 elif self.fsdict:
357 T = self.eventTree.EventHeader.GetEventTime()
358 bunchNumber = int((T%(self.div*self.Nbunches))/self.div+0.5)
359 nb1 = (self.Nbunches + bunchNumber - self.fsdict['phaseShift1'])%self.Nbunches
360 nb2 = (self.Nbunches + bunchNumber - self.fsdict['phaseShift1']- self.fsdict['phaseShift2'])%self.Nbunches
361 b1 = nb1 in self.fsdict['B1']
362 b2 = nb2 in self.fsdict['B2']
363 IP1 = False
364 IP2 = False
365 if b1:
366 IP1 = self.fsdict['B1'][nb1]['IP1']
367 if b2:
368 IP2 = self.fsdict['B2'][nb2]['IP2']
369 self.xing['IP1'] = IP1
370 self.xing['IP2'] = IP2
371 self.xing['B1'] = b1
372 self.xing['B2'] = b2
373 self.xing['B1only'] = b1 and not IP1 and not b2
374 self.xing['B2noB1'] = b2 and not b1
375 self.xing['noBeam'] = not b1 and not b2
376 if self.xing['B1only'] and self.xing['B2noB1'] or self.xing['B1only'] and self.xing['noBeam'] : print('error with b1only assignment',self.xing)
377 if self.xing['B2noB1'] and self.xing['noBeam'] : print('error with b2nob1 assignment',self.xing)
378
379 return self.eventTree
380
382 # try to copy root file with TCanvas to EOS
383 self.presenterFile.Close()
384 if self.options.online:
385 wwwPath = "/eos/experiment/sndlhc/www/online"
386 elif self.options.path.find('2022')>0 :
387 wwwPath = "/eos/experiment/sndlhc/www/reprocessing"
388 else:
389 wwwPath = "/eos/experiment/sndlhc/www/offline"
390 if self.options.sudo:
391 try:
392 rc = os.system("xrdcp -f "+self.presenterFile.GetName()+" "+os.environ['EOSSHIP']+wwwPath)
393 except:
394 print("copy of root file failed. Token expired?")
395 self.presenterFile = ROOT.TFile('run'+self.runNr+'.root','update')
396
398 wwwPath = "/eos/experiment/sndlhc/www/online/"
399 for r in self.options.runNumbers.split(','):
400 if r!= '': runList.append(int(r))
401 for r in runList:
402 runNr = str(r).zfill(6)+'.root'
403 f = ROOT.TFile.Open(options.server+wwwPath+ runNr)
404 g = ROOT.TFile('tmp.root','recreate')
405 for d in f.GetListOfKeys():
406 D = f.Get(d.GetName())
407 D.Purge()
408 for d in f.GetListOfKeys():
409 name = d.GetName()
410 s = f.Get(name)
411 g.mkdir(name)
412 g.cd(name)
413 for x in s.GetListOfKeys():
414 s.Get(x.GetName()).Write()
415 g.Close()
416 f.Close()
417 os.system('xrdcp -f tmp.root '+self.options.server+options.path+rName)
418
419 def updateHtml(self):
420 if self.options.online: destination="online"
421 elif self.options.path.find('2022')>0: destination="reprocessing"
422 else: destination="offline"
423 rc = os.system("xrdcp -f "+os.environ['EOSSHIP']+"/eos/experiment/sndlhc/www/"+destination+".html . ")
424 old = open(destination+".html")
425 oldL = old.readlines()
426 old.close()
427 tmp = open("tmp.html",'w')
428 found = False
429 for L in oldL:
430 if not L.find(self.runNr)<0: return
431 if L.find("https://snd-lhc-monitoring.web.cern.ch/"+destination+"/run.html?file=run")>0 and not found:
432 r = str(self.options.runNumber)
433 Lnew = ' <li> <a href="https://snd-lhc-monitoring.web.cern.ch/'+destination+'/run.html?file=run'
434 Lnew+= self.runNr+'.root&lastcycle">run '+r+' </a>'+self.options.startTime
435 if self.options.postScale>1: Lnew+=" post scaled:"+str(self.options.postScale)
436 Lnew+='\n'
437 tmp.write(Lnew)
438 found = True
439 tmp.write(L)
440 tmp.close()
441 os.system('cp '+destination+'.html '+destination+time.ctime().replace(' ','')+'.html ') # make backup
442 os.system('cp tmp.html '+destination+'.html')
443 try:
444 rc = os.system("xrdcp -f "+destination+".html "+os.environ['EOSSHIP']+"/eos/experiment/sndlhc/www/")
445 except:
446 print("copy of html failed. Token expired?")
447
448 def cleanUpHtml(self,destination="online"):
449 rc = os.system("xrdcp -f "+os.environ['EOSSHIP']+"/eos/experiment/sndlhc/www/"+destination+".html . ")
450 old = open(destination+".html")
451 oldL = old.readlines()
452 old.close()
453 tmp = open("tmp.html",'w')
454 dirlist = str( subprocess.check_output("xrdfs "+os.environ['EOSSHIP']+" ls /eos/experiment/sndlhc/www/"+destination+"/",shell=True) )
455 for L in oldL:
456 OK = True
457 if L.find("https://snd-lhc-monitoring.web.cern.ch/"+destination)>0:
458 k = L.find("file=")+5
459 m = L.find(".root")+5
460 R = L[k:m]
461 if not R in dirlist: OK = False
462 if OK: tmp.write(L)
463 tmp.close()
464 os.system('cp tmp.html '+destination+'.html')
465 try:
466 rc = os.system("xrdcp -f "+destination+".html "+os.environ['EOSSHIP']+"/eos/experiment/sndlhc/www/")
467 except:
468 print("copy of html failed. Token expired?")
469
470 def checkAlarm(self,minEntries=1000):
471 self.alarms = {'scifi':[]}
472 # check for empty histograms in scifi signal
473 entries = {}
474 for mat in range(30):
475 entries[mat] = self.h['scifi-mat_'+str(mat)].GetEntries()
476 res = sorted(entries.items(), key=operator.itemgetter(1),reverse=True)
477 if res[0][1]>minEntries: # choose limit which makes it sensitive to check for empty mats
478 for mat in range(30):
479 if entries[mat] < 1:
480 s = mat//6 + 1
481 if mat%6 < 3 : p='H'
482 else : p='V'
483 e = str(s)+p+str(mat%3)
484 self.alarms['scifi'].append(e)
485 if len(self.alarms['scifi']) > 0: self.sendAlarm()
486
487 self.alarms['Veto']=[]
488 # check for empty histograms in veto signal
489 entries = {}
490 s = 1
491 for p in range(2):
492 for side in ['L','R']:
493 entries[str(10*s+p)+side] = self.h['mufi-sig'+p+str(10*s+p)].GetEntries()
494 res = sorted(entries.items(), key=operator.itemgetter(1),reverse=True)
495 if res[0][1]>minEntries: # choose limit which makes it sensitive to check for empty boards
496 for p in range(2):
497 for side in ['L','R']:
498 if entries[str(10*s+p)+side] < 1:
499 self.alarms['Veto'].append(str(10*s+p)+side())
500 # check for empty histograms in US signal
501 entries = {}
502 s = 2
503 for p in range(5):
504 for side in ['L','R']:
505 entries[str(s*10+p)+side] = self.h['mufi-sig2'+p+str(s*10+p)].GetEntries()
506 res = sorted(entries.items(), key=operator.itemgetter(1),reverse=True)
507 if res[0][1]>1000: # choose limit which makes it sensitive to check for empty boards
508 for p in range(5):
509 for side in ['L','R']:
510 if entries[str(s*10+p)+side] < 1:
511 self.alarms['Veto'].append(str(s*10+p)+side())
512
513
514 def sendAlarm(self):
515 print('ALARM',self.alarms)
516
517 def systemAndOrientation(self,s,plane):
518 if s==1 or s==2: return "horizontal"
519 if plane%2==1 or plane == 6: return "vertical"
520 return "horizontal"
521
523 zPos={'MuFilter':{},'Scifi':{}}
524 for s in self.systemAndPlanes:
525 for plane in range(self.systemAndPlanes[s]):
526 bar = 4
527 p = plane
528 if s==3 and (plane%2==0 or plane==7):
529 bar = 90
530 p = plane//2
531 elif s==3 and plane%2==1:
532 bar = 30
533 p = plane//2
534 self.MuFilter.GetPosition(s*10000+p*1000+bar,A,B)
535 zPos['MuFilter'][s*10+plane] = (A.Z()+B.Z())/2.
536 for s in range(1,6):
537 mat = 1
538 sipm = 1
539 channel = 64
540 for o in range(2):
541 self.Scifi.GetPosition(channel+1000*sipm+10000*mat+100000*o+1000000*s,A,B)
542 zPos['Scifi'][s*10+o] = (A.Z()+B.Z())/2.
543 return zPos
544
545 def smallSiPMchannel(self,i):
546 if i==2 or i==5 or i==10 or i==13: return True
547 else: return False
548
549# Scifi specific code
550 def Scifi_xPos(self,detID):
551 orientation = (detID//100000)%10
552 nStation = 2*(detID//1000000-1)+orientation
553 mat = (detID%100000)//10000
554 X = detID%1000+(detID%10000)//1000*128
555 return [nStation,mat,X] # even numbers are Y (horizontal plane), odd numbers X (vertical plane)
556
557# decode MuFilter detID
558 def MuFilter_PlaneBars(self,detID):
559 s = detID//10000
560 l = (detID%10000)//1000 # plane number
561 bar = (detID%1000)
562 if s>2:
563 l=2*l
564 if bar>59:
565 bar=bar-60
566 if l<6: l+=1
567 return {'station':s,'plane':l,'bar':bar}
568
569 def map2Dict(self,aHit,T='GetAllSignals',mask=True):
570 if T=="SumOfSignals":
571 key = Tkey
572 elif T=="GetAllSignals" or T=="GetAllTimes":
573 key = Ikey
574 else:
575 print('use case not known',T)
576 1/0
577 key.clear()
578 Value.clear()
579 if T=="GetAllTimes": ROOT.fixRootT(aHit,key,Value,mask)
580 else: ROOT.fixRoot(aHit,key,Value,mask)
581 theDict = {}
582 for k in range(key.size()):
583 if T=="SumOfSignals": theDict[key[k].Data()] = Value[k]
584 else: theDict[key[k]] = Value[k]
585 return theDict
586
587 def fit_langau(self,hist,o,bmin,bmax,opt=''):
588 if opt == 2:
589 params = {0:'Width(scale)',1:'mostProbable',2:'norm',3:'sigma',4:'N2'}
590 F = ROOT.TF1('langau',langaufun,0,200,len(params))
591 else:
592 params = {0:'Width(scale)',1:'mostProbable',2:'norm',3:'sigma'}
593 F = ROOT.TF1('langau',twoLangaufun,0,200,len(params))
594 for p in params: F.SetParName(p,params[p])
595 rc = hist.Fit('landau','S'+o,'',bmin,bmax)
596 res = rc.Get()
597 if not res: return res
598 F.SetParameter(2,res.Parameter(0))
599 F.SetParameter(1,res.Parameter(1))
600 F.SetParameter(0,res.Parameter(2))
601 F.SetParameter(3,res.Parameter(2))
602 F.SetParameter(4,0)
603 F.SetParLimits(0,0,100)
604 F.SetParLimits(1,0,100)
605 F.SetParLimits(3,0,10)
606 rc = hist.Fit(F,'S'+o,'',bmin,bmax)
607 res = rc.Get()
608 return res
609
610 def twoLangaufun(self,x,par):
611 N1 = self.langaufun(x,par)
612 par2 = [par[0],par[1]*2,par[4],par[3]]
613 N2 = self.langaufun(x,par2)
614 return N1+abs(N2)
615
616 def langaufun(self,x,par):
617 #Fit parameters:
618 #par[0]=Width (scale) parameter of Landau density
619 #par[1]=Most Probable (MP, location) parameter of Landau density
620 #par[2]=Total area (integral -inf to inf, normalization constant)
621 #par[3]=Width (sigma) of convoluted Gaussian function
622 #
623 #In the Landau distribution (represented by the CERNLIB approximation),
624 #the maximum is located at x=-0.22278298 with the location parameter=0.
625 #This shift is corrected within this function, so that the actual
626 #maximum is identical to the MP parameter.
627#
628 # Numeric constants
629 invsq2pi = 0.3989422804014 # (2 pi)^(-1/2)
630 mpshift = -0.22278298 # Landau maximum location
631#
632 # Control constants
633 np = 100.0 # number of convolution steps
634 sc = 5.0 # convolution extends to +-sc Gaussian sigmas
635#
636 # Variables
637 summe = 0.0
638#
639 # MP shift correction
640 mpc = par[1] - mpshift * par[0]
641#
642 # Range of convolution integral
643 xlow = max(0,x[0] - sc * par[3])
644 xupp = x[0] + sc * par[3]
645#
646 step = (xupp-xlow) / np
647#
648 # Convolution integral of Landau and Gaussian by sum
649 i=1.0
650 if par[0]==0 or par[3]==0: return 9999
651 while i<=np/2:
652 i+=1
653 xx = xlow + (i-.5) * step
654 fland = ROOT.TMath.Landau(xx,mpc,par[0]) / par[0]
655 summe += fland * ROOT.TMath.Gaus(x[0],xx,par[3])
656#
657 xx = xupp - (i-.5) * step
658 fland = ROOT.TMath.Landau(xx,mpc,par[0]) / par[0]
659 summe += fland * ROOT.TMath.Gaus(x[0],xx,par[3])
660#
661 return (par[2] * step * summe * invsq2pi / par[3])
662
663 def myPrint(self,tc,name,subdir='',withRootFile=True):
664 srun = 'run'+str(self.options.runNumber)
665 if isinstance(tc, ROOT.TCanvas) :
666 tc.Update()
667 if withRootFile:
668 self.presenterFile.cd(subdir)
669 tc.Write()
670 else:
671 if not os.path.isdir(srun): os.system('mkdir '+srun)
672 pname = srun+'/'+name+'-'+srun
673 tc.Print(pname+'.png')
674 tc.Print(pname+'.pdf')
675
676 def fillHist1(self,hname,parx):
677 for x in ['','B1only','B2noB1','noBeam']:
678 if x=='':
679 rc = self.h[hname].Fill(parx,self.Weight)
680 elif self.xing[x]:
681 rc = self.h[hname+x].Fill(parx,self.Weight)
682 def fillHist2(self,hname,parx,pary):
683 for x in ['','B1only','B2noB1','noBeam']:
684 if x=='':
685 rc = self.h[hname].Fill(parx,pary,self.Weight)
686 elif self.xing[x]:
687 rc = self.h[hname+x].Fill(parx,pary,self.Weight)
688
690 " run reconstruction, select events with tracks"
691 def __init__(self,options):
692 self.options = options
693 self.EventNumber = -1
694 self.MonteCarlo = False
695
696 path = options.path
697 if path.find('eos')>0:
698 path = options.server+options.path
699# setup geometry
700 if (options.geoFile).find('../')<0: self.snd_geo = SndlhcGeo.GeoInterface(path+options.geoFile)
701 else: self.snd_geo = SndlhcGeo.GeoInterface(options.geoFile[3:])
702 self.MuFilter = self.snd_geo.modules['MuFilter']
703 self.Scifi = self.snd_geo.modules['Scifi']
704
705 self.runNr = str(options.runNumber).zfill(6)
706 if options.runNumber > 0:
707 if options.partition < 0:
708 partitions = []
709 if path.find('eos')>0:
710# check for partitions
711 print("xrdfs "+options.server+" ls "+options.path+"run_"+self.runNr)
712 dirlist = str( subprocess.check_output("xrdfs "+options.server+" ls "+options.path+"run_"+self.runNr,shell=True) )
713 for x in dirlist.split('\\n'):
714 ix = x.find('sndsw_raw-')
715 if ix<0: continue
716 partitions.append(x[ix:])
717 else:
718# check for partitions
719 dirlist = os.listdir(options.path+"run_"+self.runNr)
720 for x in dirlist:
721 if not x.find("sndsw_raw-")<0:
722 partitions.append(x)
723 else:
724 partitions = ["sndsw_raw-"+ str(options.partition).zfill(4)+".root"]
725 eventChain = ROOT.TChain('rawConv')
726 for p in partitions:
727 eventChain.Add(path+'run_'+self.runNr+'/'+p)
728 else:
729# for MC data
730 eventChain = ROOT.TChain("cbmsim")
731 eventChain.Add(options.fname)
732 self.MonteCarlo = True
733 partitions = []
734 rc = eventChain.GetEvent(0)
735 self.snd_geo.modules['Scifi'].InitEvent(eventChain.EventHeader)
736 self.snd_geo.modules['MuFilter'].InitEvent(eventChain.EventHeader)
737# start FairRunAna
738 self.run = ROOT.FairRunAna()
739 ioman = ROOT.FairRootManager.Instance()
740 ioman.SetTreeName(eventChain.GetName())
741 source = ROOT.FairFileSource(eventChain.GetCurrentFile())
742 ioman.SetInChain(eventChain)
743 first = True
744 for p in partitions:
745 if first:
746 first = False
747 continue
748 source.AddFile(path+'run_'+self.runNr+'/'+p+'.root')
749 self.run.SetSource(source)
750
751#avoiding some error messages
752 xrdb = ROOT.FairRuntimeDb.instance()
753 xrdb.getContainer("FairBaseParSet").setStatic()
754 xrdb.getContainer("FairGeoParSet").setStatic()
755
756# init() tracking tasks
757 if self.options.HoughTracking:
758 if self.options.trackType == 'Scifi' or self.options.trackType == 'ScifiDS':
759 self.muon_reco_task_Sf = options.FairTasks["houghTransform_Sf"]
760 self.muon_reco_task_Sf.Init()
761 self.genfitTrack = self.muon_reco_task_Sf.genfitTrack
762 if self.options.trackType == 'DS' or self.options.trackType == 'ScifiDS':
763 self.muon_reco_task_DS = options.FairTasks["houghTransform_DS"]
764 self.muon_reco_task_DS.Init()
765 self.genfitTrack = self.muon_reco_task_DS.genfitTrack
766 if self.options.simpleTracking:
767 self.trackTask = options.FairTasks["simpleTracking"]
768 if not self.options.HoughTracking:
769 self.genfitTrack = self.options.genfitTrack
770
771 self.trackTask.SetTrackClassType(self.genfitTrack)
772 self.trackTask.Init()
773
774# prepare output tree, same branches as input plus track(s)
775 self.outFile = ROOT.TFile(options.oname,'RECREATE')
776 self.fSink = ROOT.FairRootFileSink(self.outFile)
777
778 self.outTree = eventChain.CloneTree(0)
779 ROOT.gDirectory.pwd()
780
781 # after track tasks init(), output track format is known
782 if self.genfitTrack:
783 self.fittedTracks = ROOT.TClonesArray("genfit::Track")
784 self.fittedTracks.BypassStreamer(ROOT.kFALSE)
785 else:
786 self.fittedTracks = ROOT.TClonesArray("sndRecoTrack")
787 self.MuonTracksBranch = self.outTree.Branch("Reco_MuonTracks",self.fittedTracks,32000,0)
788
789 if self.options.simpleTracking and not self.options.trackType.find('Scifi')<0 and not eventChain.GetBranch("Cluster_Scifi"):
790 self.clusScifi = ROOT.TClonesArray("sndCluster")
791 self.clusScifiBranch = self.outTree.Branch("Cluster_Scifi",self.clusScifi,32000,1)
792 if self.options.simpleTracking and not self.options.trackType.find('DS')<0 and not eventChain.GetBranch("Cluster_Mufi"):
793 self.clusMufi = ROOT.TClonesArray("sndCluster")
794 self.clusMufiBranch = self.outTree.Branch("Cluster_Mufi",self.clusMufi,32000,1)
795
796 B = ROOT.TList()
797 B.SetName('BranchList')
798 B.Add(ROOT.TObjString('Reco_MuonTracks'))
799 B.Add(ROOT.TObjString('Scifi_sndCluster'))
800 B.Add(ROOT.TObjString('Mufi_sndCluster'))
801 B.Add(ROOT.TObjString('sndScifiHit'))
802 B.Add(ROOT.TObjString('MuFilterHit'))
803 B.Add(ROOT.TObjString('FairEventHeader'))
804 self.fSink.WriteObject(B,"BranchList", ROOT.TObject.kSingleKey)
805 self.fSink.SetRunId(options.runNumber)
806 self.fSink.SetOutTree(self.outTree)
807
808 self.eventTree = eventChain
809 self.run.SetSink(self.fSink)
810 self.OT = ioman.GetSink().GetOutTree()
811
812 def ExecuteEvent(self,event):
813 track_container_list = []
814 # Delete SndlhcTracking fitted tracks container
815 if self.options.simpleTracking:
816 self.trackTask.fittedTracks.Delete()
817
818 if self.options.trackType == 'ScifiDS':
819 if self.options.HoughTracking:
820 self.muon_reco_task_Sf.Exec(0)
821 self.muon_reco_task_DS.Exec(0)
822 track_container_list = [self.muon_reco_task_Sf.kalman_tracks,self.muon_reco_task_DS.kalman_tracks]
823 if self.options.simpleTracking:
824 self.trackTask.ExecuteTask(option='ScifiDS')
825 track_container_list.append(self.trackTask.fittedTracks)
826
827 elif self.options.trackType == 'Scifi':
828 if self.options.HoughTracking:
829 self.muon_reco_task_Sf.Exec(0)
830 track_container_list.append(self.muon_reco_task_Sf.kalman_tracks)
831 if self.options.simpleTracking:
832 self.trackTask.ExecuteTask(option='Scifi')
833 track_container_list.append(self.trackTask.fittedTracks)
834
835 elif self.options.trackType == 'DS':
836 if self.options.HoughTracking:
837 self.muon_reco_task_DS.Exec(0)
838 track_container_list.append(self.muon_reco_task_DS.kalman_tracks)
839 if self.options.simpleTracking:
840 self.trackTask.ExecuteTask(option='DS')
841 track_container_list.append(self.trackTask.fittedTracks)
842
843 i_muon = -1
844 for item in track_container_list:
845 for aTrack in item:
846 i_muon += 1
847 self.fittedTracks[i_muon] = aTrack
848
849 def Execute(self):
850 for n in range(self.options.nStart,self.options.nStart+self.options.nEvents):
851
852 if self.options.scaleFactor > 1:
853 if ROOT.gRandom.Rndm() > 1.0/self.options.scaleFactor: continue
854
855 self.eventTree.GetEvent(n)
856 # delete track containers
857 self.fittedTracks.Delete()
858
859 self.ExecuteEvent(self.eventTree)
860 if not self.MonteCarlo and self.fittedTracks.GetEntries() == 0: continue
861 if self.options.simpleTracking and not self.options.trackType.find('Scifi')<0:
862 if not self.eventTree.GetBranch("Cluster_Scifi"):
863 self.clusScifi.Delete()
864 self.clusScifi.Expand(len(self.trackTask.clusScifi))
865 for index, aCl in enumerate(self.trackTask.clusScifi):
866 self.clusScifi[index] = aCl
867 if self.options.simpleTracking and not self.options.trackType.find('DS')<0:
868 if not self.eventTree.GetBranch("Cluster_Mufi"):
869 self.clusMufi.Delete()
870 self.clusMufi.Expand(len(self.trackTask.clusMufi))
871 for index, aCl in enumerate(self.trackTask.clusMufi):
872 self.clusMufi[index] = aCl
873
874 # if using FairEventHeader, i.e. before sndlhc header was introduced
875 if hasattr(self.OT.EventHeader, "SetMCEntryNumber"):
876 self.OT.EventHeader.SetMCEntryNumber(n)
877 self.fSink.Fill()
878
879 def Finalize(self):
880 self.fSink.Write()
881 self.outFile.Close()
__init__(self, options, FairTasks)
Definition Monitor.py:68
publishRootFile(self)
Definition Monitor.py:381
MuFilter_PlaneBars(self, detID)
Definition Monitor.py:558
myPrint(self, tc, name, subdir='', withRootFile=True)
Definition Monitor.py:663
map2Dict(self, aHit, T='GetAllSignals', mask=True)
Definition Monitor.py:569
Scifi_xPos(self, detID)
Definition Monitor.py:550
twoLangaufun(self, x, par)
Definition Monitor.py:610
GetEvent(self, n)
Definition Monitor.py:317
fit_langau(self, hist, o, bmin, bmax, opt='')
Definition Monitor.py:587
getAverageZpositions(self)
Definition Monitor.py:522
purgeMonitorHistos(self)
Definition Monitor.py:397
langaufun(self, x, par)
Definition Monitor.py:616
fillHist2(self, hname, parx, pary)
Definition Monitor.py:682
checkAlarm(self, minEntries=1000)
Definition Monitor.py:470
cleanUpHtml(self, destination="online")
Definition Monitor.py:448
smallSiPMchannel(self, i)
Definition Monitor.py:545
fillHist1(self, hname, parx)
Definition Monitor.py:676
systemAndOrientation(self, s, plane)
Definition Monitor.py:517
updateSource(self, fname)
Definition Monitor.py:275
__init__(self, options)
Definition Monitor.py:691
ExecuteEvent(self, event)
Definition Monitor.py:812