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
13
15
16#include "MFTTracking/ROframe.h"
17#include "MFTTracking/IOUtils.h"
18#include "MFTTracking/Tracker.h"
19#include "MFTTracking/TrackCA.h"
20#include "MFTBase/GeometryTGeo.h"
21
22#include <vector>
23#include <future>
24
25#include "TGeoGlobalMagField.h"
26
36#include "Field/MagneticField.h"
41
42using namespace o2::framework;
43
44namespace o2
45{
46namespace mft
47{
48//#define _TIMING_
49
51{
53 for (int sw = 0; sw < NStopWatches; sw++) {
54 mTimer[sw].Stop();
55 mTimer[sw].Reset();
56 }
57
58 // tracking configuration parameters
59 auto& trackingParam = MFTTrackingParam::Instance(); // to avoid loading interpreter during the run
60}
61
63{
64 mTimer[SWTot].Start(false);
65
66 updateTimeDependentParams(pc);
67 gsl::span<const unsigned char> patterns = pc.inputs().get<gsl::span<unsigned char>>("patterns");
68 auto compClusters = pc.inputs().get<const std::vector<o2::itsmft::CompClusterExt>>("compClusters");
69 auto ntracks = 0;
70
71 // code further down does assignment to the rofs and the altered object is used for output
72 // we therefore need a copy of the vector rather than an object created directly on the input data,
73 // the output vector however is created directly inside the message memory thus avoiding copy by
74 // snapshot
75 auto rofsinput = pc.inputs().get<const std::vector<o2::itsmft::ROFRecord>>("ROframes");
76 auto& rofs = pc.outputs().make<std::vector<o2::itsmft::ROFRecord>>(Output{"MFT", "MFTTrackROF", 0}, rofsinput.begin(), rofsinput.end());
77
78 ROFFilter filter = [](const o2::itsmft::ROFRecord& r) { return true; };
79
80 LOG(info) << "MFTTracker pulled " << compClusters.size() << " compressed clusters in " << rofsinput.size() << " RO frames";
81
82 auto& trackingParam = MFTTrackingParam::Instance();
83 if (trackingParam.irFramesOnly) {
84 // selects only those ROFs that overlap ITS IRFrame
85 LOG(info) << "MFTTracker IRFrame filter enabled: loading ITS IR Frames. ";
86 auto irFrames = pc.inputs().get<gsl::span<o2::dataformats::IRFrame>>("IRFramesITS");
87 filter = createIRFrameFilter(irFrames);
88
89 if (fair::Logger::Logging(fair::Severity::debug)) {
90 for (const auto& irf : irFrames) {
91 LOG(debug) << "IRFrame.info = " << irf.info << " ; min = " << irf.getMin().bc << " ; max = " << irf.getMax().bc;
92 }
93 }
94 }
95
96 if (trackingParam.isMultCutRequested()) {
97 LOG(info) << "MFTTracker multiplicity filter enabled. ROF selection: Min nClusters = " << trackingParam.cutMultClusLow << " ; Max nClusters = " << trackingParam.cutMultClusHigh;
98 }
99
100 const dataformats::MCTruthContainer<MCCompLabel>* labels = mUseMC ? pc.inputs().get<const dataformats::MCTruthContainer<MCCompLabel>*>("labels").release() : nullptr;
101 gsl::span<itsmft::MC2ROFRecord const> mc2rofs;
102 if (mUseMC) {
103 // get the array as read-only span, a snapshot of the object is sent forward
104 mc2rofs = pc.inputs().get<gsl::span<itsmft::MC2ROFRecord>>("MC2ROframes");
105 LOG(info) << labels->getIndexedSize() << " MC label objects , in " << mc2rofs.size() << " MC events";
106 }
107
108 auto& allClusIdx = pc.outputs().make<std::vector<int>>(Output{"MFT", "TRACKCLSID", 0});
109 std::vector<o2::MCCompLabel> trackLabels;
110 std::vector<o2::MCCompLabel> allTrackLabels;
111 std::vector<o2::mft::TrackLTF> tracks;
112 std::vector<o2::mft::TrackLTFL> tracksL;
113 auto& allTracksMFT = pc.outputs().make<std::vector<o2::mft::TrackMFT>>(Output{"MFT", "TRACKS", 0});
114
115 std::uint32_t roFrameId = 0;
116 int nROFs = rofs.size();
117 auto rofsPerWorker = std::max(1, nROFs / mNThreads);
118 LOG(debug) << "nROFs = " << nROFs << " rofsPerWorker = " << rofsPerWorker;
119
120 auto loadData = [&, this](auto& trackerVec, auto& roFrameDataVec) {
121 auto& tracker = trackerVec[0]; // Use first tracker to load the data: serial operation
122 gsl::span<const unsigned char>::iterator pattIt = patterns.begin();
123
124 auto iROF = 0;
125
126 for (const auto& rof : rofs) {
127 int worker = std::min(int(iROF / rofsPerWorker), mNThreads - 1);
128 auto& roFrameData = roFrameDataVec[worker].emplace_back();
129 int nclUsed = ioutils::loadROFrameData(rof, roFrameData, compClusters, pattIt, mDict, labels, tracker.get(), filter);
130 LOG(debug) << "ROframeId: " << iROF << ", clusters loaded : " << nclUsed << " on worker " << worker;
131 iROF++;
132 }
133 };
134
135 auto launchTrackFinder = [](auto* tracker, auto* workerROFs) {
136#ifdef _TIMING_
137 long tStart = std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::system_clock::now()).time_since_epoch().count(), tStartROF = tStart, tEnd = tStart;
138 size_t rofCNT = 0;
139#endif
140 for (auto& rofData : *workerROFs) {
141 tracker->findTracks(rofData);
142#ifdef _TIMING_
143 long tEndROF = std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::system_clock::now()).time_since_epoch().count();
144 LOGP(info, "launchTrackFinder| tracker:{} did {}-th ROF in {} mus: {} clusters -> {} tracks", tracker->getTrackerID(), ++rofCNT, tEndROF - tStartROF, rofData.getTotalClusters(), rofData.getTracks().size());
145 tStartROF = tEnd = tEndROF;
146#endif
147 }
148#ifdef _TIMING_
149 LOGP(info, "launchTrackFinder| done: tracker:{} processed {} ROFS in {} mus", tracker->getTrackerID(), workerROFs->size(), tEnd - tStart);
150#endif
151 };
152
153 auto launchFitter = [](auto* tracker, auto* workerROFs) {
154#ifdef _TIMING_
155 long tStart = std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::system_clock::now()).time_since_epoch().count();
156#endif
157 for (auto& rofData : *workerROFs) {
158 tracker->fitTracks(rofData);
159 }
160#ifdef _TIMING_
161 long tEnd = std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::system_clock::now()).time_since_epoch().count();
162 LOGP(info, "launchTrackFitter| done: tracker:{} fitted {} ROFS in {} mus", tracker->getTrackerID(), workerROFs->size(), tEnd - tStart);
163#endif
164 };
165
166 auto runMFTTrackFinder = [&, this](auto& trackerVec, auto& roFrameDataVec) {
167 std::vector<std::future<void>> finder;
168 for (int i = 0; i < mNThreads; i++) {
169 auto& tracker = trackerVec[i];
170 auto& workerData = roFrameDataVec[i];
171 auto f = std::async(std::launch::async, launchTrackFinder, tracker.get(), &workerData);
172 finder.push_back(std::move(f));
173 }
174
175 for (int i = 0; i < mNThreads; i++) {
176 finder[i].wait();
177 }
178 };
179
180 auto runTrackFitter = [&, this](auto& trackerVec, auto& roFrameDataVec) {
181 std::vector<std::future<void>> fitter;
182 for (int i = 0; i < mNThreads; i++) {
183 auto& tracker = trackerVec[i];
184 auto& workerData = roFrameDataVec[i];
185 auto f = std::async(std::launch::async, launchFitter, tracker.get(), &workerData);
186 fitter.push_back(std::move(f));
187 }
188
189 for (int i = 0; i < mNThreads; i++) {
190 fitter[i].wait();
191 }
192 };
193
194 // snippet to convert found tracks to final output tracks with separate cluster indices
195 auto copyTracks = [](auto& new_tracks, auto& allTracks, auto& allClusIdx) {
196 for (auto& trc : new_tracks) {
197 trc.setExternalClusterIndexOffset(allClusIdx.size());
198 int ncl = trc.getNumberOfPoints();
199 for (int ic = 0; ic < ncl; ic++) {
200 auto externalClusterID = trc.getExternalClusterIndex(ic);
201 auto clusterSize = trc.getExternalClusterSize(ic);
202 auto clusterLayer = trc.getExternalClusterLayer(ic);
203 trc.setClusterSize(clusterLayer, clusterSize);
204 allClusIdx.push_back(externalClusterID);
205 }
206 allTracks.emplace_back(trc);
207 }
208 };
209
210 if (mFieldOn) {
211
212 std::vector<std::vector<o2::mft::ROframe<TrackLTF>>> roFrameVec(mNThreads); // One vector of ROFrames per thread
213 LOG(debug) << "Reserving ROFs ";
214
215 for (auto& rof : roFrameVec) {
216 rof.reserve(rofsPerWorker);
217 }
218 LOG(debug) << "Loading data into ROFs.";
219
220 mTimer[SWLoadData].Start(false);
221 loadData(mTrackerVec, roFrameVec);
222 mTimer[SWLoadData].Stop();
223
224 LOG(debug) << "Running MFT Track finder.";
225
226 mTimer[SWFindMFTTracks].Start(false);
227 runMFTTrackFinder(mTrackerVec, roFrameVec);
228 mTimer[SWFindMFTTracks].Stop();
229
230 LOG(debug) << "Runnig track fitter.";
231
232 mTimer[SWFitTracks].Start(false);
233 runTrackFitter(mTrackerVec, roFrameVec);
234 mTimer[SWFitTracks].Stop();
235
236 if (mUseMC) {
237 LOG(debug) << "Computing MC Labels.";
238
239 mTimer[SWComputeLabels].Start(false);
240 auto& tracker = mTrackerVec[0];
241
242 for (int i = 0; i < mNThreads; i++) {
243 for (auto& rofData : roFrameVec[i]) {
244 tracker->computeTracksMClabels(rofData.getTracks());
245 trackLabels.swap(tracker->getTrackLabels());
246 std::copy(trackLabels.begin(), trackLabels.end(), std::back_inserter(allTrackLabels));
247 trackLabels.clear();
248 }
249 }
250 mTimer[SWComputeLabels].Stop();
251 }
252
253 auto rof = rofs.begin();
254
255 for (int i = 0; i < mNThreads; i++) {
256 for (auto& rofData : roFrameVec[i]) {
257 int ntracksROF = 0, firstROFTrackEntry = allTracksMFT.size();
258 tracks.swap(rofData.getTracks());
259 ntracksROF = tracks.size();
260 copyTracks(tracks, allTracksMFT, allClusIdx);
261
262 rof->setFirstEntry(firstROFTrackEntry);
263 rof->setNEntries(ntracksROF);
264 *rof++;
265 roFrameId++;
266 }
267 }
268
269 } else {
270 LOG(debug) << "Field is off! ";
271 std::vector<std::vector<o2::mft::ROframe<TrackLTFL>>> roFrameVec(mNThreads); // One vector of ROFrames per thread
272 LOG(debug) << "Reserving ROFs ";
273
274 for (auto& rof : roFrameVec) {
275 rof.reserve(rofsPerWorker);
276 }
277 LOG(debug) << "Loading data into ROFs.";
278
279 mTimer[SWLoadData].Start(false);
280 loadData(mTrackerLVec, roFrameVec);
281 mTimer[SWLoadData].Stop();
282
283 LOG(debug) << "Running MFT Track finder.";
284
285 mTimer[SWFindMFTTracks].Start(false);
286 runMFTTrackFinder(mTrackerLVec, roFrameVec);
287 mTimer[SWFindMFTTracks].Stop();
288
289 LOG(debug) << "Runnig track fitter.";
290
291 mTimer[SWFitTracks].Start(false);
292 runTrackFitter(mTrackerLVec, roFrameVec);
293 mTimer[SWFitTracks].Stop();
294
295 if (mUseMC) {
296 LOG(debug) << "Computing MC Labels.";
297
298 mTimer[SWComputeLabels].Start(false);
299 auto& tracker = mTrackerLVec[0];
300
301 for (int i = 0; i < mNThreads; i++) {
302 for (auto& rofData : roFrameVec[i]) {
303 tracker->computeTracksMClabels(rofData.getTracks());
304 trackLabels.swap(tracker->getTrackLabels());
305 std::copy(trackLabels.begin(), trackLabels.end(), std::back_inserter(allTrackLabels));
306 trackLabels.clear();
307 }
308 }
309 mTimer[SWComputeLabels].Stop();
310 }
311
312 auto rof = rofs.begin();
313
314 for (int i = 0; i < mNThreads; i++) {
315 for (auto& rofData : roFrameVec[i]) {
316 int ntracksROF = 0, firstROFTrackEntry = allTracksMFT.size();
317 tracksL.swap(rofData.getTracks());
318 ntracksROF = tracksL.size();
319 copyTracks(tracksL, allTracksMFT, allClusIdx);
320 rof->setFirstEntry(firstROFTrackEntry);
321 rof->setNEntries(ntracksROF);
322 *rof++;
323 roFrameId++;
324 }
325 }
326 }
327
328 LOG(info) << "MFTTracker pushed " << allTracksMFT.size() << " tracks";
329
330 if (mUseMC) {
331 pc.outputs().snapshot(Output{"MFT", "TRACKSMCTR", 0}, allTrackLabels);
332 pc.outputs().snapshot(Output{"MFT", "TRACKSMC2ROF", 0}, mc2rofs);
333 }
334
335 static bool first = true;
336 if (first) {
337 first = false;
340 }
341 }
342
343 mTimer[SWTot].Stop();
344}
345
347{
348 for (int i = 0; i < NStopWatches; i++) {
349 LOGF(info, "Timing %18s: Cpu: %.3e s; Real: %.3e s in %d slots", TimerName[i], mTimer[i].CpuTime(), mTimer[i].RealTime(), mTimer[i].Counter() - 1);
350 }
351}
353void TrackerDPL::updateTimeDependentParams(ProcessingContext& pc)
354{
356 static bool initOnceDone = false;
357 if (!initOnceDone) { // this params need to be queried only once
358 initOnceDone = true;
359 if (pc.inputs().getPos("mftTGeo") >= 0) {
360 pc.inputs().get<o2::mft::GeometryTGeo*>("mftTGeo");
361 }
362 pc.inputs().get<o2::itsmft::TopologyDictionary*>("cldict"); // just to trigger the finaliseCCDB
363 bool continuous = o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::MFT);
364 LOG(info) << "MFTTracker RO: continuous =" << continuous;
365 mMFTTriggered = !continuous;
367 if (mMFTTriggered) {
368 setMFTROFrameLengthMUS(alpParams.roFrameLengthTrig / 1.e3); // MFT ROFrame duration in \mus
369 } else {
370 setMFTROFrameLengthInBC(alpParams.roFrameLengthInBC); // MFT ROFrame duration in BC
371 }
372
376 // tracking configuration parameters
377 auto& trackingParam = MFTTrackingParam::Instance();
378 auto field = static_cast<o2::field::MagneticField*>(TGeoGlobalMagField::Instance()->GetField());
379 double centerMFT[3] = {0, 0, -61.4}; // Field at center of MFT
380 auto Bz = field->getBz(centerMFT);
381 if (Bz == 0 || trackingParam.forceZeroField) {
382 LOG(info) << "Starting MFT Linear tracker: Field is off!";
383 LOG(info) << " MFT tracker running with " << mNThreads << " threads";
384 mFieldOn = false;
385 for (auto i = 0; i < mNThreads; i++) {
386 auto& tracker = mTrackerLVec.emplace_back(std::make_unique<o2::mft::Tracker<TrackLTFL>>(mUseMC));
387 tracker->setBz(0);
388 tracker->configure(trackingParam, i);
389 }
390 } else {
391 LOG(info) << "Starting MFT tracker: Field is on! Bz = " << Bz;
392 LOG(info) << " MFT tracker running with " << mNThreads << " threads";
393 mFieldOn = true;
394 for (auto i = 0; i < mNThreads; i++) {
395 auto& tracker = mTrackerVec.emplace_back(std::make_unique<o2::mft::Tracker<TrackLTF>>(mUseMC));
396 tracker->setBz(Bz);
397 tracker->configure(trackingParam, i);
398 }
399 }
400 }
401}
402
405{
407 return;
408 }
409 if (matcher == ConcreteDataMatcher("MFT", "CLUSDICT", 0)) {
410 LOG(info) << "cluster dictionary updated";
411 mDict = (const o2::itsmft::TopologyDictionary*)obj;
412 return;
413 }
414 if (matcher == ConcreteDataMatcher("MFT", "GEOMTGEO", 0)) {
415 LOG(info) << "MFT GeomtetryTGeo loaded from ccdb";
417 return;
418 }
419}
420
422void TrackerDPL::setMFTROFrameLengthMUS(float fums)
423{
424 mMFTROFrameLengthMUS = fums;
425 mMFTROFrameLengthMUSInv = 1. / mMFTROFrameLengthMUS;
426 mMFTROFrameLengthInBC = std::max(1, int(mMFTROFrameLengthMUS / (o2::constants::lhc::LHCBunchSpacingNS * 1e-3)));
427}
428
430void TrackerDPL::setMFTROFrameLengthInBC(int nbc)
431{
432 mMFTROFrameLengthInBC = nbc;
433 mMFTROFrameLengthMUS = nbc * o2::constants::lhc::LHCBunchSpacingNS * 1e-3;
434 mMFTROFrameLengthMUSInv = 1. / mMFTROFrameLengthMUS;
435}
436
438DataProcessorSpec getTrackerSpec(bool useMC, bool useGeom, int nThreads)
439{
440 std::vector<InputSpec> inputs;
441 inputs.emplace_back("compClusters", "MFT", "COMPCLUSTERS", 0, Lifetime::Timeframe);
442 inputs.emplace_back("patterns", "MFT", "PATTERNS", 0, Lifetime::Timeframe);
443 inputs.emplace_back("ROframes", "MFT", "CLUSTERSROF", 0, Lifetime::Timeframe);
444 inputs.emplace_back("cldict", "MFT", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("MFT/Calib/ClusterDictionary"));
445
446 auto& trackingParam = MFTTrackingParam::Instance();
447 if (trackingParam.irFramesOnly) {
448 inputs.emplace_back("IRFramesITS", "ITS", "IRFRAMES", 0, Lifetime::Timeframe);
449 }
450
451 auto ggRequest = std::make_shared<o2::base::GRPGeomRequest>(false, // orbitResetTime
452 true, // GRPECS=true
453 false, // GRPLHCIF
454 true, // GRPMagField
455 false, // askMatLUT
457 inputs,
458 true);
459 if (!useGeom) {
460 ggRequest->addInput({"mftTGeo", "MFT", "GEOMTGEO", 0, Lifetime::Condition, framework::ccdbParamSpec("MFT/Config/Geometry")}, inputs);
461 }
462 std::vector<OutputSpec> outputs;
463 outputs.emplace_back("MFT", "TRACKS", 0, Lifetime::Timeframe);
464 outputs.emplace_back("MFT", "MFTTrackROF", 0, Lifetime::Timeframe);
465 outputs.emplace_back("MFT", "TRACKCLSID", 0, Lifetime::Timeframe);
466
467 if (useMC) {
468 inputs.emplace_back("labels", "MFT", "CLUSTERSMCTR", 0, Lifetime::Timeframe);
469 inputs.emplace_back("MC2ROframes", "MFT", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe);
470 outputs.emplace_back("MFT", "TRACKSMCTR", 0, Lifetime::Timeframe);
471 outputs.emplace_back("MFT", "TRACKSMC2ROF", 0, Lifetime::Timeframe);
472 }
473
474 return DataProcessorSpec{
475 "mft-tracker",
476 inputs,
477 outputs,
478 AlgorithmSpec{adaptFromTask<TrackerDPL>(ggRequest, useMC, nThreads)},
479 Options{}};
480}
481
482} // namespace mft
483} // namespace o2
std::vector< std::string > labels
Definition of the ITS/MFT clusterer settings.
Definition of the ITSMFT compact cluster.
Definition of the Names Generator class.
Definition of the GeometryManager class.
std::ostringstream debug
int32_t i
Load pulled clusters, for a given read-out-frame, in a dedicated container.
Class for the standalone track finding.
Definition of the ITSMFT ROFrame (trigger) record.
Definition of a container to keep Monte Carlo truth external to simulation objects.
Definition of the MagF class.
The main container for the standalone track finding within a read-out-frame.
Standalone classes for the track found by the Linear-Track-Finder (LTF) and by the Cellular-Automaton...
int clusterSize
void checkUpdates(o2::framework::ProcessingContext &pc)
static GRPGeomHelper & instance()
void setRequest(std::shared_ptr< GRPGeomRequest > req)
static std::string getConfigOutputFileName(const std::string &procName, const std::string &confName="", bool json=true)
Definition NameConf.cxx:115
static void write(std::string const &filename, std::string const &keyOnly="")
A container to hold and manage MC truth information/labels.
static constexpr ID MFT
Definition DetID.h:71
Double_t getBz(const Double_t *xyz) const
Method to calculate the field at point xyz.
void snapshot(const Output &spec, T const &object)
decltype(auto) make(const Output &spec, Args... args)
int getPos(const char *name) const
decltype(auto) get(R binding, int part=0) const
DataAllocator & outputs()
The data allocator is used to allocate memory for the output data.
InputRecord & inputs()
The inputs associated with this processing context.
ServiceRegistryRef services()
The services registry associated with this processing context.
void fillMatrixCache(Int_t mask) override
static GeometryTGeo * Instance()
static void adopt(GeometryTGeo *raw, bool canDelete=false)
void init(framework::InitContext &ic) final
void run(framework::ProcessingContext &pc) final
void endOfStream(framework::EndOfStreamContext &ec) final
This is invoked whenever we have an EndOfStream event.
void finaliseCCDB(framework::ConcreteDataMatcher &matcher, void *obj) final
GLdouble f
Definition glcorearb.h:310
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition glcorearb.h:1308
GLboolean r
Definition glcorearb.h:1233
constexpr double LHCBunchSpacingNS
Defining PrimaryVertex explicitly as messageable.
std::vector< ConfigParamSpec > ccdbParamSpec(std::string const &path, int runDependent, std::vector< CCDBMetadata > metadata={}, int qrate=0)
std::vector< ConfigParamSpec > Options
int loadROFrameData(const o2::itsmft::ROFRecord &rof, ROframe< T > &events, gsl::span< const itsmft::CompClusterExt > clusters, gsl::span< const unsigned char >::iterator &pattIt, const itsmft::TopologyDictionary *dict, const dataformats::MCTruthContainer< MCCompLabel > *mClsLabels=nullptr, const o2::mft::Tracker< T > *tracker=nullptr)
Definition IOUtils.cxx:148
o2::framework::DataProcessorSpec getTrackerSpec(bool useMC, bool useGeom, int nThreads)
create a processor spec
std::function< bool(const ROFRecord &)> ROFFilter
Definition Tracker.h:40
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
std::tuple< TFile *, TTreeReader * > loadData(const std::string inFile)
std::string name
The name of the associated DataProcessorSpec.
Definition DeviceSpec.h:50
size_t inputTimesliceId
The time pipelining id of this particular device.
Definition DeviceSpec.h:68
static constexpr int T2L
Definition Cartesian.h:55
static constexpr int T2GRot
Definition Cartesian.h:57
static constexpr int T2G
Definition Cartesian.h:56
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
TStopwatch sw
std::array< std::vector< ROFRecord >, NEvTypes > rofData