Belle II Software  release-08-01-10
DBBunchStructureImporter.py
1 #!/usr/bin/env python
2 
3 
10 
11 # Import bunch structure payload for master GT.
12 # Create payload taking information from the RunDB.
13 # Usage: basf2 DBBunchStructureImporter.py <minexp> <minrun> <maxexp> <maxrun>
14 #
15 # if minexp is 0 the designed filling patter will be created. Meaning 10101010...
16 # if minexp is 1003 the early phase 3 pattern will be created. Take the most used filling pattern.
17 
18 
19 from ROOT import Belle2
20 from rundb import RunDB
21 import sys
22 import basf2
23 import collections
24 
25 
26 def getCollidingPatternFromFiles(herFilePath, lerFilePath):
27  """
28  Take HER and LER filling pattern from file and perform AND operation to take only colliding buckets.
29 
30  The format is the one given by the accelerator people. The first column is the number of the bunch,
31  the second is 1.0000 or .0000 depending on whether the bunch is filled or not. The third column is extra.
32  """
33 
34  herFile = open(herFilePath, 'r')
35  herLines = herFile.readlines()
36 
37  lerFile = open(lerFilePath, 'r')
38  lerLines = lerFile.readlines()
39 
40  pattern = []
41 
42  for lineHer, lineLer in zip(herLines, lerLines):
43  if(lineHer[0] == "#"):
44  continue
45 
46  if(lineHer.split()[1] == '1.0000' and lineLer.split()[1] == '1.0000'):
47  pattern.append(1)
48  else:
49  pattern.append(0)
50 
51  return(pattern)
52 
53 
54 def getCollidingPattern(herFPHex, lerFPHex):
55  """Take LER and HER hexadecimal fill pattern, convert to binary and perform AND operation to take only colliding buckets"""
56 
57  herFPBin = bin(int(herFPHex, 16))
58  herFPBin = herFPBin.replace('0b', '')
59 
60  lerFPBin = bin(int(lerFPHex, 16))
61  lerFPBin = lerFPBin.replace('0b', '')
62 
63  return [int(h) & int(l) for h, l in zip(herFPBin, lerFPBin)]
64 
65 
66 def fill(fillPattern, firstExp, firstRun, lastExp, lastRun):
67  """Create a payload fill pattern for the given run range"""
68 
69  if fillPattern is None:
70  return
71 
72  bunches = Belle2.BunchStructure()
73 
74  if fillPattern != "":
75  for num, bucket in enumerate(fillPattern):
76  if(bucket):
77  bunches.setBucket(num)
78 
79  iov = Belle2.IntervalOfValidity(firstExp, firstRun, lastExp, lastRun)
80 
82 
83  db.storeData("BunchStructure", bunches, iov)
84 
85  if fillPattern != "":
86  basf2.B2INFO(f"Filling new payload. iov: {firstExp} {firstRun} {lastExp} {lastRun}")
87  else:
88  basf2.B2INFO(f"Filling new payload with default filling pattern. iov:{firstExp} {firstRun} {lastExp} {lastRun}\n")
89 
90 
91 # Argument parsing
92 argvs = sys.argv
93 
94 
95 if __name__ == "__main__":
96  import argparse
97  parser = argparse.ArgumentParser(description='Create bunch structure payload taking information from the RunDB')
98  parser.add_argument('minExp', metavar='minExp', type=int,
99  help='first experiment. Use only this argument to produce filling pattern for experiment 0 or 1003')
100  parser.add_argument('minRun', metavar='minRun', type=int, nargs='?',
101  help='first run')
102  parser.add_argument('maxExp', metavar='maxExp', type=int, nargs='?',
103  help='last experiment')
104  parser.add_argument('maxRun', metavar='maxRun', type=int, nargs='?',
105  help='last run')
106  parser.add_argument(
107  '--exprun1003',
108  dest='exprun1003',
109  metavar='<exp> <run>',
110  nargs='+',
111  default=[
112  12,
113  1859],
114  type=int,
115  help='Change run and experiment of the filling pattern to be used for early phase 3 \
116  run-independent MC (exp 1003). (default: %(default)s)')
117 
118  parser.add_argument(
119  '--patternFromFiles',
120  dest='patternFromFiles',
121  metavar='<herFile> <lerFile>',
122  nargs='+',
123  default=None,
124  help='Use external files to create filling pattern. The format is the one given by accelerator group')
125 
126  parser.add_argument(
127  '--dontFillGaps',
128  dest='dontFillGaps',
129  action='store_true',
130  default=False,
131  help="Don't fill the gaps between physic iovs with default filling pattern")
132 
133  args = parser.parse_args()
134 
135  if(args.minExp == 0):
136  bunches = Belle2.BunchStructure()
137 
138  if(args.patternFromFiles):
139  basf2.B2INFO("Using external files")
140  pattern = getCollidingPatternFromFiles(args.patternFromFiles[0], args.patternFromFiles[1])
141  fill(pattern, 0, 0, 0, -1)
142 
143  else:
144  extraStep = 0
145  for b in range(0, 5120, 2):
146 
147  # Shift of 1 bunch every 300 bunches to simulate bunch trains
148  if((b % 300 == 0) & (b != 0)):
149  extraStep += 1
150 
151  bunches.setBucket(b + extraStep)
152 
153  iov = Belle2.IntervalOfValidity(0, 0, 0, -1)
154 
156  db.storeData("BunchStructure", bunches, iov)
157 
158  elif(args.minExp == 1003):
159 
160  if(args.patternFromFiles):
161  basf2.B2INFO("Using external files")
162  pattern = getCollidingPatternFromFiles(args.patternFromFiles[0], args.patternFromFiles[1])
163 
164  else:
165  username = input("Enter your DESY username:")
166  rundb = RunDB(username=username)
167 
168  basf2.B2INFO(f"Use exp {args.exprun1003[0]} and run {args.exprun1003[1]} for early phase 3 run-independent MC")
169 
170  runInfo = rundb.get_run_info(
171  min_experiment=args.exprun1003[0],
172  max_experiment=args.exprun1003[0],
173  min_run=args.exprun1003[1],
174  max_run=args.exprun1003[1],
175  run_type='physics',
176  expand=True
177  )
178 
179  for it in runInfo:
180  pattern = getCollidingPattern(it['her']['fill_pattern'], it['ler']['fill_pattern'])
181 
182  fill(pattern, 1003, 0, 1003, -1)
183 
184  elif(args.patternFromFiles):
185  basf2.B2INFO("Using external files")
186  pattern = getCollidingPatternFromFiles(args.patternFromFiles[0], args.patternFromFiles[1])
187  fill(pattern, args.minExp, args.minRun, args.maxExp, args.maxRun)
188 
189  else:
190  username = input("Enter your DESY username: ")
191  rundb = RunDB(username=username)
192 
193  minexp = args.minExp
194  minrun = args.minRun
195  maxexp = args.maxExp
196  maxrun = args.maxRun
197 
198  runInfo = rundb.get_run_info(
199  min_experiment=minexp,
200  max_experiment=maxexp,
201  min_run=minrun,
202  max_run=maxrun,
203  run_type='physics',
204  expand=True
205  )
206 
207  current_pattern = None
208  current_start = None, None
209 
210  # Necessary to fill first gap
211  current_end = minexp, minrun
212 
213  # Dictionary used to calculate integrated lumi for each filling pattern and store info for summary
214  lumiDic = {}
215 
216  lastEnd = minexp, minrun
217 
218  lumi = 0
219 
220  for it in runInfo:
221  exprun = it['experiment'], it['run']
222 
223  if(it['her']['fill_pattern'] == "" or it['ler']['fill_pattern'] == ""):
224  basf2.B2WARNING(f"Filling pattern for Exp {exprun[0]} Run {exprun[1]} is empty. \
225  Previous filling pattern will be used instead.")
226  continue
227 
228  pattern = getCollidingPattern(it['her']['fill_pattern'], it['ler']['fill_pattern'])
229 
230  # pattern different to previous one or first run
231  if pattern != current_pattern:
232 
233  if(args.dontFillGaps is False and current_start[0] is not None
234  and current_start[1] != lastEnd[1] + 1 and current_start[1] != lastEnd[1]):
235  fill("", lastEnd[0], lastEnd[1] + 1, current_start[0], current_start[1] - 1)
236 
237  # close the iov. If it's first run fill() doesn't close the iov.
238  fill(current_pattern, *current_start, *current_end)
239 
240  if current_pattern is not None:
241  basf2.B2INFO(f"Corresponding to {lumi/1000.:.2f} pb-1\n")
242  # fill iovs in lumi dic
243  lumiDic[''.join(str(i) for i in current_pattern)]["iovs"].append([*current_start, *current_end])
244 
245  # Create dictionary keys using the filling pattern itself
246  keyFP = ''.join(str(i) for i in pattern)
247 
248  if(keyFP in lumiDic):
249  lumiDic[keyFP]["lumi"] += it['statistics']['lumi_recorded']
250 
251  else:
252  lumiDic[keyFP] = {}
253  lumiDic[keyFP]["lumi"] = it['statistics']['lumi_recorded']
254  lumiDic[keyFP]["lerBunches"] = it['ler']['number_of_bunches']
255  lumiDic[keyFP]["herBunches"] = it['her']['number_of_bunches']
256  lumiDic[keyFP]["iovs"] = []
257 
258  lumi = it['statistics']['lumi_recorded']
259 
260  # last values
261  lastEnd = current_end
262 
263  # and remember new values
264  current_pattern = pattern
265  current_start = exprun
266  current_end = exprun
267 
268  else:
269  # pattern unchanged, extend current iov
270  current_end = exprun
271  lumi += it['statistics']['lumi_recorded']
272  lumiDic[''.join(str(i) for i in current_pattern)]["lumi"] += it['statistics']['lumi_recorded']
273 
274  # close the last iov if any
275  if(args.dontFillGaps is False and current_start[0] is not None and current_start[1] != lastEnd[1] + 1):
276  fill("", lastEnd[0], lastEnd[1] + 1, current_start[0], current_start[1] - 1)
277 
278  fill(current_pattern, *current_start, *current_end)
279  basf2.B2INFO(f"Corresponding to {lumi/1000.:.2f} pb-1\n")
280  lumiDic[''.join(str(i) for i in current_pattern)]["iovs"].append([*current_start, *current_end])
281 
282  if(args.dontFillGaps is False and current_end[1] != maxrun):
283  fill("", current_end[0], current_end[1] + 1, maxexp, maxrun)
284 
285  # last open iov with default filling pattern
286  fill("", maxexp, maxrun + 1, -1, -1)
287 
288  # print a summary with info on most used filling pattern
289  basf2.B2INFO("Summary:\n")
290  i = 0
291  for k, v in collections.OrderedDict(sorted(lumiDic.items(), key=lambda t: t[1]["lumi"], reverse=True)).items():
292  i += 1
293  basf2.B2INFO(f"{i} Filling pattern")
294  basf2.B2INFO(f" lumi {v['lumi']/1000.:.2f} pb-1")
295  basf2.B2INFO(f" HER/LER number of bunches {int(lumiDic[keyFP]['herBunches'])}/{int(lumiDic[keyFP]['lerBunches'])}")
296  for iov in v["iovs"]:
297  basf2.B2INFO(f" iov {iov[0]} {iov[1]} - {iov[2]} {iov[3]}")
298  print("")
Class to store the fill pattern of colliding bunches.
A class that describes the interval of experiments/runs for which an object in the database is valid.
static Database & Instance()
Instance of a singleton Database.
Definition: Database.cc:42