Belle II Software development
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
19from ROOT import Belle2
20from rundb import RunDB
21import sys
22import basf2
23import collections
24
25
26def 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)
35 herLines = herFile.readlines()
36
37 lerFile = open(lerFilePath)
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
54def 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
66def 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
92argvs = sys.argv
93
94
95if __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