Skip to content

Commit abc9b48

Browse files
shahor02claude
andauthored
Support (staggered) per-layer cluster input in all ITS-dependent workflows (#15878)
* Support per-layer ITS clusters and ROFs in TPC-ITS matching Extend MatchTPCITS to work with ITS clusters provided either as a single (monolithic) input or per layer, with layer-dependent ROF length and bias (staggered readout): - ITS clusters, sizes, cluster ROFRecords and MC labels are stored per layer, clusters are addressed by the composed ID (layer<<28)+index_in_layer (all in slot 0 for the monolithic input) - all ITS ROF timings (per-layer lengths and biases in BC and mus, the clock layer defining the ITS tracks ROFs granularity) are derived from the DPLAlpideParam object set via setAlpideParam; the setITSROFrameLength... and setITSTimeBiasInBC setters are removed - interaction candidates are related to the cluster ROFs of every AfterBurner layer within the optional abROFMarginMUS margin (allowing up to 2 compatible ROFs per layer) and are cut at the last clock-layer cluster ROF - AfterBurner reworked for CPU efficiency: unused clusters are filtered and (chip,Z)-sorted once per TF into per (layer, ROF) blocks, built only for the ROFs referenced by candidates with seeds; ITSChipClustersRefs is replaced by thread-local per-layer views (compact Y,Z,id,chip cluster info) refreshed only when the processed group of candidates changes ROFs - tpcits-match-workflow can request the per-layer ITS clusters input (through RecoContainer::setITSPerLayer) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Use per-track ITS time stamps instead of ROF duration in TPC-ITS matching MatchTPCITS previously assigned every ITS track the full clock-layer ROF duration as its time bracket. TrackITS now carries a per-track TimeStamp (BC since TF start, symmetric error, ROF bias already applied), which is typically much narrower than the ROF, especially in staggered running. - prepareITSData builds the per-track bracket from getTimeStamp(), widened by the new itsTimeStampMarginBC margin (BC, both edges), falling back to the nominal ROF bracket when the time stamp is invalid (legacy input) - mITSROFTimes is extended to the envelope of the nominal ROF bracket and the actual per-track brackets, keeping the TPC-side and triggered-mode ITS ROF entry caches conservative - mITSMaxROFOverhangMUS tracks how far track brackets extend past their ROF end in the current TF; doMatching's continuous-mode entry lookup is shifted by this amount so no compatible track is skipped - sorting by bracket min time still cannot mix tracks of different ROFs (the tracker guarantees the raw lower edge stays within the assigned ROF, and the margin shifts all tracks alike), so the existing mITSTimeStart assignment and the tBracket-based break/continue gates in doMatching remain valid without a LUT rebuild; the long-dead RejectOnTgl ROF-skip code (whose precondition never held with mixed layers) is removed - refitTrackTPCITS derives the fallback ITS time error from the track's own bracket (delta()/sqrt(12)) instead of the nominal clock-layer ROF resolution Net effect: most TPC x ITS pairs are now rejected by the cheap bracket overlap check in doMatching before the sqrt/kinematic comparisons, with no new containers and a single extra float member. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Encode the ITS layer into the per-layer ITS track cluster references With the per-layer (staggered readout) ITS clusters input the cluster index kept by TrackITS is local to its layer, so the layer must be encoded into the stored reference for the consumers to be able to find the cluster. MatchTPCITS already decoded the ITS/TRACKCLSID entries as such composed IDs, but the tracker pushed the bare per-layer index, which was correct only for the layer 0. - ITSTrackingInterface::run composes the stored reference as (layer << ClusLayerShift) + index_in_layer; with the monolithic clusters input the layer slot is 0 and the composed ID stays equal to the flat index, so the non-staggered output is unchanged - the ID composition/decomposition and the max number of separately provided ITS/MFT cluster layers move from MatchTPCITS.h to the new lightweight DataFormatsITSMFT/ClusterID.h, so that both the producer and the (many) consumers can use them without pulling in GlobalTracking; RecoContainer.h includes it and keeps MaxITSLayers/MaxMFTLayers as aliases - the layer field is shifted by 27 rather than 28 bits, so that it can accommodate the MFT layers too: the bit 31 is unusable, since the negative values of the composed ID are reserved for the "no cluster" flags - the unused TrackITSExt::setClusterIndex, carrying its own hardcoded copy of the composition (and writing to the packed slot while getClusterIndex reads the layer slot), is removed Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Fix asserts checking N layers in staggered R/O helpers * Add the ClustersPerLayer helper for the per-layer ITS/MFT clusters Stores the clusters of all layers in a single vector with the per-layer offsets and gives access by the composed ID of ClusterID.h, so that the same code works for the monolithic and the per-layer input. Detector agnostic: the filling is left to the caller, since the spacepoint conversion needs the detector geometry and dictionary. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Fix ITSMFT cluster reader in staggered mode * Support per-layer ITS clusters in strangeness-tracking-workflow The parallel cluster sizes vector is indexed via ClustersPerLayer::flatIndex so that it cannot diverge from the cluster container offsets. * Support per-layer ITS clusters in secondary-vertexing-workflow * Support per-layer ITS clusters in cosmics-match-workflow * Support per-layer ITS clusters in trd-tracking-workflow * Support per-layer ITS clusters in tpc-interpolation-workflow * Support per-layer ITS clusters in barrel-alignment-workflow Cluster-overlap bookkeeping in AlignableDetectorITS moves to a per-layer ROF loop; this is the natural granularity anyway since row-side chip overlaps are always within a single layer. Note: this whole module will be superseded by the new MP-2 based package. * Support per-layer ITS clusters in check-resid-workfow and trackMCStudy-workflow TrackMCStudy's ITS occupancy counting sums over the layer slots, each with its own ROF cursor advanced by DPLAlpideParam::getROFLengthInBC/ getROFBiasInBC(layer). * Support per-layer ITS clusters in its3-alignment-workflow * Support per-layer ITS clusters in its3-tracking-study-workflow * Support per-layer ITS clusters in the event display workflow EveWorkflowHelper only flattens the per-layer clusters; its ROF records were already used solely as an emptiness guard, not for timing. * Support per-layer ITS clusters in the GPU display workflow * Support per-layer ITS clusters in its-standalone-postprocessing-workflow All 7 study classes selectable from this single workflow. AnomalyStudy is ported in full (its nROF is a config parameter and all histograms are per layer, so per-layer ROF counters are the natural granularity). EfficiencyStudy and ITSBeamBackgroundStudy request the per-layer input but refuse to run on it (LOGP fatal): their algorithms rely on assumptions the staggered readout breaks (cluster-ROF index == track-ROF index; chaining consecutive ROFs across the 3 IB layers into one strobe series), and the replacement is an analysis choice for the study to make. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 150990b commit abc9b48

70 files changed

Lines changed: 1403 additions & 745 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainer.h‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "SimulationDataFormat/MCTruthContainer.h"
2828
#include "SimulationDataFormat/ConstMCTruthContainer.h"
2929
#include "DataFormatsCTP/LumiInfo.h"
30+
#include "DataFormatsITSMFT/ClusterID.h"
3031
#include <gsl/span>
3132
#include <memory>
3233

@@ -188,8 +189,8 @@ namespace globaltracking
188189
{
189190

190191
// max number of layers for which the ITS/MFT clusters, ROF records and patterns can be provided separately
191-
constexpr int MaxITSLayers = 7;
192-
constexpr int MaxMFTLayers = 10;
192+
constexpr int MaxITSLayers = o2::itsmft::MaxITSClusLayers;
193+
constexpr int MaxMFTLayers = o2::itsmft::MaxMFTClusLayers;
193194

194195
// helper class to request DPL input data from the processor specs definition
195196
struct DataRequest {

‎DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h‎

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -221,13 +221,6 @@ class TrackITSExt : public TrackITS
221221

222222
GPUhdDefault() TrackITSExt(const TrackITSExt& t) = default;
223223

224-
void setClusterIndex(int l, int i)
225-
{
226-
int ncl = getNumberOfClusters();
227-
mIndex[ncl++] = (l << 28) + i;
228-
getClusterRefs().setEntries(ncl);
229-
}
230-
231224
GPUhdi() int getClusterIndex(int lr) const { return mIndex[lr]; }
232225

233226
GPUh() int getFirstLayerClusterIndex() const
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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 ClusterID.h
13+
/// \brief Composition/decomposition of the ITS/MFT cluster IDs referring to per-layer cluster arrays
14+
/// \author ruben.shahoyan@cern.ch
15+
16+
#ifndef ALICEO2_ITSMFT_CLUSTERID_H
17+
#define ALICEO2_ITSMFT_CLUSTERID_H
18+
19+
namespace o2::itsmft
20+
{
21+
22+
// max number of layers for which the ITS/MFT clusters, ROF records and patterns can be provided separately
23+
constexpr int MaxITSClusLayers = 7;
24+
constexpr int MaxMFTClusLayers = 10;
25+
constexpr int MaxClusLayers = MaxITSClusLayers > MaxMFTClusLayers ? MaxITSClusLayers : MaxMFTClusLayers;
26+
27+
///< With the per-layer (staggered readout) ITS/MFT clusters input the clusters are referred to by the
28+
///< composed ID (layer << ClusLayerShift) + index_in_layer. With a single (monolithic) clusters input
29+
///< all clusters sit in the layer slot 0, hence the composed ID coincides with the flat cluster index
30+
///< and the same decoding works for both cases.
31+
///< Note: the bit 31 is excluded from the layer field, since the negative values of the composed ID
32+
///< are reserved for the "no cluster" flags.
33+
constexpr int ClusLayerShift = 27;
34+
constexpr int ClusIndexMask = (0x1 << ClusLayerShift) - 1;
35+
static_assert((1 << (31 - ClusLayerShift)) >= MaxClusLayers, "ClusLayerShift leaves no room for the layer ID");
36+
37+
constexpr int composeClusID(int lr, int idx) { return (lr << ClusLayerShift) + idx; }
38+
constexpr int clusID2Layer(int id) { return id >> ClusLayerShift; }
39+
constexpr int clusID2Index(int id) { return id & ClusIndexMask; }
40+
41+
} // namespace o2::itsmft
42+
43+
#endif
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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 ClustersPerLayer.h
13+
/// \brief Container of the ITS/MFT clusters addressed by the composed (layer,index) ID
14+
/// \author ruben.shahoyan@cern.ch
15+
16+
#ifndef ALICEO2_ITSMFT_CLUSTERSPERLAYER_H
17+
#define ALICEO2_ITSMFT_CLUSTERSPERLAYER_H
18+
19+
#include "DataFormatsITSMFT/ClusterID.h"
20+
#include "Framework/Logger.h"
21+
#include <array>
22+
#include <vector>
23+
24+
namespace o2::itsmft
25+
{
26+
27+
///< Container of the ITS/MFT clusters supplied either as a single (monolithic) array or per layer
28+
///< (staggered readout). The clusters of all layers are kept in a single vector, layer by layer,
29+
///< with the per-layer starting offsets recorded, so that a cluster referred to by the composed ID
30+
///< (layer << ClusLayerShift) + index_in_layer can be looked up directly. With the monolithic input
31+
///< only the layer slot 0 is filled and the composed ID coincides with the flat cluster index, hence
32+
///< the same lookup works for both cases.
33+
///<
34+
///< The conversion of the compact clusters to the stored objects is detector specific (and needs the
35+
///< geometry), so it is left to the caller: the container only records where each layer starts.
36+
///< Expected usage (nLr == 1 for the monolithic input):
37+
///< cont.init(nLr);
38+
///< for (int lr = 0; lr < nLr; lr++) {
39+
///< cont.beginLayer(lr);
40+
///< auto pattIt = recoData.getITSClustersPatterns(lr).begin();
41+
///< o2::its::ioutils::convertCompactClusters(recoData.getITSClusters(lr), pattIt, cont.getClusters(), dict);
42+
///< }
43+
///< cont.finalize();
44+
///< after which cont[composedID] gives the cluster referred to by a track cluster reference.
45+
template <typename T>
46+
class ClustersPerLayer
47+
{
48+
public:
49+
///< prepare for filling nLr layer slots, discarding the previous content
50+
void init(int nLr)
51+
{
52+
if (nLr < 1 || nLr > MaxClusLayers) {
53+
LOGP(fatal, "Clusters container cannot be initialized for {} layers, must be within 1:{}", nLr, MaxClusLayers);
54+
}
55+
mClusters.clear();
56+
mLrFirst.fill(0);
57+
mNLayers = nLr;
58+
}
59+
60+
///< record the start of the layer lr data, the layers must be filled in the increasing order
61+
void beginLayer(int lr)
62+
{
63+
if (lr < 0 || lr >= mNLayers) {
64+
LOGP(fatal, "Clusters container was initialized for {} layers, cannot fill the layer {}", mNLayers, lr);
65+
}
66+
mLrFirst[lr] = int(mClusters.size());
67+
}
68+
69+
///< to be called once all the layers were filled
70+
void finalize()
71+
{
72+
for (int lr = mNLayers; lr <= MaxClusLayers; lr++) { // the slots above the filled ones are empty
73+
mLrFirst[lr] = int(mClusters.size());
74+
}
75+
}
76+
77+
void clear()
78+
{
79+
mClusters.clear();
80+
mLrFirst.fill(0);
81+
mNLayers = 1;
82+
}
83+
84+
///< the clusters of all layers, to be appended to by the caller between beginLayer and finalize
85+
auto& getClusters() { return mClusters; }
86+
const auto& getClusters() const { return mClusters; }
87+
88+
auto getNLayers() const { return mNLayers; }
89+
auto getFirstIndex(int lr) const { return mLrFirst[lr]; }
90+
auto getNClusters(int lr) const { return mLrFirst[lr + 1] - mLrFirst[lr]; }
91+
auto size() const { return mClusters.size(); }
92+
bool empty() const { return mClusters.empty(); }
93+
94+
///< flat index of the cluster referred to by its composed ID
95+
int flatIndex(int composedID) const
96+
{
97+
return mLrFirst[clusID2Layer(composedID)] + clusID2Index(composedID);
98+
}
99+
const T& operator[](int composedID) const { return mClusters[flatIndex(composedID)]; }
100+
101+
private:
102+
std::vector<T> mClusters{}; ///< clusters of all layers, layer by layer
103+
std::array<int, MaxClusLayers + 1> mLrFirst{}; ///< 1st cluster of every layer, + the total in the last slot
104+
int mNLayers = 1; ///< number of filled layer slots
105+
};
106+
107+
} // namespace o2::itsmft
108+
109+
#endif

‎DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/DPLAlpideParam.h‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "CommonUtils/ConfigurableParam.h"
1717
#include "CommonUtils/ConfigurableParamHelper.h"
1818
#include "CommonConstants/LHCConstants.h"
19+
#include "DataFormatsITSMFT/ClusterID.h"
1920
#include <string_view>
2021

2122
namespace o2
@@ -76,6 +77,10 @@ struct DPLAlpideParam : public o2::conf::ConfigurableParamHelper<DPLAlpideParam<
7677
}
7778

7879
static_assert(N == o2::detectors::DetID::ITS || N == o2::detectors::DetID::MFT, "only DetID::ITS orDetID:: MFT are allowed");
80+
// the per-layer clusters are referred to by the composed ID of ClusterID.h, whose layer field must
81+
// match the number of layers of this detector, see composeClusID
82+
static_assert(getNLayers() == (N == o2::detectors::DetID::ITS ? MaxITSClusLayers : MaxMFTClusLayers),
83+
"layers count mismatch between DPLAlpideParam and the composed cluster ID encoding of ClusterID.h");
7984
static_assert(o2::constants::lhc::LHCMaxBunches % DEFROFLengthBC() == 0); // make sure ROF length is divisor of the orbit
8085
};
8186

‎Detectors/Align/Workflow/include/AlignmentWorkflow/BarrelAlignmentSpec.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ namespace align
2727

2828
/// create a processor spec
2929
framework::DataProcessorSpec getBarrelAlignmentSpec(o2::dataformats::GlobalTrackID::mask_t srcMP, o2::dataformats::GlobalTrackID::mask_t src,
30-
o2::detectors::DetID::mask_t dets, o2::detectors::DetID::mask_t skipDetClusters, bool enableCosmic, int postproc, bool useMC);
30+
o2::detectors::DetID::mask_t dets, o2::detectors::DetID::mask_t skipDetClusters, bool enableCosmic, int postproc, bool useMC, bool itsStag);
3131

3232
} // namespace align
3333
} // namespace o2

‎Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,10 +357,11 @@ void BarrelAlignmentSpec::endOfStream(EndOfStreamContext& ec)
357357
mDBGOut.reset();
358358
}
359359

360-
DataProcessorSpec getBarrelAlignmentSpec(GTrackID::mask_t srcMP, GTrackID::mask_t src, DetID::mask_t dets, DetID::mask_t skipDetClusters, bool enableCosmic, int postprocess, bool useMC)
360+
DataProcessorSpec getBarrelAlignmentSpec(GTrackID::mask_t srcMP, GTrackID::mask_t src, DetID::mask_t dets, DetID::mask_t skipDetClusters, bool enableCosmic, int postprocess, bool useMC, bool itsStag)
361361
{
362362
std::vector<OutputSpec> outputs;
363363
auto dataRequest = std::make_shared<DataRequest>();
364+
dataRequest->setITSPerLayer(itsStag);
364365
bool loadTPCCalib = false;
365366
Options opts{
366367
ConfigParamSpec{"apply-xor", o2::framework::VariantType::Bool, false, {"flip the 8-th bit of slope and position (for processing TRD CTFs from 2021 pilot beam)"}},

‎Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext)
9595
bool loadTPCTracks = configcontext.options().get<bool>("enable-tpc-tracks");
9696
bool enableCosmic = configcontext.options().get<bool>("enable-cosmic");
9797
bool useMC = configcontext.options().get<bool>("enable-mc");
98+
bool doStag = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(configcontext);
9899

99100
DetID::mask_t dets = allowedDets & DetID::getMask(configcontext.options().get<std::string>("detectors"));
100101
DetID::mask_t skipDetClusters; // optionally skip automatically loaded clusters
@@ -154,7 +155,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext)
154155
specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt));
155156
}
156157

157-
specs.emplace_back(o2::align::getBarrelAlignmentSpec(srcMP, src, dets, skipDetClusters, enableCosmic, postprocess, useMC));
158+
specs.emplace_back(o2::align::getBarrelAlignmentSpec(srcMP, src, dets, skipDetClusters, enableCosmic, postprocess, useMC, doStag));
158159
// RS FIXME: check which clusters are really needed
159160
if (!postprocess) {
160161
GID::mask_t dummy;

‎Detectors/Align/include/Align/AlignableDetectorITS.h‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "ReconstructionDataFormats/TrackParametrizationWithError.h"
2323
#include "ReconstructionDataFormats/BaseCluster.h"
2424
#include "ITSMFTReconstruction/ChipMappingITS.h"
25+
#include "DataFormatsITSMFT/ClustersPerLayer.h"
2526

2627
namespace o2
2728
{
@@ -76,9 +77,9 @@ class AlignableDetectorITS : public AlignableDetector
7677
//
7778
protected:
7879
//
79-
std::vector<ClusterD> mITSClustersArray;
80-
std::vector<int> mOverlapCandidateID; // pool of indices for potentially overlapping clusters
81-
std::vector<int> mOverlapClusRef; // 1st entry in mOverlapCandidateID for the overlapping cluster indices of each cluster
80+
o2::itsmft::ClustersPerLayer<ClusterD> mITSClustersArray;
81+
std::vector<int> mOverlapCandidateID; // pool of composed IDs of the potentially overlapping clusters
82+
std::vector<int> mOverlapClusRef; // 1st entry in mOverlapCandidateID for the overlapping clusters of each cluster, indexed by the flat cluster index
8283
std::vector<o2::itsmft::ChipMappingITS::Overlaps> mOverlaps;
8384
const o2::itsmft::TopologyDictionary* mITSDict{nullptr}; // cluster patterns dictionary
8485
//

0 commit comments

Comments
 (0)