Belle II Software  release-08-01-10
__init__.py
1 #!/usr/bin/env python3
2 
3 
10 
11 """
12 PXD calibration APIs for CAF
13 Author: qingyuan.liu@desy.de
14 """
15 
16 from basf2 import register_module, create_path
17 from ROOT.Belle2 import PXDHotPixelMaskCalibrationAlgorithm, PXDAnalyticGainCalibrationAlgorithm
18 from ROOT.Belle2 import PXDValidationAlgorithm
19 from caf.framework import Calibration
20 from caf.strategies import SequentialRunByRun, SimpleRunByRun
21 
22 run_types = ['beam', 'physics', 'cosmic', 'all']
23 gain_methods = ['analytic', 'generic_mc', 'cluster_sim', '']
24 
25 
26 def 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 
155 def 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