Project
Loading...
Searching...
No Matches
TPCDigitizerMTSpec.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
14
15#include "TPCDigitizerSpec.h"
20#include "Framework/Lifetime.h"
21#include "Framework/Task.h"
22#include "Framework/Logger.h"
23#include "Headers/DataHeader.h"
33#include <TChain.h>
34#include <TFile.h>
35#include <TROOT.h>
36#include <TRandom.h>
37#include <TStopwatch.h>
38#include <TTree.h>
39#include <algorithm>
40#include <atomic>
41#include <exception>
42#include <filesystem>
43#include <memory>
44#include <optional>
45#include <string>
46#include <thread>
47#include <utility>
48#include <vector>
49
50using namespace o2::framework;
51
52namespace o2
53{
54namespace tpc
55{
56
57namespace
58{
60struct DigitizerWorker {
61 int id = 0;
62 Digitizer digitizer;
63 std::vector<TChain*> chains;
64 SectorFlushBuffers buffers;
65 std::exception_ptr error;
66
67 ~DigitizerWorker()
68 {
69 for (auto chain : chains) {
70 delete chain;
71 }
72 }
73
74 std::string fileName() const { return "tpc_driftime_digits_lane" + std::to_string(id) + ".root"; }
75
77 void processSector(const steer::DigitizationContext& context, int sector, ULong64_t timeframe, bool withMCTruth)
78 {
79 std::unique_ptr<TFile> file(TFile::Open(fileName().c_str(), "UPDATE"));
80 if (!file || file->IsZombie()) {
81 throw std::runtime_error("TPC: cannot open " + fileName());
82 }
83 auto tree = new TTree(std::to_string(sector).c_str(), "o2sim");
84 tree->SetDirectory(file.get()); // owned by the file
85 size_t flushCounter = 0;
86 auto onFlush = [&]() {
87 ++flushCounter;
88 auto fill = [&tree](const std::string& name, auto* data) {
89 auto br = o2::base::getOrMakeBranch(*tree, name.c_str(), data);
90 br->Fill();
91 br->ResetAddress();
92 };
93 fill("TPCDigit_" + std::to_string(sector), &buffers.digits);
94 if (withMCTruth) {
95 fill("TPCDigitMCTruth_" + std::to_string(sector), &buffers.labels);
96 }
97 fill("TPCCommonMode_" + std::to_string(sector), &buffers.commonMode);
98 };
99 digitizeSector(digitizer, context, chains, sector, timeframe, buffers, onFlush);
100 tree->SetEntries(flushCounter);
101 file->Write("", TObject::kOverwrite);
102 file->Close();
103 }
104};
105} // namespace
106
108{
109 public:
110 TPCDPLDigitizerMTTask(int nThreads, int distortionType) : BaseDPLDigitizer(o2::base::InitServices::FIELD | o2::base::InitServices::GEOM), mNThreads(nThreads), mDistortionType(distortionType) {}
111
113 {
114 LOG(info) << "Initializing multithreaded TPC digitization with " << mNThreads << " threads";
115
117 const auto triggeredMode = ic.options().get<bool>("TPCtriggered");
118 mConditions.init(ic, mDistortionType);
119
120 // the workers share no random numbers: each digitizer seeds every sector from DigiParams.seed
121 const auto seed = o2::conf::DigiParams::Instance().seed;
122 if (seed == 0) {
123 LOG(warning) << "TPC sector seeding with DigiParams.seed=0 is not reproducible";
124 }
125 // with seed 0, gRandom was seeded randomly in the base class
126 const ULong64_t sectorSeed = seed != 0 ? seed : gRandom->Integer(kMaxUInt);
127
128 for (int i = 0; i < mNThreads; ++i) {
129 auto& worker = mWorkers.emplace_back(std::make_unique<DigitizerWorker>());
130 worker->id = i;
131 worker->digitizer.setContinuousReadout(!triggeredMode);
132 worker->digitizer.setDistortionScaleType(mDistortionType);
133 worker->digitizer.setUseSectorSeeding(sectorSeed);
134 // the main thread flushes the process-wide debug streamer after the workers
135 worker->digitizer.setFlushDebugStreamer(false);
136 }
137
138 // the merger picks up every lane file in the directory, so files of an earlier run must not survive
139 for (const auto& name : o2::utils::listFiles("tpc_driftime_digits_lane.*.root$")) {
140 std::filesystem::remove(name);
141 }
142
143 ROOT::EnableThreadSafety();
144 }
145
147 {
148 // the maps are set on the first digitizer; the others share them once they are final
149 mConditions.finaliseCCDB(matcher, obj, mWorkers[0]->digitizer);
150 }
151
153 {
154 LOG(info) << "Processing multithreaded TPC digitization";
155
156 auto& primary = mWorkers[0]->digitizer;
157 mConditions.update(pc, primary, [this](float vdrift, float tdriftOffset) {
158 for (auto& worker : mWorkers) {
159 worker->digitizer.setVDrift(vdrift);
160 worker->digitizer.setTDriftOffset(tdriftOffset);
161 }
162 });
163 if (mDistortionType) {
164 for (size_t i = 1; i < mWorkers.size(); ++i) {
165 mWorkers[i]->digitizer.setSCDistortionsFrom(primary);
166 }
167 }
168 if (!mSeedingShared) {
169 // the rings depend only on the seed, so they are filled once and copied
170 for (size_t i = 1; i < mWorkers.size(); ++i) {
171 mWorkers[i]->digitizer.setSectorSeedingFrom(primary);
172 }
173 mSeedingShared = true;
174 }
175
176 // the sectors of this timeframe; all inputs carry the same collision context
177 std::vector<int> sectors;
178 std::optional<DataRef> contextRef;
179 for (auto it = pc.inputs().begin(), end = pc.inputs().end(); it != end; ++it) {
180 for (auto const& inputref : it.parts()) {
181 if (inputref.spec->lifetime == o2::framework::Lifetime::Condition) {
182 continue;
183 }
184 auto const* sectorHeader = DataRefUtils::getHeader<TPCSectorHeader*>(inputref);
185 if (sectorHeader == nullptr) {
186 LOG(error) << "TPC: Sector header missing, skipping input";
187 continue;
188 }
189 const int sector = sectorHeader->sector();
190 if (sector < 0 || sector >= TPCSectorHeader::NSectors) {
191 throw std::runtime_error("Digitizer can only work on single sectors");
192 }
193 sectors.push_back(sector);
194 if (!contextRef) {
195 contextRef = inputref;
196 }
197 }
198 }
199
200 if (mWriteGRP) {
201 auto roMode = primary.isContinuousReadout() ? o2::parameters::GRPObject::CONTINUOUS : o2::parameters::GRPObject::PRESENT;
202 LOG(info) << "TPC: Sending ROMode= " << (primary.isContinuousReadout() ? "Continuous" : "Triggered") << " to GRPUpdater";
203 pc.outputs().snapshot(Output{"TPC", "ROMode", 0}, roMode);
204 mWriteGRP = false;
205 }
206 if (!contextRef) {
207 return;
208 }
209 // the chunked digit files and the dynamic sector scheduling hold one timeframe per process
210 if (mProcessedTimeframe) {
211 LOG(fatal) << "TPC: the multithreaded digitizer supports only one timeframe per process";
212 }
213 mProcessedTimeframe = true;
214 auto context = pc.inputs().get<o2::steer::DigitizationContext*>(*contextRef);
215 const ULong64_t timeframe = DataRefUtils::getHeader<o2::header::DataHeader*>(*contextRef)->tfCounter;
216 LOG(info) << "TPC: Processing " << sectors.size() << " sectors with " << context->getEventRecords().size() << " collisions";
217
218 TStopwatch timer;
219 timer.Start();
220
221 const size_t nWorkers = std::min(mWorkers.size(), sectors.size());
222 for (size_t i = 0; i < nWorkers; ++i) {
223 context->initSimChains(o2::detectors::DetID::TPC, mWorkers[i]->chains);
224 }
225
226 // the sectors go to the workers dynamically
227 std::atomic<size_t> nextSector{0};
228 std::vector<std::thread> threads;
229 for (size_t i = 0; i < nWorkers; ++i) {
230 threads.emplace_back([&, worker = mWorkers[i].get()]() {
231 try {
232 for (size_t is = nextSector++; is < sectors.size(); is = nextSector++) {
233 LOG(info) << "TPC: Worker " << worker->id << " processing sector " << sectors[is];
234 worker->processSector(*context, sectors[is], timeframe, mWithMCTruth);
235 }
236 } catch (...) {
237 worker->error = std::current_exception();
238 nextSector = sectors.size(); // let the other workers stop early
239 }
240 });
241 }
242 for (auto& thread : threads) {
243 thread.join();
244 }
245 if (auto* streamer = o2::utils::DebugStreamer::instance()) {
246 streamer->flush();
247 }
248 for (size_t i = 0; i < nWorkers; ++i) {
249 if (mWorkers[i]->error) {
250 std::rethrow_exception(mWorkers[i]->error);
251 }
252 }
253
254 timer.Stop();
255 LOG(info) << "TPC: Digitization took " << timer.CpuTime() << "s CPU, " << timer.RealTime() << "s wall";
256 }
257
258 private:
259 std::vector<std::unique_ptr<DigitizerWorker>> mWorkers;
260 TPCDigitizerConditions mConditions;
261 bool mSeedingShared = false;
262 bool mProcessedTimeframe = false;
263 int mNThreads = 1;
264 int mDistortionType = 0;
265 bool mWriteGRP = true;
266 bool mWithMCTruth = true;
267};
268
269o2::framework::DataProcessorSpec getTPCDigitizerMTSpec(int nThreads, std::vector<int> const& sectors, int distortionType)
270{
271 std::vector<InputSpec> inputs;
272 for (auto sector : sectors) {
273 inputs.emplace_back("collisioncontext" + std::to_string(sector), "SIM", "COLLISIONCONTEXT", static_cast<o2::header::DataHeader::SubSpecificationType>(sector), Lifetime::Timeframe);
274 }
275 TPCDigitizerConditions::addInputs(inputs, distortionType);
276 std::vector<OutputSpec> outputs{{"TPC", "ROMode", 0, Lifetime::Timeframe}};
277
278 // the name must not match "TPCDigitizer.*", whose completion policy would process the sectors one by one
279 return DataProcessorSpec{
280 "tpc-digitizer-mt",
281 inputs,
282 outputs,
283 AlgorithmSpec{adaptFromTask<TPCDPLDigitizerMTTask>(nThreads, distortionType)},
285}
286
287} // namespace tpc
288} // namespace o2
Definition of the base digitizer task class.
Definition of the Detector class.
Definition of class for writing debug informations.
int32_t i
GPUChain * chain
configStandalone Light OpenGL display display_light Apply smoothing to points Enable Z buffer Highlight links Highlight seeds Highlight sector tracks Highlight final tracks Show individual Show related Highlight grid Show only individual collision Color clusters belonging to track objects Set line Enable drawing TPC data Enable drawing TOF data Enable drawing magnetic field Set field line step count Invert colors configStandalone Heavy OpenGL display display_heavy Show tracks with ITS contribution Show tracks with TOF contribution Enable propagation of loopers Show clusters at nominal x of pad row for early transformed data Split CE tracks when they cross the central Mark clusters Mark adjacent clusters Hide rejected tracks Apply filter on tracks to be displayed Separate sectors
configStandalone Light OpenGL display display_light Apply smoothing to points Enable Z buffer Highlight links Highlight seeds Highlight sector tracks Highlight final tracks Show individual Show related Highlight grid Show only individual collision Color clusters belonging to track objects Set line Enable drawing TPC data Enable drawing TOF data Enable drawing magnetic field Set field line step count Invert colors configStandalone Heavy OpenGL display display_heavy Show tracks with ITS contribution Show tracks with TOF contribution Enable propagation of loopers Show clusters at nominal x of pad row for early transformed data Split CE tracks when they cross the central Mark clusters Mark adjacent clusters Hide rejected tracks Apply filter on tracks to be displayed Separate increase X h configStandalone Camera window renderer OpenGL display display_camera Orient the camera such that the y axis is always upwards Full Screen MultiSample Anti Downsampling Anti Enable Vertical Sync Target frame or if the backend cannot limit to the refresh Use OpenGL indirect draws to reduce number of draw calls Display h configStandalone Vulkan display display_vulkan Have uniform buffers in host accessible device memory h configStandalone Internal display display_internal Number of updates the Qt GUI may send ahead of the last one the display has before it waits for the display Opacity of the console background Number of console lines the history is shortened to this when it reaches twice the size Time in seconds an info message fades out after setInfoTime h OpenGL display display Font size Do not use Freetype for font Renderer for event Frontend to use for event ROOT macros used as track filter Console script file
configStandalone Light OpenGL display display_light Apply smoothing to points Enable Z buffer Highlight links Highlight seeds Highlight sector tracks Highlight final tracks Show individual sector
Timeframe settings Average distance in cm of events merged into timeframe
Header of the General Run Parameters object.
char error[CURL_ERROR_SIZE]
std::vector< TChain * > chains
Digitizer digitizer
Definition of the ALICE TPC digitizer.
void snapshot(const Output &spec, T const &object)
ConfigParamRegistry const & options()
Definition InitContext.h:33
const_iterator begin() const
decltype(auto) get(R binding, int part=0) const
const_iterator end() const
DataAllocator & outputs()
The data allocator is used to allocate memory for the output data.
InputRecord & inputs()
The inputs associated with this processing context.
bool initSimChains(o2::detectors::DetID detid, std::vector< TChain * > &simchains) const
std::vector< o2::InteractionTimeRecord > & getEventRecords(bool withQED=false)
void finaliseCCDB(framework::ConcreteDataMatcher &matcher, void *obj)
void initDigitizerTask(framework::InitContext &ic) override
TPCDPLDigitizerMTTask(int nThreads, int distortionType)
void run(framework::ProcessingContext &pc)
CCDB conditions and calibration files of the TPC digitizer devices.
static void addInputs(std::vector< framework::InputSpec > &inputs, int distortionType)
CCDB inputs common to the TPC digitizer devices.
bool finaliseCCDB(framework::ConcreteDataMatcher &matcher, void *obj, Digitizer &digitizer)
pass a CCDB object to the digitizer; true if it was a TPC condition
static std::vector< framework::ConfigParamSpec > options()
options common to the TPC digitizer devices
void init(framework::InitContext &ic, int distortionType)
void update(framework::ProcessingContext &pc, Digitizer &digitizer, const std::function< void(float, float)> &setVDrift)
GLuint GLuint end
Definition glcorearb.h:469
GLuint const GLchar * name
Definition glcorearb.h:781
TBranch * getOrMakeBranch(TTree &tree, const char *brname, T *ptr)
Definition Detector.h:313
Defining ITS Vertex explicitly as messageable.
Definition Cartesian.h:288
std::vector< dataformats::RangeReference< int, int > > digitizeSector(Digitizer &digitizer, const steer::DigitizationContext &context, const std::vector< TChain * > &simChains, int sector, ULong64_t timeframe, SectorFlushBuffers &buffers, const std::function< void()> &onFlush)
o2::framework::DataProcessorSpec getTPCDigitizerMTSpec(int nThreads, std::vector< int > const &sectors, int distortionType)
A single TPC digitizer device that digitizes the sectors with nThreads threads and writes the digits ...
std::vector< std::string > listFiles(std::string const &dir, std::string const &searchpattern)
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
class listing possible services
static constexpr int NSectors
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::unique_ptr< TTree > tree((TTree *) flIn.Get(std::string(o2::base::NameConf::CTFTREENAME).c_str()))