Belle II Software development
__init__.py
1#!/usr/bin/env python3
2
3
10
11"""
12PXD calibration APIs for CAF
13Author: qingyuan.liu@desy.de
14"""
15
16from basf2 import register_module, create_path
17from ROOT.Belle2 import PXDHotPixelMaskCalibrationAlgorithm, PXDAnalyticGainCalibrationAlgorithm
18from ROOT.Belle2 import PXDValidationAlgorithm
19from caf.framework import Calibration
20from caf.strategies import SequentialRunByRun, SimpleRunByRun
21
22run_types = ['beam', 'physics', 'cosmic', 'all']
23gain_methods = ['analytic', 'generic_mc', 'cluster_sim', '']
24
25
26def hot_pixel_mask_calibration(
27 input_files,
28 cal_name="PXDHotPixelMaskCalibration",
29 run_type=None,
30 global_tags=None,
31 local_dbs=None,
32 **kwargs):
33 """
34 Parameters:
35 input_files (list): A list of input files to be assigned to calibration.input_files
36
37 run_type ( str): The run type which will define different calibration parameters.
38 Should be in ['beam', 'physics', 'cosmic', 'all']
39
40 global_tags (list): A list of global tags
41
42 local_dbs (list): A list of local databases
43
44 **kwargs: Additional configuration to support extentions without changing scripts in calibration folder.
45 Supported options are listed below:
46
47 "activate_masking" is a boolean to activate existing masking in the payload.
48 PXDHotPixelMaskCollector will be used then instead of PXDRawHotPixelMaskCollector
49
50 "debug_hist" is the flag to save a ROOT file for debug histograms.
51 "inefficientPixelMultiplier" is the multiplier on median occupancy to define an inefficient pixel.
52 0 means only dead pixels are marked as dead.
53 A value > 0 means any pixels with occupancy below the threshold will be marked as dead.
54 "minInefficientPixels" is the minimum number of pixels to define a dead or inefficient row.
55 The rows with inefficient pixels >= this value will be marked as dead rows for now.
56 "deadPixelPayloadName" is the payload name used for more defective pixel types. The default is PXDDeadPixelPar.
57
58 Return:
59 A caf.framework.Calibration obj.
60 """
61
62 if global_tags is None:
63 global_tags = []
64 if local_dbs is None:
65 local_dbs = []
66 if run_type is None:
67 run_type = 'all'
68 if not isinstance(run_type, str) or run_type.lower() not in run_types:
69 raise ValueError(f"run_type not found in run_types : {run_type}")
70 activate_masking = kwargs.get("activate_masking", False)
71 if not isinstance(activate_masking, bool):
72 raise ValueError("activate_masking is not a boolean!")
73 debug_hist = kwargs.get("debug_hist", True)
74 if not isinstance(debug_hist, bool):
75 raise ValueError("debug_hist is not a boolean!")
76 inefficientPixelMultiplier = kwargs.get("inefficientPixelMultiplier", 0.)
77 if not isinstance(inefficientPixelMultiplier, float):
78 raise ValueError("inefficientPixelMultiplier is not a float!")
79 minInefficientPixels = kwargs.get("minInefficientPixels", 250)
80 if not isinstance(minInefficientPixels, int):
81 raise ValueError("minInefficientPixels is not an int!")
82 deadPixelPayloadName = kwargs.get("deadPixelPayloadName", "PXDDeadPixelPar")
83 if not isinstance(deadPixelPayloadName, str):
84 raise ValueError("deadPixelPayloadName is not a str!")
85
86 # Create basf2 path
87
88 gearbox = register_module('Gearbox')
89 geometry = register_module('Geometry')
90 geometry.param('components', ['PXD'])
91 pxdunpacker = register_module('PXDUnpacker')
92 # pxdunpacker.param('ContinueOnError', True)
93 checker = register_module('PXDPostErrorChecker')
94 # checker.param("CriticalErrorMask", 0)
95
96 main = create_path()
97 main.add_module(gearbox)
98 main.add_module(geometry)
99 main.add_module(pxdunpacker)
100 main.add_module(checker)
101 if activate_masking:
102 main.add_module("ActivatePXDPixelMasker")
103 main.add_module("PXDRawHitSorter")
104
105 # Collector setup
106 collector_name = "PXDRawHotPixelMaskCollector"
107 if activate_masking:
108 collector_name = "PXDHotPixelMaskCollector"
109 collector = register_module(collector_name)
110 collector.param("granularity", "run")
111
112 # Algorithm setup
113
114 algorithm = PXDHotPixelMaskCalibrationAlgorithm() # Getting a calibration algorithm instanced
115 algorithm.forceContinueMasking = False # Continue masking even when few/no events were collected
116 algorithm.minEvents = 30000 # Minimum number of collected events for masking
117 algorithm.minHits = 15 # Do dead pixel masking when median number of hits per pixel is higher
118 algorithm.pixelMultiplier = 7 # Occupancy threshold is median occupancy x multiplier
119 algorithm.maskDrains = True # Set True to allow masking of hot drain lines
120 algorithm.drainMultiplier = 7 # Occupancy threshold is median occupancy x multiplier
121 algorithm.maskRows = True # Set True to allow masking of hot rows
122 algorithm.rowMultiplier = 7 # Occupancy threshold is median occupancy x multiplier
123 # Occupancy threshold is median occupancy x multiplier
124 algorithm.inefficientPixelMultiplier = inefficientPixelMultiplier
125 # Minimum number of inefficient pixels in a dead/inefficient row
126 algorithm.minInefficientPixels = minInefficientPixels
127 algorithm.deadPixelPayloadName = deadPixelPayloadName
128 algorithm.setDebugHisto(debug_hist)
129 algorithm.setPrefix(collector_name)
130 if run_type.lower() == 'cosmic':
131 algorithm.forceContinueMasking = True
132
133 # Create the calibration instance
134
135 cal = Calibration(
136 name=cal_name,
137 collector=collector,
138 algorithms=[algorithm],
139 input_files=input_files)
140 for global_tag in global_tags:
141 cal.use_central_database(global_tag)
142 for local_db in local_dbs:
143 cal.use_local_database(local_db)
144 cal.pre_collector_path = main
145 cal.strategies = SequentialRunByRun
146
147 # Run type dependent configurations
148
149 if run_type.lower() == 'cosmic':
150 cal.strategies = SimpleRunByRun
151
152 return cal
153
154
155def gain_calibration(input_files, cal_name="PXDGainCalibration",
156 boundaries=None, global_tags=None, local_dbs=None,
157 gain_method="Analytic", validation=True, **kwargs):
158 """
159 Parameters:
160 input_files (list): A list of input files to be assigned to calibration.input_files
161
162 boundaries (c++ std::vector): boundaries for iov creation
163
164 global_tags (list): A list of global tags
165
166 local_dbs (list): A list of local databases
167
168 gain_method (str): A string of gain algorithm in
169 ['analytic', 'generic_mc', 'cluster_sim', '']. Empty str means to skip gain
170 calibration and validation has to be True otherwise no algorithm will be used.
171 Caveat: Only the analytic method is now implemented.
172
173 validation (bool): Adding validation algorithm if True (default)
174
175 **kwargs: Additional configuration to support extentions without changing scripts in calibration folder.
176 Supported options are listed below:
177
178 "collector_prefix": a string indicating which collector to be used for gain calibration. The supported
179 collectors are:
180 PXDPerformanceVariablesCollector (default),
181 PXDPerformanceCollector(using RAVE package for vertexing, obsolete)
182 "useClusterPosition": Flag to use cluster postion rather than track point to group pixels for calibration.
183 "particle_type": Particle type assigned to tracks. "e" by default.
184 "track_cuts_4gain": Track cuts used for gain calibration.
185 "track_cuts_4eff": Track cuts used for efficiency study.
186 "track_cuts_4res": Track cuts used for resolution study.
187
188 Return:
189 A caf.framework.Calibration obj.
190 """
191
192 if global_tags is None:
193 global_tags = []
194 if local_dbs is None:
195 local_dbs = []
196 if gain_method is None:
197 gain_method = 'analytic'
198 if not isinstance(gain_method, str) or gain_method.lower() not in gain_methods:
199 raise ValueError(f"gain_method not found in gain_methods : {gain_method}")
200 collector_prefix = kwargs.get("collector_prefix", "PXDPerformanceVariablesCollector")
201 supported_collectors = ["PXDPerformanceVariablesCollector", "PXDPerformanceCollector"]
202 if not isinstance(collector_prefix, str) or collector_prefix not in supported_collectors:
203 raise ValueError(f"collector_prefix not found in {supported_collectors}")
204 useClusterPosition = kwargs.get("useClusterPosition", False)
205 if not isinstance(useClusterPosition, bool):
206 raise ValueError("useClusterPosition has to be a boolean!")
207 particle_type = kwargs.get("particle_type", "e") # rely on modular analysis for value check
208 track_cuts_4gain = kwargs.get("track_cuts_4gain", "p > 1.0") # see above
209 track_cuts_4eff = kwargs.get("track_cuts_4eff", "pt > 2.0") # see above
210 track_cuts_4res = kwargs.get("track_cuts_4res", "Note2019") # NOTE-TE-2019-018
211
212 # Create basf2 path
213
214 gearbox = register_module('Gearbox')
215 geometry = register_module('Geometry', useDB=True)
216 genFitExtrapolation = register_module('SetupGenfitExtrapolation')
217 roiFinder = register_module('PXDROIFinder')
218
219 main = create_path()
220 main.add_module(gearbox)
221 main.add_module(geometry)
222 main.add_module(genFitExtrapolation)
223 main.add_module(roiFinder) # for PXDIntercepts
224 main.add_module("ActivatePXDPixelMasker")
225
226 # Collector setup
227 collector = register_module(collector_prefix)
228 if collector_prefix == "PXDPerformanceCollector":
229 main.add_module("PXDPerformance")
230 collector.param("fillEventTree", False)
231 else: # the default PXDPerformanceVariablesCollector
232 import modularAnalysis as ana
233 import vertex
234 # Particle list for gain calibration
235 p = particle_type
236 ana.fillParticleList(f'{p}+:gain', track_cuts_4gain, path=main)
237
238 # Particle list for event selection in efficiency monitoring
239 # nSVDHits > 5 doesn't help, firstSVDLayer == 3 for < 0.1% improvement?
240 ana.fillParticleList(f'{p}+:eff', track_cuts_4eff, path=main)
241 # Mass cut (9.5 < M < 11.5) below can improve efficiency by ~ 1%
242 ana.reconstructDecay(f'vpho:eff -> {p}+:eff {p}-:eff', '9.5<M<11.5', path=main)
243 # < 0.1% improvement by using kfit pvalue >= 0.01 after mass cut
244 # vertex.kFit('vpho:eff', conf_level=0.01, fit_type="fourC", daughtersUpdate=False, path=main)
245
246 # Particle list for studying impact parameter resolution
247 # Alias dosn't work with airflow implementation
248 # from variables import variables as vm
249 # vm.addAlias("pBetaSinTheta3o2", "formula(pt * (1./(1. + tanLambda**2)**0.5)**0.5)")
250 # vm.addAlias("absLambda", "abs(atan(tanLambda))")
251 track_cuts_4res_note2019 = 'pt>1.0 and abs(dz)<1.0 and dr<0.3'
252 track_cuts_4res_note2019 += ' and nCDCHits>20 and nSVDHits>=8 and nPXDHits>=1'
253 track_cuts_4res_note2019 += ' and [abs(atan(tanLambda)) < 0.5]'
254 track_cuts_4res_note2019 += ' and [formula(pt * (1./(1. + tanLambda**2)**0.5)**0.5) > 2.0]'
255 # track_cuts_4res_note2019 += ' and [absLambda<0.5]'
256 # track_cuts_4res_note2019 += ' and [pBetaSinTheta3o2>2.0]'
257 if track_cuts_4res == "Note2019":
258 track_cuts_4res = track_cuts_4res_note2019
259 ana.fillParticleList(f'{p}+:res', track_cuts_4res, path=main)
260 ana.reconstructDecay(f'vpho:res -> {p}+:res {p}-:res', '9.5<M<11.5', path=main)
261 # Remove multiple candidate events
262 ana.applyCuts('vpho:res', 'nParticlesInList(vpho:res)==1', path=main)
263 vertex.kFit('vpho:res', conf_level=0.0, path=main)
264
265 collector.param("PList4GainName", f"{p}+:gain")
266 collector.param("PList4EffName", "vpho:eff")
267 collector.param("PList4ResName", "vpho:res")
268 collector.param("maskedDistance", 3)
269 collector.param("useClusterPosition", useClusterPosition)
270
271 collector.param("granularity", "run")
272 collector.param("minClusterCharge", 8)
273 collector.param("minClusterSize", 1)
274 collector.param("maxClusterSize", 10)
275 collector.param("nBinsU", 4)
276 collector.param("nBinsV", 6)
277 collector.param("fillChargeRatioHistogram", True)
278
279 # Algorithm setup
280 algorithms = []
281 if validation:
282 validation_alg = PXDValidationAlgorithm()
283 validation_alg.setPrefix(collector_prefix)
284 validation_alg.minTrackPoints = 10 # Minimum number of track points
285 validation_alg.save2DHists = True # Flag to save 2D histogram for efficiency on layer 1 or 2 in Z-phi plane
286 validation_alg.saveD0Z0 = kwargs.get("saveD0Z0", False) # Flag to save corrected d0 (z0) difference
287 algorithms.append(validation_alg)
288
289 # validation_alg.setBoundaries(boundaries) # This takes boundaries from the expert_config
290 if (gain_method != ''):
291 algorithm = PXDAnalyticGainCalibrationAlgorithm() # Getting a calibration algorithm instanced
292 algorithm.minClusters = 200 # Minimum number of clusters in each region
293 algorithm.safetyFactor = 11 # Safety factor x minClusters/region -> <minClusters> for module
294 algorithm.forceContinue = False # Force continue the algorithm instead of return c_notEnoughData
295 algorithm.strategy = 0 # 0: medians, 1: landau fit
296 algorithm.correctForward = True # Correct default gains in forward regions due to low statistics
297 algorithm.useChargeRatioHistogram = True # Use Charge ratio histograms for the calibration
298 algorithm.setPrefix(collector_prefix)
299 # algorithm.setBoundaries(boundaries) # This takes boundaries from the expert_config
300 algorithms.append(algorithm)
301
302 # Create the calibration instance
303
304 cal = Calibration(
305 name=cal_name,
306 collector=collector,
307 algorithms=algorithms,
308 input_files=input_files)
309 for global_tag in global_tags:
310 cal.use_central_database(global_tag)
311 for local_db in local_dbs:
312 cal.use_local_database(local_db)
313 cal.pre_collector_path = main
314 # cal.strategies = SequentialBoundaries
315 cal.strategies = SequentialRunByRun
316 # cal.strategies = SimpleRunByRun
317
318 return cal
def kFit(list_name, conf_level, fit_type='vertex', constraint='', daughtersUpdate=False, decay_string='', massConstraint=[], recoilMass=0, smearing=0, path=None)
Definition: vertex.py:129