Project
Loading...
Searching...
No Matches
TPCDigitRootWriterSpec.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
16
24#include "TPCBase/Sector.h"
30#include <TFile.h>
31#include <TTree.h>
32#include <TBranch.h>
33#include <memory> // for make_shared, make_unique, unique_ptr
34#include <stdexcept>
35#include <string>
36#include <vector>
37#include <utility>
38
39using namespace o2::framework;
40using namespace o2::header;
43
44namespace o2
45{
46template <typename T>
48
49namespace tpc
50{
51
55DataProcessorSpec getTPCDigitRootWriterSpec(std::vector<int> const& laneConfiguration, bool mctruth)
56{
57 // the callback to be set as hook for custom action when the writer is closed
58 auto finishWriting = [](TFile* outputfile, TTree* outputtree) {
59 // check/verify number of entries (it should be same in all branches)
60
61 // will return a TObjArray
62 const auto brlist = outputtree->GetListOfBranches();
63 int entries = -1; // init to -1 (as unitialized)
64 for (TObject* entry : *brlist) {
65 auto br = static_cast<TBranch*>(entry);
66 int brentries = br->GetEntries();
67 entries = std::max(entries, brentries);
68 if (brentries != entries && !TString(br->GetName()).Contains("CommonMode")) {
69 LOG(warning) << "INCONSISTENT NUMBER OF ENTRIES IN BRANCH " << br->GetName() << ": " << entries << " vs " << brentries;
70 }
71 }
72 if (entries > 0) {
73 LOG(info) << "Setting entries to " << entries;
74 outputtree->SetEntries(entries);
75 // outputtree->Write("", TObject::kOverwrite);
76 outputfile->Close();
77 }
78 };
79
80 //branch definitions for RootTreeWriter spec
81 using DigitsOutputType = std::vector<o2::tpc::Digit>;
82 using CommonModeOutputType = std::vector<o2::tpc::CommonMode>;
83
84 // extracts the sector from header of an input
85 auto extractSector = [](auto const& ref) {
86 auto sectorHeader = DataRefUtils::getHeader<o2::tpc::TPCSectorHeader*>(ref);
87 if (!sectorHeader) {
88 throw std::runtime_error("Missing sector header in TPC data");
89 }
90 // the TPCSectorHeader now allows to transport information for more than one sector,
91 // e.g. for transporting clusters in one single data block. The digitization is however
92 // only on sector level
93 if (sectorHeader->sector() >= TPCSectorHeader::NSectors) {
94 throw std::runtime_error("Digitizer can only work on single sectors");
95 }
96 return sectorHeader->sector();
97 };
98
99 // The generic writer needs a way to associate incoming data with the individual branches for
100 // the TPC sectors. The sector number is transmitted as part of the sector header, the callback
101 // finds the corresponding index in the vector of configured sectors
102 auto getIndex = [laneConfiguration, extractSector](o2::framework::DataRef const& ref) -> size_t {
103 auto sector = extractSector(ref);
104 if (sector < 0) {
105 // special data sets, don't write
106 return ~(size_t)0;
107 }
108 size_t index = 0;
109 for (auto const& s : laneConfiguration) {
110 if (sector == s) {
111 return index;
112 }
113 ++index;
114 }
115 throw std::runtime_error("sector " + std::to_string(sector) + " not configured for writing");
116 };
117
118 // callback to create branch name
119 auto getName = [laneConfiguration](std::string base, size_t index) -> std::string {
120 return base + "_" + std::to_string(laneConfiguration.at(index));
121 };
122
123 // container for cached grouping of digits
124 auto trigP2Sect = std::make_shared<std::array<std::vector<DigiGroupRef>, 36>>();
125
126 // preprocessor callback
127 // read the trigger data first and store in the trigP2Sect shared pointer
128 auto preprocessor = [extractSector, trigP2Sect](ProcessingContext& pc) {
129 for (auto& cont : *trigP2Sect) {
130 cont.clear();
131 }
132 std::vector<InputSpec> filter = {
133 {"check", ConcreteDataTypeMatcher{"TPC", "DIGTRIGGERS"}, Lifetime::Timeframe},
134 };
135 for (auto const& ref : InputRecordWalker(pc.inputs(), filter)) {
136 auto sector = extractSector(ref);
137 auto const* dh = DataRefUtils::getHeader<DataHeader*>(ref);
138 LOG(info) << "HAVE TRIGGER DATA FOR SECTOR " << sector << " ON CHANNEL " << dh->subSpecification;
139 if (sector >= 0) {
140 // extract the trigger information and make it available for the other handlers
141 auto triggers = pc.inputs().get<std::vector<DigiGroupRef>>(ref);
142 (*trigP2Sect)[sector].assign(triggers.begin(), triggers.end());
143 const auto& trigS = (*trigP2Sect)[sector];
144 LOG(info) << "GOT Triggers of sector " << sector << " | SIZE " << trigS.size();
145 }
146 }
147 };
148
149 // handler to fill the digit branch, this handles filling based on the trigger information, each trigger
150 // will be a new entry
151 auto fillDigits = [extractSector, trigP2Sect](TBranch& branch, DigitsOutputType const& digiData, DataRef const& ref) {
152 auto sector = extractSector(ref);
153 auto const* dh = DataRefUtils::getHeader<DataHeader*>(ref);
154 LOG(info) << "HAVE DIGIT DATA FOR SECTOR " << sector << " ON CHANNEL " << dh->subSpecification;
155 if (sector >= 0) {
156 LOG(info) << "DIGIT SIZE " << digiData.size();
157 const auto& trigS = (*trigP2Sect.get())[sector];
158 int entries = 0;
159 if (!trigS.size()) {
160 std::runtime_error("Digits for sector " + std::to_string(sector) + " are received w/o info on grouping in triggers");
161 } else { // check consistency of Ndigits with that of expected from the trigger
162 int nExp = trigS.back().getFirstEntry() + trigS.back().getEntries() - trigS.front().getFirstEntry();
163 if (nExp != digiData.size()) {
164 LOG(error) << "Number of digits " << digiData.size() << " is inconsistent with expectation " << nExp
165 << " from digits grouping for sector " << sector;
166 }
167 }
168
169 {
170 if (trigS.size() == 1) { // just 1 entry (continous mode?), use digits directly
171 auto ptr = &digiData;
172 branch.SetAddress(&ptr);
173 branch.Fill();
174 entries++;
175 branch.ResetAddress();
176 branch.DropBaskets("all");
177 } else { // triggered mode (>1 entries will be written)
178 std::vector<o2::tpc::Digit> digGroup; // group of digits related to single trigger
179 auto ptr = &digGroup;
180 branch.SetAddress(&ptr);
181 for (auto const& group : trigS) {
182 digGroup.clear();
183 for (int i = 0; i < group.getEntries(); i++) {
184 digGroup.emplace_back(digiData[group.getFirstEntry() + i]); // fetch digits of given trigger
185 }
186 branch.Fill();
187 entries++;
188 }
189 branch.ResetAddress();
190 branch.DropBaskets("all");
191 }
192 }
193 auto tree = branch.GetTree();
194 tree->SetEntries(entries);
195 tree->Write("", TObject::kOverwrite);
196 }
197 };
198
199 // handler for labels
200 // TODO: this is almost a copy of the above, reduce to a single methods with amends
201 auto fillLabels = [extractSector, trigP2Sect](TBranch& branch, std::vector<char> const& labelbuffer, DataRef const& ref) {
204 // first of all redefine the output format (special to labels)
205 auto tree = branch.GetTree();
206 auto sector = extractSector(ref);
207 auto ptr = &outputcontainer;
209
210 auto const* dh = DataRefUtils::getHeader<DataHeader*>(ref);
211 LOG(info) << "HAVE LABEL DATA FOR SECTOR " << sector << " ON CHANNEL " << dh->subSpecification;
212 int entries = 0;
213 if (sector >= 0) {
214 LOG(info) << "MCTRUTH ELEMENTS " << labeldata.getIndexedSize()
215 << " WITH " << labeldata.getNElements() << " LABELS";
216 const auto& trigS = (*trigP2Sect.get())[sector];
217 if (!trigS.size()) {
218 throw std::runtime_error("MCTruth for sector " + std::to_string(sector) + " are received w/o info on grouping in triggers");
219 } else {
220 int nExp = trigS.back().getFirstEntry() + trigS.back().getEntries() - trigS.front().getFirstEntry();
221 if (nExp != labeldata.getIndexedSize()) {
222 LOG(error) << "Number of indexed (label) slots " << labeldata.getIndexedSize()
223 << " is inconsistent with expectation " << nExp
224 << " from digits grouping for sector " << sector;
225 }
226 }
227 {
228 if (trigS.size() == 1) { // just 1 entry (continous mode?), use labels directly
229 outputcontainer.adopt(labelbuffer);
230 br->Fill();
231 br->ResetAddress();
232 br->DropBaskets("all");
233 entries = 1;
234 } else {
235 o2::dataformats::MCTruthContainer<o2::MCCompLabel> lblGroup; // labels for group of digits related to single trigger
236 for (auto const& group : trigS) {
237 lblGroup.clear();
238 for (int i = 0; i < group.getEntries(); i++) {
239 auto lbls = labeldata.getLabels(group.getFirstEntry() + i);
240 lblGroup.addElements(i, lbls);
241 }
242 // init the output container
243 std::vector<char> flatbuffer;
244 lblGroup.flatten_to(flatbuffer);
245 outputcontainer.adopt(flatbuffer);
246 br->Fill();
247 br->DropBaskets("all");
248 entries++;
249 }
250 br->ResetAddress();
251 }
252 }
253 tree->SetEntries(entries);
254 tree->Write("", TObject::kOverwrite);
255 }
256 };
257
258 // A spectator to print logging for the common mode data
259 auto commonModeSpectator = [extractSector](CommonModeOutputType const& commonModeData, DataRef const& ref) {
260 auto sector = extractSector(ref);
261 auto const* dh = DataRefUtils::getHeader<DataHeader*>(ref);
262 LOG(info) << "HAVE COMMON MODE DATA FOR SECTOR " << sector << " ON CHANNEL " << dh->subSpecification;
263 LOG(info) << "COMMON MODE SIZE " << commonModeData.size();
264 };
265
266 auto digitsdef = BranchDefinition<DigitsOutputType>{InputSpec{"digits", ConcreteDataTypeMatcher{"TPC", "DIGITS"}},
267 "TPCDigit", "digits-branch-name",
268 laneConfiguration.size(),
269 fillDigits,
270 getIndex,
271 getName};
272
273 auto labelsdef = BranchDefinition<std::vector<char>>{InputSpec{"labelinput", ConcreteDataTypeMatcher{"TPC", "DIGITSMCTR"}},
274 "TPCDigitMCTruth", "labels-branch-name",
275 // this branch definition is disabled if MC labels are not processed
276 (mctruth ? laneConfiguration.size() : 0),
277 fillLabels,
278 getIndex,
279 getName};
280
281 auto commddef = BranchDefinition<CommonModeOutputType>{InputSpec{"commonmode", ConcreteDataTypeMatcher{"TPC", "COMMONMODE"}},
282 "TPCCommonMode", "common-mode-branch-name",
283 laneConfiguration.size(),
284 commonModeSpectator,
285 getIndex,
286 getName};
287
288 return MakeRootTreeWriterSpec("TPCDigitWriter", "tpcdigits.root", "o2sim",
289 // the preprocessor reads the trigger info object and makes it available
290 // to the Fill handlers
292 // defining the input for the trigger object, as an auxiliary input it is
293 // not written to any branch
294 MakeRootTreeWriterSpec::AuxInputRoute{{"triggerinput", ConcreteDataTypeMatcher{"TPC", "DIGTRIGGERS"}}},
295 // setting a custom callback for closing the writer
297 // passing the branch configuration as argument pack
298 std::move(digitsdef), std::move(labelsdef), std::move(commddef))();
299}
300} // end namespace tpc
301} // end namespace o2
Definition of the common mode container class.
std::string getName(const TDataMember *dm, int index, int size)
A const (ready only) version of MCTruthContainer.
Definition of the TPC Digit.
o2::framework::DataAllocator::SubSpecificationType SubSpecificationType
int32_t i
A special IO container - splitting a given vector to enable ROOT IO.
A helper class to iteratate over all parts of all input routes.
Configurable generator for RootTreeWriter processor spec.
Class to refer to the 1st entry and N elements of some group in the continuous container.
TBranch * ptr
gsl::span< const TruthElement > getLabels(uint32_t dataindex) const
void adopt(gsl::span< const char > const input)
"adopt" (without taking ownership) from an existing buffer
A container to hold and manage MC truth information/labels.
void addElements(uint32_t dataindex, gsl::span< CompatibleLabel > elements)
size_t flatten_to(ContainerType &container) const
o2::header::DataHeader::SubSpecificationType SubSpecificationType
A helper class to iteratate over all parts of all input routes.
Generate a processor spec for the RootTreeWriter utility.
static TBranch * remapBranch(TBranch &branchRef, T **newdata)
GLuint entry
Definition glcorearb.h:5735
GLuint index
Definition glcorearb.h:781
GLboolean GLuint group
Definition glcorearb.h:3991
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition glcorearb.h:1308
GLint ref
Definition glcorearb.h:291
Defining PrimaryVertex explicitly as messageable.
Definition TFIDInfo.h:20
O2 data header classes and API, v0.1.
Definition DetID.h:49
DataProcessorSpec getTPCDigitRootWriterSpec(std::vector< int > const &laneConfiguration, bool mctruth)
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
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()))