Skip to content

Commit 49b3bb0

Browse files
shahor02sawenzel
authored andcommitted
TPC quasi-triggered mode with --TPCtriggered option
For every processed sector the the TPCDigitRootWriter will recieve a vector of RangeReference objects, telling how to group the digits and labels in the output tree entries. In default continuous RO mode this vector contains just 1 RangeReference, referring to the all entries in the digits/labels container, therefore it will be dumped as a single entry. In triggered mode, this vector will contain as many elements as MC interactions processed each RangeReference will refer to the digits/labels of particular event, which will be stored in separate entries. Each RangeReference contains the index of of the 1st digit entry and N digits for the group it refers to.
1 parent 17f2473 commit 49b3bb0

File tree

10 files changed

+310
-66
lines changed

10 files changed

+310
-66
lines changed

DataFormats/common/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ set(SRCS
1010
Set(HEADERS
1111
include/${MODULE_NAME}/TimeStamp.h
1212
include/${MODULE_NAME}/EvIndex.h
13+
include/${MODULE_NAME}/RangeReference.h
1314
include/${MODULE_NAME}/InteractionRecord.h
1415
include/${MODULE_NAME}/BunchFilling.h
1516
)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright CERN and copyright holders of ALICE O2. This software is
2+
// distributed under the terms of the GNU General Public License v3 (GPL
3+
// Version 3), copied verbatim in the file "COPYING".
4+
//
5+
// See http://alice-o2.web.cern.ch/license for full licensing information.
6+
//
7+
// In applying this license CERN does not waive the privileges and immunities
8+
// granted to it by virtue of its status as an Intergovernmental Organization
9+
// or submit itself to any jurisdiction.
10+
11+
/// \file RangeReference.h
12+
/// \brief Class to refered to the 1st entry and N elements of some group in the continuous container
13+
/// \author ruben.shahoyan@cern.ch
14+
15+
#ifndef ALICEO2_RANGEREFERENCE_H
16+
#define ALICEO2_RANGEREFERENCE_H
17+
18+
#include <Rtypes.h>
19+
20+
namespace o2
21+
{
22+
namespace dataformats
23+
{
24+
// Composed range reference
25+
26+
template <typename FirstEntry = int, typename NElem = int>
27+
class RangeReference
28+
{
29+
public:
30+
RangeReference(FirstEntry ent, NElem n) { set(ent, n); }
31+
RangeReference(const RangeReference<FirstEntry, NElem>& src) = default;
32+
RangeReference() = default;
33+
~RangeReference() = default;
34+
void set(FirstEntry ent, NElem n)
35+
{
36+
mFirstEntry = ent;
37+
mEntries = n;
38+
}
39+
FirstEntry getFirstEntry() const { return mFirstEntry; }
40+
NElem getEntries() const { return mEntries; }
41+
void setFirstEntry(FirstEntry ent) { mFirstEntry = ent; }
42+
void setEntries(NElem n) { mEntries = n; }
43+
void changeEntriesBy(NElem inc) { mEntries += inc; }
44+
45+
private:
46+
FirstEntry mFirstEntry; ///< 1st entry of the group
47+
NElem mEntries = 0; ///< number of entries
48+
49+
ClassDefNV(RangeReference, 1);
50+
};
51+
} // namespace dataformats
52+
} // namespace o2
53+
54+
#endif

DataFormats/common/src/CommonDataFormatLinkDef.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
#pragma link C++ class o2::dataformats::TimeStampWithError < int, int > +;
3131

3232
#pragma link C++ class o2::dataformats::EvIndex < int, int > +;
33+
#pragma link C++ class o2::dataformats::RangeReference < int, int > +;
34+
#pragma link C++ class o2::dataformats::RangeReference < o2::dataformats::EvIndex < int, int >, int > +;
35+
3336
#pragma link C++ class o2::InteractionRecord + ;
3437
#pragma link C++ class o2::BunchFilling + ;
3538

Detectors/TPC/simulation/include/TPCSimulation/DigitizerTask.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ class DigitizerTask : public FairTask
6363
/// \param isContinuous - false for triggered readout, true for continuous readout
6464
void setContinuousReadout(bool isContinuous);
6565

66+
/// query if the r/o mode is continuous
67+
bool isContinuousReadout() const { return mIsContinuousReadout; }
68+
6669
/// Enable the use of space-charge distortions
6770
/// \param distortionType select the type of space-charge distortions (constant or realistic)
6871
/// \param hisInitialSCDensity optional space-charge density histogram to use at the beginning of the simulation

Detectors/TPC/simulation/include/TPCSimulation/HitDriftFilter.h

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,69 @@ void getHits(TChain& chain, const Collection& eventrecords, std::vector<std::vec
8080
}
8181
}
8282

83+
inline void getHits(std::vector<TChain*> const& chains, const o2::steer::RunContext& runcontext,
84+
std::vector<std::vector<o2::TPC::HitGroup>*>& hitvectors,
85+
std::vector<o2::TPC::TPCHitGroupID>& hitids, const std::string_view branchname,
86+
int collision)
87+
{
88+
// Collect hits of single collision in the run context
89+
hitids.clear();
90+
91+
// helper to fetch right branch
92+
auto fetchBranch = [chains, branchname](int source) -> TBranch* {
93+
if (!chains[source]) {
94+
return nullptr;
95+
}
96+
auto& chain = *chains[source];
97+
auto br = chain.GetBranch(branchname.data());
98+
if (!br) {
99+
return nullptr;
100+
}
101+
return br;
102+
};
103+
104+
// number of collision given by runcontext
105+
const auto& eventrecords = runcontext.getEventRecords();
106+
auto ncollisions = eventrecords.size();
107+
if (collision >= ncollisions) {
108+
LOG(ERROR) << "runContext contains" << ncollisions << " collisions, " << collision << " is requested";
109+
return;
110+
}
111+
const auto& parts = runcontext.getEventParts();
112+
const auto maxpartspercollision = runcontext.getMaxNumberParts();
113+
114+
const auto& theseparts = parts[collision];
115+
for (int p = 0; p < theseparts.size(); ++p) {
116+
// compute where to store the hits
117+
const auto eventID = theseparts[p].entryID;
118+
const auto source = theseparts[p].sourceID;
119+
const auto storeentry = eventID * maxpartspercollision + source;
120+
121+
// retrieve correct branch and fill the vector
122+
// This needs to be done only once for any entry per chain
123+
// retrieve source
124+
if (hitvectors[storeentry] == nullptr) {
125+
// TODO: instead of trying to fetch all the time .. do this outside and cache
126+
auto br = fetchBranch(source);
127+
br->SetAddress(&hitvectors[storeentry]);
128+
br->GetEntry(eventID);
129+
}
130+
131+
int groupid = -1;
132+
auto groups = hitvectors[storeentry];
133+
for (auto& singlegroup : *groups) {
134+
if (singlegroup.getSize() == 0) {
135+
// there are not hits in this group .. so continue
136+
// TODO: figure out why such a group would exist??
137+
continue;
138+
}
139+
groupid++;
140+
// need to record index of the group
141+
hitids.emplace_back(storeentry, collision, eventID, groupid, source);
142+
} // end loop over entries
143+
}
144+
}
145+
83146
inline void getHits(std::vector<TChain*> const& chains, const o2::steer::RunContext& runcontext,
84147
std::vector<std::vector<o2::TPC::HitGroup>*>& hitvectors,
85148
std::vector<o2::TPC::TPCHitGroupID>& hitids, const std::string_view branchname, float tmin /*NS*/,

Detectors/TPC/simulation/src/Digitizer.cxx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ DigitContainer* Digitizer::Process(const Sector& sector, const std::vector<o2::T
5959
if (!mIsContinuous) {
6060
eventTime = 0.f;
6161
}
62-
6362
/// TODO: if eventtime-lastUpdate>=one space-charge time slice
6463
/// 1) Propagate current space-charge density
6564
/// 2) recalculate distortion lookup tables with updated space-charge density
@@ -79,7 +78,8 @@ DigitContainer* Digitizer::Process2(const Sector& sector, const std::vector<std:
7978
const auto hitvector = hits[id.storeindex];
8079
auto& group = (*hitvector)[id.groupID];
8180
auto& MCrecord = interactRecords[id.entry];
82-
ProcessHitGroup(group, sector, MCrecord.timeNS * 0.001f, id.entry, id.sourceID);
81+
float evTime = mIsContinuous ? MCrecord.timeNS * 0.001f : 0.f;
82+
ProcessHitGroup(group, sector, evTime, id.entry, id.sourceID);
8383
}
8484

8585
return mDigitContainer;

Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -339,8 +339,7 @@ DataProcessorSpec getITSDigitizerSpec(int channel)
339339
{ "simFile", VariantType::String, "o2sim.root", { "Sim (background) input filename" } },
340340
{ "simFileS", VariantType::String, "", { "Sim (signal) input filename" } },
341341
{ "simFileQED", VariantType::String, "", { "Sim (QED) input filename" } },
342-
{ (detStr + "triggered").c_str(), VariantType::Bool, false,
343-
{ "Impose triggered RO mode (default: continuous)" } } } };
342+
{ (detStr + "triggered").c_str(), VariantType::Bool, false, { "Impose triggered RO mode (default: continuous)" } } } };
344343
}
345344

346345
DataProcessorSpec getMFTDigitizerSpec(int channel)
@@ -361,8 +360,7 @@ DataProcessorSpec getMFTDigitizerSpec(int channel)
361360
{ "simFile", VariantType::String, "o2sim.root", { "Sim (background) input filename" } },
362361
{ "simFileS", VariantType::String, "", { "Sim (signal) input filename" } },
363362
{ "simFileQED", VariantType::String, "", { "Sim (QED) input filename" } },
364-
{ (detStr + "triggered").c_str(), VariantType::Bool, false,
365-
{ "Impose triggered RO mode (default: continuous)" } } } };
363+
{ (detStr + "triggered").c_str(), VariantType::Bool, false, { "Impose triggered RO mode (default: continuous)" } } } };
366364
}
367365

368366
} // end namespace ITSMFT

Steer/DigitizerWorkflow/src/TPCDigitRootWriterSpec.cxx

Lines changed: 91 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include "TPCDigitRootWriterSpec.h"
1717
#include "DataFormatsTPC/TPCSectorHeader.h"
18+
#include "CommonDataFormat/RangeReference.h"
1819
#include "Framework/CallbackService.h"
1920
#include "Framework/ControlService.h"
2021
#include "TPCBase/Sector.h"
@@ -29,9 +30,11 @@
2930
#include <sstream>
3031
#include <string>
3132
#include <vector>
33+
#include <utility>
3234

3335
using namespace o2::framework;
3436
using SubSpecificationType = o2::framework::DataAllocator::SubSpecificationType;
37+
using DigiGroupRef = o2::dataformats::RangeReference<int, int>;
3538

3639
namespace o2
3740
{
@@ -59,31 +62,36 @@ DataProcessorSpec getTPCDigitRootWriterSpec(int numberofsourcedevices)
5962
{
6063
// assign input names to each channel
6164
auto digitchannelname = std::make_shared<std::vector<std::string>>();
65+
auto triggerchannelname = std::make_shared<std::vector<std::string>>();
6266
auto labelchannelname = std::make_shared<std::vector<std::string>>();
6367
for (int i = 0; i < numberofsourcedevices; ++i) {
6468
{
6569
std::stringstream ss;
6670
ss << "digitinput" << i;
6771
digitchannelname->push_back(ss.str());
6872
}
73+
{
74+
std::stringstream ss;
75+
ss << "triggerinput" << i;
76+
triggerchannelname->push_back(ss.str());
77+
}
6978
{
7079
std::stringstream ss;
7180
ss << "labelinput" << i;
7281
labelchannelname->push_back(ss.str());
7382
}
7483
}
7584

76-
auto initFunction = [numberofsourcedevices, digitchannelname, labelchannelname](InitContext& ic) {
85+
auto initFunction = [numberofsourcedevices, digitchannelname, labelchannelname, triggerchannelname](InitContext& ic) {
7786
// get the option from the init context
7887
auto filename = ic.options().get<std::string>("tpc-digit-outfile");
7988
auto treename = ic.options().get<std::string>("treename");
8089

8190
auto outputfile = std::make_shared<TFile>(filename.c_str(), "RECREATE");
8291
auto outputtree = std::make_shared<TTree>(treename.c_str(), treename.c_str());
8392

84-
// container for incoming digits + label
85-
auto digits = std::make_shared<std::vector<o2::TPC::Digit>>();
86-
auto labels = std::make_shared<o2::dataformats::MCTruthContainer<o2::MCCompLabel>>();
93+
// container for cashed grouping of digits
94+
auto trigP2Sect = std::make_shared<std::array<std::vector<DigiGroupRef>, 36>>();
8795

8896
// the callback to be set as hook at stop of processing for the framework
8997
auto finishWriting = [outputfile, outputtree]() {
@@ -116,8 +124,8 @@ DataProcessorSpec getTPCDigitRootWriterSpec(int numberofsourcedevices)
116124
// using by-copy capture of the worker instance shared pointer
117125
// the shared pointer makes sure to clean up the instance when the processing
118126
// function gets out of scope
119-
auto processingFct = [outputfile, outputtree, digits, digitchannelname, labelchannelname,
120-
numberofsourcedevices](ProcessingContext& pc) {
127+
auto processingFct = [outputfile, outputtree, trigP2Sect, digitchannelname, labelchannelname,
128+
triggerchannelname, numberofsourcedevices](ProcessingContext& pc) {
121129
static bool finished = false;
122130
if (finished) {
123131
// avoid being executed again when marked as finished;
@@ -130,9 +138,11 @@ DataProcessorSpec getTPCDigitRootWriterSpec(int numberofsourcedevices)
130138
// need to record which channel has completed in order to decide when we can shutdown
131139
static std::vector<bool> digitsdone;
132140
static std::vector<bool> labelsdone;
141+
static std::vector<bool> triggersdone;
133142
if (invocation == 1) {
134143
digitsdone.resize(numberofsourcedevices, false);
135144
labelsdone.resize(numberofsourcedevices, false);
145+
triggersdone.resize(numberofsourcedevices, false);
136146
}
137147

138148
// find out if all source devices (channels) are done
@@ -157,6 +167,19 @@ DataProcessorSpec getTPCDigitRootWriterSpec(int numberofsourcedevices)
157167
for (int d = 0; d < numberofsourcedevices; ++d) {
158168
const auto dname = digitchannelname->operator[](d);
159169
const auto lname = labelchannelname->operator[](d);
170+
const auto tname = triggerchannelname->operator[](d);
171+
if (pc.inputs().isValid(tname.c_str())) {
172+
sector = extractSector(tname.c_str());
173+
LOG(INFO) << "HAVE TRIGGER DATA FOR SECTOR " << sector << " ON CHANNEL " << d;
174+
if (sector == -1) {
175+
triggersdone[d] = true;
176+
} else {
177+
auto triggers = pc.inputs().get<std::vector<DigiGroupRef>>(tname.c_str());
178+
(*trigP2Sect.get())[sector] = std::move(triggers);
179+
const auto& trigS = (*trigP2Sect.get())[sector];
180+
LOG(INFO) << "GOT Triggers of sector " << sector << " | SIZE " << trigS.size();
181+
}
182+
}
160183

161184
// probe which channel has data and of what kind
162185
// a) probe for digits:
@@ -168,14 +191,39 @@ DataProcessorSpec getTPCDigitRootWriterSpec(int numberofsourcedevices)
168191
} else {
169192
// have to do work ...
170193
// the digits
171-
auto indata = pc.inputs().get<std::vector<o2::TPC::Digit>>(dname.c_str());
172-
LOG(INFO) << "DIGIT SIZE " << indata.size();
173-
*digits.get() = std::move(indata);
194+
auto digiData = pc.inputs().get<std::vector<o2::TPC::Digit>>(dname.c_str());
195+
LOG(INFO) << "DIGIT SIZE " << digiData.size();
196+
const auto& trigS = (*trigP2Sect.get())[sector];
197+
if (!trigS.size()) {
198+
LOG(FATAL) << "Digits for sector " << sector << " are received w/o info on grouping in triggers";
199+
} else { // check consistency of Ndigits with that of expected from the trigger
200+
int nExp = trigS.back().getFirstEntry() + trigS.back().getEntries() - trigS.front().getFirstEntry();
201+
if (nExp != digiData.size()) {
202+
LOG(ERROR) << "Number of digits " << digiData.size() << " is inconsistent with expectation " << nExp
203+
<< " from digits grouping for sector " << sector;
204+
}
205+
}
206+
174207
{
175-
// connect this to a particular branch
176-
auto br = getOrMakeBranch(*outputtree.get(), "TPCDigit", sector, digits.get());
177-
br->Fill();
178-
br->ResetAddress();
208+
if (trigS.size() == 1) { // just 1 entry (continous mode?), use digits directly
209+
// connect this to a particular branch
210+
auto digP = &digiData;
211+
auto br = getOrMakeBranch(*outputtree.get(), "TPCDigit", sector, digP);
212+
br->Fill();
213+
br->ResetAddress();
214+
} else { // triggered mode (>1 entrie will be written)
215+
std::vector<o2::TPC::Digit> digGroup; // group of digits related to single trigger
216+
auto digGroupPtr = &digGroup;
217+
auto br = getOrMakeBranch(*outputtree.get(), "TPCDigit", sector, digGroupPtr);
218+
for (auto grp : trigS) {
219+
digGroup.clear();
220+
for (int i = 0; i < grp.getEntries(); i++) {
221+
digGroup.emplace_back(digiData[grp.getFirstEntry() + i]); // fetch digits of given trigger
222+
}
223+
br->Fill();
224+
}
225+
br->ResetAddress();
226+
}
179227
}
180228
}
181229
} // end digit case
@@ -191,15 +239,36 @@ DataProcessorSpec getTPCDigitRootWriterSpec(int numberofsourcedevices)
191239
auto labeldata = pc.inputs().get<o2::dataformats::MCTruthContainer<o2::MCCompLabel>*>(lname.c_str());
192240
auto labeldataRaw = labeldata.get();
193241
LOG(INFO) << "MCTRUTH ELEMENTS " << labeldataRaw->getIndexedSize()
194-
<< " WITH " << labeldataRaw->getNElements() << " LABELS ";
195-
if (labeldataRaw->getIndexedSize() != digits->size()) {
196-
LOG(WARNING) << "Inconsistent number of indexed (label) slots "
197-
<< labeldataRaw->getIndexedSize() << " versus digits " << digits->size();
242+
<< " WITH " << labeldataRaw->getNElements() << " LABELS";
243+
const auto& trigS = (*trigP2Sect.get())[sector];
244+
if (!trigS.size()) {
245+
LOG(FATAL) << "MCTruth for sector " << sector << " are received w/o info on grouping in triggers";
246+
} else {
247+
int nExp = trigS.back().getFirstEntry() + trigS.back().getEntries() - trigS.front().getFirstEntry();
248+
if (nExp != labeldataRaw->getIndexedSize()) {
249+
LOG(ERROR) << "Number of indexed (label) slots " << labeldataRaw->getIndexedSize()
250+
<< " is inconsistent with expectation " << nExp
251+
<< " from digits grouping for sector " << sector;
252+
}
198253
}
199254
{
200-
auto br = getOrMakeBranch(*outputtree.get(), "TPCDigitMCTruth", sector, &labeldataRaw);
201-
br->Fill();
202-
br->ResetAddress();
255+
if (trigS.size() == 1) { // just 1 entry (continous mode?), use labels directly
256+
auto br = getOrMakeBranch(*outputtree.get(), "TPCDigitMCTruth", sector, &labeldataRaw);
257+
br->Fill();
258+
br->ResetAddress();
259+
} else {
260+
o2::dataformats::MCTruthContainer<o2::MCCompLabel> lblGroup; // labels for group of digits related to single trigger
261+
auto lblGroupPtr = &lblGroup;
262+
auto br = getOrMakeBranch(*outputtree.get(), "TPCDigitMCTruth", sector, &lblGroupPtr);
263+
for (auto grp : trigS) {
264+
lblGroup.clear();
265+
for (int i = 0; i < grp.getEntries(); i++) {
266+
auto lbls = labeldataRaw->getLabels(grp.getFirstEntry() + i);
267+
lblGroup.addElements(i, lbls);
268+
}
269+
br->Fill();
270+
}
271+
}
203272
}
204273
}
205274
} // end label case
@@ -231,6 +300,8 @@ DataProcessorSpec getTPCDigitRootWriterSpec(int numberofsourcedevices)
231300
for (int d = 0; d < numberofsourcedevices; ++d) {
232301
inputs.emplace_back(InputSpec{ (*digitchannelname.get())[d].c_str(), "TPC", "DIGITS",
233302
static_cast<SubSpecificationType>(d), Lifetime::Timeframe }); // digit input
303+
inputs.emplace_back(InputSpec{ (*triggerchannelname.get())[d].c_str(), "TPC", "DIGTRIGGERS",
304+
static_cast<SubSpecificationType>(d), Lifetime::Timeframe }); // groupping in triggers
234305
inputs.emplace_back(InputSpec{ (*labelchannelname.get())[d].c_str(), "TPC", "DIGITSMCTR",
235306
static_cast<SubSpecificationType>(d), Lifetime::Timeframe });
236307
}

0 commit comments

Comments
 (0)