Project
Loading...
Searching...
No Matches
CosmicsClusterCollectorSpec.cxx
Go to the documentation of this file.
1// Copyright 2019-2026 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
53
54#include <vector>
55#include <array>
56#include <limits>
57#include <unordered_set>
58#include <algorithm>
59#include <numeric>
60#include <iterator>
61#include <cmath>
62#include "TStopwatch.h"
63#include "Framework/Task.h"
94#include "GPUO2InterfaceRefit.h"
100#include "MathUtils/Utils.h"
102#include "TPCFastTransformPOD.h"
104
105using namespace o2::framework;
109
110namespace o2::globaltracking
111{
112
113namespace
114{
115constexpr float TanSector = 0.17632698f; // tan(10 deg): half opening of a sector
116
118bool outsideDriftVolume(bool sideA, float z, float margin)
119{
120 const float zLength = TPCGeo::getTPCzLength();
121 return sideA ? (z < -margin || z > zLength + margin) : (z > margin || z < -zLength - margin);
122}
123
125int tpcSide(const o2::tpc::TrackTPC& trk)
126{
127 return trk.hasASideClustersOnly() ? 1 : (trk.hasCSideClustersOnly() ? -1 : 0);
128}
129} // namespace
130
132{
133 public:
134 CosmicsClusterCollectorSpec(std::shared_ptr<DataRequest> dr, std::shared_ptr<o2::base::GRPGeomRequest> gr, bool useMC, DetID::mask_t roadDets) : mDataRequest(dr), mGGCCDBRequest(gr), mRoadDets(roadDets), mUseMC(useMC) {}
135 ~CosmicsClusterCollectorSpec() override = default;
136 void init(InitContext& ic) final;
137 void run(ProcessingContext& pc) final;
138 void endOfStream(EndOfStreamContext& ec) final;
139 void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final;
140
141 private:
143 struct LegBranch {
144 bool isLine = false;
145 float centerX = 0.f;
146 float centerY = 0.f;
147 float pcaX = 0.f;
148 float pcaY = 0.f;
149 float dirX = 0.f;
150 float dirY = 0.f;
151 int sign = 0;
152 int side(float x, float y) const
153 {
154 const float orientation = isLine ? (x - pcaX) * dirX + (y - pcaY) * dirY : (pcaX - centerX) * (y - centerY) - (pcaY - centerY) * (x - centerX);
155 return orientation > 0.f ? 1 : -1;
156 }
157 bool accept(float x, float y) const { return sign == 0 || side(x, y) == sign; }
158 void init(const o2::track::TrackPar& inner, const o2::track::TrackPar& outer, float bz);
159 };
160
162 struct CosmicTime {
163 bool known = false;
164 float tb = 0.f;
165 float errTB = 0.f;
166 };
167
168 void updateTimeDependentParams(ProcessingContext& pc);
169 void fillTPCOccupancy(gsl::span<const unsigned int> occupancyMap);
170 float tpcOccupancy(float timeTB, bool after) const;
171 void buildUsedMap(const RecoContainer& data, std::vector<std::pair<float, float>>& time0Windows);
172 void addTPCAttached(const RecoContainer& data, const o2::tpc::TrackTPC& trk, std::vector<o2::dataformats::CosmicTPCCluster>& out, std::unordered_set<uint32_t>& taken) const;
174 struct RoadFrame {
175 float vertexTime;
176 float dz;
177 float zTolerance;
178 int sectorMin;
179 int sectorMax;
180 uint8_t flag;
181 };
182 void addTPCCorridor(const RecoContainer& data, const o2::tpc::TrackTPC& trk, const CosmicTime& cosmicTime, std::vector<o2::dataformats::CosmicTPCCluster>& out, std::unordered_set<uint32_t>& taken) const;
183 void walkLowAngleRoad(const o2::tpc::ClusterNativeAccess& clusters, const o2::track::TrackPar& start, bool innerPart, float rMid, const LegBranch& branch, const RoadFrame& frame,
184 std::vector<o2::dataformats::CosmicTPCCluster>& out, std::unordered_set<uint32_t>& taken) const;
185 void searchRow(const o2::tpc::ClusterNativeAccess& clusters, int sector, int row, float y, float z, float snp, float tgl, float vertexTime, float zTolerance, uint8_t flag,
186 float dxRow, std::vector<o2::dataformats::CosmicTPCCluster>& out, std::unordered_set<uint32_t>& taken) const;
187 void addDebugRoadPoint(int sector, float x, float y, float z, float snp, float tgl, int row, float vertexTime, int frameCode) const;
188 struct ITSPattRequest {
189 int cosmic;
190 int cluster;
191 int index;
192 };
193 void addITS(const RecoContainer& data, GTrackID gid, uint8_t leg, std::vector<o2::dataformats::CosmicITSCluster>& out, int icosm, std::vector<ITSPattRequest>& requests, std::unordered_set<int>& matched) const;
194 void fillITSPatterns(const RecoContainer& data, std::vector<ITSPattRequest>& requests, std::vector<o2::dataformats::TrackCosmicsExtended>& cosmics) const;
195 void addTOF(const RecoContainer& data, GTrackID gid, uint8_t leg, std::vector<o2::dataformats::CosmicTOFCluster>& out, std::unordered_set<int>& matched) const;
196 void addTRD(const RecoContainer& data, GTrackID gid, uint8_t leg, std::vector<o2::dataformats::CosmicTRDTracklet>& out, std::unordered_set<int>& matched) const;
197 std::pair<float, float> timeWindowMUS(const CosmicTime& cosmicTime) const;
198 bool predictOutward(const o2::tpc::TrackTPC& leg, const CosmicTime& cosmicTime, int sector, float x, float& y, float& z) const;
199 void roadTOF(const RecoContainer& data, const o2::tpc::TrackTPC* const* legs, const CosmicTime& cosmicTime, int icosm, std::vector<o2::dataformats::CosmicTOFCluster>& out, const std::unordered_set<int>& matched, float& timeTOFMUS, float& scoreTOFPair, float& scoreTOFReversed) const;
200 void polish(const o2::tpc::TrackTPC* const* legs, float timeMUS, o2::gpu::GPUO2InterfaceRefit& refitter, o2::dataformats::TrackCosmicsExtended& out) const;
201 void roadTRD(const RecoContainer& data, const o2::tpc::TrackTPC* const* legs, const CosmicTime& cosmicTime, int icosm, std::vector<o2::dataformats::CosmicTRDTracklet>& out, const std::unordered_set<int>& matched) const;
202 void roadITS(const RecoContainer& data, const o2::dataformats::TrackCosmics& cosm, const CosmicTime& cosmicTime, int legsSide, int icosm, std::vector<o2::dataformats::CosmicITSCluster>& out, std::vector<ITSPattRequest>& requests,
203 const std::unordered_set<int>& matched) const;
204 void cacheITSChipCentres();
205 void writeDebug(const o2::dataformats::TrackCosmicsExtended& cosm, int icosm) const;
206 void flagDuplicates(std::vector<o2::dataformats::TrackCosmicsExtended>& cosmics) const;
207 void writeDebugTOF(const o2::tof::Cluster& c, int icosm, int leg, uint8_t flags) const;
208
209 std::shared_ptr<DataRequest> mDataRequest;
210 std::shared_ptr<o2::base::GRPGeomRequest> mGGCCDBRequest;
211 const o2::gpu::TPCFastTransformPOD* mCorrMap = nullptr;
212 const o2::itsmft::TopologyDictionary* mITSDict = nullptr;
213 std::vector<bool> mUsed;
214 o2::InteractionRecord mTFStart{};
215 float mCorridor = 1.f;
216 float mMaxAbsTimeErr = 0.5f;
217 size_t mMaxCosmicsPerTF = 100;
218 DetID::mask_t mRoadDets{};
219 float mRoadTOF = 5.f;
220 float mTOFFlightTol = 2.f;
221 float mTOFTimeErr = 0.1f;
222 float mRoadTRD = 5.f;
223 float mRoadITS = 1.5f;
224 std::vector<o2::math_utils::Point3D<float>> mITSChipCentres;
225 float mTPCTBinMUS = 0.2f;
226 std::vector<uint64_t> mTPCOccCumul;
227 float mTPCOccBinTB = 16.f;
228 int mTPCOccBinsPerOrbit = 1;
229 float mTPCOccTF = -1.f;
230 float mBz = 0.f;
231 bool mUseMC = false;
232 mutable bool mStaggeredWarned = false;
233 mutable bool mNoDictWarned = false;
234 mutable bool mTRDMismatchWarned = false;
235 std::unique_ptr<o2::utils::TreeStreamRedirector> mDebugOut;
236 int mDbgTF = 0;
237 int mDbgCosmic = 0;
238 int mDbgLeg = 0;
239 float mDbgTauC = 0.f;
241 struct DebugCosmic {
242 std::vector<int> roadLeg;
243 std::vector<int> roadFrame;
244 std::vector<int> roadSector;
245 std::vector<int> roadRow;
246 std::vector<float> roadX;
247 std::vector<float> roadY;
248 std::vector<float> roadZ;
249 std::vector<float> roadZCos;
250 std::vector<float> roadGx;
251 std::vector<float> roadGy;
252 std::vector<float> roadSnp;
253 std::vector<float> roadTgl;
254 std::vector<int> tofLeg;
255 std::vector<int> tofChannel;
256 std::vector<int> tofFlags;
257 std::vector<double> tofTimeRaw;
258 std::vector<double> tofTime;
259 std::vector<float> tofTot;
260 std::vector<float> tofX;
261 std::vector<float> tofY;
262 std::vector<float> tofZ;
263 std::vector<float> tofGx;
264 std::vector<float> tofGy;
265 std::vector<int> trdLeg;
266 std::vector<int> trdLayer;
267 std::vector<float> trdX;
268 std::vector<float> trdY;
269 std::vector<float> trdZ;
270 std::vector<float> trdDy;
271 std::vector<float> trdDz;
272 std::vector<float> trdTrigMUS;
273 };
274 mutable std::vector<DebugCosmic> mDbgCosmics;
275 size_t mNCosmics = 0;
276 size_t mNClAttached = 0;
277 size_t mNClCorridor = 0;
278 TStopwatch mTimer;
279};
280
282{
283 mTimer.Stop();
284 mTimer.Reset();
286 mCorridor = ic.options().get<float>("corridor-width");
287 mMaxAbsTimeErr = ic.options().get<float>("max-abs-time-err");
288 mMaxCosmicsPerTF = std::max(0, ic.options().get<int>("max-cosmics-per-tf"));
289 mRoadTOF = ic.options().get<float>("tof-road-width");
290 mTOFFlightTol = ic.options().get<float>("tof-flight-tolerance");
291 mTOFTimeErr = ic.options().get<float>("tof-time-error");
292 mRoadTRD = ic.options().get<float>("trd-road-width");
293 mRoadITS = ic.options().get<float>("its-road-width");
294 if (ic.options().get<bool>("debug-tree")) {
295 const auto timesliceId = ic.services().get<const o2::framework::DeviceSpec>().inputTimesliceId;
296 const std::string name = timesliceId == 0 ? "cosmics_collector_debug.root" : fmt::format("cosmics_collector_debug_{}.root", timesliceId);
297 mDebugOut = std::make_unique<o2::utils::TreeStreamRedirector>(name.c_str(), "recreate");
298 }
299}
300
302{
303 mTimer.Start(false);
304 RecoContainer recoData;
305 recoData.collectData(pc, *mDataRequest.get());
306 updateTimeDependentParams(pc);
307
310 mTFStart = {0, tfID.firstTForbit};
312 tfInfo.vDrift = mCorrMap->getVDrift();
313 tfInfo.t0 = mCorrMap->getT0();
314 fillTPCOccupancy(recoData.occupancyMapTPC);
315 tfInfo.tpcOcc = mTPCOccTF;
316
317 std::vector<o2::dataformats::TrackCosmicsExtended> cosmicsOut;
318 const auto cosmics = recoData.getCosmicTracks();
319 const size_t nCosmics = std::min(cosmics.size(), mMaxCosmicsPerTF);
320 if (nCosmics < cosmics.size()) {
321 LOGP(warning, "{} cosmics in TF {}, only the first {} are written (max-cosmics-per-tf)", cosmics.size(), tfID.tfCounter, nCosmics);
322 }
323 if (nCosmics) {
324 // only TPC tracks whose time0 is within two drift times of a leg's time0 can share clusters with the roads
325 const float maxDistTB = 2.2f * TPCGeo::getTPCzLength() / mCorrMap->getVDrift();
326 std::vector<std::pair<float, float>> time0Windows;
327 for (size_t ic = 0; ic < nCosmics; ic++) {
328 for (const auto leg : {cosmics[ic].getRefBottom(), cosmics[ic].getRefTop()}) {
329 const auto refs = recoData.getSingleDetectorRefs(leg);
330 if (refs[GTrackID::TPC].isIndexSet()) {
331 const float time0 = recoData.getTPCTrack(refs[GTrackID::TPC]).getTime0();
332 time0Windows.emplace_back(time0 - maxDistTB, time0 + maxDistTB);
333 }
334 }
335 }
336 buildUsedMap(recoData, time0Windows);
337 }
338 std::vector<ITSPattRequest> pattRequests; // ITS clusters whose patterns are not in the dictionary
339 std::unique_ptr<o2::gpu::GPUO2InterfaceRefit> tpcRefitter; // polish of the cosmics with a TOF time
340 if (nCosmics && mRoadDets[DetID::TOF] && recoData.inputsTPCclusters) {
341 const auto& clusterShMap = recoData.clusterShMapTPC;
342 const auto& occupancyMap = recoData.occupancyMapTPC;
343 tpcRefitter = std::make_unique<o2::gpu::GPUO2InterfaceRefit>(&recoData.inputsTPCclusters->clusterIndex, mCorrMap, mBz, recoData.getTPCTracksClusterRefs().data(), 0,
344 clusterShMap.data(), occupancyMap.data(), occupancyMap.size(), nullptr, o2::base::Propagator::Instance());
345 }
346 mDbgTF = tfID.tfCounter;
347 if (mDebugOut) {
348 mDbgCosmics.assign(nCosmics, DebugCosmic{});
349 }
350 for (size_t ic = 0; ic < nCosmics; ic++) {
351 const auto& cosm = cosmics[ic];
352 auto& out = cosmicsOut.emplace_back();
353 out.cosmic = cosm;
354 if (mUseMC) {
355 out.label = recoData.getCosmicTrackMCLabel(ic);
356 }
357 const GTrackID legs[2] = {cosm.getRefBottom(), cosm.getRefTop()};
358 const o2::tpc::TrackTPC* tpcLegs[2] = {nullptr, nullptr};
359 std::vector<o2::dataformats::CosmicTPCCluster>* tpcCl[2] = {&out.clTPCBottom, &out.clTPCTop};
360 std::unordered_set<int> matchedITS; // hits of the legs' matched tracks, not searched again on the road
361 std::unordered_set<int> matchedTOF;
362 std::unordered_set<int> matchedTRD;
363 for (uint8_t leg = 0; leg < 2; leg++) {
364 auto refs = recoData.getSingleDetectorRefs(legs[leg]);
365 if (refs[GTrackID::TPC].isIndexSet()) {
366 tpcLegs[leg] = &recoData.getTPCTrack(refs[GTrackID::TPC]);
367 (leg == 0 ? out.tpcBottom : out.tpcTop) = *tpcLegs[leg];
368 }
369 if (refs[GTrackID::ITS].isIndexSet()) {
370 addITS(recoData, refs[GTrackID::ITS], leg, out.clITS, ic, pattRequests, matchedITS);
371 }
372 if (refs[GTrackID::TOF].isIndexSet()) {
373 addTOF(recoData, refs[GTrackID::TOF], leg, out.clTOF, matchedTOF);
374 if (mDebugOut) {
375 writeDebugTOF(recoData.getTOFClusters()[refs[GTrackID::TOF].getIndex()], ic, leg, o2::dataformats::HitMatched);
376 }
377 }
378 if (refs[GTrackID::TRD].isIndexSet()) {
379 addTRD(recoData, refs[GTrackID::TRD], leg, out.trdTracklets, matchedTRD);
380 }
381 }
382 CosmicTime cosmicTime;
383 cosmicTime.tb = cosm.getTimeMUS().getTimeStamp() / mTPCTBinMUS;
384 cosmicTime.errTB = cosm.getTimeMUS().getTimeStampError() / mTPCTBinMUS;
385 cosmicTime.known = cosm.getTimeMUS().getTimeStampError() < mMaxAbsTimeErr;
386 mDbgCosmic = ic;
387 // TOF road first: a top/bottom hit pair matching the muon's flight gives the cosmic's time to ~ns (also for one-side legs on the same
388 // side, whose brackets leave it open by tens of mus); the TPC corridor of the other side and the TRD / ITS roads then use that time
389 if (mRoadDets[DetID::TOF]) {
390 roadTOF(recoData, tpcLegs, cosmicTime, ic, out.clTOF, matchedTOF, out.timeTOFMUS, out.scoreTOFPair, out.scoreTOFReversed);
391 }
392 if (out.hasTOFTime() && tpcRefitter && legs[0].getSource() == GTrackID::TPC && legs[1].getSource() == GTrackID::TPC) {
393 polish(tpcLegs, out.timeTOFMUS, *tpcRefitter, out);
394 }
395 CosmicTime roadTime = cosmicTime;
396 if (out.hasTOFTime()) {
397 roadTime.tb = out.timeTOFMUS / mTPCTBinMUS;
398 roadTime.errTB = mTOFTimeErr / mTPCTBinMUS;
399 roadTime.known = true;
400 }
401 mDbgTauC = roadTime.tb;
402 out.tpcOccBef = tpcOccupancy(roadTime.tb, false);
403 out.tpcOccAft = tpcOccupancy(roadTime.tb, true);
404 std::unordered_set<uint32_t> taken;
405 for (int leg = 0; leg < 2; leg++) { // attached clusters of both legs first, so that a road never takes the other leg's clusters
406 if (tpcLegs[leg]) {
407 addTPCAttached(recoData, *tpcLegs[leg], *tpcCl[leg], taken);
408 mNClAttached += tpcCl[leg]->size();
409 }
410 }
411 for (int leg = 0; leg < 2; leg++) {
412 if (tpcLegs[leg]) {
413 const size_t nAttached = tpcCl[leg]->size();
414 mDbgLeg = leg;
415 addTPCCorridor(recoData, *tpcLegs[leg], roadTime, *tpcCl[leg], taken);
416 mNClCorridor += tpcCl[leg]->size() - nAttached;
417 }
418 }
419 if (mRoadDets[DetID::TRD]) {
420 roadTRD(recoData, tpcLegs, roadTime, ic, out.trdTracklets, matchedTRD);
421 }
422 if (mRoadDets[DetID::ITS]) {
423 // the refitted cosmic's z moves with the time only if both legs are TPC-only on one side (an ITS part fixes it absolutely)
424 int legsSide = 0;
425 if (tpcLegs[0] && tpcLegs[1] && legs[0].getSource() == GTrackID::TPC && legs[1].getSource() == GTrackID::TPC && tpcSide(*tpcLegs[0]) == tpcSide(*tpcLegs[1])) {
426 legsSide = tpcSide(*tpcLegs[0]);
427 }
428 roadITS(recoData, cosm, roadTime, legsSide, ic, out.clITS, pattRequests, matchedITS);
429 }
430 }
431 if (!pattRequests.empty()) {
432 fillITSPatterns(recoData, pattRequests, cosmicsOut);
433 }
434 flagDuplicates(cosmicsOut);
435 if (mDebugOut) {
436 for (size_t ic = 0; ic < cosmicsOut.size(); ic++) {
437 writeDebug(cosmicsOut[ic], ic);
438 }
439 }
440 mNCosmics += cosmicsOut.size();
441 LOGP(info, "Collected clusters for {} cosmics in TF {}", cosmicsOut.size(), tfID.tfCounter);
442 pc.outputs().snapshot(Output{"GLO", "COSMFULL", 0}, cosmicsOut);
443 pc.outputs().snapshot(Output{"GLO", "COSMFULLTF", 0}, tfInfo);
444 pc.outputs().snapshot(Output{"GLO", "COSMFULLTFID", 0}, tfID);
445 mTimer.Stop();
446}
447
448void CosmicsClusterCollectorSpec::updateTimeDependentParams(ProcessingContext& pc)
449{
451 mCorrMap = &o2::gpu::TPCFastTransformPOD::get(pc.inputs().get<const char*>("corrMap"));
452 mTPCTBinMUS = o2::tpc::ParameterElectronics::Instance().ZbinWidth;
453 mBz = o2::base::Propagator::Instance()->getNominalBz();
454 if (mRoadDets[DetID::ITS] && mITSChipCentres.empty()) {
455 cacheITSChipCentres();
456 }
457}
458
459void CosmicsClusterCollectorSpec::cacheITSChipCentres()
460{
462 geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G));
463 mITSChipCentres.resize(geom->getNumberOfChips());
464 for (int chip = 0; chip < geom->getNumberOfChips(); chip++) {
465 mITSChipCentres[chip] = geom->getMatrixL2G(chip) * o2::math_utils::Point3D<float>(0.f, 0.f, 0.f);
466 }
467}
468
469void CosmicsClusterCollectorSpec::buildUsedMap(const RecoContainer& data, std::vector<std::pair<float, float>>& time0Windows)
470{
471 // merge the windows, then flag the clusters of the TPC tracks whose time0 falls into one of them
472 std::sort(time0Windows.begin(), time0Windows.end());
473 std::vector<std::pair<float, float>> merged;
474 for (const auto& window : time0Windows) {
475 if (!merged.empty() && window.first <= merged.back().second) {
476 merged.back().second = std::max(merged.back().second, window.second);
477 } else {
478 merged.push_back(window);
479 }
480 }
481 const auto& clusters = data.getTPCClusters();
482 const auto tracks = data.getTPCTracks();
483 const auto refs = data.getTPCTracksClusterRefs();
484 mUsed.assign(clusters.nClustersTotal, false);
485 for (const auto& trk : tracks) {
486 const float time0 = trk.getTime0();
487 const auto window = std::upper_bound(merged.begin(), merged.end(), time0, [](float t, const std::pair<float, float>& w) { return t < w.first; });
488 if (window == merged.begin() || time0 > std::prev(window)->second) {
489 continue;
490 }
491 for (int j = 0; j < trk.getNClusterReferences(); j++) {
492 uint8_t sector = 0;
493 uint8_t row = 0;
494 uint32_t clIdx = 0;
495 trk.getClusterReference(refs, j, sector, row, clIdx);
496 mUsed[clusters.clusterOffset[sector][row] + clIdx] = true;
497 }
498 }
499}
500
501void CosmicsClusterCollectorSpec::fillTPCOccupancy(gsl::span<const unsigned int> occupancyMap)
502{
503 // TPC occupancy map (GPUChainTrackingSectorTracker): [0] clusters per orbit (average of the TF), [1] time bins per bin << 16 | averaging,
504 // [2...] clusters per bin (all clusters, by their time)
505 mTPCOccCumul.clear();
506 mTPCOccTF = occupancyMap.empty() ? -1.f : occupancyMap[0];
507 if (occupancyMap.size() < 3 || (occupancyMap[1] >> 16) == 0) { // no time bins in the map
508 return;
509 }
510 mTPCOccBinTB = occupancyMap[1] >> 16;
511 mTPCOccBinsPerOrbit = std::max(1, static_cast<int>(o2::constants::lhc::LHCMaxBunches / 8 / mTPCOccBinTB));
512 mTPCOccCumul.resize(occupancyMap.size() - 1, 0);
513 for (size_t bin = 2; bin < occupancyMap.size(); bin++) {
514 mTPCOccCumul[bin - 1] = mTPCOccCumul[bin - 2] + occupancyMap[bin];
515 }
516}
517
518float CosmicsClusterCollectorSpec::tpcOccupancy(float timeTB, bool after) const
519{
520 // TPC clusters in the map bins of one orbit before the bin of timeTB (after: from the bin of timeTB on); -1 if the window is not
521 // fully inside the TF, where the map would understate the occupancy
522 if (mTPCOccCumul.empty()) {
523 return -1.f;
524 }
525 const int nBins = static_cast<int>(mTPCOccCumul.size()) - 1;
526 const int bin = static_cast<int>(std::floor(timeTB / mTPCOccBinTB));
527 const int first = after ? bin : bin - mTPCOccBinsPerOrbit;
528 const int last = after ? bin + mTPCOccBinsPerOrbit : bin;
529 if (first < 0 || last > nBins) {
530 return -1.f;
531 }
532 return static_cast<float>(mTPCOccCumul[last] - mTPCOccCumul[first]);
533}
534
535void CosmicsClusterCollectorSpec::addTPCAttached(const RecoContainer& data, const o2::tpc::TrackTPC& trk, std::vector<o2::dataformats::CosmicTPCCluster>& out, std::unordered_set<uint32_t>& taken) const
536{
537 const auto& clusters = data.getTPCClusters();
538 const auto refs = data.getTPCTracksClusterRefs();
539 for (int j = 0; j < trk.getNClusterReferences(); j++) {
540 uint8_t sector = 0;
541 uint8_t row = 0;
542 uint32_t clIdx = 0;
543 trk.getClusterReference(refs, j, sector, row, clIdx);
544 if (!taken.insert(clusters.clusterOffset[sector][row] + clIdx).second) {
545 continue;
546 }
547 auto& cl = out.emplace_back();
548 cl.cl = clusters.clusters[sector][row][clIdx];
549 cl.sector = sector;
550 cl.row = row;
552 }
553}
554
555void CosmicsClusterCollectorSpec::LegBranch::init(const o2::track::TrackPar& inner, const o2::track::TrackPar& outer, float bz)
556{
557 if (std::abs(inner.getCurvature(bz)) < 1e-5f) { // radius > 1 km: straight line
558 isLine = true;
559 const auto point = inner.getXYZGlo();
560 const float phi = inner.getPhi();
561 dirX = std::cos(phi);
562 dirY = std::sin(phi);
563 const float proj = point.X() * dirX + point.Y() * dirY;
564 pcaX = point.X() - proj * dirX;
565 pcaY = point.Y() - proj * dirY;
566 } else {
568 float sinAlpha = 0.f;
569 float cosAlpha = 0.f;
570 inner.getCircleParams(bz, circle, sinAlpha, cosAlpha);
571 centerX = circle.xC;
572 centerY = circle.yC;
573 const float centerDist = std::sqrt(centerX * centerX + centerY * centerY);
574 if (centerDist < 1e-3f) { // circle around the beam line: no closest approach
575 sign = 0;
576 return;
577 }
578 pcaX = centerX * (1.f - circle.rC / centerDist);
579 pcaY = centerY * (1.f - circle.rC / centerDist);
580 }
581 // side of the leg from its end farther from the closest approach; a leg with ends on both sides (both > 10 cm away) spans the
582 // closest approach and is accepted on both sides
583 const auto pointIn = inner.getXYZGlo();
584 const auto pointOut = outer.getXYZGlo();
585 const float distIn2 = (pointIn.X() - pcaX) * (pointIn.X() - pcaX) + (pointIn.Y() - pcaY) * (pointIn.Y() - pcaY);
586 const float distOut2 = (pointOut.X() - pcaX) * (pointOut.X() - pcaX) + (pointOut.Y() - pcaY) * (pointOut.Y() - pcaY);
587 const int sideIn = side(pointIn.X(), pointIn.Y());
588 const int sideOut = side(pointOut.X(), pointOut.Y());
589 constexpr float MinDist2 = 10.f * 10.f;
590 if (sideIn == sideOut) {
591 sign = sideIn;
592 } else if (std::min(distIn2, distOut2) < MinDist2) {
593 sign = distIn2 > distOut2 ? sideIn : sideOut;
594 } else {
595 sign = 0;
596 }
597}
598
599void CosmicsClusterCollectorSpec::addTPCCorridor(const RecoContainer& data, const o2::tpc::TrackTPC& trk, const CosmicTime& cosmicTime, std::vector<o2::dataformats::CosmicTPCCluster>& out, std::unordered_set<uint32_t>& taken) const
600{
601 constexpr int NSectorsA = TPCGeo::getNumberOfSectorsA();
602 const float zLength = TPCGeo::getTPCzLength();
603 const float vDrift = mCorrMap->getVDrift();
604 const auto& clusters = data.getTPCClusters();
605 const float time0Leg = trk.getTime0();
606 const int legSide = tpcSide(trk);
607
608 RoadFrame frames[2];
609 int nFrames = 0;
610 if (legSide == 0) { // CE-crossing leg: its time0 is absolute
611 frames[nFrames++] = {time0Leg, 0.f, 0.f, 0, 2 * NSectorsA, 0};
612 } else {
613 frames[nFrames++] = {time0Leg, 0.f, 0.f, legSide > 0 ? 0 : NSectorsA, legSide > 0 ? NSectorsA : 2 * NSectorsA, 0};
614 if (cosmicTime.known) { // z of a one-side leg moves by side * vD * (t - time0) when its clusters are transformed with the time t
615 frames[nFrames++] = {cosmicTime.tb, legSide * (cosmicTime.tb - time0Leg) * vDrift, cosmicTime.errTB * vDrift,
616 legSide > 0 ? NSectorsA : 0, legSide > 0 ? 2 * NSectorsA : NSectorsA, o2::dataformats::CosmicTPCCluster::AbsTime};
617 }
618 }
619
620 const o2::track::TrackPar refPar[2] = {trk, trk.getParamOut()}; // rows below rMid: inner parameter, above: outer
621 LegBranch branch;
622 branch.init(refPar[0], refPar[1], mBz);
623 const auto pointIn = refPar[0].getXYZGlo();
624 const auto pointOut = refPar[1].getXYZGlo();
625 const float rIn = std::hypot(pointIn.X(), pointIn.Y());
626 const float rOut = std::hypot(pointOut.X(), pointOut.Y());
627 const float rMid = 0.5f * (rIn + rOut);
628
629 for (int iFrame = 0; iFrame < nFrames; iFrame++) {
630 const auto& frame = frames[iFrame];
631 for (int sector = frame.sectorMin; sector < frame.sectorMax; sector++) {
632 const float alpha = o2::math_utils::sector2Angle(sector % NSectorsA);
633 const float sinAlpha = std::sin(alpha);
634 const float cosAlpha = std::cos(alpha);
635 const bool sideA = sector < NSectorsA;
636 for (int iPar = 0; iPar < 2; iPar++) {
637 auto par = refPar[iPar];
638 par.setZ(par.getZ() + frame.dz);
639 if (!par.rotateParam(alpha)) { // the leg points away from this sector frame: same helix, opposite direction
640 par.invertParam();
641 if (!par.rotateParam(alpha)) {
642 continue;
643 }
644 }
645 for (int row = 0; row < o2::tpc::constants::MAXGLOBALPADROW; row++) {
646 const float xRow = TPCGeo::getRowInfoX(row);
647 if ((xRow < rMid) != (iPar == 0)) {
648 continue;
649 }
650 auto parRow = par;
651 if (!parRow.propagateParamTo(xRow, mBz)) {
652 continue;
653 }
654 // coarse acceptance at the nominal x of the row before the more expensive move to its real x
655 constexpr float CoarseMargin = 10.f; // [cm] covers the shift of y and z between the nominal and the real x
656 if (std::abs(parRow.getY()) > xRow * TanSector + mCorridor + CoarseMargin || outsideDriftVolume(sideA, parRow.getZ(), mCorridor + frame.zTolerance + CoarseMargin)) {
657 continue;
658 }
659 // the corrected clusters of this row lie at the real x of the row, x + dx(y, z), not at its nominal x
660 float xReal = xRow;
661 const float zInside = sideA ? std::clamp(parRow.getZ(), 0.f, zLength) : std::clamp(parRow.getZ(), -zLength, 0.f);
662 mCorrMap->InverseTransformYZtoX(sector, row, parRow.getY(), zInside, xReal);
663 if (!parRow.propagateParamTo(xReal, mBz)) {
664 continue;
665 }
666 const float y = parRow.getY();
667 const float z = parRow.getZ();
668 if (std::abs(y) > xRow * TanSector + mCorridor) {
669 continue;
670 }
671 if (outsideDriftVolume(sideA, z, mCorridor + frame.zTolerance)) {
672 continue;
673 }
674 if (!branch.accept(xRow * cosAlpha - y * sinAlpha, xRow * sinAlpha + y * cosAlpha)) {
675 continue;
676 }
677 if (mDebugOut) {
678 addDebugRoadPoint(sector, xReal, y, z, parRow.getSnp(), parRow.getTgl(), row, frame.vertexTime, frame.flag != 0);
679 }
680 searchRow(clusters, sector, row, y, z, parRow.getSnp(), parRow.getTgl(), frame.vertexTime, frame.zTolerance, frame.flag, 0.f, out, taken);
681 }
682 }
683 }
684 // parts of the leg nearly parallel to the pad rows, which the propagation row by row cannot reach (e.g. a closest approach to the
685 // beam line inside the TPC: in the sector of the closest approach the track runs along one pad row); a leg whose inner end is not
686 // inside its outer end (all its clusters along one pad row) is walked from the inner end alone
687 const bool splitAtRMid = rOut > rIn;
688 for (int iPar = 0; iPar < (splitAtRMid ? 2 : 1); iPar++) {
689 auto par = refPar[iPar];
690 par.setZ(par.getZ() + frame.dz);
691 walkLowAngleRoad(clusters, par, iPar == 0, splitAtRMid ? rMid : std::numeric_limits<float>::max(), branch, frame, out, taken);
692 }
693 }
694}
695
696void CosmicsClusterCollectorSpec::walkLowAngleRoad(const o2::tpc::ClusterNativeAccess& clusters, const o2::track::TrackPar& start, bool innerPart, float rMid, const LegBranch& branch,
697 const RoadFrame& frame, std::vector<o2::dataformats::CosmicTPCCluster>& out, std::unordered_set<uint32_t>& taken) const
698{
699 constexpr int NSectorsA = TPCGeo::getNumberOfSectorsA();
700 constexpr float CosSector = 0.98480775f; // cos(10 deg)
701 constexpr float Step = 0.5f; // [cm] path length per step, below the road width along the pad row
702 constexpr float MaxPath = 600.f; // [cm] protection; the walk normally ends at the TPC boundary or the closest approach
703 constexpr float MaxEntryPath = 20.f; // [cm] a start outside the leg's part (up to 10 cm past the closest approach, see LegBranch) is stepped into
704 constexpr float MinSnp = 0.8f; // below, the row crossings are well defined and found row by row
705 constexpr float XMargin = 10.f; // [cm] covers the shift between the nominal and the real x of a row (CoarseMargin of the row loop)
706 constexpr int NRows = o2::tpc::constants::MAXGLOBALPADROW;
707 const float zLength = TPCGeo::getTPCzLength();
708 const float rLow = TPCGeo::getRowInfoX(0) - mCorridor;
709 const float rHigh = TPCGeo::getRowInfoX(NRows - 1) / CosSector + mCorridor;
710 std::array<float, NSectorsA> sinSector{};
711 std::array<float, NSectorsA> cosSector{};
712 for (int sector = 0; sector < NSectorsA; sector++) {
713 const float alpha = o2::math_utils::sector2Angle(sector);
714 sinSector[sector] = std::sin(alpha);
715 cosSector[sector] = std::cos(alpha);
716 }
717
718 bool startInside = false; // the start point lies in the leg's part
719 for (int direction = 0; direction < 2; direction++) {
720 auto par = start;
721 if (direction == 1) {
722 par.invertParam();
723 }
724 bool entered = direction == 1 && startInside; // the walk has reached the leg's part
725 // the start point is processed in the first direction only
726 for (float path = direction == 0 ? 0.f : Step; path < MaxPath; path += Step) {
727 // step along the helix in the frame of its direction (snp = 0), where the propagation is always defined
728 if (path > 0.f && (!par.rotateParam(par.getPhi()) || !par.propagateParamTo(par.getX() + Step, mBz))) {
729 break;
730 }
731 const auto pos = par.getXYZGlo();
732 const float r = std::hypot(pos.X(), pos.Y());
733 if (r > rHigh || std::abs(pos.Z()) > zLength + mCorridor + frame.zTolerance) {
734 break;
735 }
736 // outside the leg's part: past its closest approach to the beam line, beyond rMid or inside the inner radius; the start of the walk
737 // can lie there (a leg's end a few cm past the closest approach, an inner end inside the nominal inner radius)
738 if (r < rLow || (r < rMid) != innerPart || !branch.accept(pos.X(), pos.Y())) {
739 if (entered || path > MaxEntryPath) {
740 break;
741 }
742 continue;
743 }
744 entered = true;
745 startInside = startInside || path == 0.f;
746 const float phiDir = par.getPhi();
747 const int sectorPos = o2::math_utils::angle2Sector(std::atan2(pos.Y(), pos.X()));
748 for (int dSector = -1; dSector <= 1; dSector++) { // the road can reach into the neighbouring sectors
749 const int sectorInSide = (sectorPos + dSector + NSectorsA) % NSectorsA;
750 for (int sector = sectorInSide; sector < 2 * NSectorsA; sector += NSectorsA) {
751 if (sector < frame.sectorMin || sector >= frame.sectorMax) {
752 continue;
753 }
754 const bool sideA = sector < NSectorsA;
755 if (outsideDriftVolume(sideA, pos.Z(), mCorridor + frame.zTolerance)) {
756 continue;
757 }
758 const float alpha = o2::math_utils::sector2Angle(sectorInSide);
759 const float sinAlpha = sinSector[sectorInSide];
760 const float cosAlpha = cosSector[sectorInSide];
761 const float x = pos.X() * cosAlpha + pos.Y() * sinAlpha;
762 const float y = -pos.X() * sinAlpha + pos.Y() * cosAlpha;
763 float snp = std::sin(phiDir - alpha);
764 float tgl = par.getTgl();
765 if (std::cos(phiDir - alpha) < 0.f) { // the same line with its direction along +x of the sector frame, as in the row search
766 snp = -snp;
767 tgl = -tgl;
768 }
769 if (std::abs(snp) < MinSnp || std::abs(y) > x * TanSector + mCorridor) {
770 continue;
771 }
772 const float zInside = sideA ? std::clamp(pos.Z(), 0.f, zLength) : std::clamp(pos.Z(), -zLength, 0.f);
773 // rows whose real x lies within the road width of the point: the first by bisection on the nominal x, which grows with the row
774 int rowFirst = 0;
775 int rowEnd = NRows;
776 while (rowFirst < rowEnd) {
777 const int rowMid = (rowFirst + rowEnd) / 2;
778 if (TPCGeo::getRowInfoX(rowMid) < x - mCorridor - XMargin) {
779 rowFirst = rowMid + 1;
780 } else {
781 rowEnd = rowMid;
782 }
783 }
784 for (int row = rowFirst; row < NRows && TPCGeo::getRowInfoX(row) <= x + mCorridor + XMargin; row++) {
785 float xReal = TPCGeo::getRowInfoX(row);
786 mCorrMap->InverseTransformYZtoX(sector, row, y, zInside, xReal);
787 const float dxRow = xReal - x;
788 if (std::abs(dxRow) > mCorridor) {
789 continue;
790 }
791 if (mDebugOut) {
792 addDebugRoadPoint(sector, x, y, pos.Z(), snp, tgl, row, frame.vertexTime, 2 + (frame.flag != 0));
793 }
794 searchRow(clusters, sector, row, y, pos.Z(), snp, tgl, frame.vertexTime, frame.zTolerance, frame.flag, dxRow, out, taken);
795 }
796 }
797 }
798 }
799 }
800}
801
802void CosmicsClusterCollectorSpec::searchRow(const o2::tpc::ClusterNativeAccess& clusters, int sector, int row, float y, float z, float snp, float tgl, float vertexTime, float zTolerance, uint8_t flag,
803 float dxRow, std::vector<o2::dataformats::CosmicTPCCluster>& out, std::unordered_set<uint32_t>& taken) const
804{
805 // (y, z) is the predicted point, dxRow the real x of the row minus the x of that point (0 when the point lies on the row)
806 const float zLength = TPCGeo::getTPCzLength();
807 const float vDrift = mCorrMap->getVDrift();
808 const float t0 = mCorrMap->getT0();
809 // nominal (measured) coordinates of the predicted real point; the correction is evaluated inside the drift volume
810 const float zInside = sector < TPCGeo::getNumberOfSectorsA() ? std::clamp(z, 0.f, zLength) : std::clamp(z, -zLength, 0.f);
811 float yNominal = 0.f;
812 float zNominal = 0.f;
813 mCorrMap->InverseTransformYZtoNominalYZ(sector, row, y, zInside, yNominal, zNominal);
814 zNominal += z - zInside;
815 float padPred = 0.f;
816 float driftLengthPred = 0.f;
817 TPCGeo::convLocalToPadDriftLength(sector, row, yNominal, zNominal, padPred, driftLengthPred);
818 const float timePred = driftLengthPred / vDrift + t0 + vertexTime;
819 // the road is a cylinder around the track; its section with the pad-row plane is an ellipse with half-axes W / cos(phi) in y and
820 // W * sqrt(cos^2(phi) + tgl^2) / cos(phi) in z
821 const float cosPhi = std::sqrt((1.f - snp) * (1.f + snp));
822 const float cosPhiWindow = std::max(cosPhi, 0.1f); // limits the window for tracks nearly parallel to the pad row
823 const float norm = 1.f / std::sqrt(1.f + tgl * tgl);
824 const float dirX = cosPhi * norm; // track direction
825 const float dirY = snp * norm;
826 const float dirZ = tgl * norm;
827 const float corridor2 = mCorridor * mCorridor;
828 const float windowPad = mCorridor / (cosPhiWindow * TPCGeo::getRowInfoPadWidth(row));
829 const float windowTime = (mCorridor * std::sqrt(cosPhiWindow * cosPhiWindow + tgl * tgl) / cosPhiWindow + zTolerance) / vDrift;
830 const auto* rowClusters = clusters.clusters[sector][row];
831 const uint32_t rowOffset = clusters.clusterOffset[sector][row];
832 for (uint32_t k = 0; k < clusters.nClusters[sector][row]; k++) {
833 const auto& c = rowClusters[k];
834 const float time = c.getTime();
835 const float pad = c.getPad();
836 if (std::abs(time - timePred) > windowTime || std::abs(pad - padPred) > windowPad) {
837 continue;
838 }
839 float yCluster = 0.f;
840 float zCluster = 0.f;
841 TPCGeo::convPadDriftLengthToLocal(sector, row, pad, (time - t0 - vertexTime) * vDrift, yCluster, zCluster);
842 const float dy = yCluster - yNominal;
843 float dz = zCluster - zNominal;
844 if (zTolerance > 0.f) { // the vertex time of this frame is known within zTolerance / vD: allow that shift along z
845 dz = std::abs(dz) > zTolerance ? dz - std::copysign(zTolerance, dz) : 0.f;
846 }
847 const float proj = dxRow * dirX + dy * dirY + dz * dirZ;
848 if (dxRow * dxRow + dy * dy + dz * dz - proj * proj > corridor2) { // distance perpendicular to the track
849 continue;
850 }
851 if (!taken.insert(rowOffset + k).second) {
852 continue;
853 }
854 auto& cl = out.emplace_back();
855 cl.cl = c;
856 cl.sector = sector;
857 cl.row = row;
858 cl.flags = o2::dataformats::CosmicTPCCluster::Corridor | flag | (mUsed[rowOffset + k] ? o2::dataformats::CosmicTPCCluster::Used : 0);
859 }
860}
861
862void CosmicsClusterCollectorSpec::addDebugRoadPoint(int sector, float x, float y, float z, float snp, float tgl, int row, float vertexTime, int frameCode) const
863{
864 // predicted point, z in the frame of the road and in the common frame of the cosmic's time
866 const float sinAlpha = std::sin(alpha);
867 const float cosAlpha = std::cos(alpha);
868 const bool sideA = sector < TPCGeo::getNumberOfSectorsA();
869 auto& dbg = mDbgCosmics[mDbgCosmic];
870 dbg.roadLeg.push_back(mDbgLeg);
871 dbg.roadFrame.push_back(frameCode);
872 dbg.roadSector.push_back(sector);
873 dbg.roadRow.push_back(row);
874 dbg.roadX.push_back(x);
875 dbg.roadY.push_back(y);
876 dbg.roadZ.push_back(z);
877 dbg.roadZCos.push_back(z + (sideA ? 1.f : -1.f) * (mDbgTauC - vertexTime) * mCorrMap->getVDrift());
878 dbg.roadGx.push_back(x * cosAlpha - y * sinAlpha);
879 dbg.roadGy.push_back(x * sinAlpha + y * cosAlpha);
880 dbg.roadSnp.push_back(snp);
881 dbg.roadTgl.push_back(tgl);
882}
883
884void CosmicsClusterCollectorSpec::addITS(const RecoContainer& data, GTrackID gid, uint8_t leg, std::vector<o2::dataformats::CosmicITSCluster>& out, int icosm, std::vector<ITSPattRequest>& requests, std::unordered_set<int>& matched) const
885{
886 if (gid.getSource() != GTrackID::ITS) { // ITS-AB tracklets are not stored
887 return;
888 }
889 if (data.getITSPerLayer()) {
890 if (!mStaggeredWarned) {
891 LOGP(warning, "Staggered ITS clusters are not supported, ITS clusters of cosmics are not stored");
892 mStaggeredWarned = true;
893 }
894 return;
895 }
896 const auto& trk = data.getITSTrack(gid);
897 const auto refs = data.getITSTracksClusterRefs();
898 const auto clusters = data.getITSClusters();
899 const auto rofs = data.getITSClustersROFRecords();
900 for (int i = 0; i < trk.getNumberOfClusters(); i++) {
901 const int idx = refs[trk.getFirstClusterEntry() + i];
902 const auto& c = clusters[idx];
903 auto& cl = out.emplace_back();
904 cl.chipID = c.getSensorID();
905 cl.row = c.getRow();
906 cl.col = c.getCol();
907 cl.pattID = c.getPatternID();
908 cl.leg = leg;
910 matched.insert(idx);
911 if (c.getPatternID() == o2::itsmft::CompCluster::InvalidPatternID || (mITSDict && mITSDict->isGroup(c.getPatternID()))) {
912 requests.push_back({icosm, int(out.size()) - 1, idx}); // the pattern is in the TF's pattern stream, copied by fillITSPatterns
913 }
914 auto rof = std::upper_bound(rofs.begin(), rofs.end(), idx, [](int v, const o2::itsmft::ROFRecord& r) { return v < r.getFirstEntry(); });
915 if (rof != rofs.begin()) {
916 cl.rofBC = std::prev(rof)->getBCData().differenceInBC(mTFStart);
917 }
918 }
919}
920
921void CosmicsClusterCollectorSpec::addTOF(const RecoContainer& data, GTrackID gid, uint8_t leg, std::vector<o2::dataformats::CosmicTOFCluster>& out, std::unordered_set<int>& matched) const
922{
923 const auto& c = data.getTOFClusters()[gid.getIndex()];
924 auto& cl = out.emplace_back();
925 cl.timeRaw = c.getTimeRaw();
926 cl.tot = c.getTot();
927 cl.channel = c.getMainContributingChannel();
928 cl.leg = leg;
930 matched.insert(gid.getIndex());
931}
932
933std::pair<float, float> CosmicsClusterCollectorSpec::timeWindowMUS(const CosmicTime& cosmicTime) const
934{
935 // a time fixed by z continuity is given with a 1 sigma error, otherwise the error is the half-width of the legs' time-bracket overlap
936 const float timeMUS = cosmicTime.tb * mTPCTBinMUS;
937 const float errMUS = cosmicTime.errTB * mTPCTBinMUS;
938 const float halfWidth = cosmicTime.known ? 5.f * errMUS + 0.2f : errMUS;
939 return {timeMUS - halfWidth, timeMUS + halfWidth};
940}
941
942bool CosmicsClusterCollectorSpec::predictOutward(const o2::tpc::TrackTPC& leg, const CosmicTime& cosmicTime, int sector, float x, float& y, float& z) const
943{
944 // outward continuation of a leg in the frame of a sector; z in the frame of the cosmic's time (a one-side TPC-only leg has z relative to
945 // its time0)
946 o2::track::TrackPar par = leg.getParamOut();
947 if (!par.rotateParam(o2::math_utils::sector2Angle(sector % TPCGeo::getNumberOfSectorsA())) || !par.propagateParamTo(x, mBz)) {
948 return false;
949 }
950 y = par.getY();
951 z = par.getZ() + tpcSide(leg) * (cosmicTime.tb - leg.getTime0()) * mCorrMap->getVDrift();
952 return true;
953}
954
955void CosmicsClusterCollectorSpec::roadTOF(const RecoContainer& data, const o2::tpc::TrackTPC* const* legs, const CosmicTime& cosmicTime, int icosm, std::vector<o2::dataformats::CosmicTOFCluster>& out, const std::unordered_set<int>& matched, float& timeTOFMUS, float& scoreTOFPair, float& scoreTOFReversed) const
956{
957 // TOF clusters along the legs' outward continuations (the road in PbPb also contains hits of collision tracks). Preferred: the top/bottom
958 // pair whose time difference matches the muon's flight between them; its mean time fixes the cosmic's time, also when the legs' brackets
959 // leave it open by tens of mus (one-side legs on the same side), and the z of one-side legs is shifted to it. Otherwise per leg the
960 // cluster closest to its continuation. The same search in the impossible order (bottom hit first) only finds accidental pairs: its best
961 // score is kept as QA of the flag's background.
962 constexpr float MaxFlightMUS = 0.1f; // flight time of the muon between the TPC and the TOF, slow tails
963 constexpr float CmPerNS = 29.9792458f;
964 const auto window = timeWindowMUS(cosmicTime);
965 const float zTimeTol = 0.5f * (window.second - window.first) / mTPCTBinMUS * mCorrMap->getVDrift(); // z uncertainty of one-side legs
966 const float vDriftPerMUS = mCorrMap->getVDrift() / mTPCTBinMUS;
967 const float cosmicTimeMUS = cosmicTime.tb * mTPCTBinMUS;
968 int legSide[2] = {0, 0};
969 float curvature = 0.f; // |1/R| of the muon [1/cm] for the flight path between the TOF hits
970 int nLegs = 0;
971 for (int leg = 0; leg < 2; leg++) {
972 if (legs[leg]) {
973 legSide[leg] = tpcSide(*legs[leg]);
974 curvature += std::abs(legs[leg]->getCurvature(mBz));
975 nLegs++;
976 }
977 }
978 curvature = nLegs ? curvature / nLegs : 0.f;
979 struct Candidate {
980 int index;
981 bool matched; // hit of the leg's matched global track (already stored)
982 double timeNS; // since the start of the TF
983 float dy;
984 float dzAtCosmicTime;
985 float gx;
986 float gy;
987 float gz;
988 };
989 std::vector<Candidate> candidates[2];
990 const auto clusters = data.getTOFClusters();
991 int best[2] = {-1, -1};
992 float bestScore[2] = {1.f, 1.f};
993 for (int i = 0; i < (int)clusters.size(); i++) {
994 const auto& c = clusters[i];
995 const float timeMUS = c.getTime() * 1e-6f; // [ps] since the start of the TF
996 if (timeMUS < window.first - MaxFlightMUS || timeMUS > window.second + MaxFlightMUS) {
997 continue;
998 }
999 const bool isMatched = matched.count(i) > 0;
1000 const float alpha = o2::math_utils::sector2Angle(c.getSector());
1001 const float sinAlpha = std::sin(alpha);
1002 const float cosAlpha = std::cos(alpha);
1003 for (int leg = 0; leg < 2; leg++) {
1004 float y = 0.f;
1005 float z = 0.f;
1006 if (!legs[leg] || !predictOutward(*legs[leg], cosmicTime, c.getSector(), c.getX(), y, z)) {
1007 continue;
1008 }
1009 const float normY = (c.getY() - y) / mRoadTOF;
1010 const float normZ = (c.getZ() - z) / (mRoadTOF + zTimeTol);
1011 const float score = std::max(normY * normY, normZ * normZ);
1012 if (score >= 1.f) {
1013 continue;
1014 }
1015 if (!isMatched && score < bestScore[leg]) {
1016 bestScore[leg] = score;
1017 best[leg] = i;
1018 }
1019 candidates[leg].push_back(Candidate{i, isMatched, c.getTime() * 1e-3, c.getY() - y, c.getZ() - z, c.getX() * cosAlpha - c.getY() * sinAlpha,
1020 c.getX() * sinAlpha + c.getY() * cosAlpha, c.getZ()});
1021 }
1022 }
1023 float bestPairScore = -1.f;
1024 float bestReversedScore = -1.f;
1025 int bestPair[2] = {-1, -1};
1026 for (const auto& c0 : candidates[0]) {
1027 for (const auto& c1 : candidates[1]) {
1028 if (c0.index == c1.index) {
1029 continue;
1030 }
1031 // top / bottom by the hits' height: a muon goes down (for near-horizontal cosmics both orders are possible, but rare)
1032 const auto& top = c0.gy > c1.gy ? c0 : c1;
1033 const auto& bottom = c0.gy > c1.gy ? c1 : c0;
1034 // flight path along the helix: arc in the transverse plane from the chord, then the dip
1035 const float chordXY = std::hypot(top.gx - bottom.gx, top.gy - bottom.gy);
1036 const float halfAngleSin = 0.5f * curvature * chordXY;
1037 const float arcXY = halfAngleSin > 1e-4f && halfAngleSin < 1.f ? 2.f * std::asin(halfAngleSin) / curvature : chordXY;
1038 const float length = std::hypot(arcXY, top.gz - bottom.gz);
1039 const float timeDiff = float(top.timeNS - bottom.timeNS);
1040 const float flightDev = timeDiff + length / CmPerNS; // the muon crosses the top TOF first
1041 const float reversedDev = timeDiff - length / CmPerNS; // bottom hit first: impossible for a muon, only accidental pairs
1042 const bool reversed = std::abs(flightDev) > mTOFFlightTol; // the two cannot both pass (L/c ~ 25 ns >> tolerance)
1043 if (reversed && std::abs(reversedDev) > mTOFFlightTol) {
1044 continue;
1045 }
1046 const double pairTimeNS = 0.5 * (c0.timeNS + c1.timeNS);
1047 const float shiftMUS = float(pairTimeNS * 1e-3) - cosmicTimeMUS;
1048 const float dz0 = c0.dzAtCosmicTime - legSide[0] * shiftMUS * vDriftPerMUS;
1049 const float dz1 = c1.dzAtCosmicTime - legSide[1] * shiftMUS * vDriftPerMUS;
1050 if (std::abs(dz0) > mRoadTOF || std::abs(dz1) > mRoadTOF) {
1051 continue;
1052 }
1053 const float timeDev = reversed ? reversedDev : flightDev;
1054 const float score = (c0.dy * c0.dy + c1.dy * c1.dy + dz0 * dz0 + dz1 * dz1) / (mRoadTOF * mRoadTOF) + timeDev * timeDev / (mTOFFlightTol * mTOFFlightTol);
1055 if (reversed) {
1056 if (bestReversedScore < 0.f || score < bestReversedScore) {
1057 bestReversedScore = score;
1058 }
1059 continue;
1060 }
1061 if (bestPairScore < 0.f || score < bestPairScore) {
1062 bestPairScore = score;
1063 bestPair[0] = c0.index;
1064 bestPair[1] = c1.index;
1065 timeTOFMUS = float(pairTimeNS * 1e-3);
1066 }
1067 }
1068 }
1069 scoreTOFPair = bestPairScore;
1070 scoreTOFReversed = bestReversedScore;
1072 if (bestPairScore >= 0.f) {
1073 best[0] = bestPair[0];
1074 best[1] = bestPair[1];
1076 } else if (best[0] >= 0 && best[0] == best[1]) { // one TOF cluster in the roads of both legs: keep it for the closer one
1077 best[bestScore[0] <= bestScore[1] ? 1 : 0] = -1;
1078 }
1079 for (int leg = 0; leg < 2; leg++) {
1080 if (best[leg] < 0) {
1081 continue;
1082 }
1083 const auto& c = clusters[best[leg]];
1084 if (matched.count(best[leg])) { // the leg's matched hit, already stored: flag it as part of the flight pair
1085 for (auto& cl : out) {
1086 if (cl.leg == leg && cl.channel == c.getMainContributingChannel() && cl.timeRaw == c.getTimeRaw()) {
1088 }
1089 }
1090 if (mDebugOut) {
1091 auto& dbg = mDbgCosmics[icosm];
1092 for (size_t j = 0; j < dbg.tofFlags.size(); j++) {
1093 if (dbg.tofLeg[j] == leg && dbg.tofChannel[j] == c.getMainContributingChannel() && dbg.tofTimeRaw[j] == c.getTimeRaw()) {
1094 dbg.tofFlags[j] |= o2::dataformats::HitTOFFlight;
1095 }
1096 }
1097 }
1098 continue;
1099 }
1100 auto& cl = out.emplace_back();
1101 cl.timeRaw = c.getTimeRaw();
1102 cl.tot = c.getTot();
1103 cl.channel = c.getMainContributingChannel();
1104 cl.leg = leg;
1105 cl.flags = flags;
1106 if (mDebugOut) {
1107 writeDebugTOF(c, icosm, leg, flags);
1108 }
1109 }
1110}
1111
1112void CosmicsClusterCollectorSpec::polish(const o2::tpc::TrackTPC* const* legs, float timeMUS, o2::gpu::GPUO2InterfaceRefit& refitter, o2::dataformats::TrackCosmicsExtended& out) const
1113{
1114 // refit of a cosmic with TPC-only legs at its TOF time, as MatchCosmics::refitWinners: the bottom leg inward, then to the closest approach
1115 // to the beam line; the top leg inward, then to the same point; the two halves combined. Muon mass, energy loss along the muon's flight
1116 // (top to bottom): going inward along the bottom leg and up to the closest approach is against the flight (gain), the top leg inward
1117 // and down to the closest approach is with it (loss). With a precise time the one-side legs get their real z, hence also the right
1118 // material, which the matcher's time (the legs' bracket overlap for one-side legs on the same side) cannot give.
1119 constexpr int ELossGain = 1;
1120 constexpr int ELossLoss = -1;
1121 const auto& matchParams = o2::globaltracking::MatchCosmicsParams::Instance(); // propagation settings as in the matcher
1122 const auto prop = o2::base::Propagator::Instance();
1123 const float timeTB = timeMUS / mTPCTBinMUS;
1124 o2::track::TrackParCov bottom = legs[0]->getParamOut();
1125 bottom.setPID(o2::track::PID::Muon, true);
1126 bottom.resetCovariance();
1127 if (std::abs(mBz) <= 0.01f) {
1128 // B = 0: both legs carry the same conventional q/pt; as the matcher, flip the bottom one so that it is right after the inversion
1130 }
1131 if (refitter.RefitTrackAsTrackParCov(bottom, legs[0]->getClusterRef(), timeTB, nullptr, false, false, ELossGain) < 0) {
1132 return;
1133 }
1134 bottom.invert();
1136 if (!prop->propagateToDCABxByBz(origin, bottom, matchParams.maxStep, matchParams.matCorr, nullptr, nullptr, ELossGain)) {
1137 return;
1138 }
1139 o2::track::TrackParCov top = legs[1]->getParamOut();
1140 top.setPID(o2::track::PID::Muon, true);
1141 if (refitter.RefitTrackAsTrackParCov(top, legs[1]->getClusterRef(), timeTB, nullptr, false, true, ELossLoss) < 0) {
1142 return;
1143 }
1144 if (!top.rotate(bottom.getAlpha()) || !prop->PropagateToXBxByBz(top, bottom.getX(), matchParams.maxSnp, matchParams.maxStep, matchParams.matCorr, nullptr, ELossLoss)) {
1145 return;
1146 }
1147 o2::track::TrackParCov::MatrixDSym5 cov5;
1148 const float chi2Match = bottom.getPredictedChi2(top, cov5);
1149 if (!bottom.update(top, cov5)) {
1150 return;
1151 }
1152 out.polished = bottom;
1153 out.chi2MatchPolished = chi2Match;
1154}
1155
1156void CosmicsClusterCollectorSpec::roadTRD(const RecoContainer& data, const o2::tpc::TrackTPC* const* legs, const CosmicTime& cosmicTime, int icosm, std::vector<o2::dataformats::CosmicTRDTracklet>& out, const std::unordered_set<int>& matched) const
1157{
1158 // per leg and layer the TRD tracklet closest to the leg's outward continuation, from triggers whose readout window can contain the cosmic
1159 constexpr float ReadoutWindowMUS = 3.f; // a cosmic leaves tracklets only if it passes within the readout window after a trigger
1160 constexpr float PadLength = 10.f; // [cm] longest TRD pads: the tracklet z is the pad-row centre
1161 constexpr int NLayers = o2::trd::constants::NLAYER;
1162 const auto window = timeWindowMUS(cosmicTime);
1163 const float zTimeTol = 0.5f * (window.second - window.first) / mTPCTBinMUS * mCorrMap->getVDrift();
1164 const auto tracklets = data.getTRDTracklets();
1165 const auto calibrated = data.getTRDCalibratedTracklets();
1166 if (calibrated.size() != tracklets.size()) {
1167 if (!mTRDMismatchWarned) {
1168 LOGP(warning, "{} calibrated vs {} raw TRD tracklets: TRD road of cosmics skipped", calibrated.size(), tracklets.size());
1169 mTRDMismatchWarned = true;
1170 }
1171 return;
1172 }
1173 struct Candidate {
1174 int tracklet = -1;
1175 int trigBC = 0;
1176 float score = 1.f;
1177 };
1178 Candidate best[2][NLayers];
1179 for (const auto& trig : data.getTRDTriggerRecords()) {
1180 const int trigBC = trig.getBCData().differenceInBC(mTFStart);
1181 const float trigMUS = trigBC * o2::constants::lhc::LHCBunchSpacingMUS;
1182 if (trigMUS < window.first - ReadoutWindowMUS || trigMUS > window.second) {
1183 continue;
1184 }
1185 for (int it = trig.getFirstTracklet(); it < trig.getFirstTracklet() + trig.getNumberOfTracklets(); it++) {
1186 if (matched.count(it)) {
1187 continue;
1188 }
1189 const int detector = tracklets[it].getDetector();
1190 const int sector = detector / o2::trd::constants::NCHAMBERPERSEC;
1191 const int layer = detector % NLayers;
1192 const auto& point = calibrated[it];
1193 for (int leg = 0; leg < 2; leg++) {
1194 float y = 0.f;
1195 float z = 0.f;
1196 if (!legs[leg] || !predictOutward(*legs[leg], cosmicTime, sector, point.getX(), y, z)) {
1197 continue;
1198 }
1199 const float normY = (point.getY() - y) / mRoadTRD;
1200 const float normZ = (point.getZ() - z) / (mRoadTRD + PadLength + zTimeTol);
1201 const float score = std::max(normY * normY, normZ * normZ);
1202 if (score < best[leg][layer].score) {
1203 best[leg][layer] = {it, trigBC, score};
1204 }
1205 }
1206 }
1207 }
1208 for (int leg = 0; leg < 2; leg++) {
1209 for (int layer = 0; layer < NLayers; layer++) {
1210 const auto& cand = best[leg][layer];
1211 if (cand.tracklet < 0) {
1212 continue;
1213 }
1214 auto& tr = out.emplace_back();
1215 tr.word = tracklets[cand.tracklet].getTrackletWord();
1216 tr.trigBC = cand.trigBC;
1217 tr.layer = layer;
1218 tr.leg = leg;
1219 tr.flags = o2::dataformats::HitRoad;
1220 if (mDebugOut) {
1221 const auto& point = calibrated[cand.tracklet];
1222 const int sector = tracklets[cand.tracklet].getDetector() / o2::trd::constants::NCHAMBERPERSEC;
1223 float y = 0.f;
1224 float z = 0.f;
1225 predictOutward(*legs[leg], cosmicTime, sector, point.getX(), y, z); // succeeded in the search
1226 auto& dbg = mDbgCosmics[icosm];
1227 dbg.trdLeg.push_back(leg);
1228 dbg.trdLayer.push_back(layer);
1229 dbg.trdX.push_back(point.getX());
1230 dbg.trdY.push_back(point.getY());
1231 dbg.trdZ.push_back(point.getZ());
1232 dbg.trdDy.push_back(point.getY() - y);
1233 dbg.trdDz.push_back(point.getZ() - z);
1234 dbg.trdTrigMUS.push_back(cand.trigBC * float(o2::constants::lhc::LHCBunchSpacingMUS));
1235 }
1236 }
1237 }
1238}
1239
1240void CosmicsClusterCollectorSpec::roadITS(const RecoContainer& data, const o2::dataformats::TrackCosmics& cosm, const CosmicTime& cosmicTime, int legsSide, int icosm, std::vector<o2::dataformats::CosmicITSCluster>& out,
1241 std::vector<ITSPattRequest>& requests, const std::unordered_set<int>& matched) const
1242{
1243 if (data.getITSPerLayer() || mITSChipCentres.empty()) {
1244 return;
1245 }
1246 // trajectory near the beam line from the combined cosmic, sampled every cm along its direction (closest approach at local x = 0)
1247 constexpr float MaxRadius = 45.f; // [cm] outer ITS barrel + margin
1248 constexpr float ChipHalfDiag = 1.7f; // [cm] half diagonal of an ITS chip
1249 o2::track::TrackPar par = cosm;
1250 if (!par.rotateParam(par.getPhi())) {
1251 return;
1252 }
1253 std::vector<o2::math_utils::Point3D<float>> points;
1254 for (float x = -MaxRadius - 5.f; x <= MaxRadius + 5.f; x += 1.f) {
1255 bool ok = false;
1256 const auto point = par.getXYZGloAt(x, mBz, ok);
1257 if (ok) {
1258 points.push_back(point);
1259 }
1260 }
1261 if (points.size() < 2) {
1262 return;
1263 }
1264 // closest segment in the transverse plane: distance, z of the trajectory there
1265 auto closest = [&points](float x, float y, float& dist2, float& zTraj) {
1266 dist2 = 1e10f;
1267 for (size_t i = 0; i + 1 < points.size(); i++) {
1268 const float segX = points[i + 1].X() - points[i].X();
1269 const float segY = points[i + 1].Y() - points[i].Y();
1270 const float len2 = segX * segX + segY * segY;
1271 const float frac = len2 > 0.f ? std::clamp(((x - points[i].X()) * segX + (y - points[i].Y()) * segY) / len2, 0.f, 1.f) : 0.f;
1272 const float dx = x - (points[i].X() + frac * segX);
1273 const float dy = y - (points[i].Y() + frac * segY);
1274 if (dx * dx + dy * dy < dist2) {
1275 dist2 = dx * dx + dy * dy;
1276 zTraj = points[i].Z() + frac * (points[i + 1].Z() - points[i].Z());
1277 }
1278 }
1279 };
1280 float minR2 = 1e10f;
1281 float pcaY = 0.f;
1282 for (const auto& point : points) {
1283 const float r2 = point.X() * point.X() + point.Y() * point.Y();
1284 if (r2 < minR2) {
1285 minR2 = r2;
1286 pcaY = point.Y();
1287 }
1288 }
1289 if (minR2 > MaxRadius * MaxRadius) { // the cosmic does not cross the ITS
1290 return;
1291 }
1292 std::vector<bool> candidateChip(mITSChipCentres.size(), false);
1293 bool anyChip = false;
1294 const float chipCut2 = (mRoadITS + ChipHalfDiag) * (mRoadITS + ChipHalfDiag);
1295 for (size_t chip = 0; chip < mITSChipCentres.size(); chip++) {
1296 float dist2 = 0.f;
1297 float zTraj = 0.f;
1298 closest(mITSChipCentres[chip].X(), mITSChipCentres[chip].Y(), dist2, zTraj);
1299 if (dist2 < chipCut2) {
1300 candidateChip[chip] = true;
1301 anyChip = true;
1302 }
1303 }
1304 if (!anyChip) {
1305 return;
1306 }
1307 const auto window = timeWindowMUS(cosmicTime);
1308 const float zTimeTol = 0.5f * (window.second - window.first) / mTPCTBinMUS * mCorrMap->getVDrift(); // z of the cosmic is in the frame of its time
1309 // the refitted cosmic has the z of its TPC time; with legs on one TPC side and a road time from the TOF its z moves by side * vD * dt
1310 const float zShift = legsSide * (cosmicTime.tb - cosm.getTimeMUS().getTimeStamp() / mTPCTBinMUS) * mCorrMap->getVDrift();
1311 const float rofLengthMUS = o2::itsmft::DPLAlpideParam<DetID::ITS>::Instance().roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingMUS;
1312 // per half of the cosmic and layer the ITS clusters closest to the trajectory, best first: one in the outer barrel, several in the inner
1313 // barrel, where the road near the beam line is dense with collision clusters and the TPC prediction (~mm) does not single out the hit
1314 constexpr int NLayers = 7;
1315 constexpr int NLayersIB = 3;
1316 constexpr int MaxHitsIB = 5;
1317 struct Candidate {
1318 int index = -1;
1319 int rofBC = 0;
1320 float score = 1.f;
1321 };
1322 Candidate best[2][NLayers][MaxHitsIB];
1323 const auto clusters = data.getITSClusters();
1324 auto geom = o2::its::GeometryTGeo::Instance();
1325 for (const auto& rof : data.getITSClustersROFRecords()) {
1326 const int rofBC = rof.getBCData().differenceInBC(mTFStart);
1327 const float rofMUS = rofBC * o2::constants::lhc::LHCBunchSpacingMUS;
1328 if (rofMUS + rofLengthMUS < window.first || rofMUS > window.second) {
1329 continue;
1330 }
1331 for (int idx = rof.getFirstEntry(); idx < rof.getFirstEntry() + rof.getNEntries(); idx++) {
1332 const auto& c = clusters[idx];
1333 if (!candidateChip[c.getSensorID()] || matched.count(idx)) {
1334 continue;
1335 }
1337 if (mITSDict && c.getPatternID() != o2::itsmft::CompCluster::InvalidPatternID && !mITSDict->isGroup(c.getPatternID())) {
1338 local = mITSDict->getClusterCoordinates<float>(c);
1339 } else { // anchor pixel: good enough for a cm road
1341 }
1342 const auto global = geom->getMatrixL2G(c.getSensorID()) * local;
1343 float dist2 = 0.f;
1344 float zTraj = 0.f;
1345 closest(global.X(), global.Y(), dist2, zTraj);
1346 const float normZ = (global.Z() - zTraj - zShift) / (mRoadITS + zTimeTol);
1347 const float score = std::max(dist2 / (mRoadITS * mRoadITS), normZ * normZ);
1348 const int half = global.Y() < pcaY ? 0 : 1; // bottom / top half of the cosmic
1349 const int layer = geom->getLayer(c.getSensorID());
1350 if (layer < 0 || layer >= NLayers) {
1351 continue;
1352 }
1353 auto* candidates = best[half][layer];
1354 int slot = (layer < NLayersIB ? MaxHitsIB : 1) - 1;
1355 if (!(score < candidates[slot].score)) { // also rejects a NaN score
1356 continue;
1357 }
1358 for (; slot > 0 && score < candidates[slot - 1].score; slot--) {
1359 candidates[slot] = candidates[slot - 1];
1360 }
1361 candidates[slot] = {idx, rofBC, score};
1362 }
1363 }
1364 for (int half = 0; half < 2; half++) {
1365 for (int layer = 0; layer < NLayers; layer++) {
1366 for (const auto& candidate : best[half][layer]) {
1367 if (candidate.index < 0) {
1368 break;
1369 }
1370 const auto& c = clusters[candidate.index];
1371 auto& cl = out.emplace_back();
1372 cl.chipID = c.getSensorID();
1373 cl.row = c.getRow();
1374 cl.col = c.getCol();
1375 cl.pattID = c.getPatternID();
1376 cl.rofBC = candidate.rofBC;
1377 cl.leg = half;
1378 cl.flags = o2::dataformats::HitRoad;
1379 if (c.getPatternID() == o2::itsmft::CompCluster::InvalidPatternID || (mITSDict && mITSDict->isGroup(c.getPatternID()))) {
1380 requests.push_back({icosm, int(out.size()) - 1, candidate.index});
1381 }
1382 }
1383 }
1384 }
1385}
1386
1387void CosmicsClusterCollectorSpec::fillITSPatterns(const RecoContainer& data, std::vector<ITSPattRequest>& requests, std::vector<o2::dataformats::TrackCosmicsExtended>& cosmics) const
1388{
1389 // the TF's pattern stream holds, in cluster order, the patterns of the clusters with an invalid or a group pattern ID
1390 if (!mITSDict) {
1391 if (!mNoDictWarned) {
1392 LOGP(warning, "No ITS cluster dictionary: patterns of ITS clusters of cosmics are not stored");
1393 mNoDictWarned = true;
1394 }
1395 return;
1396 }
1397 std::sort(requests.begin(), requests.end(), [](const ITSPattRequest& a, const ITSPattRequest& b) { return a.index < b.index; });
1398 const auto clusters = data.getITSClusters();
1399 auto pattIt = data.getITSClustersPatterns().begin();
1400 size_t ir = 0;
1401 for (int k = 0; k < (int)clusters.size() && ir < requests.size(); k++) {
1402 const auto pattID = clusters[k].getPatternID();
1403 if (pattID != o2::itsmft::CompCluster::InvalidPatternID && !mITSDict->isGroup(pattID)) {
1404 continue;
1405 }
1406 const auto start = pattIt;
1408 for (; ir < requests.size() && requests[ir].index == k; ir++) {
1409 auto& cosm = cosmics[requests[ir].cosmic];
1410 cosm.clITS[requests[ir].cluster].pattEntry = cosm.itsPatterns.size();
1411 cosm.itsPatterns.insert(cosm.itsPatterns.end(), start, pattIt);
1412 }
1413 }
1414}
1415
1416void CosmicsClusterCollectorSpec::addTRD(const RecoContainer& data, GTrackID gid, uint8_t leg, std::vector<o2::dataformats::CosmicTRDTracklet>& out, std::unordered_set<int>& matched) const
1417{
1418 const auto& trk = data.getTrack<o2::trd::TrackTRD>(gid); // TPC-TRD or ITS-TPC-TRD track
1419 const auto tracklets = data.getTRDTracklets();
1420 const auto trigs = data.getTRDTriggerRecords();
1421 for (int layer = 0; layer < 6; layer++) {
1422 const int it = trk.getTrackletIndex(layer);
1423 if (it < 0) {
1424 continue;
1425 }
1426 auto& tr = out.emplace_back();
1427 tr.word = tracklets[it].getTrackletWord();
1428 tr.layer = layer;
1429 tr.leg = leg;
1430 tr.flags = o2::dataformats::HitMatched;
1431 matched.insert(it);
1432 auto trig = std::upper_bound(trigs.begin(), trigs.end(), it, [](int v, const o2::trd::TriggerRecord& t) { return v < t.getFirstTracklet(); });
1433 if (trig != trigs.begin()) {
1434 tr.trigBC = std::prev(trig)->getBCData().differenceInBC(mTFStart);
1435 }
1436 }
1437}
1438
1439void CosmicsClusterCollectorSpec::flagDuplicates(std::vector<o2::dataformats::TrackCosmicsExtended>& cosmics) const
1440{
1441 // the same muon can be matched twice, e.g. when a leg is split into two TPC tracks: the roads then collect largely the same clusters
1442 // (PbPb 567939: 10 of 47 cosmic pairs in a TF share 56-99 % of the clusters of the smaller one, all others none). The best one (TOF time,
1443 // then more attached clusters) stays, the others point to it.
1444 constexpr float MinSharedFraction = 0.3f;
1445 const size_t n = cosmics.size();
1446 if (n < 2) {
1447 return;
1448 }
1449 std::vector<std::vector<uint64_t>> keys(n); // sorted cluster addresses: sector, row and the packed time and pad of the raw cluster
1450 std::vector<int> nAttached(n, 0);
1451 for (size_t ic = 0; ic < n; ic++) {
1452 for (const auto* legClusters : {&cosmics[ic].clTPCBottom, &cosmics[ic].clTPCTop}) {
1453 for (const auto& c : *legClusters) {
1454 keys[ic].push_back((uint64_t(c.sector) << 56) | (uint64_t(c.row) << 48) | (uint64_t(c.cl.timeFlagsPacked) << 16) | c.cl.padPacked);
1455 nAttached[ic] += c.isAttached();
1456 }
1457 }
1458 std::sort(keys[ic].begin(), keys[ic].end());
1459 }
1460 std::vector<int> order(n);
1461 std::iota(order.begin(), order.end(), 0);
1462 std::stable_sort(order.begin(), order.end(), [&](int a, int b) {
1463 const bool timedA = cosmics[a].hasTOFTime();
1464 const bool timedB = cosmics[b].hasTOFTime();
1465 return timedA != timedB ? timedA : nAttached[a] > nAttached[b];
1466 });
1467 std::vector<int> kept;
1468 for (int ic : order) {
1469 for (int ik : kept) {
1470 std::vector<uint64_t> shared;
1471 std::set_intersection(keys[ic].begin(), keys[ic].end(), keys[ik].begin(), keys[ik].end(), std::back_inserter(shared));
1472 const size_t nMin = std::min(keys[ic].size(), keys[ik].size());
1473 if (nMin > 0 && shared.size() >= MinSharedFraction * nMin) {
1474 cosmics[ic].duplicateOf = ik;
1475 break;
1476 }
1477 }
1478 if (cosmics[ic].duplicateOf < 0) {
1479 kept.push_back(ic);
1480 }
1481 }
1482}
1483
1484void CosmicsClusterCollectorSpec::writeDebug(const o2::dataformats::TrackCosmicsExtended& cosm, int icosm) const
1485{
1486 constexpr int NSectorsA = TPCGeo::getNumberOfSectorsA();
1487 // the time the other-side corridor used (and the common frame zCos): the TOF time if the TOF road found one, else the TPC time
1488 const float timeCosmic = (cosm.hasTOFTime() ? cosm.timeTOFMUS : cosm.cosmic.getTimeMUS().getTimeStamp()) / mTPCTBinMUS;
1489 const bool absTimeKnown = cosm.cosmic.getTimeMUS().getTimeStampError() < mMaxAbsTimeErr;
1490 int nAttached[2] = {0, 0};
1491 int nRoad[2] = {0, 0};
1492 int side[2] = {-2, -2};
1493 std::vector<int> clLeg;
1494 std::vector<int> clFlags;
1495 std::vector<int> clSector;
1496 std::vector<int> clRow;
1497 std::vector<float> clPad;
1498 std::vector<float> clTime;
1499 std::vector<float> clQMax;
1500 std::vector<float> clQTot;
1501 std::vector<float> clX;
1502 std::vector<float> clY;
1503 std::vector<float> clZ;
1504 std::vector<float> clZCos;
1505 std::vector<float> clGx;
1506 std::vector<float> clGy;
1507 const o2::tpc::TrackTPC* legs[2] = {&cosm.tpcBottom, &cosm.tpcTop};
1508 const std::vector<o2::dataformats::CosmicTPCCluster>* legClusters[2] = {&cosm.clTPCBottom, &cosm.clTPCTop};
1509 for (int leg = 0; leg < 2; leg++) {
1510 if (legs[leg]->getNClusters() == 0) { // no TPC part
1511 continue;
1512 }
1513 side[leg] = tpcSide(*legs[leg]);
1514 for (const auto& c : *legClusters[leg]) {
1515 // transform with the vertex time used by the road: the leg's time0, or the cosmic's time for clusters found on the other side
1516 const float vertexTime = c.isAbsTime() ? timeCosmic : legs[leg]->getTime0();
1517 float x = 0.f;
1518 float y = 0.f;
1519 float z = 0.f;
1520 mCorrMap->Transform(c.sector, c.row, c.cl.getPad(), c.cl.getTime(), x, y, z, vertexTime);
1521 float xCos = 0.f;
1522 float yCos = 0.f;
1523 float zCos = 0.f;
1524 mCorrMap->Transform(c.sector, c.row, c.cl.getPad(), c.cl.getTime(), xCos, yCos, zCos, timeCosmic);
1525 const float alpha = o2::math_utils::sector2Angle(c.sector % NSectorsA);
1526 const float sinAlpha = std::sin(alpha);
1527 const float cosAlpha = std::cos(alpha);
1528 clLeg.push_back(leg);
1529 clFlags.push_back(c.flags);
1530 clSector.push_back(c.sector);
1531 clRow.push_back(c.row);
1532 clPad.push_back(c.cl.getPad());
1533 clTime.push_back(c.cl.getTime());
1534 clQMax.push_back(c.cl.getQmax());
1535 clQTot.push_back(c.cl.getQtot());
1536 clX.push_back(x);
1537 clY.push_back(y);
1538 clZ.push_back(z);
1539 clZCos.push_back(zCos);
1540 clGx.push_back(x * cosAlpha - y * sinAlpha);
1541 clGy.push_back(x * sinAlpha + y * cosAlpha);
1542 (c.isAttached() ? nAttached : nRoad)[leg]++;
1543 }
1544 }
1545 std::vector<int> itsLeg;
1546 std::vector<int> itsFlags;
1547 std::vector<int> itsChip;
1548 std::vector<int> itsRow;
1549 std::vector<int> itsCol;
1550 std::vector<int> itsPattID;
1551 std::vector<int> itsHasPatt;
1552 std::vector<int> itsRofBC;
1553 std::vector<float> itsGx;
1554 std::vector<float> itsGy;
1555 std::vector<float> itsGz;
1556 std::vector<float> itsXLoc;
1557 std::vector<float> itsZLoc;
1558 for (const auto& c : cosm.clITS) { // local coordinates on the chip from the dictionary or the stored pattern
1559 const o2::itsmft::CompClusterExt compCluster(c.row, c.col, c.pattID, c.chipID);
1561 if (c.pattEntry >= 0) {
1563 auto pattIt = cosm.itsPatterns.begin() + c.pattEntry;
1564 pattern.acquirePattern(pattIt);
1565 local = o2::itsmft::TopologyDictionary::getClusterCoordinates<float>(compCluster, pattern, c.pattID != o2::itsmft::CompCluster::InvalidPatternID);
1566 } else if (mITSDict && c.pattID != o2::itsmft::CompCluster::InvalidPatternID) {
1567 local = mITSDict->getClusterCoordinates<float>(compCluster);
1568 } else { // pattern not available: pixel centre
1570 }
1571 o2::math_utils::Point3D<float> global(0.f, 0.f, 0.f);
1572 if (!mITSChipCentres.empty()) { // geometry loaded for the ITS road
1573 global = o2::its::GeometryTGeo::Instance()->getMatrixL2G(c.chipID) * local;
1574 }
1575 itsLeg.push_back(c.leg);
1576 itsFlags.push_back(c.flags);
1577 itsChip.push_back(c.chipID);
1578 itsRow.push_back(c.row);
1579 itsCol.push_back(c.col);
1580 itsPattID.push_back(c.pattID);
1581 itsHasPatt.push_back(c.pattEntry >= 0);
1582 itsRofBC.push_back(c.rofBC);
1583 itsGx.push_back(global.X());
1584 itsGy.push_back(global.Y());
1585 itsGz.push_back(global.Z());
1586 itsXLoc.push_back(local.X());
1587 itsZLoc.push_back(local.Z());
1588 }
1589 const auto& dbg = mDbgCosmics[icosm];
1590 const auto& time = cosm.cosmic.getTimeMUS();
1591 (*mDebugOut) << "cosmics"
1592 << "tf=" << mDbgTF << "cosm=" << icosm << "t=" << time.getTimeStamp() << "tErr=" << time.getTimeStampError() << "absTime=" << int(absTimeKnown)
1593 << "chi2Match=" << cosm.cosmic.getChi2Match() << "chi2Refit=" << cosm.cosmic.getChi2Refit() << "q2pt=" << cosm.cosmic.getQ2Pt()
1594 << "tgl=" << cosm.cosmic.getTgl() << "side0=" << side[0] << "side1=" << side[1] << "nAtt0=" << nAttached[0] << "nAtt1=" << nAttached[1]
1595 << "nRoad0=" << nRoad[0] << "nRoad1=" << nRoad[1] << "nITS=" << int(cosm.clITS.size()) << "nTOF=" << int(cosm.clTOF.size())
1596 << "nTRD=" << int(cosm.trdTracklets.size()) << "tTOF=" << cosm.timeTOFMUS << "scoreTOF=" << cosm.scoreTOFPair << "scoreTOFRev=" << cosm.scoreTOFReversed << "dupOf=" << cosm.duplicateOf << "polChi2Match=" << cosm.chi2MatchPolished << "polQ2Pt=" << cosm.polished.getQ2Pt() << "polZ=" << cosm.polished.getZ()
1597 << "mcEvent=" << (cosm.label.isSet() ? cosm.label.getEventID() : -1) << "mcTrack=" << (cosm.label.isSet() ? cosm.label.getTrackID() : -1)
1598 << "clLeg=" << clLeg << "clFlags=" << clFlags << "clSector=" << clSector << "clRow=" << clRow << "clPad=" << clPad << "clTime=" << clTime
1599 << "clQMax=" << clQMax << "clQTot=" << clQTot << "clX=" << clX << "clY=" << clY << "clZ=" << clZ << "clZCos=" << clZCos << "clGx=" << clGx
1600 << "clGy=" << clGy << "roadLeg=" << dbg.roadLeg << "roadFrame=" << dbg.roadFrame << "roadSector=" << dbg.roadSector << "roadRow=" << dbg.roadRow
1601 << "roadX=" << dbg.roadX << "roadY=" << dbg.roadY << "roadZ=" << dbg.roadZ << "roadZCos=" << dbg.roadZCos << "roadGx=" << dbg.roadGx
1602 << "roadGy=" << dbg.roadGy << "roadSnp=" << dbg.roadSnp << "roadTgl=" << dbg.roadTgl << "itsLeg=" << itsLeg << "itsFlags=" << itsFlags
1603 << "itsChip=" << itsChip << "itsRow=" << itsRow << "itsCol=" << itsCol << "itsPattID=" << itsPattID << "itsHasPatt=" << itsHasPatt
1604 << "itsRofBC=" << itsRofBC << "itsGx=" << itsGx << "itsGy=" << itsGy << "itsGz=" << itsGz << "itsXLoc=" << itsXLoc << "itsZLoc=" << itsZLoc
1605 << "tofLeg=" << dbg.tofLeg << "tofChannel=" << dbg.tofChannel << "tofFlags=" << dbg.tofFlags << "tofTimeRaw=" << dbg.tofTimeRaw
1606 << "tofTime=" << dbg.tofTime << "tofTot=" << dbg.tofTot << "tofX=" << dbg.tofX << "tofY=" << dbg.tofY << "tofZ=" << dbg.tofZ << "tofGx=" << dbg.tofGx
1607 << "tofGy=" << dbg.tofGy << "trdLeg=" << dbg.trdLeg << "trdLayer=" << dbg.trdLayer << "trdX=" << dbg.trdX << "trdY=" << dbg.trdY << "trdZ=" << dbg.trdZ
1608 << "trdDy=" << dbg.trdDy << "trdDz=" << dbg.trdDz << "trdTrigMUS=" << dbg.trdTrigMUS << "\n";
1609}
1610
1611void CosmicsClusterCollectorSpec::writeDebugTOF(const o2::tof::Cluster& c, int icosm, int leg, uint8_t flags) const
1612{
1613 // the TOF cluster position is in the frame of its sector
1614 const float alpha = o2::math_utils::sector2Angle(c.getSector());
1615 const float sinAlpha = std::sin(alpha);
1616 const float cosAlpha = std::cos(alpha);
1617 auto& dbg = mDbgCosmics[icosm];
1618 dbg.tofLeg.push_back(leg);
1619 dbg.tofChannel.push_back(c.getMainContributingChannel());
1620 dbg.tofFlags.push_back(flags);
1621 dbg.tofTimeRaw.push_back(c.getTimeRaw());
1622 dbg.tofTime.push_back(c.getTime());
1623 dbg.tofTot.push_back(c.getTot());
1624 dbg.tofX.push_back(c.getX());
1625 dbg.tofY.push_back(c.getY());
1626 dbg.tofZ.push_back(c.getZ());
1627 dbg.tofGx.push_back(c.getX() * cosAlpha - c.getY() * sinAlpha);
1628 dbg.tofGy.push_back(c.getX() * sinAlpha + c.getY() * cosAlpha);
1629}
1630
1632{
1633 if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) {
1634 return;
1635 }
1636 if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) {
1637 mITSDict = (const o2::itsmft::TopologyDictionary*)obj;
1638 return;
1639 }
1640}
1641
1643{
1644 mDebugOut.reset();
1645 LOGP(info, "Cosmics cluster collector: {} cosmics, {} attached and {} road TPC clusters; Cpu: {:.3e} Real: {:.3e} s in {} slots",
1646 mNCosmics, mNClAttached, mNClCorridor, mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1);
1647}
1648
1650{
1651 if (itsStag && roadDets[DetID::ITS]) {
1652 LOGP(warning, "Staggered ITS clusters are not supported: ITS road of cosmics disabled");
1653 roadDets &= ~DetID::getMask(DetID::ITS);
1654 }
1655 auto dataRequest = std::make_shared<DataRequest>();
1656 dataRequest->setITSPerLayer(itsStag);
1657 dataRequest->requestTracks(src, false);
1658 dataRequest->requestTracks(getLegITSSources(src), false); // the ITS clusters of legs with an ITS part are read through their ITS track
1659 dataRequest->requestClusters(src, false);
1660 if (roadDets[DetID::ITS]) {
1661 dataRequest->requestITSClusters(false);
1662 }
1663 if (roadDets[DetID::TOF]) {
1664 dataRequest->requestTOFClusters(false);
1665 }
1666 if (roadDets[DetID::TRD]) {
1667 dataRequest->requestTRDTracklets(false);
1668 }
1669 dataRequest->requestCoscmicTracks(useMC);
1670 auto ggRequest = std::make_shared<o2::base::GRPGeomRequest>(false, // orbitResetTime
1671 false, // GRPECS
1672 false, // GRPLHCIF
1673 true, // GRPMagField
1674 roadDets[DetID::TOF], // askMatLUT: polish refit of TOF-timed cosmics
1675 roadDets[DetID::ITS] ? o2::base::GRPGeomRequest::Aligned : o2::base::GRPGeomRequest::None, // ITS road: chip positions
1676 dataRequest->inputs,
1677 true);
1678 dataRequest->inputs.emplace_back("corrMap", o2::header::gDataOriginTPC, "TPCCORRMAP", 0, Lifetime::Timeframe);
1679
1680 std::vector<OutputSpec> outputs;
1681 outputs.emplace_back("GLO", "COSMFULL", 0, Lifetime::Timeframe);
1682 outputs.emplace_back("GLO", "COSMFULLTF", 0, Lifetime::Timeframe);
1683 outputs.emplace_back("GLO", "COSMFULLTFID", 0, Lifetime::Timeframe);
1684
1685 return DataProcessorSpec{
1686 "cosmics-cluster-collector",
1687 dataRequest->inputs,
1688 outputs,
1689 AlgorithmSpec{adaptFromTask<CosmicsClusterCollectorSpec>(dataRequest, ggRequest, useMC, roadDets)},
1690 Options{
1691 {"corridor-width", VariantType::Float, 1.f, {"half-width of the road around each leg [cm]"}},
1692 {"max-abs-time-err", VariantType::Float, 0.5f, {"max. time error of a cosmic [mus] to search the other TPC side of its one-side legs"}},
1693 {"max-cosmics-per-tf", VariantType::Int, 100, {"write at most this many cosmics per TF (with their clusters), the others are dropped"}},
1694 {"tof-road-width", VariantType::Float, 5.f, {"half-width of the road at the TOF [cm]"}},
1695 {"tof-flight-tolerance", VariantType::Float, 2.f, {"max. deviation of the top/bottom TOF time difference from the muon's flight time [ns]"}},
1696 {"tof-time-error", VariantType::Float, 0.1f, {"error of a cosmic's TOF time for the TPC other-side corridor and the TRD / ITS roads [mus]"}},
1697 {"trd-road-width", VariantType::Float, 5.f, {"half-width of the road at the TRD [cm] (in z plus the pad length)"}},
1698 {"its-road-width", VariantType::Float, 1.5f, {"half-width of the road in the ITS [cm]"}},
1699 {"debug-tree", VariantType::Bool, false, {"write cosmics_collector_debug.root with transformed clusters and road points (test runs)"}}}};
1700}
1701
1702} // namespace o2::globaltracking
header::DataOrigin origin
Class of a TPC cluster in TPC-native coordinates (row, time)
General auxilliary methods.
Definition of the ITSMFT compact cluster.
Collects the raw clusters of matched cosmics (attached + road around the legs) for offline refits.
Wrapper container for different reconstructed object types.
Definition of the ClusterTopology class.
Definition of the TOF cluster.
Global TRD definitions and constants.
int16_t time
Definition RawEventData.h:4
std::vector< std::string > keys
int32_t i
Helper for geometry and GRP related CCDB requests.
Definition of the GeometryTGeo class.
Definition of the ITSMFT ROFrame (trigger) record.
std::vector< o2::its::TrackITS > tracks
Header to collect LHC related constants.
Configurable params for cosmics matching.
Definition of the parameter class for the detector electronics.
Declarations of 2D primitives.
uint16_t pos
Definition RawData.h:3
uint32_t j
Definition RawData.h:0
uint32_t side
Definition RawData.h:0
uint32_t c
Definition RawData.h:2
Definition of the SegmentationAlpide class.
POD correction map.
Matched cosmic track with the raw clusters of its legs and of the road around them,...
Result of top-bottom cosmic tracks leg matching.
Definition of the ITS track.
Helper class to obtain TPC clusters / digits / labels from DPL.
int getTrackID() const
bool isSet() const
Definition MCCompLabel.h:74
int getEventID() const
void checkUpdates(o2::framework::ProcessingContext &pc)
static GRPGeomHelper & instance()
void setRequest(std::shared_ptr< GRPGeomRequest > req)
GPUd() value_type estimateLTFast(o2 static GPUd() float estimateLTIncrement(const o2 PropagatorImpl * Instance(bool uninitialized=false)
Definition Propagator.h:180
const timeEst & getTimeMUS() const
Static class with identifiers, bitmasks and names for ALICE detectors.
Definition DetID.h:60
const Mat3D & getMatrixL2G(int sensID) const
void snapshot(const Output &spec, T const &object)
ServiceRegistryRef services()
Definition InitContext.h:34
ConfigParamRegistry const & options()
Definition InitContext.h:33
decltype(auto) get(R binding, int part=0) const
DataAllocator & outputs()
The data allocator is used to allocate memory for the output data.
InputRecord & inputs()
The inputs associated with this processing context.
void finaliseCCDB(ConcreteDataMatcher &matcher, void *obj) final
void endOfStream(EndOfStreamContext &ec) final
This is invoked whenever we have an EndOfStream event.
CosmicsClusterCollectorSpec(std::shared_ptr< DataRequest > dr, std::shared_ptr< o2::base::GRPGeomRequest > gr, bool useMC, DetID::mask_t roadDets)
int32_t RefitTrackAsTrackParCov(o2::tpc::TrackTPC &trk, bool outward=false, bool resetCov=false)
static GeometryTGeo * Instance()
static void skipPattern(iterator &pattIt)
static constexpr unsigned short InvalidPatternID
Definition CompCluster.h:46
static void detectorToLocalUnchecked(L row, L col, T &xRow, T &zCol)
math_utils::Point3D< T > getClusterCoordinates(const CompCluster &cl) const
bool isGroup(int n) const
Returns true if the element corresponds to a group of rare topologies.
Cluster class for TOF.
Definition Cluster.h:37
Header for data corresponding to the same hardware trigger adapted from DataFormatsITSMFT/ROFRecord.
GLdouble n
Definition glcorearb.h:1982
GLfloat GLfloat GLfloat alpha
Definition glcorearb.h:279
GLint GLenum GLint x
Definition glcorearb.h:403
GLenum src
Definition glcorearb.h:1767
GLsizeiptr size
Definition glcorearb.h:659
GLuint GLuint end
Definition glcorearb.h:469
const GLdouble * v
Definition glcorearb.h:832
GLuint index
Definition glcorearb.h:781
GLdouble GLdouble GLdouble GLdouble top
Definition glcorearb.h:4077
GLuint const GLchar * name
Definition glcorearb.h:781
GLint first
Definition glcorearb.h:399
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLint y
Definition glcorearb.h:270
GLboolean * data
Definition glcorearb.h:298
GLuint GLsizei GLsizei * length
Definition glcorearb.h:790
GLint GLint bottom
Definition glcorearb.h:1979
GLbitfield flags
Definition glcorearb.h:1570
GLsizei const GLchar *const * path
Definition glcorearb.h:3591
GLenum GLuint GLint GLint layer
Definition glcorearb.h:1310
GLenum clamp
Definition glcorearb.h:1245
GLboolean r
Definition glcorearb.h:1233
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t0
Definition glcorearb.h:5034
GLuint start
Definition glcorearb.h:469
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLubyte GLubyte GLubyte GLubyte w
Definition glcorearb.h:852
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
constexpr o2::header::DataOrigin gDataOriginTPC
Definition DataHeader.h:579
uint8_t int statusCode int
uint8_t itsSharedClusterMap uint8_t
double norm(const Vec3 &vector)
@ HitTOFFlight
TOF road hit of the top/bottom pair whose time difference matches the muon's flight (fixes the cosmic...
@ HitMatched
part of the leg's matched global track
@ HitRoad
found by the road search around the cosmic
Defining ITS Vertex explicitly as messageable.
Definition Cartesian.h:288
o2::dataformats::GlobalTrackID::mask_t getLegITSSources(o2::dataformats::GlobalTrackID::mask_t src)
framework::DataProcessorSpec getCosmicsClusterCollectorSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC, bool itsStag, o2::detectors::DetID::mask_t roadDets)
return *o2::gpu::CAMath::Sqrt(xX0) *(1.f+0.038f *o2 const float const float const float const float rOut
Definition MathUtils.h:133
return *o2::gpu::CAMath::Sqrt(xX0) *(1.f+0.038f *o2 const float const float rIn
Definition MathUtils.h:133
return *o2::gpu::CAMath::Sqrt(xX0) *(1.f+0.038f *o2 const float const float const float rMid
Definition MathUtils.h:133
const bool const bool const int FollowDirection BestTrial TrackITSInternal< NLayers > & best
o2::track::TrackParametrization< float > par
const float const float const float const float std::array< float, 3 > std::array< float, 3 > & direction
int angle2Sector(float phi)
Definition Utils.h:183
float sector2Angle(int sect)
Definition Utils.h:193
float dSector(float phi, int nSectors=18, float sectorPhaseRad=0.f)
Fractional azimuthal position inside a periodic sector pattern.
Enum< T >::Iterator begin(Enum< T >)
Definition Defs.h:158
TrackParCovF TrackParCov
Definition Track.h:33
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
static void fillTFIDInfo(o2::framework::ProcessingContext &pc, o2::dataformats::TFIDInfo &ti)
@ Corridor
found in the road around the leg
@ Used
attached to some TPC track (for corridor clusters: another track, e.g. a split piece of the leg)
@ Attached
attached to the TPC track of this leg
per-TF quantities: the TPC transformation used in the reconstruction and the TPC occupancy
float t0
time offset of the transformation [time bins]
float vDrift
drift velocity of the transformation [cm/time bin]
float tpcOcc
TPC clusters per orbit, average of the TF (TPC occupancy map) (< 0: no map)
matched cosmic with everything needed for an offline refit
float scoreTOFPair
score of the HitTOFFlight pair: road and flight-time residuals squared in units of the cuts (< 0: non...
std::vector< uint8_t > itsPatterns
pattern bytes (row span, column span, bitmap) of the ITS clusters that need them
std::vector< CosmicTRDTracklet > trdTracklets
TRD tracklets of the legs' matched tracks and on the road.
o2::dataformats::TrackCosmics cosmic
matcher output (time in mus; the leg references are only valid within the TF)
float timeTOFMUS
time of the cosmic [mus since the TF start, negative before it] from its HitTOFFlight pair (valid if ...
float chi2MatchPolished
chi2 of the polished bottom and top halves at the closest approach (< 0: not polished)
std::vector< CosmicTPCCluster > clTPCBottom
attached + corridor TPC clusters of the bottom leg
std::vector< CosmicITSCluster > clITS
ITS clusters of the legs' matched tracks and on the road (per half and layer the closest,...
o2::tpc::TrackTPC tpcTop
TPC part of the top leg.
o2::MCCompLabel label
MC label of the cosmic (MC only)
std::vector< CosmicTOFCluster > clTOF
TOF clusters of the legs' matched tracks and on the road.
std::vector< CosmicTPCCluster > clTPCTop
same for the top leg
bool hasTOFTime() const
a HitTOFFlight pair gave the cosmic its time timeTOFMUS
GlobalIDSet getSingleDetectorRefs(GTrackID gidx) const
const o2::tpc::TrackTPC & getTPCTrack(GTrackID id) const
gsl::span< const unsigned char > clusterShMapTPC
externally set TPC clusters sharing map
void collectData(o2::framework::ProcessingContext &pc, const DataRequest &request)
std::unique_ptr< o2::tpc::internal::getWorkflowTPCInput_ret > inputsTPCclusters
gsl::span< const unsigned int > occupancyMapTPC
externally set TPC clusters occupancy map
auto getCosmicTrackMCLabel(int i) const
static constexpr void convPadDriftLengthToLocal(uint32_t sector, uint32_t row, float pad, float driftLength, float &y, float &z)
convert Pad, DriftLength -> Local c.s.
static constexpr float getRowInfoX(uint32_t row)
Gives TPC row info.
static constexpr void convLocalToPadDriftLength(uint32_t sector, uint32_t row, float y, float z, float &pad, float &l)
convert Local c.s. -> Pad, DriftLength
static constexpr int32_t getNumberOfSectorsA()
Gives number of TPC sectors on the A side.
static constexpr float getRowInfoPadWidth(uint32_t row)
static constexpr float getTPCzLength()
Gives Z length of the TPC, one Z side.
constexpr size_t max
o2::InteractionRecord ir(0, 0)
std::vector< Cluster > clusters
std::vector< int > row
std::array< uint16_t, 5 > pattern
std::vector< Tracklet64 > tracklets