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 auto eventT0Detector = new TH1F("eventT0Detector",
163 "detector used for eventT0 (SVD=2, CDC=4, TOP=8, ECL=32);detector number; number of events", 48, 0, 48) ;
164 registerObject<TH1F>("eventT0Detector", eventT0Detector) ;
165
166 auto clusterTimeE0E1diff = new TH1F("clusterTimeE0E1diff",
167 ";ECL cluster time of max E photon - ECL cluster time of 2nd max E photon [ns]; number of photon ECL cluster time differences",
168 nbins, min_t, max_t) ;
169 registerObject<TH1F>("clusterTimeE0E1diff", clusterTimeE0E1diff) ;
170
171
172 //=== Required data objects
173 tracks.isRequired() ;
174 m_eclClusterArray.isRequired() ;
175 m_eclCalDigitArray.isRequired() ;
176
177}
178
180{
181 int cutIndexPassed = 0;
182 getObjectPtr<TH1F>("cutflow")->Fill(cutIndexPassed);
183 B2DEBUG(22, "Cutflow: no cuts: index = " << cutIndexPassed);
184
185
186
187 // Storage crystal energies
189 for (auto& eclCalDigit : m_eclCalDigitArray) {
190 int tempCrysID = eclCalDigit.getCellId() - 1;
191 m_EperCrys[tempCrysID] = eclCalDigit.getEnergy();
192 }
193
194 // Getting the event t0 using the full event t0
195
196 double evt_t0 = -1000 ;
197 double evt_t0_unc = -1000 ;
198 int evt_t0_detector = 0;
199
200 // Determine if there is an event t0 to use and then extract the information about it
201 if (m_eventT0.isOptional()) {
202 if (!m_eventT0.isValid()) {
203 return;
204 }
205 if (!m_eventT0->hasEventT0()) {
206 return;
207 } else {
208 // Overall event t0 (combination of multiple event t0s from different detectors)
209 evt_t0 = m_eventT0->getEventT0() ;
210 evt_t0_unc = m_eventT0->getEventT0Uncertainty() ;
211 if (m_eventT0->isSVDEventT0()) {evt_t0_detector += 2;}
212 if (m_eventT0->isCDCEventT0()) {evt_t0_detector += 4;}
213 if (m_eventT0->isTOPEventT0()) {evt_t0_detector += 8;}
214 if (m_eventT0->isECLEventT0()) {evt_t0_detector += 32;}
215 }
216 B2DEBUG(26, "Found event t0") ;
217 }
218
219 //---------------------------------------------------------------------
220 //..Track properties. Use pion (211) mass hypothesis,
221 // which is the only particle hypothesis currently available???
222 int nTrkAll = tracks.getEntries() ;
223
224 int nTrkLoose = 0 ;
225 int nTrkTight = 0 ;
228 /* Loop over all the tracks to define the tight and loose selection tracks.
229 We will select events with only a few tight tracks and no additional loose tracks.
230 Tight tracks are a subset of looses tracks. */
231 for (int iTrk = 0 ; iTrk < nTrkAll ; iTrk++) {
232
233 // Get track biasing towards the particle being a pion
234 const TrackFitResult* tempTrackFit = tracks[iTrk]->getTrackFitResultWithClosestMass(Const::pion) ;
235 if (not tempTrackFit) {continue ;}
236
237 // Collect track info to be used for categorizing
238 //short charge = tempTrackFit->getChargeSign() ;
239 double z0 = tempTrackFit->getZ0() ;
240 double d0 = tempTrackFit->getD0() ;
241 int nCDChits = tempTrackFit->getHitPatternCDC().getNHits() ;
242 //double pt = tempTrackFit->getTransverseMomentum() ;
243 //double p = tempTrackFit->getMomentum().Mag() ;
244
245 /* Test if loose track */
246
247 // d0 and z0 cuts
248 if (fabs(d0) > m_looseTrkD0) {
249 continue;
250 }
251 if (fabs(z0) > m_looseTrkZ0) {
252 continue;
253 }
254 // Number of hits in the CDC
255 if (nCDChits < 1) {
256 continue;
257 }
258 nTrkLoose++;
259
260
261
262 /* Test if the loose track is also a tight track */
263
264 // Number of hits in the CDC
265 if (nCDChits < 20) {
266 continue;
267 }
268 // d0 and z0 cuts
269 if (fabs(d0) > m_tightTrkD0) {
270 continue;
271 }
272 if (fabs(z0) > m_tightTrkZ0) {
273 continue;
274 }
275 nTrkTight++;
276
277 }
278 // After that last section the numbers of loose and tight tracks are known
279 B2DEBUG(26, "Found loose and tight tracks") ;
280
281
282 int numGoodTightTracks_minCut = 4 ;
283 if (nTrkTight < numGoodTightTracks_minCut) {
284 return ;
285 }
286 // There are at least X tight tracks
287 cutIndexPassed++ ;
288 getObjectPtr<TH1F>("cutflow")->Fill(cutIndexPassed) ;
289 B2DEBUG(22, "Cutflow: At least " << numGoodTightTracks_minCut << " tight tracks: index = " << cutIndexPassed) ;
290
291
292 int numGoodLooseTracks_minCut = numGoodTightTracks_minCut ;
293 if (nTrkLoose < numGoodLooseTracks_minCut) {
294 return ;
295 }
296 // 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
297 cutIndexPassed++ ;
298 getObjectPtr<TH1F>("cutflow")->Fill(cutIndexPassed) ;
299 B2DEBUG(22, "Cutflow: No additional loose tracks: index = " << cutIndexPassed) ;
300
301
302 //------------------------------------------------------------------------
303 // Find the good ECL clusters
304 double clusterE_minCut = 0.1 ; // GeV
305 int nclust = m_eclClusterArray.getEntries();
306 int nGoodClusts = 0 ;
307 vector<int> goodClusterIdxs ;
308 for (int ic = 0; ic < nclust; ic++) {
310 if (eClust > clusterE_minCut) {
311 goodClusterIdxs.push_back(ic) ;
312 nGoodClusts++ ;
313 }
314 }
315
316
317 // Cut on the minimum number of good clusters
318 int numGoodEMclusters_minCut = 5 ;
319 if (nGoodClusts < numGoodEMclusters_minCut) {
320 return ;
321 }
322 // There are at least 5 good EM clusters (photon = basically all clusters)
323 cutIndexPassed++ ;
324 getObjectPtr<TH1F>("cutflow")->Fill(cutIndexPassed) ;
325 B2DEBUG(22, "Cutflow: At least " << numGoodEMclusters_minCut << " ECL clusters: index = " << cutIndexPassed) ;
326
327
328 //------------------------------------------------------------------------
329 // Find the good photons first before doing anything with them
330
331 double photonE_minCut = 0.05 ; // GeV
332 double zernikeMVA_minCut = 0.2 ;
333 int nPhotons = 0 ;
334
335 vector<int> goodPhotonClusterIdxs ;
336 for (int ic = 0; ic < nclust; ic++) {
339 double photonTime = m_eclClusterArray[ic]->getTime();
340 double zernikeMVA = m_eclClusterArray[ic]->getZernikeMVA();
341 bool badPhotonTime = m_eclClusterArray[ic]->hasFailedFitTime();
342 bool badPhotonTimeResolution = m_eclClusterArray[ic]->hasFailedTimeResolution();
343 bool hasTrack = m_eclClusterArray[ic]->isTrack();
344 if ((eClust > photonE_minCut) &&
345 (fabs(photonTime) < m_timeAbsMax) &&
346 (!badPhotonTime) &&
347 (!badPhotonTimeResolution) &&
348 (zernikeMVA > zernikeMVA_minCut) &&
349 (!hasTrack)) {
350 goodPhotonClusterIdxs.push_back(ic) ;
351 nPhotons++;
352 }
353 }
354 }
355
356
357 // Cut on the minimum number of good photon clusters
358 int numGoodPhotonclusters_minCut = 1 ;
359 if (nPhotons < numGoodPhotonclusters_minCut) {
360 return ;
361 }
362 // There is at least one good photon in the event
363 cutIndexPassed++ ;
364 getObjectPtr<TH1F>("cutflow")->Fill(cutIndexPassed) ;
365 B2DEBUG(22, "Cutflow: At least " << numGoodPhotonclusters_minCut << " good photon: index = " << cutIndexPassed) ;
366
367
368
369 //------------------------------------------------------------------------
370 /* Extract the times of the good clusters and
371 save the maximum energy crystal information (cid) */
372 vector<double> goodClustTimes ;
373 vector<double> goodClust_dt99 ;
374 vector<double> goodClusters_crysE ;
375 vector<double> goodClustE ;
376 vector<int> goodClustMaxEcrys_cid ;
377 for (long unsigned int i = 0; i < goodPhotonClusterIdxs.size(); i++) {
378 int ic = goodPhotonClusterIdxs[i] ;
379
382 short cid = m_eclClusterArray[ic]->getMaxECellId() ;
383
384 goodClustMaxEcrys_cid.push_back(cid) ;
385 goodClustTimes.push_back(m_eclClusterArray[ic]->getTime()) ;
386 goodClust_dt99.push_back(m_eclClusterArray[ic]->getDeltaTime99()) ;
387 goodClusters_crysE.push_back(m_EperCrys[cid - 1]) ;
388 goodClustE.push_back(eClust);
389 }
390 }
391
392
393 // Define a pair (energy,time) so that we can quickly and easily sort the cluster information
394 // based on the energy of the clusters
395 vector< pair<double, double> > pair_energy_time ;
396 for (long unsigned int ic = 0; ic < goodClusters_crysE.size(); ic++) {
397 pair_energy_time.push_back(make_pair(goodClusters_crysE[ic], goodClustTimes[ic])) ;
398 }
399
400 // sorts pairs in decreasing order of their first value (energy)
401 // i.e. highest energy first
402 sort(pair_energy_time.begin(), pair_energy_time.end(), greater<>()) ;
403
404
405
406 B2DEBUG(22, "Event passed all cuts");
407
408
409 // Fill the histogram for the event level variables
410 getObjectPtr<TH1F>("eventT0")->Fill(evt_t0) ;
411 getObjectPtr<TH1F>("eventT0Detector")->Fill(evt_t0_detector + 0.00001) ;
412
413 bool isSVDt0 = m_eventT0->isSVDEventT0();
414 bool isCDCt0 = m_eventT0->isCDCEventT0();
415 bool isECLt0 = m_eventT0->isECLEventT0();
416 string t0Detector = "UNKNOWN... WHY?";
417 if (isSVDt0) {
418 t0Detector = "SVD" ;
419 } else if (isCDCt0) {
420 t0Detector = "CDC" ;
421 } else if (isECLt0) {
422 t0Detector = "ECL" ;
423 }
424
425 B2DEBUG(26, "t0 = " << evt_t0 << " ns. t0 is from SVD?=" << isSVDt0 << ", t0 is from CDC?=" << isCDCt0 << ", t0 is from ECL?=" <<
426 isECLt0 << " t0 from " <<
427 t0Detector);
428
429
430
431 //=== For each good photon cluster in the processed event and fill histogram.
432 for (long unsigned int i = 0 ; i < goodPhotonClusterIdxs.size() ; i++) {
433 getObjectPtr<TH1F>("clusterTime")->Fill(goodClustTimes[i]) ;
434 getObjectPtr<TH2F>("clusterTime_cid")->Fill(goodClustMaxEcrys_cid[i] + 0.001, goodClustTimes[i], 1) ;
435 getObjectPtr<TH2F>("clusterTime_run")->Fill(m_EventMetaData->getRun() + 0.001, goodClustTimes[i], 1) ;
436 getObjectPtr<TH2F>("clusterTimeClusterE")->Fill(goodClustE[i], goodClustTimes[i], 1) ;
437 getObjectPtr<TH2F>("dt99_clusterE")->Fill(goodClustE[i], goodClust_dt99[i], 1) ;
438
439 //== Save debug TTree with detailed information if necessary.
440 if (m_saveTree) {
441
442 m_tree_time = goodClustTimes[i] ;
443 m_E_photon_clust = goodClusters_crysE[i] ;
444 m_tree_t0 = evt_t0 ;
445 m_tree_t0_unc = evt_t0_unc ;
446 m_NtightTracks = nTrkTight ;
447 m_NphotonClusters = nPhotons ;
448 m_NGoodClusters = nGoodClusts ;
449 m_tree_evt_num = m_EventMetaData->getEvent() ;
450 m_tree_run = m_EventMetaData->getRun() ;
451 m_tree_cid = goodClustMaxEcrys_cid[i] ;
452 m_tree_dt99 = goodClust_dt99[i] ;
453
455
456 }
457 }
458 B2DEBUG(26, "Filled cluster tree") ;
459
460 //=== Fill histogram for cluster time difference of the two max E photons
461 if (pair_energy_time.size() >= 2) {
462 getObjectPtr<TH1F>("clusterTimeE0E1diff")->Fill(pair_energy_time[0].second - pair_energy_time[1].second) ;
463 }
464
465
466
467 if (m_saveTree) {
468 m_tree_t0 = evt_t0 ;
469 m_tree_t0_unc = evt_t0_unc ;
470 m_tree_evt_num = m_EventMetaData->getEvent() ;
471 m_tree_run = m_EventMetaData->getRun() ;
472 m_NtightTracks = nTrkTight ;
473 m_NphotonClusters = nPhotons ;
474 m_NGoodClusters = nGoodClusts ;
475
476 m_tree_E0 = pair_energy_time[0].first ;
477 m_tree_time_fromE0 = pair_energy_time[0].second ;
478 m_dbg_tree_event->Fill() ;
479 }
480
481 B2DEBUG(26, "Filled event tree") ;
482
483}
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:559
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:649
const int c_NCrystals
Number of crystals.
Abstract base class for different kinds of events.
STL namespace.