Belle II Software  release-08-01-10
eclHadronTimeCalibrationValidationCollectorModule.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 /* Own header. */
10 #include <ecl/modules/eclHadronTimeCalibrationValidationCollector/eclHadronTimeCalibrationValidationCollectorModule.h>
11 
12 /* ECL headers. */
13 #include <ecl/dataobjects/ECLCalDigit.h>
14 #include <ecl/dataobjects/ECLElementNumbers.h>
15 
16 /* Basf2 headers. */
17 #include <framework/gearbox/Const.h>
18 #include <mdst/dataobjects/ECLCluster.h>
19 #include <mdst/dataobjects/Track.h>
20 #include <mdst/dataobjects/HitPatternCDC.h>
21 
22 /* ROOT headers. */
23 #include <TH2F.h>
24 #include <TTree.h>
25 
26 using namespace Belle2;
27 using namespace std;
28 
29 //-----------------------------------------------------------------
30 // Register the Module
31 //-----------------------------------------------------------------
32 REG_MODULE(eclHadronTimeCalibrationValidationCollector);
33 
34 //-----------------------------------------------------------------
35 // Implementation
36 //-----------------------------------------------------------------
37 
40  m_dbg_tree_photonClusters(0),
41  m_dbg_tree_event(0),
42  m_tree_evt_num(0)//,
43  //m_GammaGammaECalib("ECLCrystalEnergyGammaGamma")
44 {
45  setDescription("This module validates the ECL cluster times");
46 
47  addParam("timeAbsMax", m_timeAbsMax, // (Time in ns)
48  "Events with fabs(getTimeFit) > m_timeAbsMax "
49  "are excluded", (short)80);
50 
51  addParam("saveTree", m_saveTree,
52  "If true, TTree 'tree' with more detailed event info is saved in "
53  "the output file specified by HistoManager",
54  false);
55 
56  addParam("looseTrkZ0", m_looseTrkZ0, "max Z0 for loose tracks (cm)", 10.);
57  addParam("tightTrkZ0", m_tightTrkZ0, "max Z0 for tight tracks (cm)", 2.);
58  addParam("looseTrkD0", m_looseTrkD0, "max D0 for loose tracks (cm)", 2.);
59  addParam("tightTrkD0", m_tightTrkD0, "max D0 for tight tracks (cm)", 0.5); // beam pipe radius = 1cm in 2019
60 
61 
62  // specify this flag if you need parallel processing
64 }
65 
67 {
68 }
69 
71 {
72 
73  //=== Prepare TTree for debug output
74  if (m_saveTree) {
75  // Per photon cluster
76  m_dbg_tree_photonClusters = new TTree("tree_photonClusters",
77  "Validating crystal and crate time calibrations using photon clusters in events with lots of tracks and clusters") ;
78  m_dbg_tree_photonClusters->Branch("EventNum", &m_tree_evt_num) ->SetTitle("Event number") ;
79  m_dbg_tree_photonClusters->Branch("cluster_time", &m_tree_time) ->SetTitle("Cluster time t (calibrated), ns") ;
80  m_dbg_tree_photonClusters->Branch("clust_E", &m_E_photon_clust) ->SetTitle("Photon type cluster energy, GeV") ;
81  m_dbg_tree_photonClusters->Branch("Ntracks", &m_NtightTracks) ->SetTitle("Number of tracks") ;
82  m_dbg_tree_photonClusters->Branch("NphotonClusters", &m_NphotonClusters) ->SetTitle("Number of photons") ;
83  m_dbg_tree_photonClusters->Branch("NGoodClusters", &m_NGoodClusters) ->SetTitle("Number of good ECL clusters") ;
84  m_dbg_tree_photonClusters->Branch("t0", &m_tree_t0) ->SetTitle("T0, ns") ;
85  m_dbg_tree_photonClusters->Branch("t0_unc", &m_tree_t0_unc) ->SetTitle("T0 uncertainty, ns") ;
86  m_dbg_tree_photonClusters->Branch("runNum", &m_tree_run) ->SetTitle("Run number") ;
87  m_dbg_tree_photonClusters->Branch("CrystalCellID", &m_tree_cid) ->SetTitle("Cell ID, 1..8736") ;
88  m_dbg_tree_photonClusters->Branch("dt99", &m_tree_dt99) ->SetTitle("Cluster dt99, ns") ;
89  m_dbg_tree_photonClusters->SetAutoSave(10) ;
90 
91  // Per event
92  m_dbg_tree_event = new TTree("tree_event",
93  "Validating crystal and crate time calibrations using photon clusters in events with lots of tracks and clusters") ;
94  m_dbg_tree_event->Branch("EventNum", &m_tree_evt_num) ->SetTitle("Event number") ;
95  m_dbg_tree_event->Branch("t0", &m_tree_t0) ->SetTitle("T0, ns") ;
96  m_dbg_tree_event->Branch("t0_unc", &m_tree_t0_unc) ->SetTitle("T0 uncertainty, ns") ;
97  m_dbg_tree_event->Branch("runNum", &m_tree_run) ->SetTitle("Run number") ;
98  m_dbg_tree_event->Branch("Ntracks", &m_NtightTracks) ->SetTitle("Number of tracks") ;
99  m_dbg_tree_event->Branch("NphotonClusters", &m_NphotonClusters) ->SetTitle("Number of photons") ;
100  m_dbg_tree_event->Branch("NGoodClusters", &m_NGoodClusters) ->SetTitle("Number of good ECL clusters") ;
101  m_dbg_tree_event->Branch("E0", &m_tree_E0) ->SetTitle("Highest E cluster E") ;
102  m_dbg_tree_event->Branch("time_E0", &m_tree_time_fromE0) ->SetTitle("Cluster time of highest E cluster") ;
103  m_dbg_tree_event->SetAutoSave(10) ;
104  }
105 }
106 
108 {
109  //=== MetaData
110  B2INFO("eclHadronTimeCalibrationValidationCollector: Experiment = " << m_EventMetaData->getExperiment() <<
111  " run = " << m_EventMetaData->getRun()) ;
112 
113  //=== Create histograms and register them in the data store
114 
115  // Define the bin size, which is equivalent to the
116  double binSize = 2000.0 / pow(2, 12);
117  double halfBinSize = binSize / 2.0;
118 
119  /* Determine the number of bins required to go from the edge of the bin centred
120  on zero to a value just larger than the negative cut off */
121  double nBinsNeg = floor((m_timeAbsMax - halfBinSize) / binSize);
122  double min_t = -nBinsNeg * binSize - halfBinSize; // lower edge value of left most bin
123  int nbins = nBinsNeg * 2 + 1; // number of negative time bins + t=0 bin + number of positive time bins
124  double max_t = min_t + nbins * binSize; // upper edge value of right most bin
125 
126  /* Variable bin width information for the time information vs energy since
127  the time width should vary as a function of 1/E */
128  const Int_t N_E_BIN_EDGES = 64;
129  const Int_t N_E_BINS = N_E_BIN_EDGES - 1;
130  Double_t energyBinEdges[N_E_BIN_EDGES] = {0, 0.05, 0.051, 0.052, 0.053, 0.054, 0.055, 0.056, 0.057, 0.058, 0.059, 0.06, 0.062, 0.064, 0.066, 0.068, 0.07, 0.075, 0.08, 0.085, 0.09, 0.095, 0.1, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.2, 0.25, 0.3, 0.35, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.2, 1.4, 1.6, 1.8, 2, 2.25, 2.5, 2.8, 3.2, 3.6, 4, 4.4, 4.8, 5.2, 5.6, 6, 6.4, 6.8, 7.2, 7.6, 8};
131 
132 
133  auto cutflow = new TH1F("cutflow", " ;Cut label number ;Number of events passing cut", 10, 0, 10) ;
134  registerObject<TH1F>("cutflow", cutflow) ;
135 
136  auto clusterTime = new TH1F("clusterTime", ";Photon ECL cluster time [ns]; number of photon ECL clusters", nbins, min_t, max_t) ;
137  registerObject<TH1F>("clusterTime", clusterTime) ;
138 
139  auto clusterTime_cid = new TH2F("clusterTime_cid",
140  ";crystal Cell ID ;Photon ECL cluster time [ns]", ECLElementNumbers::c_NCrystals, 1, ECLElementNumbers::c_NCrystals + 1, nbins,
141  min_t, max_t) ;
142  registerObject<TH2F>("clusterTime_cid", clusterTime_cid) ;
143 
144  auto clusterTime_run = new TH2F("clusterTime_run",
145  ";Run number ;Photon ECL cluster time [ns]", 7000, 0, 7000, nbins, min_t, max_t) ;
146  registerObject<TH2F>("clusterTime_run", clusterTime_run) ;
147 
148 
149  auto clusterTimeClusterE = new TH2F("clusterTimeClusterE",
150  ";Photon cluster energy [GeV];Photon cluster time [ns]", N_E_BINS, energyBinEdges, nbins, min_t, max_t) ;
151  registerObject<TH2F>("clusterTimeClusterE", clusterTimeClusterE) ;
152 
153 
154  auto dt99_clusterE = new TH2F("dt99_clusterE",
155  ";Photon cluster energy [GeV];dt99 [ns]", N_E_BINS, energyBinEdges, nbins, 0, max_t) ;
156  registerObject<TH2F>("dt99_clusterE", dt99_clusterE) ;
157 
158 
159  auto eventT0 = new TH1F("eventT0", ";event t0 [ns]; number of events", nbins, min_t, max_t) ;
160  registerObject<TH1F>("eventT0", eventT0) ;
161 
162 
163  auto clusterTimeE0E1diff = new TH1F("clusterTimeE0E1diff",
164  ";ECL cluster time of max E photon - ECL cluster time of 2nd max E photon [ns]; number of photon ECL cluster time differences",
165  nbins, min_t, max_t) ;
166  registerObject<TH1F>("clusterTimeE0E1diff", clusterTimeE0E1diff) ;
167 
168 
169  //=== Required data objects
170  tracks.isRequired() ;
171  m_eclClusterArray.isRequired() ;
172  m_eclCalDigitArray.isRequired() ;
173 
174 }
175 
177 {
178  int cutIndexPassed = 0;
179  getObjectPtr<TH1F>("cutflow")->Fill(cutIndexPassed);
180  B2DEBUG(22, "Cutflow: no cuts: index = " << cutIndexPassed);
181 
182 
183 
184  // Storage crystal energies
186  for (auto& eclCalDigit : m_eclCalDigitArray) {
187  int tempCrysID = eclCalDigit.getCellId() - 1;
188  m_EperCrys[tempCrysID] = eclCalDigit.getEnergy();
189  }
190 
191  // Getting the event t0 using the full event t0 rather than from the CDC specifically
192 
193  double evt_t0 = -1000 ;
194  double evt_t0_unc = -1000 ;
195 
196  // Determine if there is an event t0 to use and then extract the information about it
197  if (m_eventT0.isOptional()) {
198  if (!m_eventT0.isValid()) {
199  return;
200  }
201  if (!m_eventT0->hasEventT0()) {
202  return;
203  } else {
204  // Overall event t0 (combination of multiple event t0s from different detectors)
205  evt_t0 = m_eventT0->getEventT0() ;
206  evt_t0_unc = m_eventT0->getEventT0Uncertainty() ;
207  }
208  B2DEBUG(26, "Found event t0") ;
209  }
210 
211  //---------------------------------------------------------------------
212  //..Track properties. Use pion (211) mass hypothesis,
213  // which is the only particle hypothesis currently available???
214  int nTrkAll = tracks.getEntries() ;
215 
216  int nTrkLoose = 0 ;
217  int nTrkTight = 0 ;
220  /* Loop over all the tracks to define the tight and loose selection tracks.
221  We will select events with only a few tight tracks and no additional loose tracks.
222  Tight tracks are a subset of looses tracks. */
223  for (int iTrk = 0 ; iTrk < nTrkAll ; iTrk++) {
224 
225  // Get track biasing towards the particle being a pion
226  const TrackFitResult* tempTrackFit = tracks[iTrk]->getTrackFitResultWithClosestMass(Const::pion) ;
227  if (not tempTrackFit) {continue ;}
228 
229  // Collect track info to be used for categorizing
230  //short charge = tempTrackFit->getChargeSign() ;
231  double z0 = tempTrackFit->getZ0() ;
232  double d0 = tempTrackFit->getD0() ;
233  int nCDChits = tempTrackFit->getHitPatternCDC().getNHits() ;
234  //double pt = tempTrackFit->getTransverseMomentum() ;
235  //double p = tempTrackFit->getMomentum().Mag() ;
236 
237  /* Test if loose track */
238 
239  // d0 and z0 cuts
240  if (fabs(d0) > m_looseTrkD0) {
241  continue;
242  }
243  if (fabs(z0) > m_looseTrkZ0) {
244  continue;
245  }
246  // Number of hits in the CDC
247  if (nCDChits < 1) {
248  continue;
249  }
250  nTrkLoose++;
251 
252 
253 
254  /* Test if the loose track is also a tight track */
255 
256  // Number of hits in the CDC
257  if (nCDChits < 20) {
258  continue;
259  }
260  // d0 and z0 cuts
261  if (fabs(d0) > m_tightTrkD0) {
262  continue;
263  }
264  if (fabs(z0) > m_tightTrkZ0) {
265  continue;
266  }
267  nTrkTight++;
268 
269  }
270  // After that last section the numbers of loose and tight tracks are known
271  B2DEBUG(26, "Found loose and tight tracks") ;
272 
273 
274  int numGoodTightTracks_minCut = 4 ;
275  if (nTrkTight < numGoodTightTracks_minCut) {
276  return ;
277  }
278  // There are at least X tight tracks
279  cutIndexPassed++ ;
280  getObjectPtr<TH1F>("cutflow")->Fill(cutIndexPassed) ;
281  B2DEBUG(22, "Cutflow: At least " << numGoodTightTracks_minCut << " tight tracks: index = " << cutIndexPassed) ;
282 
283 
284  int numGoodLooseTracks_minCut = numGoodTightTracks_minCut ;
285  if (nTrkLoose < numGoodLooseTracks_minCut) {
286  return ;
287  }
288  // There are more loose tracks than tight tracks then veto the event. If there are fewer loose tracks than tight tracks then veto the event, although this should be impossible
289  cutIndexPassed++ ;
290  getObjectPtr<TH1F>("cutflow")->Fill(cutIndexPassed) ;
291  B2DEBUG(22, "Cutflow: No additional loose tracks: index = " << cutIndexPassed) ;
292 
293 
294  //------------------------------------------------------------------------
295  // Find the good ECL clusters
296  double clusterE_minCut = 0.1 ; // GeV
297  int nclust = m_eclClusterArray.getEntries();
298  int nGoodClusts = 0 ;
299  vector<int> goodClusterIdxs ;
300  for (int ic = 0; ic < nclust; ic++) {
301  double eClust = m_eclClusterArray[ic]->getEnergy(Belle2::ECLCluster::EHypothesisBit::c_nPhotons) ;
302  if (eClust > clusterE_minCut) {
303  goodClusterIdxs.push_back(ic) ;
304  nGoodClusts++ ;
305  }
306  }
307 
308 
309  // Cut on the minimum number of good clusters
310  int numGoodEMclusters_minCut = 5 ;
311  if (nGoodClusts < numGoodEMclusters_minCut) {
312  return ;
313  }
314  // There are at least 5 good EM clusters (photon = basically all clusters)
315  cutIndexPassed++ ;
316  getObjectPtr<TH1F>("cutflow")->Fill(cutIndexPassed) ;
317  B2DEBUG(22, "Cutflow: At least " << numGoodEMclusters_minCut << " ECL clusters: index = " << cutIndexPassed) ;
318 
319 
320  //------------------------------------------------------------------------
321  // Find the good photons first before doing anything with them
322 
323  double photonE_minCut = 0.05 ; // GeV
324  double zernikeMVA_minCut = 0.2 ;
325  int nPhotons = 0 ;
326 
327  vector<int> goodPhotonClusterIdxs ;
328  for (int ic = 0; ic < nclust; ic++) {
331  double photonTime = m_eclClusterArray[ic]->getTime();
332  double zernikeMVA = m_eclClusterArray[ic]->getZernikeMVA();
333  bool badPhotonTime = m_eclClusterArray[ic]->hasFailedFitTime();
334  bool badPhotonTimeResolution = m_eclClusterArray[ic]->hasFailedTimeResolution();
335  bool hasTrack = m_eclClusterArray[ic]->isTrack();
336  if ((eClust > photonE_minCut) &&
337  (fabs(photonTime) < m_timeAbsMax) &&
338  (!badPhotonTime) &&
339  (!badPhotonTimeResolution) &&
340  (zernikeMVA > zernikeMVA_minCut) &&
341  (!hasTrack)) {
342  goodPhotonClusterIdxs.push_back(ic) ;
343  nPhotons++;
344  }
345  }
346  }
347 
348 
349  // Cut on the minimum number of good photon clusters
350  int numGoodPhotonclusters_minCut = 1 ;
351  if (nPhotons < numGoodPhotonclusters_minCut) {
352  return ;
353  }
354  // There is at least one good photon in the event
355  cutIndexPassed++ ;
356  getObjectPtr<TH1F>("cutflow")->Fill(cutIndexPassed) ;
357  B2DEBUG(22, "Cutflow: At least " << numGoodPhotonclusters_minCut << " good photon: index = " << cutIndexPassed) ;
358 
359 
360 
361  //------------------------------------------------------------------------
362  /* Extract the times of the good clusters and
363  save the maximum energy crystal information (cid) */
364  vector<double> goodClustTimes ;
365  vector<double> goodClust_dt99 ;
366  vector<double> goodClusters_crysE ;
367  vector<double> goodClustE ;
368  vector<int> goodClustMaxEcrys_cid ;
369  for (long unsigned int i = 0; i < goodPhotonClusterIdxs.size(); i++) {
370  int ic = goodPhotonClusterIdxs[i] ;
371 
374  short cid = m_eclClusterArray[ic]->getMaxECellId() ;
375 
376  goodClustMaxEcrys_cid.push_back(cid) ;
377  goodClustTimes.push_back(m_eclClusterArray[ic]->getTime()) ;
378  goodClust_dt99.push_back(m_eclClusterArray[ic]->getDeltaTime99()) ;
379  goodClusters_crysE.push_back(m_EperCrys[cid - 1]) ;
380  goodClustE.push_back(eClust);
381  }
382  }
383 
384 
385  // Define a pair (energy,time) so that we can quickly and easily sort the cluster information
386  // based on the energy of the clusters
387  vector< pair<double, double> > pair_energy_time ;
388  for (long unsigned int ic = 0; ic < goodClusters_crysE.size(); ic++) {
389  pair_energy_time.push_back(make_pair(goodClusters_crysE[ic], goodClustTimes[ic])) ;
390  }
391 
392  // sorts pairs in decreasing order of their first value (energy)
393  // i.e. highest energy first
394  sort(pair_energy_time.begin(), pair_energy_time.end(), greater<>()) ;
395 
396 
397 
398  B2DEBUG(22, "Event passed all cuts");
399 
400 
401  // Fill the histogram for the event level variables
402  getObjectPtr<TH1F>("eventT0")->Fill(evt_t0) ;
403 
404  bool isCDCt0 = m_eventT0->isCDCEventT0();
405  bool isECLt0 = m_eventT0->isECLEventT0();
406  string t0Detector = "UNKNOWN... WHY?";
407  if (isCDCt0) {
408  t0Detector = "CDC" ;
409  } else if (isECLt0) {
410  t0Detector = "ECL" ;
411  }
412 
413  B2DEBUG(26, "t0 = " << evt_t0 << " ns. t0 is from CDC?=" << isCDCt0 << ", t0 is from ECL?=" << isECLt0 << " t0 from " <<
414  t0Detector);
415 
416 
417 
418  //=== For each good photon cluster in the processed event and fill histogram.
419  for (long unsigned int i = 0 ; i < goodPhotonClusterIdxs.size() ; i++) {
420  getObjectPtr<TH1F>("clusterTime")->Fill(goodClustTimes[i]) ;
421  getObjectPtr<TH2F>("clusterTime_cid")->Fill(goodClustMaxEcrys_cid[i] + 0.001, goodClustTimes[i], 1) ;
422  getObjectPtr<TH2F>("clusterTime_run")->Fill(m_EventMetaData->getRun() + 0.001, goodClustTimes[i], 1) ;
423  getObjectPtr<TH2F>("clusterTimeClusterE")->Fill(goodClustE[i], goodClustTimes[i], 1) ;
424  getObjectPtr<TH2F>("dt99_clusterE")->Fill(goodClustE[i], goodClust_dt99[i], 1) ;
425 
426  //== Save debug TTree with detailed information if necessary.
427  if (m_saveTree) {
428 
429  m_tree_time = goodClustTimes[i] ;
430  m_E_photon_clust = goodClusters_crysE[i] ;
431  m_tree_t0 = evt_t0 ;
432  m_tree_t0_unc = evt_t0_unc ;
433  m_NtightTracks = nTrkTight ;
434  m_NphotonClusters = nPhotons ;
435  m_NGoodClusters = nGoodClusts ;
436  m_tree_evt_num = m_EventMetaData->getEvent() ;
437  m_tree_run = m_EventMetaData->getRun() ;
438  m_tree_cid = goodClustMaxEcrys_cid[i] ;
439  m_tree_dt99 = goodClust_dt99[i] ;
440 
441  m_dbg_tree_photonClusters->Fill() ;
442 
443  }
444  }
445  B2DEBUG(26, "Filled cluster tree") ;
446 
447  //=== Fill histogram for cluster time difference of the two max E photons
448  if (pair_energy_time.size() >= 2) {
449  getObjectPtr<TH1F>("clusterTimeE0E1diff")->Fill(pair_energy_time[0].second - pair_energy_time[1].second) ;
450  }
451 
452 
453 
454  if (m_saveTree) {
455  m_tree_t0 = evt_t0 ;
456  m_tree_t0_unc = evt_t0_unc ;
457  m_tree_evt_num = m_EventMetaData->getEvent() ;
458  m_tree_run = m_EventMetaData->getRun() ;
459  m_NtightTracks = nTrkTight ;
460  m_NphotonClusters = nPhotons ;
461  m_NGoodClusters = nGoodClusts ;
462 
463  m_tree_E0 = pair_energy_time[0].first ;
464  m_tree_time_fromE0 = pair_energy_time[0].second ;
465  m_dbg_tree_event->Fill() ;
466  }
467 
468  B2DEBUG(26, "Filled event tree") ;
469 
470 }
Calibration collector module base class.
static const ChargedStable pion
charged pion particle
Definition: Const.h:652
@ c_nPhotons
CR is split into n photons (N1)
unsigned short getNHits() const
Get the total Number of CDC hits in the fit.
void setDescription(const std::string &description)
Sets the description of the module.
Definition: Module.cc:214
void setPropertyFlags(unsigned int propertyFlags)
Sets the flags for the module properties.
Definition: Module.cc:208
@ c_ParallelProcessingCertified
This module can be run in parallel processing mode safely (All I/O must be done through the data stor...
Definition: Module.h:80
Values of the result of a track fit with a given particle hypothesis.
double getD0() const
Getter for d0.
double getZ0() const
Getter for z0.
HitPatternCDC getHitPatternCDC() const
Getter for the hit pattern in the CDC;.
StoreArray< ECLCluster > m_eclClusterArray
Required input array of ECLClusters.
short m_timeAbsMax
Events with abs(time) > m_timeAbsMax are excluded, mostly for histogram x-range purposes.
int m_tree_cid
ECL Cell ID (1..ECLElementNumbers::c_NCrystals) for debug TTree output.
void collect() override
Select events and crystals and accumulate histograms.
StoreArray< ECLCalDigit > m_eclCalDigitArray
Required input array of ECLCalDigits.
void inDefineHisto() override
Replacement for defineHisto() in CalibrationCollector modules.
REG_MODULE(arichBtest)
Register the Module.
void addParam(const std::string &name, T &paramVariable, const std::string &description, const T &defaultValue)
Adds a new parameter to the module.
Definition: Module.h:560
const int c_NCrystals
Number of crystals.
Abstract base class for different kinds of events.