Belle II Software development
KLMStripEfficiency Class Reference
Inheritance diagram for KLMStripEfficiency:

Public Member Functions

 __init__ (self, algorithm)
 
 run (self, iov, iteration, queue)
 
 execute_over_run_list (self, run_list, iteration, forced_calibration, calibration_stage, output_file)
 
 process_experiment (self, experiment, experiment_runs, iteration, lowest_exprun, highest_exprun)
 

Public Attributes

 machine = AlgorithmMachine(self.algorithm)
 :py:class:caf.state_machines.AlgorithmMachine used to help set up and execute CalibrationAlgorithm.
 
bool first_execution = True
 Flag for the first execution of this AlgorithmStrategy.
 
 queue = queue
 The multiprocessing queue used to pass back results one at a time.
 

Static Public Attributes

dict usable_params = {'iov_coverage': IoV}
 The parameters of Algorithm object which this Strategy would use.
 
list ignored_runs [int]
 
 COMPLETED = AlgorithmStrategy.COMPLETED
 
 FAILED = AlgorithmStrategy.FAILED
 

Detailed Description

Custom strategy for executing the KLM strip efficiency. Requires complex
run merging rules.

This uses a `caf.state_machines.AlgorithmMachine` to actually execute
the various steps rather than operating on a CalibrationAlgorithm
C++ class directly.

Definition at line 26 of file klm_strip_efficiency.py.

Constructor & Destructor Documentation

◆ __init__()

__init__ ( self,
algorithm )
 

Definition at line 47 of file klm_strip_efficiency.py.

47 def __init__(self, algorithm):
48 """
49 """
50 super().__init__(algorithm)
51
54 self.machine = AlgorithmMachine(self.algorithm)
55
56 self.first_execution = True
57

Member Function Documentation

◆ execute_over_run_list()

execute_over_run_list ( self,
run_list,
iteration,
forced_calibration,
calibration_stage,
output_file )
Execute over run list.

Definition at line 132 of file klm_strip_efficiency.py.

133 calibration_stage, output_file):
134 """
135 Execute over run list.
136 """
137 if not self.first_execution:
138 self.machine.setup_algorithm()
139 else:
140 self.first_execution = False
141 self.machine.algorithm.algorithm.setForcedCalibration(
142 forced_calibration)
143 self.machine.algorithm.algorithm.setCalibrationStage(calibration_stage)
144 if (output_file is not None):
145 self.machine.algorithm.algorithm.setOutputFileName(output_file)
146 self.machine.execute_runs(runs=run_list, iteration=iteration,
147 apply_iov=None)
148 if (self.machine.result.result == AlgResult.ok.value) or \
149 (self.machine.result.result == AlgResult.iterate.value):
150 self.machine.complete()
151 else:
152 self.machine.fail()
153

◆ process_experiment()

process_experiment ( self,
experiment,
experiment_runs,
iteration,
lowest_exprun,
highest_exprun )
Process runs from experiment.

Definition at line 154 of file klm_strip_efficiency.py.

155 lowest_exprun, highest_exprun):
156 """
157 Process runs from experiment.
158 """
159 # Run lists. They have the following format: run number,
160 # calibration result code, ExpRun, algorithm results,
161 # merge information, payload.
162 run_data = []
163
164 # Initial run.
165 for exp_run in experiment_runs:
166 self.execute_over_run_list(
167 [exp_run], iteration, False,
168 KLMStripEfficiencyAlgorithm.c_MeasurablePlaneCheck, None)
169 result = self.machine.result.result
170 algorithm_results = KLMStripEfficiencyAlgorithm.Results(
171 self.machine.algorithm.algorithm.getResults())
172 # If number of hits is 0, then KLM is excluded. Such runs
173 # can be ignored safely.
174 if (algorithm_results.getExtHits() > 0):
175 run_data.append([exp_run.run, result, [exp_run],
176 algorithm_results, '', None])
177 result_str = calibration_result_string(result)
178 basf2.B2INFO(f'Run {int(exp_run.run)}: {result_str}.')
179
180 # Sort by run number.
181 run_data.sort(key=lambda x: x[0])
182
183 # Create list of runs that do not have enough data.
184 run_ranges = []
185 i = 0
186 while (i < len(run_data)):
187 if (run_data[i][1] == 2):
188 j = i
189 while (run_data[j][1] == 2):
190 j += 1
191 if (j >= len(run_data)):
192 break
193 run_ranges.append([i, j])
194 i = j
195 else:
196 i += 1
197
198 # Determine whether the runs with insufficient data can be merged to
199 # the next or previous normal run.
200 def can_merge(run_data, run_not_enough_data, run_normal):
201 return run_data[run_not_enough_data][3].newExtHitsPlanes(
202 run_data[run_normal][3].getExtHitsPlane()) == 0
203
204 for run_range in run_ranges:
205 next_run = run_range[1]
206 # To mark as 'none' at the end if there are no normal runs.
207 j = run_range[0]
208 i = next_run - 1
209 if (next_run < len(run_data)):
210 while (i >= run_range[0]):
211 if (can_merge(run_data, i, next_run)):
212 basf2.B2INFO(f'Run {int(run_data[i][0])} (not enough data) can be merged into the next normal run ' +
213 f'{int(run_data[next_run][0])}.')
214 run_data[i][4] = 'next'
215 else:
216 basf2.B2INFO(f'Run {int(run_data[i][0])} (not enough data) cannot be merged into the next normal run ' +
217 f'{int(run_data[next_run][0])}, will try the previous one.')
218 break
219 i -= 1
220 if (i < run_range[0]):
221 continue
222 previous_run = run_range[0] - 1
223 if (previous_run >= 0):
224 while (j <= i):
225 if (can_merge(run_data, j, previous_run)):
226 basf2.B2INFO(f'Run {int(run_data[j][0])} (not enough data) can be merged into the previous normal run ' +
227 f'{int(run_data[previous_run][0])}.')
228 run_data[j][4] = 'previous'
229 else:
230 basf2.B2INFO(f'Run {int(run_data[j][0])} (not enough data) cannot be merged into the previous normal ' +
231 f'run {int(run_data[previous_run][0])}.')
232 break
233 j += 1
234 if (j > i):
235 continue
236 basf2.B2INFO('A range of runs with not enough data is found that cannot be merged into neither previous nor ' +
237 f'next normal run: from {int(run_data[j][0])} to {int(run_data[i][0])}.')
238 while (j <= i):
239 run_data[j][4] = 'none'
240 j += 1
241
242 # Merge runs that do not have enough data. If both this and next
243 # run do not have enough data, then merge the collected data.
244 i = 0
245 j = 0
246 while (i < len(run_data) - 1):
247 while ((run_data[i][1] == 2) and (run_data[i + 1][1] == 2)):
248 if (run_data[i][4] != run_data[i + 1][4]):
249 break
250 basf2.B2INFO(f'Merging run {int(run_data[i + 1][0])} (not enough data) into run {int(run_data[i][0])} ' +
251 '(not enough data).')
252 run_data[i][2].extend(run_data[i + 1][2])
253 del run_data[i + 1]
254 self.execute_over_run_list(
255 run_data[i][2], iteration, False,
256 KLMStripEfficiencyAlgorithm.c_MeasurablePlaneCheck, None)
257 run_data[i][1] = self.machine.result.result
258 run_data[i][3] = KLMStripEfficiencyAlgorithm.Results(
259 self.machine.algorithm.algorithm.getResults())
260 result_str = calibration_result_string(run_data[i][1])
261 basf2.B2INFO(f'Run {int(run_data[i][0])}: {result_str}.')
262 if (i >= len(run_data) - 1):
263 break
264 i += 1
265
266 # Merge runs that do not have enough data into normal runs.
267 def merge_runs(run_data, run_not_enough_data, run_normal, forced):
268 basf2.B2INFO(f'Merging run {int(run_data[run_not_enough_data][0])} (not enough data) into run ' +
269 f'{int(run_data[run_normal][0])} (normal).')
270 run_data[run_normal][2].extend(run_data[run_not_enough_data][2])
271 self.execute_over_run_list(
272 run_data[run_normal][2], iteration, forced,
273 KLMStripEfficiencyAlgorithm.c_MeasurablePlaneCheck, None)
274 run_data[run_normal][1] = self.machine.result.result
275 run_data[run_normal][3] = KLMStripEfficiencyAlgorithm.Results(
276 self.machine.algorithm.algorithm.getResults())
277 result_str = calibration_result_string(run_data[run_normal][1])
278 basf2.B2INFO(f'Run {int(run_data[run_normal][0])}: {result_str}.')
279 if (run_data[run_normal][1] != 0):
280 basf2.B2FATAL(f'Merging run {int(run_data[run_not_enough_data][0])} into ' +
281 f'run {int(run_data[run_normal][0])} failed.')
282 del run_data[run_not_enough_data]
283
284 i = 0
285 while (i < len(run_data)):
286 if (run_data[i][1] == 2):
287 if (run_data[i][4] == 'next'):
288 merge_runs(run_data, i, i + 1, False)
289 elif (run_data[i][4] == 'previous'):
290 merge_runs(run_data, i, i - 1, False)
291 else:
292 i += 1
293 else:
294 i += 1
295 i = 0
296 while (i < len(run_data)):
297 if (run_data[i][1] == 2 and run_data[i][4] == 'none'):
298 new_planes_previous = -1
299 new_planes_next = -1
300 if (i < len(run_data) - 1):
301 new_planes_next = run_data[i][3].newExtHitsPlanes(
302 run_data[i + 1][3].getExtHitsPlane())
303 basf2.B2INFO(f'There are {int(new_planes_next)} new active modules in run {int(run_data[i][0])} ' +
304 f'relatively to run {int(run_data[i + 1][0])}.')
305 if (i > 0):
306 new_planes_previous = run_data[i][3].newExtHitsPlanes(
307 run_data[i - 1][3].getExtHitsPlane())
308 basf2.B2INFO(f'There are {int(new_planes_previous)} new active modules in run {int(run_data[i][0])} ' +
309 f'relatively to run {int(run_data[i - 1][0])}.')
310 run_for_merging = -1
311 # If a forced merge of the normal run with another run from
312 # a different range of runs with not enough data has already
313 # been performed, then the list of active modules may change
314 # and there would be 0 new modules. Consequently, the number
315 # of modules is checked to be greater or equal than 0. However,
316 # there is no guarantee that the same added module would be
317 # calibrated normally. Thus, a forced merge is performed anyway.
318 if (new_planes_previous >= 0 and new_planes_next < 0):
319 run_for_merging = i - 1
320 elif (new_planes_previous < 0 and new_planes_next >= 0):
321 run_for_merging = i + 1
322 elif (new_planes_previous >= 0 and new_planes_next >= 0):
323 if (new_planes_previous < new_planes_next):
324 run_for_merging = i - 1
325 else:
326 run_for_merging = i + 1
327 else:
328 basf2.B2INFO(f'Cannot determine run for merging for run {int(run_data[i][0])}, performing its' +
329 ' forced calibration.')
330 self.execute_over_run_list(
331 run_data[i][2], iteration, True,
332 KLMStripEfficiencyAlgorithm.c_MeasurablePlaneCheck,
333 None)
334 run_data[i][1] = self.machine.result.result
335 run_data[i][3] = KLMStripEfficiencyAlgorithm.Results(
336 self.machine.algorithm.algorithm.getResults())
337 result_str = calibration_result_string(run_data[i][1])
338 basf2.B2INFO(f'Run {int(run_data[i][0])}: {result_str}.')
339 if (run_data[i][1] != 0):
340 basf2.B2FATAL(f'Forced calibration of run {int(run_data[i][0])} failed.')
341 if (run_for_merging >= 0):
342 merge_runs(run_data, i, run_for_merging, True)
343 else:
344 i += 1
345
346 # Stage 2: determination of maximal run ranges.
347 # The set of calibrated planes should be the same for all small
348 # run ranges within the large run range.
349 run_ranges.clear()
350 i = 0
351 while (i < len(run_data)):
352 j = i + 1
353 while (j < len(run_data)):
354 planes_differ = False
355 if (run_data[j][3].newMeasuredPlanes(
356 run_data[i][3].getEfficiency()) != 0):
357 planes_differ = True
358 if (run_data[i][3].newMeasuredPlanes(
359 run_data[j][3].getEfficiency()) != 0):
360 planes_differ = True
361 if (planes_differ):
362 basf2.B2INFO(f'Run {int(run_data[j][0])}: the set of planes is different from run {int(run_data[i][0])}.')
363 break
364 else:
365 basf2.B2INFO(f'Run {int(run_data[j][0])}: the set of planes is the same as for run {int(run_data[i][0])}.')
366 j = j + 1
367 run_ranges.append([i, j])
368 i = j
369
370 # Stage 3: final calibration.
371
372 # Output directory.
373 if (not os.path.isdir('efficiency')):
374 os.mkdir('efficiency')
375
376 # Merge runs.
377 def merge_runs_2(run_data, run_1, run_2, forced):
378 basf2.B2INFO(f'Merging run {int(run_data[run_2][0])} into run {int(run_data[run_1][0])}.')
379 run_data[run_1][2].extend(run_data[run_2][2])
380 output_file = f'efficiency/efficiency_{int(run_data[run_1][2][0].exp)}_{int(run_data[run_1][2][0].run)}.root'
381 self.execute_over_run_list(
382 run_data[run_1][2], iteration, forced,
383 KLMStripEfficiencyAlgorithm.c_EfficiencyMeasurement,
384 output_file)
385 run_data[run_1][1] = self.machine.result.result
386 run_data[run_1][3] = KLMStripEfficiencyAlgorithm.Results(
387 self.machine.algorithm.algorithm.getResults())
388 run_data[run_1][5] = \
389 self.machine.algorithm.algorithm.getPayloadValues()
390 result_str = calibration_result_string(run_data[run_1][1])
391 basf2.B2INFO(f'Run {int(run_data[run_1][0])}: {result_str}; requested precision {0.02:f}, achieved precision ' +
392 f'{run_data[run_1][3].getAchievedPrecision():f}.')
393
394 for run_range in run_ranges:
395 i = run_range[0]
396 while (i < run_range[1]):
397 output_file = f'efficiency/efficiency_{int(run_data[i][2][0].exp)}_{int(run_data[i][2][0].run)}.root'
398 # Force calibration if there are no more runs in the range.
399 if (i == run_range[1] - 1):
400 forced_calibration = True
401 else:
402 forced_calibration = False
403 self.execute_over_run_list(
404 run_data[i][2], iteration, forced_calibration,
405 KLMStripEfficiencyAlgorithm.c_EfficiencyMeasurement,
406 output_file)
407 run_data[i][1] = self.machine.result.result
408 run_data[i][3] = KLMStripEfficiencyAlgorithm.Results(
409 self.machine.algorithm.algorithm.getResults())
410 run_data[i][5] = \
411 self.machine.algorithm.algorithm.getPayloadValues()
412 result_str = calibration_result_string(run_data[i][1])
413 basf2.B2INFO(f'Run {int(run_data[i][0])}: {result_str}; requested precision {0.02:f}, achieved precision ' +
414 f'{run_data[i][3].getAchievedPrecision():f}.')
415 if (run_data[i][1] == 2):
416 j = i + 1
417 while (j < run_range[1]):
418 # Force calibration if there are no more runs
419 # in the range.
420 if (j == run_range[1] - 1):
421 forced_calibration = True
422 else:
423 forced_calibration = False
424 merge_runs_2(run_data, i, j, forced_calibration)
425 run_data[j][1] = -1
426 j = j + 1
427 if (run_data[i][1] == 0):
428 break
429 i = j
430 else:
431 i = i + 1
432
433 i = 0
434 while (i < len(run_data)):
435 if (run_data[i][1] == -1):
436 del run_data[i]
437 else:
438 i = i + 1
439
440 # Stage 4: write the results to the database.
441 def commit_payload(run_data, run):
442 basf2.B2INFO(f'Writing run {int(run_data[run][0])}.')
443 self.machine.algorithm.algorithm.commit(run_data[run][5])
444
445 for i in range(0, len(run_data)):
446 # Get first run again due to possible mergings.
447 run_data[i][2].sort(key=lambda x: x.run)
448 first_run = run_data[i][2][0].run
449 # Set IOV for the current run.
450 # The last run will be overwritten when writing the result.
451 run_data[i][5].front().iov = \
452 Belle2.IntervalOfValidity(experiment, first_run, experiment, -1)
453 # Write the previous run.
454 if (i > 0):
455 iov = run_data[previous_run][5].front().iov
456 if (previous_run == 0):
457 run_data[previous_run][5].front().iov = \
459 lowest_exprun.exp, lowest_exprun.run,
460 experiment, first_run - 1)
461 else:
462 run_data[previous_run][5].front().iov = \
463 Belle2.IntervalOfValidity(experiment, iov.getRunLow(),
464 experiment, first_run - 1)
465 commit_payload(run_data, previous_run)
466 previous_run = i
467 if (i == 0):
468 previous_run = 0
469 # Write the current run if it is the last run.
470 if (i == len(run_data) - 1):
471 iov = run_data[i][5].front().iov
472 run_data[i][5].front().iov = \
474 experiment, iov.getRunLow(),
475 highest_exprun.exp, highest_exprun.run)
476 commit_payload(run_data, i)
A class that describes the interval of experiments/runs for which an object in the database is valid.

◆ run()

run ( self,
iov,
iteration,
queue )
Runs the algorithm machine over the collected data and
fills the results.

Definition at line 58 of file klm_strip_efficiency.py.

58 def run(self, iov, iteration, queue):
59 """
60 Runs the algorithm machine over the collected data and
61 fills the results.
62 """
63 if not self.is_valid():
64 raise StrategyError('The strategy KLMStripEfficiency was not '
65 'set up correctly.')
66
67 self.queue = queue
68
69 basf2.B2INFO(f'Setting up {self.__class__.__name__} strategy '
70 f'for {self.algorithm.name}')
71 # Add all the necessary parameters for a strategy to run.
72 machine_params = {}
73 machine_params['database_chain'] = self.database_chain
74 machine_params['dependent_databases'] = self.dependent_databases
75 machine_params['output_dir'] = self.output_dir
76 machine_params['output_database_dir'] = self.output_database_dir
77 machine_params['input_files'] = self.input_files
78 machine_params['ignored_runs'] = self.ignored_runs
79 self.machine.setup_from_dict(machine_params)
80 # Start moving through machine states.
81 basf2.B2INFO(f'Starting AlgorithmMachine of {self.algorithm.name}')
82 self.algorithm.algorithm.setCalibrationStage(
83 KLMStripEfficiencyAlgorithm.c_EfficiencyMeasurement)
84 # This sets up the logging and database chain and assigns all
85 # input files from collector jobs.
86 self.machine.setup_algorithm(iteration=iteration)
87 # After this point, the logging is in the stdout of the algorithm.
88 basf2.B2INFO(f'Beginning execution of {self.algorithm.name} using '
89 f'strategy {self.__class__.__name__}')
90
91 # Select of runs for calibration.
92 runs = self.algorithm.algorithm.getRunListFromAllData()
93 all_runs_collected = set(runs_from_vector(runs))
94 # Select runs overlapping with the calibration IOV if it is specified.
95 if iov:
96 runs_to_execute = runs_overlapping_iov(iov, all_runs_collected)
97 else:
98 runs_to_execute = all_runs_collected
99 # Remove the ignored runs.
100 if self.ignored_runs:
101 basf2.B2INFO(f'Removing the ignored_runs from the runs '
102 f'to execute for {self.algorithm.name}')
103 runs_to_execute.difference_update(set(self.ignored_runs))
104
105 # Creation of sorted run list split by experiment.
106 runs_to_execute = sorted(runs_to_execute)
107 runs_to_execute = split_runs_by_exp(runs_to_execute)
108
109 # Get IOV coverage,
110 iov_coverage = None
111 if 'iov_coverage' in self.algorithm.params:
112 iov_coverage = self.algorithm.params['iov_coverage']
113
114 # Iterate over experiment run lists.
115 number_of_experiments = len(runs_to_execute)
116 for i_exp, run_list in enumerate(runs_to_execute, start=1):
117 lowest_exprun = get_lowest_exprun(number_of_experiments, i_exp,
118 run_list, iov_coverage)
119 highest_exprun = get_highest_exprun(number_of_experiments, i_exp,
120 run_list, iov_coverage)
121 self.process_experiment(run_list[0].exp, run_list, iteration,
122 lowest_exprun, highest_exprun)
123
124 # Send final state and result to CAF.
125 self.send_result(self.machine.result)
126 if (self.machine.result.result == AlgResult.ok.value) or \
127 (self.machine.result.result == AlgResult.iterate.value):
128 self.send_final_state(self.COMPLETED)
129 else:
130 self.send_final_state(self.FAILED)
131

Member Data Documentation

◆ COMPLETED

COMPLETED = AlgorithmStrategy.COMPLETED
static

Definition at line 43 of file klm_strip_efficiency.py.

◆ FAILED

FAILED = AlgorithmStrategy.FAILED
static

Definition at line 45 of file klm_strip_efficiency.py.

◆ first_execution

bool first_execution = True

Flag for the first execution of this AlgorithmStrategy.

Definition at line 56 of file klm_strip_efficiency.py.

◆ ignored_runs

ignored_runs [int]
static

Definition at line 41 of file klm_strip_efficiency.py.

◆ machine

machine = AlgorithmMachine(self.algorithm)

:py:class:caf.state_machines.AlgorithmMachine used to help set up and execute CalibrationAlgorithm.

It gets set up properly in :py:func:run.

Definition at line 54 of file klm_strip_efficiency.py.

◆ queue

queue = queue

The multiprocessing queue used to pass back results one at a time.

Definition at line 67 of file klm_strip_efficiency.py.

◆ usable_params

dict usable_params = {'iov_coverage': IoV}
static

The parameters of Algorithm object which this Strategy would use.

Just here for documentation reasons.

Definition at line 38 of file klm_strip_efficiency.py.


The documentation for this class was generated from the following file: