Skip to content

Commit e206155

Browse files
committed
Optionally prefer ROFs with physics triggers with downscaling ITS reco
With fastMultConfig.preferTriggered=true (default) the random preselection of ITS ROFs to process will prefer ROFs with most of physics triggers. In the raw-data workflow these triggers are provided by the its-stf-decoder. In cluster / digits based workflows we don't have physicsl triggers, therefore cluster-reader and digit-readers are pushing empty triggers vector to the output (can be suppressed by --suppress-triggers-output).
1 parent 3b7c793 commit e206155

23 files changed

Lines changed: 208 additions & 106 deletions

File tree

DataFormats/Detectors/ITSMFT/common/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ o2_target_root_dictionary(DataFormatsITSMFT
3434
include/DataFormatsITSMFT/ClusterTopology.h
3535
include/DataFormatsITSMFT/TopologyDictionary.h
3636
include/DataFormatsITSMFT/CTF.h
37-
include/DataFormatsITSMFT/TrkClusRef.h
37+
include/DataFormatsITSMFT/TrkClusRef.h
38+
include/DataFormatsITSMFT/PhysTrigger.h
3839
LINKDEF src/ITSMFTDataFormatsLinkDef.h)
3940

4041
o2_add_test(Cluster
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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+
/// \file PhysTrigger.h
13+
/// \brief Definition Physics trigger record extracted from the ITS/MFT stream
14+
15+
#ifndef ALICEO2_ITSMFT_PHYSTRIGGER_H
16+
#define ALICEO2_ITSMFT_PHYSTRIGGER_H
17+
18+
#include "CommonDataFormat/InteractionRecord.h"
19+
20+
namespace o2::itsmft
21+
{
22+
// root friendly version of the trigger (root does not support anonymous structs)
23+
struct PhysTrigger {
24+
o2::InteractionRecord ir;
25+
uint16_t triggerType = 0;
26+
bool internal = false;
27+
bool noData = false;
28+
29+
ClassDefNV(PhysTrigger, 1);
30+
};
31+
32+
} // namespace o2::itsmft
33+
34+
#endif

DataFormats/Detectors/ITSMFT/common/src/ITSMFTDataFormatsLinkDef.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@
4141
#pragma link C++ class o2::itsmft::TrkClusRef + ;
4242
#pragma link C++ class std::vector < o2::itsmft::TrkClusRef> + ;
4343

44+
#pragma link C++ class o2::itsmft::PhysTrigger + ;
45+
#pragma link C++ class std::vector < o2::itsmft::PhysTrigger> + ;
46+
4447
#pragma link C++ class o2::itsmft::CTFHeader + ;
4548
#pragma link C++ class o2::itsmft::CompressedClusters + ;
4649
#pragma link C++ class o2::itsmft::CTF + ;

Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEst.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
#define ALICEO2_ITS_FASTMULTEST_
1818

1919
#include "ITSMFTReconstruction/ChipMappingITS.h"
20+
#include "DataFormatsITSMFT/ROFRecord.h"
2021
#include "DataFormatsITSMFT/CompCluster.h"
22+
#include <DataFormatsITSMFT/PhysTrigger.h>
2123
#include "ITSReconstruction/FastMultEstConfig.h"
2224
#include <gsl/span>
2325
#include <array>
@@ -38,6 +40,9 @@ struct FastMultEst {
3840
int nLayersUsed = 0; /// number of layers actually used
3941
std::array<int, NLayers> nClPerLayer{0}; // measured N Cl per layer
4042

43+
int selectROFs(const gsl::span<const o2::itsmft::ROFRecord> rofs, const gsl::span<const o2::itsmft::CompClusterExt> clus,
44+
const gsl::span<const o2::itsmft::PhysTrigger> trig, std::vector<bool>& sel);
45+
4146
void fillNClPerLayer(const gsl::span<const o2::itsmft::CompClusterExt>& clusters);
4247
float process(const std::array<int, NLayers> ncl)
4348
{

Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEstConfig.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ struct FastMultEstConfig : public o2::conf::ConfigurableParamHelper<FastMultEstC
3939
float cutMultVtxLow = -1; /// reject seed vertex if its multiplicity below this value (no cut if <0)
4040
float cutMultVtxHigh = -1; /// reject seed vertex if its multiplicity above this value (no cut if <0)
4141
float cutRandomFraction = -1.; /// apply random cut rejecting requested fraction
42+
bool preferTriggered = true; /// prefer ROFs with highest number of physics triggers
4243

4344
bool isMultCutRequested() const { return cutMultClusLow >= 0.f && cutMultClusHigh > 0.f; };
4445
bool isVtxMultCutRequested() const { return cutMultVtxLow >= 0.f && cutMultVtxHigh > 0.f; };

Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
/// \author ruben.shahoyan@cern.ch
1515

1616
#include "ITSReconstruction/FastMultEst.h"
17-
#include <cstring>
17+
#include "ITSMFTBase/DPLAlpideParam.h"
1818
#include "Framework/Logger.h"
19+
#include <cstring>
20+
#include <TRandom.h>
1921

2022
using namespace o2::its;
2123

@@ -127,3 +129,66 @@ float FastMultEst::processNoiseImposed(const std::array<int, NLayers> ncl)
127129
chi2 = nLayersUsed > 2 ? chi2 / (nLayersUsed - 2) : 0.;
128130
return mult > 0 ? mult : 0;
129131
}
132+
133+
int FastMultEst::selectROFs(const gsl::span<const o2::itsmft::ROFRecord> rofs, const gsl::span<const o2::itsmft::CompClusterExt> clus,
134+
const gsl::span<const o2::itsmft::PhysTrigger> trig, std::vector<bool>& sel)
135+
{
136+
int nrof = rofs.size(), nsel = 0;
137+
const auto& multEstConf = FastMultEstConfig::Instance(); // parameters for mult estimation and cuts
138+
sel.clear();
139+
sel.resize(nrof, true); // by default select all
140+
141+
if (multEstConf.isMultCutRequested()) {
142+
for (uint32_t irof = 0; irof < nrof; irof++) {
143+
nsel += sel[irof] = multEstConf.isPassingMultCut(process(rofs[irof].getROFData(clus)));
144+
}
145+
} else {
146+
nsel = nrof;
147+
}
148+
using IdNT = std::pair<int, int>;
149+
if (multEstConf.cutRandomFraction > 0.) {
150+
int ntrig = trig.size(), currTrig = 0;
151+
if (multEstConf.preferTriggered) {
152+
const auto& alpParams = o2::itsmft::DPLAlpideParam<o2::detectors::DetID::ITS>::Instance();
153+
std::vector<IdNT> nTrigROF;
154+
nTrigROF.reserve(nrof);
155+
for (uint32_t irof = 0; irof < nrof; irof++) {
156+
if (sel[irof]) {
157+
if (nsel && gRandom->Rndm() < multEstConf.cutRandomFraction) {
158+
nsel--;
159+
}
160+
auto irROF = rofs[irof].getBCData();
161+
while (currTrig < ntrig && trig[currTrig].ir < irROF) { // triggers are sorted, jump to 1st one not less than current ROF
162+
currTrig++;
163+
}
164+
auto& trof = nTrigROF.emplace_back(irof, 0);
165+
irROF += alpParams.roFrameLengthInBC;
166+
while (currTrig < ntrig && trig[currTrig].ir < irROF) {
167+
trof.second++;
168+
currTrig++;
169+
}
170+
}
171+
}
172+
if (nsel > 0) {
173+
sort(nTrigROF.begin(), nTrigROF.end(), [](const IdNT& a, const IdNT& b) { return a.second > b.second; }); // order in number of triggers
174+
auto last = nTrigROF.begin() + nsel;
175+
sort(nTrigROF.begin(), last, [](const IdNT& a, const IdNT& b) { return a.first < b.first; }); // order in ROF ID first nsel ROFs
176+
}
177+
for (int i = nsel; i < int(nTrigROF.size()); i++) { // reject ROFs in the tail
178+
sel[nTrigROF[i].first] = false;
179+
}
180+
} else { // dummy random rejection
181+
for (int irof = 0; irof < nrof; irof++) {
182+
if (sel[irof]) {
183+
if (gRandom->Rndm() < multEstConf.cutRandomFraction) {
184+
sel[irof] = false;
185+
nsel--;
186+
}
187+
}
188+
}
189+
}
190+
}
191+
LOGP(debug, "NSel = {} of {} rofs", nsel, nrof);
192+
193+
return nsel;
194+
}

Detectors/ITSMFT/ITS/workflow/src/CookedTrackerSpec.cxx

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "SimulationDataFormat/MCCompLabel.h"
2626
#include "SimulationDataFormat/MCTruthContainer.h"
2727
#include "DataFormatsITSMFT/ROFRecord.h"
28+
#include <DataFormatsITSMFT/PhysTrigger.h>
2829
#include "ITSMFTBase/DPLAlpideParam.h"
2930

3031
#include "Field/MagneticField.h"
@@ -77,6 +78,8 @@ void CookedTrackerDPL::run(ProcessingContext& pc)
7778
// the output vector however is created directly inside the message memory thus avoiding copy by
7879
// snapshot
7980
auto rofsinput = pc.inputs().get<gsl::span<o2::itsmft::ROFRecord>>("ROframes");
81+
auto physTriggers = pc.inputs().get<gsl::span<o2::itsmft::PhysTrigger>>("phystrig");
82+
8083
auto& rofs = pc.outputs().make<std::vector<o2::itsmft::ROFRecord>>(Output{"ITS", "ITSTrackROF", 0, Lifetime::Timeframe}, rofsinput.begin(), rofsinput.end());
8184

8285
std::unique_ptr<const o2::dataformats::MCTruthContainer<o2::MCCompLabel>> labels;
@@ -86,8 +89,6 @@ void CookedTrackerDPL::run(ProcessingContext& pc)
8689
// get the array as read-onlt span, a snapshot is send forward
8790
mc2rofs = pc.inputs().get<gsl::span<itsmft::MC2ROFRecord>>("MC2ROframes");
8891
}
89-
const auto& multEstConf = FastMultEstConfig::Instance(); // parameters for mult estimation and cuts
90-
FastMultEst multEst; // mult estimator
9192
TimeFrame mTimeFrame;
9293

9394
LOG(info) << "ITSCookedTracker pulled " << compClusters.size() << " clusters, in " << rofs.size() << " RO frames";
@@ -114,25 +115,11 @@ void CookedTrackerDPL::run(ProcessingContext& pc)
114115
gsl::span<itsmft::ROFRecord> rofspan(rofs);
115116
mTimeFrame.loadROFrameData(rofspan, compClusters, pattIt_timeframe, mDict, labels.get());
116117

118+
const auto& multEstConf = FastMultEstConfig::Instance(); // parameters for mult estimation and cuts
119+
FastMultEst multEst; // mult estimator
117120
std::vector<bool> processingMask;
118-
int cutRandomMult{0}, cutClusterMult{0}, cutVertexMult{0};
121+
int cutVertexMult{0}, cutRandomMult = multEst.selectROFs(rofsinput, compClusters, physTriggers, processingMask);
119122

120-
for (size_t iRof{0}; iRof < rofspan.size(); ++iRof) {
121-
auto& rof = rofspan[iRof];
122-
bool selROF = multEstConf.isPassingRandomRejection();
123-
if (!selROF) {
124-
cutRandomMult++;
125-
} else if (multEstConf.isMultCutRequested()) { // cut was requested
126-
float mult = multEst.process(rof.getROFData(compClusters));
127-
selROF = multEstConf.isPassingMultCut(mult);
128-
if (!selROF) {
129-
LOG(info) << "Estimated cluster mult. " << mult << " is outside of requested range "
130-
<< multEstConf.cutMultClusLow << " : " << multEstConf.cutMultClusHigh << " | ROF " << rof.getBCData();
131-
}
132-
cutClusterMult += !selROF;
133-
}
134-
processingMask.push_back(selROF);
135-
}
136123
// auto processingMask_ephemeral = processingMask;
137124
mTimeFrame.setMultiplicityCutMask(processingMask);
138125
float vertexerElapsedTime;
@@ -195,7 +182,7 @@ void CookedTrackerDPL::run(ProcessingContext& pc)
195182
irFrames.emplace_back(rof.getBCData(), rof.getBCData() + nBCPerTF - 1).info = tracks.size();
196183
}
197184
}
198-
185+
LOGP(info, " - rejected {}/{} ROFs: random/mult.sel:{}, vtx.sel:{}", cutRandomMult + cutVertexMult, rofspan.size(), cutRandomMult, cutVertexMult);
199186
LOG(info) << "ITSCookedTracker pushed " << tracks.size() << " tracks and " << vertices.size() << " vertices";
200187

201188
if (mUseMC) {
@@ -268,6 +255,7 @@ DataProcessorSpec getCookedTrackerSpec(bool useMC, const std::string& trMode)
268255
inputs.emplace_back("ROframes", "ITS", "CLUSTERSROF", 0, Lifetime::Timeframe);
269256
inputs.emplace_back("cldict", "ITS", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/ClusterDictionary"));
270257
inputs.emplace_back("alppar", "ITS", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam"));
258+
inputs.emplace_back("phystrig", "ITS", "PHYSTRIG", 0, Lifetime::Timeframe);
271259

272260
std::vector<OutputSpec> outputs;
273261
outputs.emplace_back("ITS", "TRACKS", 0, Lifetime::Timeframe);

Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ framework::WorkflowSpec getWorkflow(bool useMC, bool useCAtracker, const std::st
3636
framework::WorkflowSpec specs;
3737

3838
if (!(upstreamDigits || upstreamClusters)) {
39-
specs.emplace_back(o2::itsmft::getITSDigitReaderSpec(useMC, false, "itsdigits.root"));
39+
specs.emplace_back(o2::itsmft::getITSDigitReaderSpec(useMC, false, true, "itsdigits.root"));
4040
}
4141

4242
if (!upstreamClusters) {

Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "SimulationDataFormat/MCCompLabel.h"
2525
#include "SimulationDataFormat/MCTruthContainer.h"
2626
#include "DataFormatsITSMFT/ROFRecord.h"
27+
#include "DataFormatsITSMFT/PhysTrigger.h"
2728

2829
#include "ITStracking/ROframe.h"
2930
#include "ITStracking/IOUtils.h"
@@ -138,7 +139,7 @@ void TrackerDPL::run(ProcessingContext& pc)
138139
updateTimeDependentParams(pc);
139140
auto compClusters = pc.inputs().get<gsl::span<o2::itsmft::CompClusterExt>>("compClusters");
140141
gsl::span<const unsigned char> patterns = pc.inputs().get<gsl::span<unsigned char>>("patterns");
141-
142+
auto physTriggers = pc.inputs().get<gsl::span<o2::itsmft::PhysTrigger>>("phystrig");
142143
// code further down does assignment to the rofs and the altered object is used for output
143144
// we therefore need a copy of the vector rather than an object created directly on the input data,
144145
// the output vector however is created directly inside the message memory thus avoiding copy by
@@ -175,13 +176,11 @@ void TrackerDPL::run(ProcessingContext& pc)
175176

176177
bool continuous = o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::ITS);
177178
LOG(info) << "ITSTracker RO: continuous=" << continuous;
178-
const auto& multEstConf = FastMultEstConfig::Instance(); // parameters for mult estimation and cuts
179-
FastMultEst multEst; // mult estimator
180179
TimeFrame* timeFrame = mChainITS->GetITSTimeframe();
181180
mTracker->adoptTimeFrame(*timeFrame);
181+
182182
mTracker->setBz(o2::base::Propagator::Instance()->getNominalBz());
183183
mVertexer->adoptTimeFrame(*timeFrame);
184-
185184
gsl::span<const unsigned char>::iterator pattIt = patterns.begin();
186185

187186
gsl::span<itsmft::ROFRecord> rofspan(rofs);
@@ -191,31 +190,17 @@ void TrackerDPL::run(ProcessingContext& pc)
191190
auto logger = [&](std::string s) { LOG(info) << s; };
192191
auto errorLogger = [&](std::string s) { LOG(error) << s; };
193192

193+
FastMultEst multEst; // mult estimator
194194
std::vector<bool> processingMask;
195-
int cutRandomMult{0}, cutClusterMult{0}, cutVertexMult{0};
196-
for (size_t iRof{0}; iRof < rofspan.size(); ++iRof) {
197-
auto& rof = rofspan[iRof];
198-
bool selROF = multEstConf.isPassingRandomRejection();
199-
if (!selROF) {
200-
cutRandomMult++;
201-
} else if (multEstConf.isMultCutRequested()) { // cut was requested
202-
float mult = multEst.process(rof.getROFData(compClusters));
203-
selROF = multEstConf.isPassingMultCut(mult);
204-
if (!selROF) {
205-
LOG(debug) << fmt::format("ROF {} rejected by the cluster multiplicity selection [{},{}]", processingMask.size(), multEstConf.cutMultClusLow, multEstConf.cutMultClusHigh);
206-
}
207-
cutClusterMult += !selROF;
208-
}
209-
processingMask.push_back(selROF);
210-
}
195+
int cutVertexMult{0}, cutRandomMult = multEst.selectROFs(rofs, compClusters, physTriggers, processingMask);
211196
timeFrame->setMultiplicityCutMask(processingMask);
212197

213198
float vertexerElapsedTime{0.f};
214199
if (mRunVertexer) {
215200
// Run seeding vertexer
216201
vertexerElapsedTime = mVertexer->clustersToVertices(false, logger);
217202
}
218-
203+
const auto& multEstConf = FastMultEstConfig::Instance(); // parameters for mult estimation and cuts
219204
for (auto iRof{0}; iRof < rofspan.size(); ++iRof) {
220205
std::vector<Vertex> vtxVecLoc;
221206
auto& vtxROF = vertROFvec.emplace_back(rofspan[iRof]);
@@ -249,7 +234,7 @@ void TrackerDPL::run(ProcessingContext& pc)
249234
timeFrame->addPrimaryVertices(vtxVecLoc);
250235
}
251236
}
252-
LOG(info) << fmt::format(" - rejected {}/{} ROFs: random:{}, mult.sel:{}, vtx.sel:{}", cutRandomMult + cutClusterMult + cutVertexMult, rofspan.size(), cutRandomMult, cutClusterMult, cutVertexMult);
237+
LOG(info) << fmt::format(" - rejected {}/{} ROFs: random/mult.sel:{}, vtx.sel:{}", cutRandomMult + cutVertexMult, rofspan.size(), cutRandomMult, cutVertexMult);
253238
LOG(info) << fmt::format(" - Vertex seeding total elapsed time: {} ms for {} vertices found in {} ROFs", vertexerElapsedTime, timeFrame->getPrimaryVerticesNum(), rofspan.size());
254239
LOG(info) << fmt::format(" - Beam position computed for the TF: {}, {}", timeFrame->getBeamX(), timeFrame->getBeamY());
255240

@@ -354,6 +339,8 @@ DataProcessorSpec getTrackerSpec(bool useMC, const std::string& trModeS, o2::gpu
354339
inputs.emplace_back("compClusters", "ITS", "COMPCLUSTERS", 0, Lifetime::Timeframe);
355340
inputs.emplace_back("patterns", "ITS", "PATTERNS", 0, Lifetime::Timeframe);
356341
inputs.emplace_back("ROframes", "ITS", "CLUSTERSROF", 0, Lifetime::Timeframe);
342+
inputs.emplace_back("phystrig", "ITS", "PHYSTRIG", 0, Lifetime::Timeframe);
343+
357344
inputs.emplace_back("cldict", "ITS", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/ClusterDictionary"));
358345
inputs.emplace_back("alppar", "ITS", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam"));
359346
auto ggRequest = std::make_shared<o2::base::GRPGeomRequest>(false, // orbitResetTime

Detectors/ITSMFT/ITS/workflow/src/its-cluster-reader-workflow.cxx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ void customize(std::vector<o2::framework::CallbacksPolicy>& policies)
2323

2424
void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions)
2525
{
26+
workflowOptions.push_back(
27+
ConfigParamSpec{
28+
"suppress-triggers-output",
29+
o2::framework::VariantType::Bool,
30+
false,
31+
{"suppress dummy triggers output"}});
2632
workflowOptions.push_back(
2733
ConfigParamSpec{
2834
"with-mc",
@@ -51,10 +57,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cc)
5157
{
5258
WorkflowSpec specs;
5359
o2::conf::ConfigurableParam::updateFromString(cc.options().get<std::string>("configKeyValues"));
60+
auto withTriggers = !cc.options().get<bool>("suppress-triggers-output");
5461
auto withMC = cc.options().get<bool>("with-mc");
5562
auto withPatterns = !cc.options().get<bool>("without-patterns");
5663

57-
specs.emplace_back(o2::itsmft::getITSClusterReaderSpec(withMC, withPatterns));
64+
specs.emplace_back(o2::itsmft::getITSClusterReaderSpec(withMC, withPatterns, withTriggers));
5865

5966
// configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit
6067
o2::raw::HBFUtilsInitializer hbfIni(cc, specs);

0 commit comments

Comments
 (0)