Project
Loading...
Searching...
No Matches
TrackerSpec.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
12#include <vector>
13#include <chrono>
14
18#include "Field/MagneticField.h"
19#include "Field/MagFieldParam.h"
27#include "TRKSimulation/Hit.h"
30#include <TGeoGlobalMagField.h>
31
32#include <TFile.h>
33#include <TTree.h>
34
35namespace o2
36{
37using namespace framework;
38namespace trk
39{
41
42TrackerDPL::TrackerDPL(std::shared_ptr<o2::base::GRPGeomRequest> gr,
43 bool isMC,
44 const std::string& hitRecoConfigFileName,
46{
47 if (!hitRecoConfigFileName.empty()) {
48 std::ifstream configFile(hitRecoConfigFileName);
49 mHitRecoConfig = nlohmann::json::parse(configFile);
50 }
51
52 // mITSTrackingInterface.setTrackingMode(trMode);
53}
54
56{
57 // mTimer.Stop();
58 // mTimer.Reset();
59 // o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest);
60 // mChainITS.reset(mRecChain->AddChain<o2::gpu::GPUChainITS>());
61 // mITSTrackingInterface.setTraitsFromProvider(mChainITS->GetITSVertexerTraits(),
62 // mChainITS->GetITSTrackerTraits(),
63 // mChainITS->GetITSTimeframe());
64}
65
67{
68 LOGF(info, "CPU Reconstruction total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1);
69}
70
71std::vector<o2::its::TrackingParameters> TrackerDPL::createTrackingParamsFromConfig()
72{
73 std::vector<o2::its::TrackingParameters> trackingParams;
74
75 if (!mHitRecoConfig.contains("trackingparams") || !mHitRecoConfig["trackingparams"].is_array()) {
76 LOGP(fatal, "No trackingparams field found in configuration or it is not an array. Returning empty vector.");
77 return trackingParams;
78 }
79
80 for (const auto& paramConfig : mHitRecoConfig["trackingparams"]) {
82
83 // Parse integer parameters
84 if (paramConfig.contains("NLayers")) {
85 params.NLayers = paramConfig["NLayers"].get<int>();
86 }
87 if (paramConfig.contains("DeltaROF")) {
88 params.DeltaROF = paramConfig["DeltaROF"].get<int>();
89 }
90 if (paramConfig.contains("ZBins")) {
91 params.ZBins = paramConfig["ZBins"].get<int>();
92 }
93 if (paramConfig.contains("PhiBins")) {
94 params.PhiBins = paramConfig["PhiBins"].get<int>();
95 }
96 if (paramConfig.contains("nROFsPerIterations")) {
97 params.nROFsPerIterations = paramConfig["nROFsPerIterations"].get<int>();
98 }
99 if (paramConfig.contains("ClusterSharing")) {
100 params.ClusterSharing = paramConfig["ClusterSharing"].get<int>();
101 }
102 if (paramConfig.contains("MinTrackLength")) {
103 params.MinTrackLength = paramConfig["MinTrackLength"].get<int>();
104 }
105 if (paramConfig.contains("ReseedIfShorter")) {
106 params.ReseedIfShorter = paramConfig["ReseedIfShorter"].get<int>();
107 }
108 if (paramConfig.contains("StartLayerMask")) {
109 params.StartLayerMask = paramConfig["StartLayerMask"].get<uint16_t>();
110 }
111
112 // Parse float parameters
113 if (paramConfig.contains("NSigmaCut")) {
114 params.NSigmaCut = paramConfig["NSigmaCut"].get<float>();
115 }
116 if (paramConfig.contains("PVres")) {
117 params.PVres = paramConfig["PVres"].get<float>();
118 }
119 if (paramConfig.contains("TrackletMinPt")) {
120 params.TrackletMinPt = paramConfig["TrackletMinPt"].get<float>();
121 }
122 if (paramConfig.contains("TrackletsPerClusterLimit")) {
123 params.TrackletsPerClusterLimit = paramConfig["TrackletsPerClusterLimit"].get<float>();
124 }
125 if (paramConfig.contains("CellDeltaTanLambdaSigma")) {
126 params.CellDeltaTanLambdaSigma = paramConfig["CellDeltaTanLambdaSigma"].get<float>();
127 }
128 if (paramConfig.contains("CellsPerClusterLimit")) {
129 params.CellsPerClusterLimit = paramConfig["CellsPerClusterLimit"].get<float>();
130 }
131 if (paramConfig.contains("MaxChi2ClusterAttachment")) {
132 params.MaxChi2ClusterAttachment = paramConfig["MaxChi2ClusterAttachment"].get<float>();
133 }
134 if (paramConfig.contains("MaxChi2NDF")) {
135 params.MaxChi2NDF = paramConfig["MaxChi2NDF"].get<float>();
136 }
137 if (paramConfig.contains("TrackFollowerNSigmaCutZ")) {
138 params.TrackFollowerNSigmaCutZ = paramConfig["TrackFollowerNSigmaCutZ"].get<float>();
139 }
140 if (paramConfig.contains("TrackFollowerNSigmaCutPhi")) {
141 params.TrackFollowerNSigmaCutPhi = paramConfig["TrackFollowerNSigmaCutPhi"].get<float>();
142 }
143
144 // Parse boolean parameters
145 if (paramConfig.contains("UseDiamond")) {
146 params.UseDiamond = paramConfig["UseDiamond"].get<bool>();
147 }
148 if (paramConfig.contains("AllowSharingFirstCluster")) {
149 params.AllowSharingFirstCluster = paramConfig["AllowSharingFirstCluster"].get<bool>();
150 }
151 if (paramConfig.contains("RepeatRefitOut")) {
152 params.RepeatRefitOut = paramConfig["RepeatRefitOut"].get<bool>();
153 }
154 if (paramConfig.contains("ShiftRefToCluster")) {
155 params.ShiftRefToCluster = paramConfig["ShiftRefToCluster"].get<bool>();
156 }
157 if (paramConfig.contains("FindShortTracks")) {
158 params.FindShortTracks = paramConfig["FindShortTracks"].get<bool>();
159 }
160 if (paramConfig.contains("PerPrimaryVertexProcessing")) {
161 params.PerPrimaryVertexProcessing = paramConfig["PerPrimaryVertexProcessing"].get<bool>();
162 }
163 if (paramConfig.contains("SaveTimeBenchmarks")) {
164 params.SaveTimeBenchmarks = paramConfig["SaveTimeBenchmarks"].get<bool>();
165 }
166 if (paramConfig.contains("DoUPCIteration")) {
167 params.DoUPCIteration = paramConfig["DoUPCIteration"].get<bool>();
168 }
169 if (paramConfig.contains("FataliseUponFailure")) {
170 params.FataliseUponFailure = paramConfig["FataliseUponFailure"].get<bool>();
171 }
172 if (paramConfig.contains("UseTrackFollower")) {
173 params.UseTrackFollower = paramConfig["UseTrackFollower"].get<bool>();
174 }
175 if (paramConfig.contains("UseTrackFollowerTop")) {
176 params.UseTrackFollowerTop = paramConfig["UseTrackFollowerTop"].get<bool>();
177 }
178 if (paramConfig.contains("UseTrackFollowerBot")) {
179 params.UseTrackFollowerBot = paramConfig["UseTrackFollowerBot"].get<bool>();
180 }
181 if (paramConfig.contains("UseTrackFollowerMix")) {
182 params.UseTrackFollowerMix = paramConfig["UseTrackFollowerMix"].get<bool>();
183 }
184 if (paramConfig.contains("createArtefactLabels")) {
185 params.createArtefactLabels = paramConfig["createArtefactLabels"].get<bool>();
186 }
187 if (paramConfig.contains("PrintMemory")) {
188 params.PrintMemory = paramConfig["PrintMemory"].get<bool>();
189 }
190 if (paramConfig.contains("DropTFUponFailure")) {
191 params.DropTFUponFailure = paramConfig["DropTFUponFailure"].get<bool>();
192 }
193
194 // Parse vector parameters
195 if (paramConfig.contains("LayerZ")) {
196 params.LayerZ = paramConfig["LayerZ"].get<std::vector<float>>();
197 }
198 if (paramConfig.contains("LayerRadii")) {
199 params.LayerRadii = paramConfig["LayerRadii"].get<std::vector<float>>();
200 }
201 if (paramConfig.contains("LayerxX0")) {
202 params.LayerxX0 = paramConfig["LayerxX0"].get<std::vector<float>>();
203 }
204 if (paramConfig.contains("LayerResolution")) {
205 params.LayerResolution = paramConfig["LayerResolution"].get<std::vector<float>>();
206 }
207 if (paramConfig.contains("SystErrorY2")) {
208 params.SystErrorY2 = paramConfig["SystErrorY2"].get<std::vector<float>>();
209 }
210 if (paramConfig.contains("SystErrorZ2")) {
211 params.SystErrorZ2 = paramConfig["SystErrorZ2"].get<std::vector<float>>();
212 }
213 if (paramConfig.contains("MinPt")) {
214 params.MinPt = paramConfig["MinPt"].get<std::vector<float>>();
215 }
216
217 // Parse Diamond array
218 if (paramConfig.contains("Diamond") && paramConfig["Diamond"].is_array() && paramConfig["Diamond"].size() == 3) {
219 params.Diamond[0] = paramConfig["Diamond"][0].get<float>();
220 params.Diamond[1] = paramConfig["Diamond"][1].get<float>();
221 params.Diamond[2] = paramConfig["Diamond"][2].get<float>();
222 }
223
224 // Parse size_t parameter
225 if (paramConfig.contains("MaxMemory")) {
226 params.MaxMemory = paramConfig["MaxMemory"].get<size_t>();
227 }
228
229 // Parse CorrType enum
230 if (paramConfig.contains("CorrType")) {
231 int corrTypeInt = paramConfig["CorrType"].get<int>();
232 params.CorrType = static_cast<o2::base::PropagatorImpl<float>::MatCorrType>(corrTypeInt);
233 }
234
235 trackingParams.push_back(params);
236 }
237
238 LOGP(info, "Loaded {} tracking parameter sets from configuration", trackingParams.size());
239 return trackingParams;
240}
241
243{
244 auto cput = mTimer.CpuTime();
245 auto realt = mTimer.RealTime();
246 mTimer.Start(false);
247
248 if (!mHitRecoConfig.empty()) {
249 TFile hitsFile(mHitRecoConfig["inputfiles"]["hits"].get<std::string>().c_str(), "READ");
250 TFile mcHeaderFile(mHitRecoConfig["inputfiles"]["mcHeader"].get<std::string>().c_str(), "READ");
251 TTree* hitsTree = hitsFile.Get<TTree>("o2sim");
252 std::vector<o2::trk::Hit>* trkHit = nullptr;
253 hitsTree->SetBranchAddress("TRKHit", &trkHit);
254
255 TTree* mcHeaderTree = mcHeaderFile.Get<TTree>("o2sim");
256 auto mcheader = new o2::dataformats::MCEventHeader;
257 mcHeaderTree->SetBranchAddress("MCEventHeader.", &mcheader);
258
259 o2::base::GeometryManager::loadGeometry(mHitRecoConfig["inputfiles"]["geometry"].get<std::string>().c_str(), false, true);
260 auto* gman = o2::trk::GeometryTGeo::Instance();
261
262 const Long64_t nEvents{hitsTree->GetEntries()};
263 LOGP(info, "Starting reconstruction from hits for {} events", nEvents);
264
265 if (mMemoryPool.get() == nullptr) {
266 mMemoryPool = std::make_shared<its::BoundedMemoryResource>();
267 }
268 if (mTaskArena.get() == nullptr) {
269 mTaskArena = std::make_shared<tbb::task_arena>(1);
270 }
271
272 o2::trk::TimeFrame<11> timeFrame;
273 o2::its::TrackerTraits<11> itsTrackerTraits;
274 o2::its::Tracker<11> itsTracker(&itsTrackerTraits);
275 timeFrame.setMemoryPool(mMemoryPool);
276 itsTrackerTraits.setMemoryPool(mMemoryPool);
277 itsTrackerTraits.setNThreads(mTaskArena->max_concurrency(), mTaskArena);
278 itsTrackerTraits.adoptTimeFrame(static_cast<o2::its::TimeFrame<11>*>(&timeFrame));
279 itsTracker.adoptTimeFrame(timeFrame);
280 itsTrackerTraits.setBz(mHitRecoConfig["geometry"]["bz"].get<float>());
281 auto field = new field::MagneticField("ALICE3Mag", "ALICE 3 Magnetic Field", mHitRecoConfig["geometry"]["bz"].get<float>() / 5.f, 0.0, o2::field::MagFieldParam::k5kGUniform);
282 TGeoGlobalMagField::Instance()->SetField(field);
283 TGeoGlobalMagField::Instance()->Lock();
284
285 int nRofs = timeFrame.loadROFsFromHitTree(hitsTree, gman, mHitRecoConfig);
286
287 const int inROFpileup{mHitRecoConfig.contains("inROFpileup") ? mHitRecoConfig["inROFpileup"].get<int>() : 1};
288
289 // Add primary vertices from MC headers for each ROF
290 timeFrame.getPrimaryVerticesFromMC(mcHeaderTree, nRofs, nEvents, inROFpileup);
291 // Create tracking parameters from config and set them in the time frame
292 auto trackingParams = createTrackingParamsFromConfig();
293
294 itsTrackerTraits.updateTrackingParameters(trackingParams);
295
296 const auto trackingLoopStart = std::chrono::steady_clock::now();
297 for (size_t iter{0}; iter < trackingParams.size(); ++iter) {
298 LOGP(info, "{}", trackingParams[iter].asString());
299 timeFrame.initialise(iter, trackingParams[iter], 11, false);
300 itsTrackerTraits.computeLayerTracklets(iter, -1, -1);
301 LOGP(info, "Number of tracklets in iteration {}: {}", iter, timeFrame.getNumberOfTracklets());
302 itsTrackerTraits.computeLayerCells(iter);
303 LOGP(info, "Number of cells in iteration {}: {}", iter, timeFrame.getNumberOfCells());
304 itsTrackerTraits.findCellsNeighbours(iter);
305 LOGP(info, "Number of cell neighbours in iteration {}: {}", iter, timeFrame.getNumberOfNeighbours());
306 itsTrackerTraits.findRoads(iter);
307 LOGP(info, "Number of roads in iteration {}: {}", iter, timeFrame.getNumberOfTracks());
308 itsTrackerTraits.extendTracks(iter);
309 }
310 const auto trackingLoopElapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - trackingLoopStart).count();
311 LOGP(info, "Tracking iterations block took {} ms", trackingLoopElapsedMs);
312
313 itsTracker.computeTracksMClabels();
314
315 // Stream tracks and their MC labels to the output
316 // Collect all tracks and labels from all ROFs
317 std::vector<o2::its::TrackITS> allTracks;
318 std::vector<o2::MCCompLabel> allLabels;
319
320 int totalTracks = 0;
321 int goodTracks = 0;
322 int fakeTracks = 0;
323
324 for (int iRof = 0; iRof < nRofs; ++iRof) {
325 const auto& rofTracks = timeFrame.getTracks(iRof);
326 const auto& rofLabels = timeFrame.getTracksLabel(iRof);
327
328 allTracks.insert(allTracks.end(), rofTracks.begin(), rofTracks.end());
329 allLabels.insert(allLabels.end(), rofLabels.begin(), rofLabels.end());
330
331 totalTracks += rofTracks.size();
332 for (const auto& label : rofLabels) {
333 if (label.isFake()) {
334 fakeTracks++;
335 } else {
336 goodTracks++;
337 }
338 }
339 }
340
341 LOGP(info, "=== Tracking Summary ===");
342 LOGP(info, "Total tracks reconstructed: {}", totalTracks);
343 LOGP(info, "Good tracks: {} ({:.1f}%)", goodTracks, totalTracks > 0 ? 100.0 * goodTracks / totalTracks : 0);
344 LOGP(info, "Fake tracks: {} ({:.1f}%)", fakeTracks, totalTracks > 0 ? 100.0 * fakeTracks / totalTracks : 0);
345
346 // Stream tracks and labels to DPL output
347 pc.outputs().snapshot(o2::framework::Output{"TRK", "TRACKS", 0}, allTracks);
348 pc.outputs().snapshot(o2::framework::Output{"TRK", "TRACKSMCTR", 0}, allLabels);
349
350 LOGP(info, "Tracks and MC labels streamed to output");
351
354 }
355
356 mTimer.Stop();
357 LOGP(info, "CPU Reconstruction time for this TF {} s (cpu), {} s (wall)", mTimer.CpuTime() - cput, mTimer.RealTime() - realt);
358}
359
360// void TrackerDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj)
361// {
362// // mITSTrackingInterface.finaliseCCDB(matcher, obj);
363// }
364
366{
367 LOGF(info, "TRK CA-Tracker total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1);
368}
369
370DataProcessorSpec getTrackerSpec(bool useMC, const std::string& hitRecoConfig, o2::gpu::gpudatatypes::DeviceType dType)
371{
372 std::vector<InputSpec> inputs;
373 std::vector<OutputSpec> outputs;
374 outputs.emplace_back("TRK", "TRACKS", 0, Lifetime::Timeframe);
375 auto ggRequest = std::make_shared<o2::base::GRPGeomRequest>(false, // orbitResetTime
376 false, // GRPECS=true
377 false, // GRPLHCIF
378 false, // GRPMagField
379 false, // askMatLUT
380 o2::base::GRPGeomRequest::None, // geometry, but ignored until it will be put in the CCDB
381 inputs,
382 true);
383
384 if (!hitRecoConfig.empty()) {
385 outputs.emplace_back("TRK", "TRACKSMCTR", 0, Lifetime::Timeframe);
386 return DataProcessorSpec{
387 "trk-hits-tracker",
388 {},
389 outputs,
390 AlgorithmSpec{adaptFromTask<TrackerDPL>(ggRequest,
391 useMC,
392 hitRecoConfig,
393 dType)},
394 Options{ConfigParamSpec{"max-loops", VariantType::Int, 1, {"max number of loops"}}}};
395 }
396
397 inputs.emplace_back("dummy", "TRK", "DUMMY", 0, Lifetime::Timeframe);
398
399 // inputs.emplace_back("compClusters", "TRK", "COMPCLUSTERS", 0, Lifetime::Timeframe);
400 // inputs.emplace_back("patterns", "TRK", "PATTERNS", 0, Lifetime::Timeframe);
401 // inputs.emplace_back("ROframes", "TRK", "CLUSTERSROF", 0, Lifetime::Timeframe);
402
403 // inputs.emplace_back("itscldict", "TRK", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/ClusterDictionary"));
404 // inputs.emplace_back("itsalppar", "TRK", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam"));
405
406 // outputs.emplace_back("TRK", "TRACKCLSID", 0, Lifetime::Timeframe);
407 // outputs.emplace_back("TRK", "TRKTrackROF", 0, Lifetime::Timeframe);
408 // outputs.emplace_back("TRK", "VERTICES", 0, Lifetime::Timeframe);
409 // outputs.emplace_back("TRK", "VERTICESROF", 0, Lifetime::Timeframe);
410 // outputs.emplace_back("TRK", "IRFRAMES", 0, Lifetime::Timeframe);
411
412 if (useMC) {
413 // inputs.emplace_back("trkmclabels", "TRK", "CLUSTERSMCTR", 0, Lifetime::Timeframe);
414 // inputs.emplace_back("TRKMC2ROframes", "TRK", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe);
415 // outputs.emplace_back("TRK", "VERTICESMCTR", 0, Lifetime::Timeframe);
416 // outputs.emplace_back("TRK", "VERTICESMCPUR", 0, Lifetime::Timeframe);
417 // outputs.emplace_back("TRK", "TRACKSMCTR", 0, Lifetime::Timeframe);
418 // outputs.emplace_back("TRK", "TRKTrackMC2ROF", 0, Lifetime::Timeframe);
419 }
420
421 return DataProcessorSpec{
422 "trk-tracker",
423 inputs,
424 outputs,
425 AlgorithmSpec{adaptFromTask<TrackerDPL>(ggRequest,
426 useMC,
427 hitRecoConfig,
428 dType)},
429 Options{}};
430}
431
432} // namespace trk
433} // namespace o2
std::string asString(TDataMember const &dm, char *pointer)
Definition of the GeometryManager class.
TRK TimeFrame class derived from ITS TimeFrame.
Definition of the TRK Hit class.
Definition of the MagFieldParam: container for ALICE mag. field parameters.
Definition of the MagF class.
Definition of the SegmentationChipclass.
static void loadGeometry(std::string_view geomFilePath="", bool applyMisalignment=false, bool preferAlignedFile=true)
void snapshot(const Output &spec, T const &object)
DataAllocator & outputs()
The data allocator is used to allocate memory for the output data.
ServiceRegistryRef services()
The services registry associated with this processing context.
virtual void findRoads(const int iteration)
void setMemoryPool(std::shared_ptr< BoundedMemoryResource > pool) noexcept
void setNThreads(int n, std::shared_ptr< tbb::task_arena > &arena)
void updateTrackingParameters(const std::vector< TrackingParameters > &trkPars)
virtual void computeLayerTracklets(const int iteration, int iROFslice, int iVertex)
virtual void adoptTimeFrame(TimeFrame< nLayers > *tf)
virtual void computeLayerCells(const int iteration)
virtual void setBz(float bz)
virtual void findCellsNeighbours(const int iteration)
virtual void extendTracks(const int iteration)
void computeTracksMClabels()
Definition Tracker.cxx:276
void adoptTimeFrame(TimeFrame< nLayers > &tf)
Definition Tracker.cxx:350
static GeometryTGeo * Instance()
int loadROFsFromHitTree(TTree *hitsTree, GeometryTGeo *gman, const nlohmann::json &config)
Definition TimeFrame.cxx:30
void getPrimaryVerticesFromMC(TTree *mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup)
TrackerDPL(std::shared_ptr< o2::base::GRPGeomRequest > gr, bool isMC, const std::string &hitRecoConfig, gpu::gpudatatypes::DeviceType dType=gpu::gpudatatypes::DeviceType::CPU)
void endOfStream(framework::EndOfStreamContext &ec) final
This is invoked whenever we have an EndOfStream event.
void run(framework::ProcessingContext &pc) final
void stop() final
This is invoked on stop.
void init(framework::InitContext &ic) final
GLenum const GLfloat * params
Definition glcorearb.h:272
GLuint GLsizei const GLchar * label
Definition glcorearb.h:2519
@ Me
Only quit this data processor.
template std::string ConfigParamRegistry::get< std::string >(const char *key) const
std::vector< ConfigParamSpec > Options
framework::DataProcessorSpec getTrackerSpec(bool useMC, const std::string &hitRecoConfig, gpu::gpudatatypes::DeviceType dType=gpu::gpudatatypes::DeviceType::CPU)
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
void initialise(const int iteration, const TrackingParameters &trkParam, const int maxLayers=7, bool resetVertices=true)
auto & getTracks(int rofId)
Definition TimeFrame.h:177
size_t getNumberOfTracks() const
Definition TimeFrame.h:664
virtual int getNumberOfTracklets() const
Definition TimeFrame.h:644
void setMemoryPool(std::shared_ptr< BoundedMemoryResource > pool)
memory management
virtual int getNumberOfCells() const
Definition TimeFrame.h:634
virtual int getNumberOfNeighbours() const
Definition TimeFrame.h:654
auto & getTracksLabel(const int rofId)
Definition TimeFrame.h:178
const int nEvents
Definition test_Fifo.cxx:27