CMS 3D CMS Logo

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
specificLumi.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 ########################################################################
3 # Command to produce perbunch and specific lumi #
4 # #
5 # Author: Zhen Xie #
6 ########################################################################
7 #
8 # dump all fills into files.
9 # allfills.txt all the existing fills.
10 # fill_num.txt all the runs in the fill
11 # dumpFill -o outputdir
12 # dumpFill -f fillnum generate runlist for the given fill
13 #
14 import os,os.path,sys,math,array,datetime,time,calendar,re
15 import coral
16 
17 from RecoLuminosity.LumiDB import argparse,sessionManager,lumiTime,CommonUtil,lumiCalcAPI,lumiParameters,revisionDML,normDML
18 MINFILL=1800
19 MAXFILL=9999
20 allfillname='allfills.txt'
21 
22 def getFillFromDB(schema,fillnum):
23  '''
24  output: {run:starttime}
25  '''
26  runtimesInFill={}
27  fillrundict=lumiCalcAPI.fillrunMap(schema,fillnum)
28  if len(fillrundict)>0:
29  runs=fillrundict.values()[0]
30  runlsdict=dict(zip(runs,[None]*len(runs)))
31  runresult=lumiCalcAPI.runsummary(schema,runlsdict)
32  for perrundata in runresult:
33  runtimesInFill[perrundata[0]]=perrundata[7]
34  return runtimesInFill
35 
36 def listfilldir(indir):
37  '''
38  list all fills contained in the given dir
39  input: indir
40  output: [fill]
41  '''
42  fillnamepat=r'^[0-9]{4}$'
43  p=re.compile(fillnamepat)
44  processedfills=[]
45  dirList=os.listdir(indir)
46  for fname in dirList:
47  if p.match(fname) and os.path.isdir(os.path.join(indir,fname)):#found fill dir
48  allfs=os.listdir(os.path.join(indir,fname))
49  for myfile in allfs:
50  sumfilenamepat=r'^[0-9]{4}_bxsum_CMS.txt$'
51  s=re.compile(sumfilenamepat)
52  if s.match(myfile):
53  #only if fill_summary_CMS.txt file exists
54  processedfills.append(int(fname))
55  return processedfills
56 
57 def lastcompleteFill(infile):
58  '''
59  parse infile to find LASTCOMPLETEFILL
60  input: input file name
61  output: last completed fill number
62  '''
63  lastfill=None
64  hlinepat=r'(LASTCOMPLETEFILL )([0-9]{4})'
65  h=re.compile(hlinepat)
66  dqmfile=open(infile,'r')
67  for line in dqmfile:
68  result=h.match(line)
69  if result:
70  lastfill=result.group(2)
71  break
72  return int(lastfill)
73 
74 def calculateSpecificLumi(lumi,lumierr,beam1intensity,beam1intensityerr,beam2intensity,beam2intensityerr):
75  '''
76  calculate specific lumi
77  input: instlumi, instlumierror,beam1intensity,beam1intensityerror,beam2intensity,beam2intensityerror
78  output (specific lumi value,specific lumi error)
79  '''
80  specificlumi=0.0
81  specificlumierr=0.0
82  if beam1intensity<0: beam1intensity=0
83  if beam2intensity<0: beam2intensity=0
84  if beam1intensity>0.0 and beam2intensity>0.0:
85  specificlumi=float(lumi)/(float(beam1intensity)*float(beam2intensity))
86  specificlumierr=specificlumi*math.sqrt(lumierr**2/lumi**2+beam1intensityerr**2/beam1intensity**2+beam2intensityerr**2/beam2intensity**2)
87  return (specificlumi,specificlumierr)
88 
89 def getFillFromFile(fillnum,inputdir):
90  '''
91  parse fill_xxx.txt files in the input directory for runs, starttime in the fill
92  input: fillnumber, input dir
93  output: {run:tarttime}
94  '''
95  runtimesInFill={}
96  #look for files 'fill_num.txt' in inputdir
97  for filename in os.listdir(inputdir):
98  mpat=r'^fill_[0-9]{4}.txt$'
99  m=re.compile(mpat)
100  if m.match(filename) is None:
101  continue
102  filename=filename.strip()
103  if filename.find('.')==-1: continue
104  basename,extension=filename.split('.')
105  if not extension or extension!='txt':
106  continue
107  if basename.find('_')==-1: continue
108  prefix,number=basename.split('_')
109  if not number : continue
110  if fillnum!=int(number):continue
111  f=open(os.path.join(inputdir,'fill_'+number+'.txt'),'r')
112  for line in f:
113  l=line.strip()
114  fields=l.split(',')
115  if len(fields)<2 : continue
116  runtimesInFill[int(fields[0])]=fields[1]
117  f.close()
118  return runtimesInFill
119 
120 #####output methods####
121 def filltofiles(allfills,runsperfill,runtimes,dirname):
122  '''
123  write runnumber:starttime map per fill to files
124  '''
125  f=open(os.path.join(dirname,allfillname),'w')
126  for fill in allfills:
127  print >>f,'%d'%(fill)
128  f.close()
129  for fill,runs in runsperfill.items():
130  filename='fill_'+str(fill)+'.txt'
131  if len(runs)!=0:
132  f=open(os.path.join(dirname,filename),'w')
133  for run in runs:
134  print >>f,'%d,%s'%(run,runtimes[run])
135  f.close()
136 
137 def specificlumiTofile(fillnum,filldata,outdir):
138  #
139  #input : fillnum
140  # filldata: {bxidx:[[lstime,beamstatusfrac,lumivalue,lumierror,speclumi,speclumierr]],[]}
141  #sorted by bxidx, sorted by lstime inside list
142  #check outdir/fillnum subdir exists; if not, create it; else outdir=outdir/fillnum
143  #
144  if not filldata:
145  print 'empty input data, do nothing for fill ',fillnum
146  return
147  timedict={}#{lstime:[[stablebeamfrac,lumi,lumierr,speclumi,speclumierr]]}
148  filloutdir=os.path.join(outdir,str(fillnum))
149  if not os.path.exists(filloutdir):
150  os.mkdir(filloutdir)
151  for cmsbxidx,perbxdata in filldata.items():
152  lhcbucket=0
153  if cmsbxidx!=0:
154  lhcbucket=(cmsbxidx-1)*10+1
155  a=sorted(perbxdata,key=lambda x:x[0])
156  filename=str(fillnum)+'_lumi_'+str(lhcbucket)+'_CMS.txt'
157  linedata=[]
158  for perlsdata in a:
159  ts=int(perlsdata[0])
160  beamstatusfrac=perlsdata[1]
161  lumi=perlsdata[2]
162  lumierror=perlsdata[3]
163  #beam1intensity=perlsdata[4]
164  #beam2intensity=perlsdata[5]
165  speclumi=perlsdata[4]
166  speclumierror= perlsdata[5]
167  if lumi>0:
168  linedata.append([ts,beamstatusfrac,lumi,lumierror,speclumi,speclumierror])
169  if not timedict.has_key(ts):
170  timedict[ts]=[]
171  timedict[ts].append([beamstatusfrac,lumi,lumierror,speclumi,speclumierror])
172  if len(linedata)>10:#at least 10 good ls
173  f=open(os.path.join(filloutdir,filename),'w')
174  for line in linedata:
175  print >>f, '%d\t%e\t%e\t%e\t%e\t%e'%(line[0],line[1],line[2],line[3],line[4],line[5])
176  f.close()
177  #print 'writing avg file'
178  summaryfilename=str(fillnum)+'_lumi_CMS.txt'
179  f=None
180  lstimes=timedict.keys()
181  lstimes.sort()
182  fillseg=[]
183  lscounter=0
184  for lstime in lstimes:
185  allvalues=timedict[lstime]
186  transposedvalues=CommonUtil.transposed(allvalues,0.0)
187  bstatfrac=transposedvalues[0][0]#beamstatus does not change with bx position
188  lumivals=transposedvalues[1]
189  lumitot=sum(lumivals)
190  if bstatfrac==1.0 :
191  fillseg.append([lstime,lumitot])
192  lumierrs=transposedvalues[2]
193  lumierrortot=math.sqrt(sum(map(lambda x:x**2,lumierrs)))
194  specificvals=transposedvalues[3]
195  specificavg=sum(specificvals)/float(len(specificvals))#avg spec lumi
196  specificerrs=transposedvalues[4]
197  specifictoterr=math.sqrt(sum(map(lambda x:x**2,specificerrs)))
198  specificerravg=specifictoterr/float(len(specificvals))
199  if lscounter==0:
200  f=open(os.path.join(filloutdir,summaryfilename),'w')
201  lscounter+=1
202  print >>f,'%d\t%e\t%e\t%e\t%e\t%e'%(lstime,bstatfrac,lumitot,lumierrortot,specificavg,specificerravg)
203  if f is not None:
204  f.close()
205  #print 'writing summary file'
206  fillsummaryfilename=str(fillnum)+'_bxsum_CMS.txt'
207  f=open(os.path.join(filloutdir,fillsummaryfilename),'w')
208  if len(fillseg)==0:
209  print >>f,'%s'%('#no stable beams')
210  f.close()
211  return
212  previoustime=fillseg[0][0]
213  boundarytime=fillseg[0][0]
214  #print 'boundary time ',boundarytime
215  summaryls={}
216  summaryls[boundarytime]=[]
217  for [lstime,lumitot] in fillseg:#fillseg is everything with stable beam flag
218  if lstime-previoustime>50.0:
219  boundarytime=lstime
220  #print 'found new boundary ',boundarytime
221  summaryls[boundarytime]=[]
222  # print 'appending ',boundarytime,lstime,lumitot
223  summaryls[boundarytime].append([lstime,lumitot])
224  previoustime=lstime
225  #print summaryls
226 
227  summarylstimes=summaryls.keys()
228  summarylstimes.sort()
230  for bts in summarylstimes:
231  startts=bts
232  tsdatainseg=summaryls[bts]
233  #print 'tsdatainseg ',tsdatainseg
234  stopts=tsdatainseg[-1][0]
235  plu=max(CommonUtil.transposed(tsdatainseg,0.0)[1])
236  lui=sum(CommonUtil.transposed(tsdatainseg,0.0)[1])*lumip.lslengthsec()
237  print >>f,'%d\t%d\t%e\t%e'%(startts,stopts,plu,lui)
238  f.close()
239 
240 def getSpecificLumi(schema,fillnum,inputdir,dataidmap,normmap,xingMinLum=0.0,amodetag='PROTPHYS',bxAlgo='OCC1'):
241  '''
242  specific lumi in 1e-30 (ub-1s-1) unit
243  lumidetail occlumi in 1e-27
244  1309_lumi_401_CMS.txt
245  time(in seconds since January 1,2011,00:00:00 UTC) stab(fraction of time spent in stable beams for this time bin) l(lumi in Hz/ub) dl(point-to-point error on lumi in Hz/ub) sl(specific lumi in Hz/ub) dsl(error on specific lumi)
246  20800119.0 1 -0.889948 0.00475996848729 0.249009 0.005583287562 -0.68359 6.24140208607 0.0 0.0 0.0 0.0 0.0 0.0 0.0383576 0.00430892097862 0.0479095 0.00430892097862 66.6447 4.41269758764 0.0 0.0 0.0
247  result [(time,beamstatusfrac,lumi,lumierror,speclumi,speclumierror)]
248  '''
250  fillbypos={}#{bxidx:[[ts,beamstatusfrac,lumi,lumierror,spec1,specerror],[]]}
251  runtimesInFill=getFillFromDB(schema,fillnum)#{runnum:starttimestr}
252  runlist=runtimesInFill.keys()
253  if not runlist: return fillbypos
254  irunlsdict=dict(zip(runlist,[None]*len(runlist)))
255  #prirunlsdict
256  GrunsummaryData=lumiCalcAPI.runsummaryMap(session.nominalSchema(),irunlsdict)
257  lumidetails=lumiCalcAPI.deliveredLumiForIds(schema,irunlsdict,dataidmap,GrunsummaryData,beamstatusfilter=None,normmap=normmap,withBXInfo=True,bxAlgo=bxAlgo,xingMinLum=xingMinLum,withBeamIntensity=True,lumitype='HF')
258  #
259  #output: {run:[lumilsnum(0),cmslsnum(1),timestamp(2),beamstatus(3),beamenergy(4),deliveredlumi(5),calibratedlumierr(6),(bxvalues,bxerrs)(7),(bxidx,b1intensities,b2intensities)(8),fillnum(9)]}
260  #
261  totalstablebeamls=0
262  orderedrunlist=sorted(lumidetails)
263  for run in orderedrunlist:
264  perrundata=lumidetails[run]
265  for perlsdata in perrundata:
266  beamstatus=perlsdata[3]
267  if beamstatus=='STABLE BEAMS':
268  totalstablebeamls+=1
269  #print 'totalstablebeamls in fill ',totalstablebeamls
270  if totalstablebeamls<10:#less than 10 LS in a fill has 'stable beam', it's no a good fill
271  print 'fill ',fillnum,' , having less than 10 stable beam lS, is not good, skip'
272  return fillbypos
274  for run in orderedrunlist:
275  perrundata=lumidetails[run]
276  for perlsdata in perrundata:
277  beamstatusfrac=0.0
278  tsdatetime=perlsdata[2]
279  ts=calendar.timegm(tsdatetime.utctimetuple())
280  beamstatus=perlsdata[3]
281  if beamstatus=='STABLE BEAMS':
282  beamstatusfrac=1.0
283  (bxidxlist,bxvaluelist,bxerrolist)=perlsdata[7]
284  #instbxvaluelist=[x/lumiparam.lslengthsec() for x in bxvaluelist if x]
285  instbxvaluelist=[x for x in bxvaluelist if x]
286  maxlumi=0.0
287  if len(instbxvaluelist)!=0:
288  maxlumi=max(instbxvaluelist)
289  avginstlumi=0.0
290  if len(instbxvaluelist)!=0:
291  avginstlumi=sum(instbxvaluelist)
292  (intbxidxlist,b1intensities,b2intensities)=perlsdata[8]#contains only non-zero bx
293  for bxidx in bxidxlist:
294  idx=bxidxlist.index(bxidx)
295  instbxvalue=bxvaluelist[idx]
296  bxerror=bxerrolist[idx]
297  if instbxvalue<max(xingMinLum,maxlumi*0.2):
298  continue
299  bintensityPos=-1
300  try:
301  bintensityPos=intbxidxlist.index(bxidx)
302  except ValueError:
303  pass
304  if bintensityPos<=0:
305  fillbypos.setdefault(bxidx,[]).append([ts,beamstatusfrac,instbxvalue,bxerror,0.0,0.0])
306  continue
307  b1intensity=b1intensities[bintensityPos]
308  b2intensity=b2intensities[bintensityPos]
309  speclumi=calculateSpecificLumi(instbxvalue,bxerror,b1intensity,0.0,b2intensity,0.0)
310  fillbypos.setdefault(bxidx,[]).append([ts,beamstatusfrac,instbxvalue,bxerror,speclumi[0],speclumi[1]])
311  return fillbypos
312 
313 
314 ##############################
315 ## ######################## ##
316 ## ## ################## ## ##
317 ## ## ## Main Program ## ## ##
318 ## ## ################## ## ##
319 ## ######################## ##
320 ##############################
321 
322 if __name__ == '__main__':
323  parser = argparse.ArgumentParser(prog=os.path.basename(sys.argv[0]),description = "specific lumi",formatter_class=argparse.ArgumentDefaultsHelpFormatter)
324  amodetagChoices = [ "PROTPHYS","IONPHYS",'PAPHYS' ]
325  xingAlgoChoices =[ "OCC1","OCC2","ET"]
326  # parse arguments
327  parser.add_argument('-c',dest='connect',
328  action='store',
329  required=False,
330  help='connect string to lumiDB,optional',
331  default='frontier://LumiCalc/CMS_LUMI_PROD')
332  parser.add_argument('-P',dest='authpath',
333  action='store',
334  help='path to authentication file,optional')
335  parser.add_argument('-i',dest='inputdir',
336  action='store',
337  required=False,
338  help='output dir',
339  default='.')
340  parser.add_argument('-o',dest='outputdir',
341  action='store',
342  required=False,
343  help='output dir',
344  default='.')
345  parser.add_argument('-f','--fill',dest='fillnum',
346  action='store',
347  required=False,
348  help='specific fill',
349  default=None)
350  parser.add_argument('--minfill',dest='minfill',
351  type=int,
352  action='store',
353  required=False,
354  default=MINFILL,
355  help='min fill')
356  parser.add_argument('--maxfill',dest='maxfill',
357  type=int,
358  action='store',
359  required=False,
360  default=MAXFILL,
361  help='maximum fillnumber '
362  )
363  parser.add_argument('--amodetag',dest='amodetag',
364  action='store',
365  choices=amodetagChoices,
366  required=False,
367  help='specific accelerator mode choices [PROTOPHYS,IONPHYS,PAPHYS] (optional)')
368  parser.add_argument('--xingMinLum', dest = 'xingMinLum',
369  type=float,
370  default=1e-03,
371  required=False,
372  help='Minimum luminosity considered for lumibylsXing action')
373  parser.add_argument('--xingAlgo', dest = 'bxAlgo',
374  default='OCC1',
375  required=False,
376  help='algorithm name for per-bunch lumi ')
377  parser.add_argument('--normtag',dest='normtag',action='store',
378  required=False,
379  help='norm tag',
380  default=None)
381  parser.add_argument('--datatag',dest='datatag',action='store',
382  required=False,
383  help='data tag',
384  default=None)
385  #
386  #command configuration
387  #
388  parser.add_argument('--siteconfpath',dest='siteconfpath',action='store',
389  help='specific path to site-local-config.xml file, optional. If path undefined, fallback to cern proxy&server')
390  #
391  #switches
392  #
393  parser.add_argument('--without-correction',dest='withoutNorm',action='store_true',
394  help='without any correction/calibration' )
395  parser.add_argument('--debug',dest='debug',action='store_true',
396  help='debug')
397  options=parser.parse_args()
398  if options.authpath:
399  os.environ['CORAL_AUTH_PATH'] = options.authpath
400  ##
401  #query DB for all fills and compare with allfills.txt
402  #if found newer fills, store in mem fill number
403  #reprocess anyway the last 1 fill in the dir
404  #redo specific lumi for all marked fills
405  ##
406  svc=sessionManager.sessionManager(options.connect,authpath=options.authpath,debugON=options.debug)
407  session=svc.openSession(isReadOnly=True,cpp2sqltype=[('unsigned int','NUMBER(10)'),('unsigned long long','NUMBER(20)')])
408 
409  fillstoprocess=[]
410  maxfillnum=options.maxfill
411  minfillnum=options.minfill
412  if options.fillnum is not None: #if process a specific single fill
413  fillstoprocess.append(int(options.fillnum))
414  else:
415  session.transaction().start(True)
416  schema=session.nominalSchema()
417  allfillsFromDB=lumiCalcAPI.fillInRange(schema,fillmin=minfillnum,fillmax=maxfillnum,amodetag=options.amodetag)
418  processedfills=listfilldir(options.outputdir)
419  lastcompletedFill=lastcompleteFill(os.path.join(options.inputdir,'runtofill_dqm.txt'))
420  for pf in processedfills:
421  if pf>lastcompletedFill:
422  print '\tremove unfinished fill from processed list ',pf
423  processedfills.remove(pf)
424  for fill in allfillsFromDB:
425  if fill not in processedfills :
426  if int(fill)<=lastcompletedFill:
427  if int(fill)>minfillnum and int(fill)<maxfillnum:
428  fillstoprocess.append(fill)
429  else:
430  print 'ongoing fill...',fill
431  session.transaction().commit()
432  print 'fills to process : ',fillstoprocess
433  if len(fillstoprocess)==0:
434  print 'no fill to process, exit '
435  exit(0)
436 
437 
438  print '===== Start Processing Fills',fillstoprocess
439  print '====='
440  filldata={}
441  #
442  # check datatag
443  #
444  reqfillmin=min(fillstoprocess)
445  reqfillmax=max(fillstoprocess)
446  session.transaction().start(True)
447  runlist=lumiCalcAPI.runList(session.nominalSchema(),options.fillnum,runmin=None,runmax=None,fillmin=reqfillmin,fillmax=reqfillmax,startT=None,stopT=None,l1keyPattern=None,hltkeyPattern=None,amodetag=options.amodetag,nominalEnergy=None,energyFlut=None,requiretrg=False,requirehlt=False)
448 
449  datatagname=options.datatag
450  if not datatagname:
451  (datatagid,datatagname)=revisionDML.currentDataTag(session.nominalSchema())
452  dataidmap=revisionDML.dataIdsByTagId(session.nominalSchema(),datatagid,runlist=runlist,withcomment=False)
453  #{run:(lumidataid,trgdataid,hltdataid,())}
454  else:
455  dataidmap=revisionDML.dataIdsByTagName(session.nominalSchema(),datatagname,runlist=runlist,withcomment=False)
456 
457  #
458  # check normtag and get norm values if required
459  #
460  normname='NONE'
461  normid=0
462  normvalueDict={}
463  if not options.withoutNorm:
464  normname=options.normtag
465  if not normname:
466  normmap=normDML.normIdByType(session.nominalSchema(),lumitype='HF',defaultonly=True)
467  if len(normmap):
468  normname=normmap.keys()[0]
469  normid=normmap[normname]
470  else:
471  normid=normDML.normIdByName(session.nominalSchema(),normname)
472  if not normid:
473  raise RuntimeError('[ERROR] cannot resolve norm/correction')
474  sys.exit(-1)
475  normvalueDict=normDML.normValueById(session.nominalSchema(),normid) #{since:[corrector(0),{paramname:paramvalue}(1),amodetag(2),egev(3),comment(4)]}
476  session.transaction().commit()
477  for fillnum in fillstoprocess:# process per fill
478  session.transaction().start(True)
479  filldata=getSpecificLumi(session.nominalSchema(),fillnum,options.inputdir,dataidmap,normvalueDict,xingMinLum=options.xingMinLum,amodetag=options.amodetag,bxAlgo=options.bxAlgo)
480  specificlumiTofile(fillnum,filldata,options.outputdir)
481  session.transaction().commit()
482 
483 
Definition: start.py:1
def calculateSpecificLumi
Definition: specificLumi.py:74
def getFillFromFile
Definition: specificLumi.py:89
def dataIdsByTagId
Definition: revisionDML.py:798
def transposed
Definition: CommonUtil.py:146
const T & max(const T &a, const T &b)
def normIdByType
Definition: normDML.py:92
def dataIdsByTagName
Definition: revisionDML.py:689
def runsummary
Lumi data management and calculation API # # Author: Zhen Xie #.
Definition: lumiCalcAPI.py:10
def specificlumiTofile
def fillrunMap
Definition: lumiCalcAPI.py:41
def currentDataTag
Definition: revisionDML.py:513
def lastcompleteFill
Definition: specificLumi.py:57
def fillInRange
Definition: lumiCalcAPI.py:35
def normValueById
Definition: normDML.py:181
def deliveredLumiForIds
Definition: lumiCalcAPI.py:369
def filltofiles
output methods####
def runsummaryMap
Definition: lumiCalcAPI.py:21
def normIdByName
Definition: normDML.py:60