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