Belle II Software development
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
26using namespace Belle2;
27using namespace std;
28
29//-----------------------------------------------------------------
30// Register the Module
31//-----------------------------------------------------------------
32REG_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++) {
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
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:661
@ 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.
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
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:650
const int c_NCrystals
Number of crystals.
Abstract base class for different kinds of events.
STL namespace.