diff --git a/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h b/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h index f36150e18fbbc..2f15923419795 100644 --- a/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h +++ b/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h @@ -49,7 +49,9 @@ class Cluster : public o2::BaseCluster kDownRight = 4, // 2^4, 5th bit kDown = 5, // 2^5, 6th bit kDownLeft = 6, // 2^6, 7th bit - kLeft = 7 }; // 2^7, 8th bit + kLeft = 7, // 2^7, 8th bit + // + FrameBit = 6 }; // this bit set means that the cluster is in the nominal (alpha=20*sector+10 deg.) sector frame rather than aligned Cluster() = default; @@ -57,6 +59,9 @@ class Cluster : public o2::BaseCluster ~Cluster() = default; + bool isInNominalSector() const { return isBitSet(FrameBit); } + void setInNominalSector() { setBit(FrameBit); } + std::int8_t getSector() const { return getCount(); } void setSector(std::int8_t value) { setCount(value); } diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCInterpolationSpec.h b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCInterpolationSpec.h index 86064f84d881f..83dbb1bd0f5fe 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCInterpolationSpec.h +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCInterpolationSpec.h @@ -38,7 +38,8 @@ namespace tpc class TPCInterpolationDPL : public Task { public: - TPCInterpolationDPL(std::shared_ptr dr, o2::dataformats::GlobalTrackID::mask_t src, o2::dataformats::GlobalTrackID::mask_t srcMap, std::shared_ptr gr, bool useMC, bool processITSTPConly, bool sendTrackData, bool debugOutput) : mDataRequest(dr), mSources(src), mSourcesMap(srcMap), mGGCCDBRequest(gr), mUseMC(useMC), mProcessITSTPConly(processITSTPConly), mSendTrackData(sendTrackData), mDebugOutput(debugOutput) {} + TPCInterpolationDPL(std::shared_ptr dr, o2::dataformats::GlobalTrackID::mask_t src, o2::dataformats::GlobalTrackID::mask_t srcMap, std::shared_ptr gr, bool useMC, + bool processITSTPConly, bool sendTrackData, bool debugOutput, bool extDetResid) : mDataRequest(dr), mSources(src), mSourcesMap(srcMap), mGGCCDBRequest(gr), mUseMC(useMC), mProcessITSTPConly(processITSTPConly), mSendTrackData(sendTrackData), mDebugOutput(debugOutput), mExtDetResid(extDetResid) {} ~TPCInterpolationDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -58,6 +59,7 @@ class TPCInterpolationDPL : public Task bool mProcessITSTPConly{false}; ///< should also tracks without outer point (ITS-TPC only) be processed? bool mProcessSeeds{false}; ///< process not only most complete track, but also its shorter parts bool mDebugOutput{false}; ///< add more information to the output (track points of ITS, TRD and TOF) + bool mExtDetResid{true}; ///< produce unbinned residuals for external detectors bool mSendTrackData{false}; ///< if true, not only the clusters but also corresponding track data will be sent uint32_t mSlotLength{600u}; ///< the length of one calibration slot required to calculate max number of tracks per TF int mMatCorr{2}; ///< the material correction to be used for track interpolation @@ -65,7 +67,8 @@ class TPCInterpolationDPL : public Task }; /// create a processor spec -framework::DataProcessorSpec getTPCInterpolationSpec(o2::dataformats::GlobalTrackID::mask_t srcCls, o2::dataformats::GlobalTrackID::mask_t srcVtx, o2::dataformats::GlobalTrackID::mask_t srcTrk, o2::dataformats::GlobalTrackID::mask_t srcTrkMap, bool useMC, bool processITSTPConly, bool sendTrackData, bool debugOutput); +framework::DataProcessorSpec getTPCInterpolationSpec(o2::dataformats::GlobalTrackID::mask_t srcCls, o2::dataformats::GlobalTrackID::mask_t srcVtx, o2::dataformats::GlobalTrackID::mask_t srcTrk, + o2::dataformats::GlobalTrackID::mask_t srcTrkMap, bool useMC, bool processITSTPConly, bool sendTrackData, bool debugOutput, bool extDetResid); } // namespace tpc } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx index 521a02cabcbee..da2fcaab913d7 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx @@ -105,8 +105,9 @@ void TPCInterpolationDPL::updateTimeDependentParams(ProcessingContext& pc) } if (mDebugOutput) { mInterpolation.setDumpTrackPoints(); - mInterpolation.setITSClusterDictionary(mITSDict); } + mInterpolation.setExtDetResid(mExtDetResid); + mInterpolation.setITSClusterDictionary(mITSDict); } void TPCInterpolationDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) @@ -158,7 +159,7 @@ void TPCInterpolationDPL::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getTPCInterpolationSpec(GTrackID::mask_t srcCls, GTrackID::mask_t srcVtx, GTrackID::mask_t srcTrk, GTrackID::mask_t srcTrkMap, bool useMC, bool processITSTPConly, bool sendTrackData, bool debugOutput) +DataProcessorSpec getTPCInterpolationSpec(GTrackID::mask_t srcCls, GTrackID::mask_t srcVtx, GTrackID::mask_t srcTrk, GTrackID::mask_t srcTrkMap, bool useMC, bool processITSTPConly, bool sendTrackData, bool debugOutput, bool extDetResid) { auto dataRequest = std::make_shared(); std::vector outputs; @@ -199,7 +200,7 @@ DataProcessorSpec getTPCInterpolationSpec(GTrackID::mask_t srcCls, GTrackID::mas "tpc-track-interpolation", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, srcTrk, srcTrkMap, ggRequest, useMC, processITSTPConly, sendTrackData, debugOutput)}, + AlgorithmSpec{adaptFromTask(dataRequest, srcTrk, srcTrkMap, ggRequest, useMC, processITSTPConly, sendTrackData, debugOutput, extDetResid)}, Options{ {"matCorrType", VariantType::Int, 2, {"material correction type (definition in Propagator.h)"}}, {"sec-per-slot", VariantType::UInt32, 600u, {"number of seconds per calibration time slot (put 0 for infinite slot length)"}}, diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-interpolation-workflow.cxx b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-interpolation-workflow.cxx index 0905942c956a4..2f28fc5bb2d34 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-interpolation-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-interpolation-workflow.cxx @@ -42,6 +42,7 @@ void customize(std::vector& workflowOptions) {"tracking-sources-map-extraction", VariantType::String, std::string{GID::ALL}, {"can be subset of \"tracking-sources\""}}, {"send-track-data", VariantType::Bool, false, {"Send also the track information to the aggregator"}}, {"debug-output", VariantType::Bool, false, {"Dump extended tracking information for debugging"}}, + {"skip-ext-det-residuals", VariantType::Bool, false, {"Do not produce residuals for external detectors"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); @@ -104,8 +105,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) useMC = false; // force disabling MC as long as it is not implemented auto sendTrackData = configcontext.options().get("send-track-data"); auto debugOutput = configcontext.options().get("debug-output"); + auto extDetResid = !configcontext.options().get("skip-ext-det-residuals"); - specs.emplace_back(o2::tpc::getTPCInterpolationSpec(srcClusters, srcVtx, srcTracks, srcTracksMap, useMC, processITSTPConly, sendTrackData, debugOutput)); + specs.emplace_back(o2::tpc::getTPCInterpolationSpec(srcClusters, srcVtx, srcTracks, srcTracksMap, useMC, processITSTPConly, sendTrackData, debugOutput, extDetResid)); if (!configcontext.options().get("disable-root-output")) { specs.emplace_back(o2::tpc::getTPCResidualWriterSpec(sendTrackData, debugOutput)); } diff --git a/Detectors/TOF/base/include/TOFBase/Geo.h b/Detectors/TOF/base/include/TOFBase/Geo.h index 761528d472d71..24e8fbdf51174 100644 --- a/Detectors/TOF/base/include/TOFBase/Geo.h +++ b/Detectors/TOF/base/include/TOFBase/Geo.h @@ -66,6 +66,8 @@ class Geo static void antiRotateToSector(Float_t* xyz, Int_t isector); static void antiRotateToStrip(Float_t* xyz, Int_t iplate, Int_t istrip, Int_t isector); + static void alignedToNominalSector(Float_t* xyz, Int_t isector); + static void antiRotate(Float_t* xyz, Double_t rotationAngles[6]); static void getDetID(Float_t* pos, Int_t* det); static Int_t getIndex(const Int_t* detId); // Get channel index from det Id (for calibration mainly) diff --git a/Detectors/TOF/base/src/Geo.cxx b/Detectors/TOF/base/src/Geo.cxx index c76e6b4d83943..08cda68c6d12e 100644 --- a/Detectors/TOF/base/src/Geo.cxx +++ b/Detectors/TOF/base/src/Geo.cxx @@ -988,6 +988,7 @@ void Geo::translate(Float_t* xyz, Float_t translationVector[3]) return; } + void Geo::translate(Float_t& x, Float_t& y, Float_t& z, Float_t translationVector[3]) { // @@ -1045,6 +1046,18 @@ void Geo::rotateToSector(Float_t* xyz, Int_t isector) return; } +void Geo::alignedToNominalSector(Float_t* xyz, Int_t isector) +{ + // rotate from the aligned sector frame coordinates to nominal ones (i.e. alpha=20*sector+10 deg.) + constexpr float CS[18] = {.848077e-01, 8.660254e-01, 6.427876e-01, 3.420202e-01, -4.371139e-08, -3.420201e-01, -6.427876e-01, -8.660254e-01, -9.848077e-01, -9.848077e-01, -8.660254e-01, -6.427875e-01, -3.420201e-01, 1.192488e-08, 3.420201e-01, 6.427875e-01, 8.660253e-01, 9.848078e-01}; + constexpr float SN[18] = {1.736482e-01, 5.000000e-01, 7.660444e-01, 9.396926e-01, 1.000000e+00, 9.396926e-01, 7.660444e-01, 5.000001e-01, 1.736483e-01, -1.736482e-01, -5.000000e-01, -7.660446e-01, -9.396927e-01, -1.000000e+00, -9.396926e-01, -7.660445e-01, -5.000002e-01, -1.736480e-01}; + Float_t xyzDummy[3] = {xyz[1], xyz[2], xyz[0]}; // go to twisted coordinates... + o2::tof::Geo::antiRotateToSector(xyzDummy, isector); // lab coordinates + xyz[0] = xyzDummy[0] * CS[isector] + xyzDummy[1] * SN[isector]; + xyz[1] = -xyzDummy[0] * SN[isector] + xyzDummy[1] * CS[isector]; + xyz[2] = xyzDummy[2]; +} + void Geo::antiRotateToStrip(Float_t* xyz, Int_t iplate, Int_t istrip, Int_t isector) { Float_t xyzDummy[3] = {0., 0., 0.}; diff --git a/Detectors/TPC/calibration/SpacePoints/CMakeLists.txt b/Detectors/TPC/calibration/SpacePoints/CMakeLists.txt index 1b5e79f601211..566558b7e982f 100644 --- a/Detectors/TPC/calibration/SpacePoints/CMakeLists.txt +++ b/Detectors/TPC/calibration/SpacePoints/CMakeLists.txt @@ -19,6 +19,7 @@ o2_add_library(SpacePoints O2::CommonUtils O2::TPCBase O2::TRDBase + O2::TOFBase O2::TPCReconstruction O2::TPCFastTransformation O2::ITStracking diff --git a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackInterpolation.h b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackInterpolation.h index 9cbcf3d117661..9f7c6d0fc8fbc 100644 --- a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackInterpolation.h +++ b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackInterpolation.h @@ -67,31 +67,49 @@ struct TPCClusterResiduals { /// (this is the data type which will be sent from the EPNs to the aggregator) struct UnbinnedResid { UnbinnedResid() = default; - UnbinnedResid(float dyIn, float dzIn, float tgSlpIn, float yIn, float zIn, unsigned char rowIn, unsigned char secIn) : dy(static_cast(dyIn * 0x7fff / param::MaxResid)), - dz(static_cast(dzIn * 0x7fff / param::MaxResid)), - tgSlp(static_cast(tgSlpIn * 0x7fff / param::MaxTgSlp)), - y(static_cast(yIn * 0x7fff / param::MaxY)), - z(static_cast(zIn * 0x7fff / param::MaxZ)), - row(rowIn), - sec(secIn) {} - short dy; ///< residual in y - short dz; ///< residual in z - short tgSlp; ///< tan of the phi angle between padrow and track - short y; ///< y position of the track, needed for binning - short z; ///< z position of the track, needed for binning - unsigned char row; ///< TPC pad row - unsigned char sec; ///< TPC sector (0..35) - ClassDefNV(UnbinnedResid, 1); + UnbinnedResid(float dyIn, float dzIn, float tgSlpIn, float yIn, float zIn, unsigned char rowIn, unsigned char secIn, short chanIn = -1) : dy(static_cast(dyIn * 0x7fff / param::MaxResid)), + dz(static_cast(dzIn * 0x7fff / param::MaxResid)), + tgSlp(static_cast(tgSlpIn * 0x7fff / param::MaxTgSlp)), + y(static_cast(yIn * 0x7fff / param::MaxY)), + z(static_cast(zIn * 0x7fff / param::MaxZ)), + row(rowIn), + sec(secIn), + channel(chanIn) {} + short dy{0}; ///< residual in y + short dz{0}; ///< residual in z + short tgSlp{0}; ///< tan of the phi angle between padrow and track + short y{0}; ///< y position of the track, needed for binning + short z{0}; ///< z position of the track, needed for binning + unsigned char row{0}; ///< TPC pad row + unsigned char sec{0}; ///< TPC sector (0..35) + short channel{-1}; ///< extra channel info (ITS chip ID, TRD chamber, TOF main pad within the sector) + + bool isTPC() const { return row < constants::MAXGLOBALPADROW; } + bool isTRD() const { return row >= 160 && row < 166; } + bool isTOF() const { return row == 170; } + bool isITS() const { return row >= 180; } + int getDetID() const { return isTPC() ? 1 : (isITS() ? 0 : (isTRD() ? 2 : (isTOF() ? 3 : -1))); } + int getITSLayer() const { return row - 180; } + int getTRDLayer() const { return row - 170; } + float getAlpha() const; + float getX() const; + + static void init(long timestamp = -1); + static void checkInitDone(); + static bool gInitDone; + + ClassDefNV(UnbinnedResid, 2); }; /// Structure for the information required to associate each residual with a given track type (ITS-TPC-TRD-TOF, etc) struct TrackDataCompact { TrackDataCompact() = default; - TrackDataCompact(uint32_t idx, uint8_t nRes, uint8_t source) : idxFirstResidual(idx), nResiduals(nRes), sourceId(source) {} + TrackDataCompact(uint32_t idx, uint8_t nRes, uint8_t source, uint8_t nextraRes = 0) : idxFirstResidual(idx), nResiduals(nRes), sourceId(source), nExtDetResid(nextraRes) {} uint32_t idxFirstResidual; ///< the index of the first residual from this track - uint8_t nResiduals; ///< total number of residuals associated to this track + uint8_t nResiduals; ///< total number of TPC residuals associated to this track + uint8_t nExtDetResid = 0; ///< number of external detectors (wrt TPC) residuals stored, on top of clIdx.getEntries uint8_t sourceId; ///< source ID obtained from the global track ID - ClassDefNV(TrackDataCompact, 1); + ClassDefNV(TrackDataCompact, 2); }; // TODO add to UnbinnedResid::sec flag if cluster was used or not @@ -110,7 +128,8 @@ struct TrackDataExtended { std::vector clsTRD{}; ///< the TRD space points (if available) o2::tof::Cluster clsTOF{}; ///< the TOF cluster (if available) o2::dataformats::RangeReference<> clIdx{}; ///< index of first cluster residual and total number of cluster residuals of this track - ClassDefNV(TrackDataExtended, 2); + uint8_t nExtDetResid = 0; ///< number of external detectors (to TPC) residuals stored, on top of clIdx.getEntries + ClassDefNV(TrackDataExtended, 3); }; /// Structure filled for each track with track quality information and a vector with TPCClusterResiduals @@ -121,12 +140,14 @@ struct TrackData { float chi2TPC{}; ///< chi2 of TPC track float chi2ITS{}; ///< chi2 of ITS track float chi2TRD{}; ///< chi2 of TRD track + unsigned short nClsTPC{}; ///< number of attached TPC clusters unsigned short nClsITS{}; ///< number of attached ITS clusters unsigned short nTrkltsTRD{}; ///< number of attached TRD tracklets unsigned short clAvailTOF{}; ///< whether or not track seed has a matched TOF cluster - o2::dataformats::RangeReference<> clIdx{}; ///< index of first cluster residual and total number of cluster residuals of this track - ClassDefNV(TrackData, 6); + uint8_t nExtDetResid = 0; ///< number of external detectors (to TPC) residuals stored, on top of clIdx.getEntries + o2::dataformats::RangeReference<> clIdx{}; ///< index of first cluster residual and total number of TPC cluster residuals of this track + ClassDefNV(TrackData, 7); }; /// \class TrackInterpolation @@ -265,6 +286,10 @@ class TrackInterpolation /// Set the centre of mass energy required for pT downsampling Tsalis function void setSqrtS(float s) { mSqrtS = s; } + void setExtDetResid(bool v) { mExtDetResid = v; } + + int processTRDLayer(const o2::trd::TrackTRD& trkTRD, int iLayer, o2::track::TrackParCov& trkWork, std::array* trkltTRDYZ = nullptr, std::array* trkltTRDCov = nullptr); + // --------------------------------- output --------------------------------------------- std::vector& getClusterResiduals() { return mClRes; } std::vector& getTrackDataCompact() { return mTrackDataCompact; } @@ -285,6 +310,7 @@ class TrackInterpolation int mMaxTracksPerTF{-1}; ///< max number of tracks to be processed per TF (-1 means there is no limit) int mAddTracksForMapPerTF{0}; ///< in case residuals from different track types are used for vDrift calibration and map creation this defines the statistics for the latter bool mDumpTrackPoints{false}; ///< dump also track points in ITS, TRD and TOF + bool mExtDetResid{true}; ///< produce unbinned residuals for external detectors bool mProcessSeeds{false}; ///< in case for global tracks also their shorter parts are processed separately bool mProcessITSTPConly{false}; ///< flag, whether or not to extrapolate ITS-only through TPC o2::dataformats::GlobalTrackID::mask_t mSourcesConfigured; ///< the track sources taken into account for extra-/interpolation @@ -297,6 +323,7 @@ class TrackInterpolation std::vector mGIDtables{}; ///< GIDs of contributors from single detectors for each seed std::vector mTrackTimes{}; ///< time estimates for all input tracks in micro seconds std::vector mSeeds{}; ///< seeding track parameters (ITS tracks) + std::vector mParentID{}; ///< entry of more global parent track for skimmed seeds (-1: no parent) std::map mTrackTypes; ///< mapping of track source to array index in mTrackIndices std::array, 4> mTrackIndices; ///< keep GIDs of input tracks separately for each track type gsl::span mTPCTracksClusIdx; ///< input TPC cluster indices from span diff --git a/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx index 015c0ef1df416..1daaa897e9756 100644 --- a/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx +++ b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx @@ -18,7 +18,9 @@ #include "SpacePoints/TrackInterpolation.h" #include "SpacePoints/TrackResiduals.h" #include "ITStracking/IOUtils.h" +#include "ITSBase/GeometryTGeo.h" #include "TPCBase/ParameterElectronics.h" +#include "TOFBase/Geo.h" #include "DataFormatsTPC/TrackTPC.h" #include "DataFormatsTPC/Defs.h" #include "DataFormatsTRD/Constants.h" @@ -30,6 +32,7 @@ #include "TMath.h" #include "DataFormatsTPC/VDriftCorrFact.h" #include "Framework/Logger.h" +#include "CCDB/BasicCCDBManager.h" #include #include #include @@ -38,6 +41,71 @@ using namespace o2::tpc; using GTrackID = o2::dataformats::GlobalTrackID; using DetID = o2::detectors::DetID; +bool UnbinnedResid::gInitDone = false; + +float UnbinnedResid::getAlpha() const +{ + if (!isITS()) { + return o2::math_utils::sector2Angle(sec % 18); + } + // ITS alpha repends on the chip ID + checkInitDone(); + return o2::its::GeometryTGeo::Instance()->getSensorRefAlpha(channel); +} + +float UnbinnedResid::getX() const +{ + if (isTPC()) { + return param::RowX[row]; + } + checkInitDone(); + if (isITS()) { + return o2::its::GeometryTGeo::Instance()->getSensorRefX(channel); // ITS X repends on the chip ID + } + if (isTRD()) { + auto geo = o2::trd::Geometry::instance(); + ROOT::Math::Impl::Transform3D::Point local{geo->cdrHght() - 0.5 - 0.279, 0., 0.}; // see TrackletTransformer::transformTracklet + return (geo->getMatrixT2L(channel) ^ local).X(); + } + if (isTOF()) { + int det[5]; + o2::tof::Geo::getVolumeIndices(channel + sec * o2::tof::Geo::NPADSXSECTOR, det); + float pos[3] = {0.f, 0.f, 0.f}; + o2::tof::Geo::getPos(det, pos); + float posl[3] = {pos[0], pos[1], pos[2]}; + o2::tof::Geo::rotateToSector(pos, sec); + return pos[2]; // coordinates in sector frame: note that the rotation above puts z in pos[1], the radial coordinate in pos[2], and the tangent coordinate in pos[0] (this is to match the TOF residual system, where we don't use the radial component), so we swap their positions. + } + LOGP(fatal, "Did not recognize detector type: row:{}, sec:{}, channel:{}", row, sec, channel); + return 0.; +} + +void UnbinnedResid::checkInitDone() +{ + if (!gInitDone) { + LOGP(warn, "geometry initialization was not done, doing this for the current timestamp"); + init(); + if (!gInitDone) { + LOGP(fatal, "geometry initialization failed"); + } + } +} + +void UnbinnedResid::init(long timestamp) +{ + if (gInitDone) { + LOGP(warn, "Initialization was already done"); + return; + } + if (!gGeoManager) { + o2::ccdb::BasicCCDBManager::instance().getSpecific("GLO/Config/GeometryAligned", timestamp); + } + auto geoTRD = o2::trd::Geometry::instance(); + geoTRD->createPadPlaneArray(); + geoTRD->createClusterMatrixArray(); + gInitDone = true; +} + void TrackInterpolation::init(o2::dataformats::GlobalTrackID::mask_t src, o2::dataformats::GlobalTrackID::mask_t srcMap) { // perform initialization @@ -65,6 +133,9 @@ void TrackInterpolation::init(o2::dataformats::GlobalTrackID::mask_t src, o2::da mTrackTypes.insert({GTrackID::ITSTPCTOF, 2}); mTrackTypes.insert({GTrackID::ITSTPCTRDTOF, 3}); + auto geom = o2::its::GeometryTGeo::Instance(); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); + mInitDone = true; LOGP(info, "Done initializing TrackInterpolation. Configured track input: {}. Track input specifically for map: {}", GTrackID::getSourcesNames(mSourcesConfigured), mSingleSourcesConfigured ? "identical" : GTrackID::getSourcesNames(mSourcesConfiguredMap)); @@ -241,7 +312,7 @@ void TrackInterpolation::process() // set the input containers mTPCTracksClusIdx = mRecoCont->getTPCTracksClusterRefs(); mTPCClusterIdxStruct = &mRecoCont->getTPCClusters(); - if (mDumpTrackPoints) { + { if (!mITSDict) { LOG(error) << "No ITS dictionary available"; return; @@ -273,6 +344,9 @@ void TrackInterpolation::process() trackIndices.insert(trackIndices.end(), mTrackIndices[mTrackTypes[GTrackID::ITSTPC]].begin(), mTrackIndices[mTrackTypes[GTrackID::ITSTPC]].end()); int nSeeds = mSeeds.size(), lastChecked = 0; + mParentID.clear(); + mParentID.resize(nSeeds, -1); + int maxOutputTracks = (mMaxTracksPerTF >= 0) ? mMaxTracksPerTF + mAddTracksForMapPerTF : nSeeds; mTrackData.reserve(maxOutputTracks); mClRes.reserve(maxOutputTracks * param::NPadRows); @@ -292,6 +366,7 @@ void TrackInterpolation::process() this->mGIDtables.push_back(this->mRecoCont->getSingleDetectorRefs(this->mGIDs.back())); this->mTrackTimes.push_back(this->mTrackTimes[seedIndex]); this->mSeeds.push_back(this->mSeeds[seedIndex]); + this->mParentID.push_back(seedIndex); // store parent seed id }; GTrackID::mask_t partsAdded; @@ -378,6 +453,7 @@ void TrackInterpolation::interpolateTrack(int iSeed) trackData.gid = mGIDs[iSeed]; trackData.par = mSeeds[iSeed]; auto& trkWork = mSeeds[iSeed]; + o2::track::TrackPar trkInner{trkWork}; // reset the cache array (sufficient to set cluster available to zero) for (auto& elem : mCache) { elem.clAvailable = 0; @@ -436,10 +512,13 @@ void TrackInterpolation::interpolateTrack(int iSeed) LOG(debug) << "Failed to rotate into TOF cluster sector frame"; return; } - float clTOFX = clTOF.getX(); - std::array clTOFYZ{clTOF.getY(), clTOF.getZ()}; + float clTOFxyz[3] = {clTOF.getX(), clTOF.getY(), clTOF.getZ()}; + if (!clTOF.isInNominalSector()) { + o2::tof::Geo::alignedToNominalSector(clTOFxyz, clTOFSec); // go from the aligned to nominal sector frame + } + std::array clTOFYZ{clTOFxyz[1], clTOFxyz[2]}; std::array clTOFCov{mParams->sigYZ2TOF, 0.f, mParams->sigYZ2TOF}; // assume no correlation between y and z and equal cluster error sigma^2 = (3cm)^2 / 12 - if (!propagator->PropagateToXBxByBz(trkWork, clTOFX, mParams->maxSnp, mParams->maxStep, mMatCorr)) { + if (!propagator->PropagateToXBxByBz(trkWork, clTOFxyz[0], mParams->maxSnp, mParams->maxStep, mMatCorr)) { LOG(debug) << "Failed final propagation to TOF radius"; return; } @@ -457,41 +536,15 @@ void TrackInterpolation::interpolateTrack(int iSeed) (*trackDataExtended).trkTRD = trkTRD; } for (int iLayer = o2::trd::constants::NLAYER - 1; iLayer >= 0; --iLayer) { - int trkltIdx = trkTRD.getTrackletIndex(iLayer); - if (trkltIdx < 0) { - // no TRD tracklet in this layer + std::array trkltTRDYZ{}; + std::array trkltTRDCov{}; + int res = processTRDLayer(trkTRD, iLayer, trkWork, &trkltTRDYZ, &trkltTRDCov); + if (res == -1) { // no TRD tracklet in this layer continue; } - const auto& trdSP = mRecoCont->getTRDCalibratedTracklets()[trkltIdx]; - const auto& trdTrklt = mRecoCont->getTRDTracklets()[trkltIdx]; - if (mDumpTrackPoints) { - (*trackDataExtended).trkltTRD.push_back(trdTrklt); - (*trackDataExtended).clsTRD.push_back(trdSP); - } - auto trkltDet = trdTrklt.getDetector(); - auto trkltSec = trkltDet / (o2::trd::constants::NLAYER * o2::trd::constants::NSTACK); - if (trkltSec != o2::math_utils::angle2Sector(trkWork.getAlpha())) { - if (!trkWork.rotate(o2::math_utils::sector2Angle(trkltSec))) { - LOG(debug) << "Track could not be rotated in TRD tracklet coordinate system in layer " << iLayer; - return; - } - } - if (!propagator->PropagateToXBxByBz(trkWork, trdSP.getX(), mParams->maxSnp, mParams->maxStep, mMatCorr)) { - LOG(debug) << "Failed propagation to TRD layer " << iLayer; + if (res < -1) { // failed to reach this layer return; } - - const auto* pad = mGeoTRD->getPadPlane(trkltDet); - float tilt = tan(TMath::DegToRad() * pad->getTiltingAngle()); // tilt is signed! and returned in degrees - float tiltCorrUp = tilt * (trdSP.getZ() - trkWork.getZ()); - float zPosCorrUp = trdSP.getZ() + mRecoParam.getZCorrCoeffNRC() * trkWork.getTgl(); // maybe Z can be corrected on avarage already by the tracklet transformer? - float padLength = pad->getRowSize(trdTrklt.getPadRow()); - if (!((trkWork.getSigmaZ2() < (padLength * padLength / 12.f)) && (std::fabs(trdSP.getZ() - trkWork.getZ()) < padLength))) { - tiltCorrUp = 0.f; - } - std::array trkltTRDYZ{trdSP.getY() - tiltCorrUp, zPosCorrUp}; - std::array trkltTRDCov; - mRecoParam.recalcTrkltCov(tilt, trkWork.getSnp(), pad->getRowSize(trdTrklt.getPadRow()), trkltTRDCov); if (!trkWork.update(trkltTRDYZ, trkltTRDCov)) { LOG(debug) << "Failed to update track at TRD layer " << iLayer; return; @@ -502,6 +555,7 @@ void TrackInterpolation::interpolateTrack(int iSeed) if (mDumpTrackPoints) { (*trackDataExtended).trkOuter = trkWork; } + auto trkOuter = trkWork; // outer param // go back through the TPC and store updated track positions bool outerParamStored = false; @@ -594,13 +648,98 @@ void TrackInterpolation::interpolateTrack(int iSeed) } } trackData.clIdx.setEntries(nClValidated); + + bool stopPropagation = !mExtDetResid; + if (!stopPropagation) { + // do we have TRD residuals to add? + trkWork = trkOuter; + if (gidTable[GTrackID::TRD].isIndexSet()) { + const auto& trkTRD = mRecoCont->getITSTPCTRDTrack(gidTable[GTrackID::ITSTPCTRD]); + for (int iLayer = 0; iLayer < o2::trd::constants::NLAYER; iLayer++) { + std::array trkltTRDYZ{}; + int res = processTRDLayer(trkTRD, iLayer, trkWork, &trkltTRDYZ); + if (res == -1) { // no traklet on this layer + continue; + } + if (res < -1) { // failed to reach this layer + stopPropagation = true; + break; + } + + float tgPhi = trkWork.getSnp() / std::sqrt((1.f - trkWork.getSnp()) * (1.f + trkWork.getSnp())); + auto dy = trkltTRDYZ[0] - trkWork.getY(); + auto dz = trkltTRDYZ[1] - trkWork.getZ(); + if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(trkWork.getY()) < param::MaxY) && (std::abs(trkWork.getZ()) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) { + mClRes.emplace_back(dy, dz, tgPhi, trkWork.getY(), trkWork.getZ(), 160 + iLayer, o2::math_utils::angle2Sector(trkWork.getAlpha()), (short)res); + trackData.nExtDetResid++; + } + } + } + + // do we have TOF residual to add? + while (gidTable[GTrackID::TOF].isIndexSet() && !stopPropagation) { + const auto& clTOF = mRecoCont->getTOFClusters()[gidTable[GTrackID::TOF]]; + float clTOFxyz[3] = {clTOF.getX(), clTOF.getY(), clTOF.getZ()}; + if (!clTOF.isInNominalSector()) { + o2::tof::Geo::alignedToNominalSector(clTOFxyz, clTOF.getCount()); // go from the aligned to nominal sector frame + } + const float clTOFAlpha = o2::math_utils::sector2Angle(clTOF.getCount()); + if (trkWork.getAlpha() != clTOFAlpha && !trkWork.rotate(clTOFAlpha)) { + LOG(debug) << "Failed to rotate into TOF cluster sector frame"; + stopPropagation = true; + break; + } + if (!propagator->PropagateToXBxByBz(trkWork, clTOFxyz[0], mParams->maxSnp, mParams->maxStep, mMatCorr)) { + LOG(debug) << "Failed final propagation to TOF radius"; + break; + } + + float tgPhi = trkWork.getSnp() / std::sqrt((1.f - trkWork.getSnp()) * (1.f + trkWork.getSnp())); + auto dy = clTOFxyz[1] - trkWork.getY(); + auto dz = clTOFxyz[2] - trkWork.getZ(); + if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(trkWork.getY()) < param::MaxY) && (std::abs(trkWork.getZ()) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) { + mClRes.emplace_back(dy, dz, tgPhi, trkWork.getY(), trkWork.getZ(), 170, clTOF.getCount(), clTOF.getPadInSector()); + trackData.nExtDetResid++; + } + break; + } + + // add ITS residuals + while (!stopPropagation) { + auto& trkWorkITS = trkInner; // this is ITS outer param + auto nCl = trkITS.getNumberOfClusters(); + auto clEntry = trkITS.getFirstClusterEntry(); + auto geom = o2::its::GeometryTGeo::Instance(); + for (int iCl = 0; iCl < nCl; iCl++) { // clusters are stored from outer to inner layers + const auto& cls = mITSClustersArray[mITSTrackClusIdx[clEntry + iCl]]; + int chip = cls.getSensorID(); + float chipX, chipAlpha; + geom->getSensorXAlphaRefPlane(cls.getSensorID(), chipX, chipAlpha); + if (!trkWorkITS.rotate(chipAlpha) || !propagator->PropagateToXBxByBz(trkWorkITS, chipX, mParams->maxSnp, mParams->maxStep, mMatCorr)) { + LOGP(debug, "Failed final propagation to ITS X={} alpha={}", chipX, chipAlpha); + stopPropagation = true; + break; + } + float tgPhi = trkWorkITS.getSnp() / std::sqrt((1.f - trkWorkITS.getSnp()) * (1.f + trkWorkITS.getSnp())); + auto dy = cls.getY() - trkWorkITS.getY(); + auto dz = cls.getZ() - trkWorkITS.getZ(); + if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(trkWorkITS.getY()) < param::MaxY) && (std::abs(trkWorkITS.getZ()) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) { + mClRes.emplace_back(dy, dz, tgPhi, trkWorkITS.getY(), trkWorkITS.getZ(), 180 + geom->getLayer(cls.getSensorID()), -1, cls.getSensorID()); + trackData.nExtDetResid++; + } + } + break; + } + } + + mGIDsSuccess.push_back(mGIDs[iSeed]); + mTrackDataCompact.emplace_back(trackData.clIdx.getFirstEntry(), nClValidated, mGIDs[iSeed].getSource(), trackData.nExtDetResid); mTrackData.push_back(std::move(trackData)); if (mDumpTrackPoints) { (*trackDataExtended).clIdx.setEntries(nClValidated); + (*trackDataExtended).nExtDetResid = trackData.nExtDetResid; mTrackDataExtended.push_back(std::move(*trackDataExtended)); } - mGIDsSuccess.push_back(mGIDs[iSeed]); - mTrackDataCompact.emplace_back(mClRes.size() - nClValidated, nClValidated, mGIDs[iSeed].getSource()); } if (mParams->writeUnfiltered) { TrackData trkDataTmp = trackData; @@ -611,6 +750,46 @@ void TrackInterpolation::interpolateTrack(int iSeed) } } +int TrackInterpolation::processTRDLayer(const o2::trd::TrackTRD& trkTRD, int iLayer, o2::track::TrackParCov& trkWork, + std::array* trkltTRDYZ, std::array* trkltTRDCov) +{ + // return chamber ID (0:539) in case of successful processing, -1 if there is no TRD tracklet at given layer, -2 if processing failed + int trkltIdx = trkTRD.getTrackletIndex(iLayer); + if (trkltIdx < 0) { + return -1; // no TRD tracklet in this layer + } + const auto& trdSP = mRecoCont->getTRDCalibratedTracklets()[trkltIdx]; + const auto& trdTrklt = mRecoCont->getTRDTracklets()[trkltIdx]; + auto trkltDet = trdTrklt.getDetector(); + auto trkltSec = trkltDet / (o2::trd::constants::NLAYER * o2::trd::constants::NSTACK); + if (trkltSec != o2::math_utils::angle2Sector(trkWork.getAlpha())) { + if (!trkWork.rotate(o2::math_utils::sector2Angle(trkltSec))) { + LOG(debug) << "Track could not be rotated in TRD tracklet coordinate system in layer " << iLayer; + return -2; + } + } + if (!o2::base::Propagator::Instance()->PropagateToXBxByBz(trkWork, trdSP.getX(), mParams->maxSnp, mParams->maxStep, mMatCorr)) { + LOG(debug) << "Failed propagation to TRD layer " << iLayer; + return -2; + } + if (trkltTRDYZ) { + const auto* pad = mGeoTRD->getPadPlane(trkltDet); + float tilt = tan(TMath::DegToRad() * pad->getTiltingAngle()); // tilt is signed! and returned in degrees + float tiltCorrUp = tilt * (trdSP.getZ() - trkWork.getZ()); + float zPosCorrUp = trdSP.getZ() + mRecoParam.getZCorrCoeffNRC() * trkWork.getTgl(); // maybe Z can be corrected on avarage already by the tracklet transformer? + float padLength = pad->getRowSize(trdTrklt.getPadRow()); + if (!((trkWork.getSigmaZ2() < (padLength * padLength / 12.f)) && (std::fabs(trdSP.getZ() - trkWork.getZ()) < padLength))) { + tiltCorrUp = 0.f; + } + (*trkltTRDYZ)[0] = trdSP.getY() - tiltCorrUp; + (*trkltTRDYZ)[1] = zPosCorrUp; + if (trkltTRDCov) { + mRecoParam.recalcTrkltCov(tilt, trkWork.getSnp(), pad->getRowSize(trdTrklt.getPadRow()), *trkltTRDCov); + } + } + return trkltDet; +} + void TrackInterpolation::extrapolateTrack(int iSeed) { // extrapolate ITS-only track through TPC and store residuals to TPC clusters in the output vectors @@ -638,7 +817,7 @@ void TrackInterpolation::extrapolateTrack(int iSeed) trackData.gid = mGIDs[iSeed]; trackData.par = mSeeds[iSeed]; - auto& trkWork = mSeeds[iSeed]; + auto trkWork = mSeeds[iSeed]; float clusterTimeBinOffset = mTrackTimes[iSeed] / mTPCTimeBinMUS; auto propagator = o2::base::Propagator::Instance(); unsigned short rowPrev = 0; // used to calculate dRow of two consecutive cluster residuals @@ -681,6 +860,13 @@ void TrackInterpolation::extrapolateTrack(int iSeed) rowPrev = row; ++nMeasurements; } + + TrackParams params; // for refitted track parameters and flagging rejected clusters + if (clusterResiduals.size() > constants::MAXGLOBALPADROW) { + LOGP(warn, "Extrapolated ITS-TPC track and found more reesiduals than possible ({})", clusterResiduals.size()); + return; + } + trackData.chi2TPC = trkTPC.getChi2(); trackData.chi2ITS = trkITS.getChi2(); trackData.nClsTPC = trkTPC.getNClusterReferences(); @@ -691,19 +877,14 @@ void TrackInterpolation::extrapolateTrack(int iSeed) (*trackDataExtended).trkOuter = trkWork; } - TrackParams params; // for refitted track parameters and flagging rejected clusters - if (clusterResiduals.size() > constants::MAXGLOBALPADROW) { - LOGP(warn, "Extrapolated ITS-TPC track and found more reesiduals than possible ({})", clusterResiduals.size()); - return; - } if (mParams->skipOutlierFiltering || validateTrack(trackData, params, clusterResiduals)) { - // track is good - int nClValidated = 0; - int iRow = 0; - for (unsigned int iCl = 0; iCl < clusterResiduals.size(); ++iCl) { + // track is good, store TPC part + + int nClValidated = 0, iRow = 0; + unsigned int iCl = 0; + for (iCl = 0; iCl < clusterResiduals.size(); ++iCl) { iRow += clusterResiduals[iCl].dRow; - if (params.flagRej[iCl]) { - // skip masked cluster residual + if (iRow < param::NPadRows && params.flagRej[iCl]) { // skip masked cluster residual continue; } ++nClValidated; @@ -712,19 +893,107 @@ void TrackInterpolation::extrapolateTrack(int iSeed) const auto dz = clusterResiduals[iCl].dz; const auto y = clusterResiduals[iCl].y; const auto z = clusterResiduals[iCl].z; - const auto sec = clusterResiduals[iCl].sec; if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(y) < param::MaxY) && (std::abs(z) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) { - mClRes.emplace_back(dy, dz, tgPhi, y, z, iRow, sec); + mClRes.emplace_back(dy, dz, tgPhi, y, z, iRow, clusterResiduals[iCl].sec); } else { ++mRejectedResiduals; } } trackData.clIdx.setEntries(nClValidated); + + bool stopPropagation = !mExtDetResid; + if (!stopPropagation) { + // do we have TRD residuals to add? + int iSeedFull = mParentID[iSeed] == -1 ? iSeed : mParentID[iSeed]; + auto gidFull = mGIDs[iSeedFull]; + const auto& gidTableFull = mGIDtables[iSeedFull]; + if (gidTableFull[GTrackID::TRD].isIndexSet()) { + const auto& trkTRD = mRecoCont->getITSTPCTRDTrack(gidTableFull[GTrackID::ITSTPCTRD]); + for (int iLayer = 0; iLayer < o2::trd::constants::NLAYER; iLayer++) { + std::array trkltTRDYZ{}; + int res = processTRDLayer(trkTRD, iLayer, trkWork, &trkltTRDYZ); + if (res == -1) { // no traklet on this layer + continue; + } + if (res < -1) { // failed to reach this layer + stopPropagation = true; + break; + } + + float tgPhi = trkWork.getSnp() / std::sqrt((1.f - trkWork.getSnp()) * (1.f + trkWork.getSnp())); + auto dy = trkltTRDYZ[0] - trkWork.getY(); + auto dz = trkltTRDYZ[1] - trkWork.getZ(); + const auto sec = clusterResiduals[iCl].sec; + if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(trkWork.getY()) < param::MaxY) && (std::abs(trkWork.getZ()) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) { + mClRes.emplace_back(dy, dz, tgPhi, trkWork.getY(), trkWork.getZ(), 160 + iLayer, o2::math_utils::angle2Sector(trkWork.getAlpha()), (short)res); + trackData.nTrkltsTRD++; + trackData.nExtDetResid++; + } + } + } + + // do we have TOF residual to add? + while (gidTableFull[GTrackID::TOF].isIndexSet() && !stopPropagation) { + const auto& clTOF = mRecoCont->getTOFClusters()[gidTableFull[GTrackID::TOF]]; + const float clTOFAlpha = o2::math_utils::sector2Angle(clTOF.getCount()); + float clTOFxyz[3] = {clTOF.getX(), clTOF.getY(), clTOF.getZ()}; + if (!clTOF.isInNominalSector()) { + o2::tof::Geo::alignedToNominalSector(clTOFxyz, clTOF.getCount()); // go from the aligned to nominal sector frame + } + if (trkWork.getAlpha() != clTOFAlpha && !trkWork.rotate(clTOFAlpha)) { + LOG(debug) << "Failed to rotate into TOF cluster sector frame"; + stopPropagation = true; + break; + } + if (!propagator->PropagateToXBxByBz(trkWork, clTOFxyz[0], mParams->maxSnp, mParams->maxStep, mMatCorr)) { + LOG(debug) << "Failed final propagation to TOF radius"; + break; + } + + float tgPhi = trkWork.getSnp() / std::sqrt((1.f - trkWork.getSnp()) * (1.f + trkWork.getSnp())); + auto dy = clTOFxyz[1] - trkWork.getY(); + auto dz = clTOFxyz[2] - trkWork.getZ(); + if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(trkWork.getY()) < param::MaxY) && (std::abs(trkWork.getZ()) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) { + mClRes.emplace_back(dy, dz, tgPhi, trkWork.getY(), trkWork.getZ(), 170, clTOF.getCount(), clTOF.getPadInSector()); + trackData.clAvailTOF = 1; + trackData.nExtDetResid++; + } + break; + } + + // add ITS residuals + while (!stopPropagation) { + o2::track::TrackPar trkWorkITS{trackData.par}; // this is ITS outer param + auto nCl = trkITS.getNumberOfClusters(); + auto clEntry = trkITS.getFirstClusterEntry(); + auto geom = o2::its::GeometryTGeo::Instance(); + for (int iCl = 0; iCl < nCl; iCl++) { // clusters are stored from outer to inner layers + const auto& cls = mITSClustersArray[mITSTrackClusIdx[clEntry + iCl]]; + int chip = cls.getSensorID(); + float chipX, chipAlpha; + geom->getSensorXAlphaRefPlane(cls.getSensorID(), chipX, chipAlpha); + if (!trkWorkITS.rotate(chipAlpha) || !propagator->PropagateToXBxByBz(trkWorkITS, chipX, mParams->maxSnp, mParams->maxStep, mMatCorr)) { + LOGP(debug, "Failed final propagation to ITS X={} alpha={}", chipX, chipAlpha); + stopPropagation = true; + break; + } + float tgPhi = trkWorkITS.getSnp() / std::sqrt((1.f - trkWorkITS.getSnp()) * (1.f + trkWorkITS.getSnp())); + auto dy = cls.getY() - trkWorkITS.getY(); + auto dz = cls.getZ() - trkWorkITS.getZ(); + if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(trkWorkITS.getY()) < param::MaxY) && (std::abs(trkWorkITS.getZ()) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) { + mClRes.emplace_back(dy, dz, tgPhi, trkWorkITS.getY(), trkWorkITS.getZ(), 180 + geom->getLayer(cls.getSensorID()), -1, cls.getSensorID()); + trackData.nExtDetResid++; + } + } + break; + } + } mTrackData.push_back(std::move(trackData)); mGIDsSuccess.push_back(mGIDs[iSeed]); - mTrackDataCompact.emplace_back(mClRes.size() - nClValidated, nClValidated, mGIDs[iSeed].getSource()); + mTrackDataCompact.emplace_back(trackData.clIdx.getFirstEntry(), nClValidated, mGIDs[iSeed].getSource(), trackData.nExtDetResid); if (mDumpTrackPoints) { (*trackDataExtended).clIdx.setEntries(nClValidated); + (*trackDataExtended).nExtDetResid = trackData.nExtDetResid; mTrackDataExtended.push_back(std::move(*trackDataExtended)); } }