From 96a6a753d2e56ec9db66ad200d693589a188f30a Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 21 Jan 2026 11:07:47 +0100 Subject: [PATCH 001/133] GPU QA: Improvements for some plots --- GPU/GPUTracking/Definitions/GPUSettingsList.h | 5 +- GPU/GPUTracking/qa/GPUQA.cxx | 62 ++++++++++--------- GPU/GPUTracking/qa/GPUQA.h | 8 +-- 3 files changed, 39 insertions(+), 36 deletions(-) diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index d70fac115eab7..9bfe6feb14d8d 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -527,9 +527,10 @@ AddOption(histMaxNClusters, uint32_t, 500000000, "", 0, "Maximum number of clust AddOption(minNClFindable, uint32_t, 70, "", 0, "Minimum number of (weighted) MC clusters for a track to count as findable") AddOption(minNClEff, uint32_t, 10, "", 0, "Minimum number of (weighted) MC clusters for a track to contribute to all-tracks efficiency histogramm") AddOption(minNClRes, uint32_t, 40, "", 0, "Minimum number of (weighted) MC clusters for a track to contribute to resolution histogram") -AddOption(perfFigure, int32_t, 0, "", 0, "Show as performance figure, positive value for MC, negative value for data") +AddOption(perfFigure, std::string, "", "", 0, "Show as performance figure, provide mc/MC or data as asgument, or a custom string", def("MC")) AddOption(plotsDir, std::string, "plots", "", 0, "Directory to write plots to") -AddShortcut("compare", 0, "--QAinput", "Compare QA histograms", "--qa", "--QAinputHistogramsOnly") +AddOption(plotsNoTitle, bool, false, "", 0, "Do not print titles on figures") +AddShortcut("compare", 0, "--QAinput", "Compare QA histograms", "-c", "--qa", "--QAinputHistogramsOnly") AddHelp("help", 'h') EndConfig() diff --git a/GPU/GPUTracking/qa/GPUQA.cxx b/GPU/GPUTracking/qa/GPUQA.cxx index 852ac5c1feefb..b58209efff744 100644 --- a/GPU/GPUTracking/qa/GPUQA.cxx +++ b/GPU/GPUTracking/qa/GPUQA.cxx @@ -413,20 +413,21 @@ void GPUQA::DrawHisto(TH1* histo, char* filename, char* options) void GPUQA::doPerfFigure(float x, float y, float size) { - const char* str_perf_figure_1 = "ALICE Performance"; - const char* str_perf_figure_2_mc = "MC, Pb#minusPb, #sqrt{s_{NN}} = 5.36 TeV"; - const char* str_perf_figure_2_data = "Pb#minusPb, #sqrt{s_{NN}} = 5.36 TeV"; - - if (mConfig.perfFigure == 0) { + if (mConfig.perfFigure == "") { return; } + static constexpr const char* str_perf_figure_1 = "ALICE Performance"; + static constexpr const char* str_perf_figure_2_mc = "MC, Pb#minusPb, #sqrt{s_{NN}} = 5.36 TeV"; + static constexpr const char* str_perf_figure_2_data = "Pb#minusPb, #sqrt{s_{NN}} = 5.36 TeV"; + const char* str_perf_figure_2 = (mConfig.perfFigure == "mc" || mConfig.perfFigure == "MC") ? str_perf_figure_2_mc : (mConfig.perfFigure == "data" ? str_perf_figure_2_data : mConfig.perfFigure.c_str()); + TLatex* t = createGarbageCollected(); // TODO: We could perhaps put everything in a legend, to get a white background if there is a grid t->SetNDC(kTRUE); t->SetTextColor(1); t->SetTextSize(size); t->DrawLatex(x, y, str_perf_figure_1); t->SetTextSize(size * 0.8); - t->DrawLatex(x, y - 0.01 - size, mConfig.perfFigure > 0 ? str_perf_figure_2_mc : str_perf_figure_2_data); + t->DrawLatex(x, y - 0.01 - size, str_perf_figure_2); } void GPUQA::SetMCTrackRange(int32_t min, int32_t max) @@ -539,7 +540,7 @@ int32_t GPUQA::InitQACreateHistograms() createHist(mNCl[i], name, name, 160, 0, 159); } std::unique_ptr binsPt{CreateLogAxis(AXIS_BINS[4], PT_MIN_CLUST, PT_MAX)}; - createHist(mTracks, "tracks_pt", "tracks_pt", AXIS_BINS[4], binsPt.get()); + createHist(mTrackPt, "tracks_pt", "tracks_pt", AXIS_BINS[4], binsPt.get()); const uint32_t maxTime = (mTracking && mTracking->GetParam().continuousMaxTimeBin > 0) ? mTracking->GetParam().continuousMaxTimeBin : TPC_MAX_TIME_BIN_TRIGGERED; createHist(mT0[0], "tracks_t0", "tracks_t0", (maxTime + 1) / 10, 0, maxTime); createHist(mT0[1], "tracks_t0_res", "tracks_t0_res", 1000, -100, 100); @@ -1738,7 +1739,7 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx if (!track.OK()) { continue; } - mTracks->Fill(1.f / fabsf(track.GetParam().GetQPt())); + mTrackPt->Fill(1.f / fabsf(track.GetParam().GetQPt())); mNCl[0]->Fill(track.NClustersFitted()); uint32_t nClCorrected = 0; const auto& trackClusters = mTracking->mIOPtrs.mergedTrackHits; @@ -2247,12 +2248,12 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) // Create Canvas for track statistic histos if (mQATasks & taskTrackStatistics) { - mCTracks = createGarbageCollected("ctrackspt", "ctrackspt", 0, 0, 700, 700. * 2. / 3.); - mCTracks->cd(); - mPTracks = createGarbageCollected("p0", "", 0.0, 0.0, 1.0, 1.0); - mPTracks->Draw(); - mLTracks = createGarbageCollected(0.9 - legendSpacingString * 1.5, 0.93 - (0.93 - 0.86) / 2. * (float)ConfigNumInputs, 0.98, 0.949); - SetLegend(mLTracks, true); + mCTrackPt = createGarbageCollected("ctrackspt", "ctrackspt", 0, 0, 700, 700. * 2. / 3.); + mCTrackPt->cd(); + mPTrackPt = createGarbageCollected("p0", "", 0.0, 0.0, 1.0, 1.0); + mPTrackPt->Draw(); + mLTrackPt = createGarbageCollected(0.9 - legendSpacingString * 1.5, 0.93 - (0.93 - 0.86) / 2. * (float)ConfigNumInputs, 0.98, 0.949); + SetLegend(mLTrackPt, true); for (int32_t i = 0; i < 2; i++) { snprintf(name, 2048, "ctrackst0%d", i); @@ -2800,7 +2801,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) continue; } - e->SetTitle(CLUSTER_TITLES[i]); + e->SetTitle(mConfig.plotsNoTitle ? "" : CLUSTER_TITLES[i]); e->GetYaxis()->SetTitle(i == 0 ? "Number of TPC clusters" : i == 1 ? "Fraction of TPC clusters" : CLUST_HIST_INT_SUM ? "Total TPC clusters (integrated)" : "Fraction of TPC clusters (integrated)"); e->GetXaxis()->SetTitle("#it{p}_{Tmc} (GeV/#it{c})"); e->GetXaxis()->SetTitleOffset(1.1); @@ -2878,7 +2879,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) } title += ")"; - e->SetTitle(title.c_str()); + e->SetTitle(mConfig.plotsNoTitle ? "" : title.c_str()); e->GetXaxis()->SetTitle(i == 3 ? "Local Occupancy" : (i ? "#Phi_{Cl} (sector)" : "First MC Pad Row")); e->GetYaxis()->SetTitle("First Pad Row"); e->Draw(); @@ -2910,7 +2911,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) // Process track statistic histograms float tmpMax = 0.; for (int32_t k = 0; k < ConfigNumInputs; k++) { // TODO: Simplify this drawing, avoid copy&paste - TH1F* e = mTracks; + TH1F* e = mTrackPt; if (GetHist(e, tin, k, nNewInput) == nullptr) { continue; } @@ -2919,10 +2920,10 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) tmpMax = e->GetMaximum(); } } - mPTracks->cd(); - mPTracks->SetLogx(); + mPTrackPt->cd(); + mPTrackPt->SetLogx(); for (int32_t k = 0; k < ConfigNumInputs; k++) { - TH1F* e = mTracks; + TH1F* e = mTrackPt; if (GetHist(e, tin, k, nNewInput) == nullptr) { continue; } @@ -2933,9 +2934,10 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) e->SetMinimum(tmpMax * -0.02); e->SetStats(kFALSE); e->SetLineWidth(1); - e->SetTitle("Number of Tracks vs #it{p}_{T}"); + e->SetTitle(mConfig.plotsNoTitle ? "" : "Number of Tracks vs #it{p}_{T}"); e->GetYaxis()->SetTitle("Number of Tracks"); e->GetXaxis()->SetTitle("#it{p}_{T} (GeV/#it{c})"); + e->GetXaxis()->SetTitleOffset(1.2); if (qcout) { qcout->Add(e); } @@ -2943,14 +2945,14 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) e->SetLineColor(colorNums[k % COLORCOUNT]); e->Draw(k == 0 ? "" : "same"); GetName(fname, k, mConfig.inputHistogramsOnly); - mLTracks->AddEntry(e, Form(mConfig.inputHistogramsOnly ? "%s" : "%sTrack #it{p}_{T}", fname), "l"); + mLTrackPt->AddEntry(e, Form(mConfig.inputHistogramsOnly ? "%s" : "%sTrack #it{p}_{T}", fname), "l"); } - mLTracks->Draw(); + mLTrackPt->Draw(); doPerfFigure(0.63, 0.7, 0.030); - mCTracks->cd(); - mCTracks->Print(Form("%s/tracks.pdf", mConfig.plotsDir.c_str())); + mCTrackPt->cd(); + mCTrackPt->Print(Form("%s/tracks.pdf", mConfig.plotsDir.c_str())); if (mConfig.writeFileExt != "") { - mCTracks->Print(Form("%s/tracks.%s", mConfig.plotsDir.c_str(), mConfig.writeFileExt.c_str())); + mCTrackPt->Print(Form("%s/tracks.%s", mConfig.plotsDir.c_str(), mConfig.writeFileExt.c_str())); } for (int32_t i = 0; i < 2; i++) { @@ -2978,7 +2980,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) e->SetMinimum(tmpMax * -0.02); e->SetStats(kFALSE); e->SetLineWidth(1); - e->SetTitle(i ? "Track t_{0} resolution" : "Track t_{0} distribution"); + e->SetTitle(mConfig.plotsNoTitle ? "" : (i ? "Track t_{0} resolution" : "Track t_{0} distribution")); e->GetYaxis()->SetTitle("a.u."); e->GetXaxis()->SetTitle(i ? "t_{0} - t_{0, mc}" : "t_{0}"); if (qcout) { @@ -3022,7 +3024,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) e->SetMinimum(tmpMax * -0.02); e->SetStats(kFALSE); e->SetLineWidth(1); - e->SetTitle(i ? "Number of Rows with attached Cluster" : "Number of Clusters"); + e->SetTitle(mConfig.plotsNoTitle ? "" : (i ? "Number of Rows with attached Cluster" : "Number of Clusters")); e->GetYaxis()->SetTitle("a.u."); e->GetXaxis()->SetTitle(i ? "N_{Rows with Clusters}" : "N_{Clusters}"); if (qcout) { @@ -3061,7 +3063,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) mClRej[i]->Write(); } mPClRej[i]->cd(); - mClRej[i]->SetTitle(REJECTED_NAMES[i]); + mClRej[i]->SetTitle(mConfig.plotsNoTitle ? "" : REJECTED_NAMES[i]); mClRej[i]->SetOption("colz"); mClRej[i]->Draw(); mCClRej[i]->cd(); @@ -3098,7 +3100,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) delete proj2; e->SetMinimum(-0.02); e->SetMaximum(0.22); - e->SetTitle("Rejected Clusters"); + e->SetTitle(mConfig.plotsNoTitle ? "" : "Rejected Clusters"); e->GetXaxis()->SetTitle("Pad Row"); e->GetYaxis()->SetTitle("Rejected Clusters (fraction)"); e->Draw(k == 0 ? "" : "same"); diff --git a/GPU/GPUTracking/qa/GPUQA.h b/GPU/GPUTracking/qa/GPUQA.h index 346c56a898806..bd3c9be3a9aa5 100644 --- a/GPU/GPUTracking/qa/GPUQA.h +++ b/GPU/GPUTracking/qa/GPUQA.h @@ -290,10 +290,10 @@ class GPUQA double nUnaccessible = 0; } mClusterCounts; - TH1F* mTracks; - TCanvas* mCTracks; - TPad* mPTracks; - TLegend* mLTracks; + TH1F* mTrackPt; + TCanvas* mCTrackPt; + TPad* mPTrackPt; + TLegend* mLTrackPt; TH1F* mNCl[2]; TCanvas* mCNCl[2]; From a4989384162de86181f18515a2431fab19182ed8 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 20 Jan 2026 10:32:03 +0100 Subject: [PATCH 002/133] DPL: avoid needless copy of messages when cleaning up early forwarding --- .../include/Framework/DataProcessingHelpers.h | 2 + Framework/Core/src/DataProcessingDevice.cxx | 7 ++- Framework/Core/src/DataProcessingHelpers.cxx | 54 +++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/Framework/Core/include/Framework/DataProcessingHelpers.h b/Framework/Core/include/Framework/DataProcessingHelpers.h index a9bd95b69f4c7..87aeeb8922da3 100644 --- a/Framework/Core/include/Framework/DataProcessingHelpers.h +++ b/Framework/Core/include/Framework/DataProcessingHelpers.h @@ -59,6 +59,8 @@ struct DataProcessingHelpers { /// Helper to route messages for forwarding static void routeForwardedMessages(FairMQDeviceProxy& proxy, std::span& currentSetOfInputs, std::vector& forwardedParts, bool copy, bool consume); + + static void cleanForwardedMessages(std::span& currentSetOfInputs, bool consume); }; } // namespace o2::framework #endif // O2_FRAMEWORK_DATAPROCESSINGHELPERS_H_ diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index f65477c573772..38c57c66c8a01 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -626,9 +626,12 @@ static auto cleanEarlyForward = [](ServiceRegistryRef registry, TimesliceSlot sl slot.index, oldestTimeslice.timeslice.value, copy ? "with copy" : "", copy && consume ? " and " : "", consume ? "with consume" : ""); // Always copy them, because we do not want to actually send them. // We merely need the side effect of the consume, if applicable. - auto forwardedParts = DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, true, consume); + for (size_t ii = 0, ie = currentSetOfInputs.size(); ii < ie; ++ii) { + auto span = std::span(currentSetOfInputs[ii].messages); + DataProcessingHelpers::cleanForwardedMessages(span, consume); + } - O2_SIGNPOST_END(forwarding, sid, "forwardInputs", "Forwarding done"); + O2_SIGNPOST_END(forwarding, sid, "forwardInputs", "Cleaning done"); }; extern volatile int region_read_global_dummy_variable; diff --git a/Framework/Core/src/DataProcessingHelpers.cxx b/Framework/Core/src/DataProcessingHelpers.cxx index 87e7c9bf8962f..334a0fc6045f6 100644 --- a/Framework/Core/src/DataProcessingHelpers.cxx +++ b/Framework/Core/src/DataProcessingHelpers.cxx @@ -338,6 +338,60 @@ void DataProcessingHelpers::routeForwardedMessages(FairMQDeviceProxy& proxy, std } } +void DataProcessingHelpers::cleanForwardedMessages(std::span& messages, bool consume) +{ + size_t pi = 0; + while (pi < messages.size()) { + auto& header = messages[pi]; + + // If is now possible that the record is not complete when + // we forward it, because of a custom completion policy. + // this means that we need to skip the empty entries in the + // record for being forwarded. + if (header->GetData() == nullptr || + o2::header::get(header->GetData()) || + o2::header::get(header->GetData())) { + pi += 2; + continue; + } + + auto dph = o2::header::get(header->GetData()); + auto dh = o2::header::get(header->GetData()); + + if (dph == nullptr || dh == nullptr) { + // Complain only if this is not an out-of-band message + LOGP(error, "Data is missing {}{}{}", + dph ? "DataProcessingHeader" : "", dph || dh ? "and" : "", dh ? "DataHeader" : ""); + pi += 2; + continue; + } + + // At least one payload. + auto& payload = messages[pi + 1]; + // Calculate the number of messages which should be handled together + // all in one go. + size_t numberOfMessages = 0; + if (dh->splitPayloadParts > 0 && dh->splitPayloadParts == dh->splitPayloadIndex) { + // Sequence of (header, payload[0], ... , payload[splitPayloadParts - 1]) pairs belonging together. + numberOfMessages = dh->splitPayloadParts + 1; // one is for the header + } else { + // Sequence of splitPayloadParts (header, payload) pairs belonging together. + // In case splitPayloadParts = 0, we consider this as a single message pair + numberOfMessages = (dh->splitPayloadParts > 0 ? dh->splitPayloadParts : 1) * 2; + } + + if (payload.get() == nullptr && consume == true) { + // If the payload is not there, it means we already + // processed it with ConsumeExisiting. Therefore we + // need to do something only if this is the last consume. + header.reset(nullptr); + } + + // Nothing to forward go to the next messageset + pi += numberOfMessages; + } +} + auto DataProcessingHelpers::routeForwardedMessageSet(FairMQDeviceProxy& proxy, std::vector& currentSetOfInputs, const bool copyByDefault, bool consume) -> std::vector From 8562189e36996069bd0de7a79c57454ebb3fb8fe Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 21 Jan 2026 16:55:11 +0100 Subject: [PATCH 003/133] DPL: make new early forward optional The new behavior breaks the TPC custom policy. --- Framework/Core/src/DataProcessingDevice.cxx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 38c57c66c8a01..fb54af9402079 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -1054,7 +1054,11 @@ void DataProcessingDevice::fillContext(DataProcessorContext& context, DeviceCont } auto decideEarlyForward = [&context, &deviceContext, &spec, this]() -> ForwardPolicy { - ForwardPolicy defaultEarlyForwardPolicy = getenv("DPL_OLD_EARLY_FORWARD") ? ForwardPolicy::AtCompletionPolicySatisified : ForwardPolicy::AtInjection; + //ForwardPolicy defaultEarlyForwardPolicy = getenv("DPL_OLD_EARLY_FORWARD") ? ForwardPolicy::AtCompletionPolicySatisified : ForwardPolicy::AtInjection; + // Make the new policy optional until we handle some of the corner cases + // with custom policies which expect the early forward to happen only when + // all the data is available, like in the TPC case. + ForwardPolicy defaultEarlyForwardPolicy = getenv("DPL_NEW_EARLY_FORWARD") ? ForwardPolicy::AtInjection : ForwardPolicy::AtCompletionPolicySatisified; /// We must make sure there is no optional /// if we want to optimize the forwarding From be1d553177b4c8edc9ad47863369f5390f1fefaa Mon Sep 17 00:00:00 2001 From: shahoian Date: Wed, 21 Jan 2026 18:06:42 +0100 Subject: [PATCH 004/133] Store missing GlobalTrackID in the CheckResid output --- Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx b/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx index 691d731503b88..d665af5747c60 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx @@ -252,6 +252,7 @@ void CheckResidSpec::process() auto& accum = slots[0]; #endif auto& resTrack = accum.emplace_back(); + resTrack.gid = vid; if (!processITSTrack(itsTrack, pve, resTrack)) { accum.pop_back(); continue; From 587eb9487ae816790b3c4f7fe7db3fd22e9a8d85 Mon Sep 17 00:00:00 2001 From: Ernst Hellbar Date: Wed, 21 Jan 2026 09:48:37 +0100 Subject: [PATCH 005/133] TRD: add missing OutputSpec in trd-pulseheight device --- Detectors/TRD/workflow/include/TRDWorkflow/TRDPulseHeightSpec.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/TRDPulseHeightSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/TRDPulseHeightSpec.h index 3cfbb16644e54..be00e478608ec 100644 --- a/Detectors/TRD/workflow/include/TRDWorkflow/TRDPulseHeightSpec.h +++ b/Detectors/TRD/workflow/include/TRDWorkflow/TRDPulseHeightSpec.h @@ -69,6 +69,7 @@ class PuseHeightDevice : public o2::framework::Task mPulseHeight->reset(); mPulseHeight->process(); pc.outputs().snapshot(Output{"TRD", "PULSEHEIGHT", 0}, mPulseHeight->getPHData()); + pc.outputs().snapshot(Output{"TRD", "PULSEHEIGHTHD", 0}, mPulseHeight->getPHDataHD()); if (pc.transitionState() == TransitionHandlingState::Requested) { LOG(info) << "Run stop requested, finalizing"; mRunStopRequested = true; @@ -103,6 +104,7 @@ DataProcessorSpec getTRDPulseHeightSpec(GID::mask_t src, bool digitsFromReader) std::vector outputs; outputs.emplace_back(o2::header::gDataOriginTRD, "PULSEHEIGHT", 0, Lifetime::Timeframe); + outputs.emplace_back(o2::header::gDataOriginTRD, "PULSEHEIGHTHD", 0, Lifetime::Timeframe); bool isTPCavailable = false; if (GID::includesSource(GID::Source::ITSTPC, src)) { From 397e0194d0ab4d02f61c75ca40eb024703208478 Mon Sep 17 00:00:00 2001 From: Barthelemy Date: Wed, 21 Jan 2026 16:55:50 +0100 Subject: [PATCH 006/133] [O2-6625] Fix the missing filename in the CCDb --- CCDB/src/CcdbApi.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CCDB/src/CcdbApi.cxx b/CCDB/src/CcdbApi.cxx index f083d97b533df..42bc13904bf61 100644 --- a/CCDB/src/CcdbApi.cxx +++ b/CCDB/src/CcdbApi.cxx @@ -416,7 +416,7 @@ int CcdbApi::storeAsBinaryFile(const char* buffer, size_t size, const std::strin auto mime = curl_mime_init(curl); auto field = curl_mime_addpart(mime); curl_mime_name(field, "send"); - if (filename.empty()) { + if (!filename.empty()) { curl_mime_filedata(field, filename.c_str()); } if (buffer != nullptr && size > 0) { From 078eb5d8b2ed24527f7488e5b8fbae866820a46d Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 22 Jan 2026 15:58:36 +0100 Subject: [PATCH 007/133] Revert "DPL Analysis: Use dangling edges context in more places (#14953)" This reverts commit c990996954857d60b683fadc3bc037f055276c5d. --- .../AnalysisSupport/src/AODWriterHelpers.cxx | 21 +++---- .../CCDBSupport/src/AnalysisCCDBHelpers.cxx | 59 +++++++++---------- .../CCDBSupport/src/AnalysisCCDBHelpers.h | 2 +- .../Core/include/Framework/AnalysisTask.h | 4 +- Framework/Core/src/AnalysisSupportHelpers.cxx | 4 +- 5 files changed, 45 insertions(+), 45 deletions(-) diff --git a/Framework/AnalysisSupport/src/AODWriterHelpers.cxx b/Framework/AnalysisSupport/src/AODWriterHelpers.cxx index b76ffca13977e..d868b7498fb76 100644 --- a/Framework/AnalysisSupport/src/AODWriterHelpers.cxx +++ b/Framework/AnalysisSupport/src/AODWriterHelpers.cxx @@ -62,13 +62,13 @@ const static std::unordered_map ROOTfileNa AlgorithmSpec AODWriterHelpers::getOutputTTreeWriter(ConfigContext const& ctx) { + auto& ac = ctx.services().get(); auto dod = AnalysisSupportHelpers::getDataOutputDirector(ctx); int compressionLevel = 505; if (ctx.options().hasOption("aod-writer-compression")) { compressionLevel = ctx.options().get("aod-writer-compression"); } - return AlgorithmSpec{[dod, compressionLevel](InitContext& ic) -> std::function { - auto outputInputs = ic.services().get().outputsInputsAOD; + return AlgorithmSpec{[dod, outputInputs = ac.outputsInputsAOD, compressionLevel](InitContext& ic) -> std::function { LOGP(debug, "======== getGlobalAODSink::Init =========="); // find out if any table needs to be saved @@ -241,13 +241,14 @@ AlgorithmSpec AODWriterHelpers::getOutputTTreeWriter(ConfigContext const& ctx) }; } -AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& /*ctx*/) +AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& ctx) { - return AlgorithmSpec{[](InitContext& ic) -> std::function { - using namespace monitoring; - auto& dec = ic.services().get(); - auto tskmap = dec.outTskMap; - auto objmap = dec.outObjHistMap; + using namespace monitoring; + auto& ac = ctx.services().get(); + auto tskmap = ac.outTskMap; + auto objmap = ac.outObjHistMap; + + return AlgorithmSpec{[objmap, tskmap](InitContext& ic) -> std::function { auto& callbacks = ic.services().get(); auto inputObjects = std::make_shared>>(); @@ -277,7 +278,7 @@ AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& /*ct callbacks.set(endofdatacb); return [inputObjects, objmap, tskmap](ProcessingContext& pc) mutable -> void { - auto mergePart = [&inputObjects, &objmap, &tskmap](DataRef const& ref) { + auto mergePart = [&inputObjects, &objmap, &tskmap, &pc](DataRef const& ref) { O2_SIGNPOST_ID_GENERATE(hid, histogram_registry); O2_SIGNPOST_START(histogram_registry, hid, "mergePart", "Merging histogram"); if (!ref.header) { @@ -473,7 +474,7 @@ AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& /*ct }; O2_SIGNPOST_ID_GENERATE(rid, histogram_registry); O2_SIGNPOST_START(histogram_registry, rid, "processParts", "Start merging %zu parts received together.", pc.inputs().getNofParts(0)); - for (auto pi = 0U; pi < pc.inputs().getNofParts(0); ++pi) { + for (int pi = 0; pi < pc.inputs().getNofParts(0); ++pi) { mergePart(pc.inputs().get("x", pi)); } O2_SIGNPOST_END(histogram_registry, rid, "processParts", "Done histograms in multipart message."); diff --git a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx index 413adfddecf04..9ec911518f754 100644 --- a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx +++ b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx @@ -67,39 +67,38 @@ void fillValidRoutes(CCDBFetcherHelper& helper, std::vector(); - std::vector> schemas; - auto schemaMetadata = std::make_shared(); + auto& ac = ctx.services().get(); + std::vector> schemas; + auto schemaMetadata = std::make_shared(); - for (auto& input : dec.analysisCCDBInputs) { - std::vector> fields; - schemaMetadata->Append("outputRoute", DataSpecUtils::describe(input)); - schemaMetadata->Append("outputBinding", input.binding); + for (auto& input : ac.analysisCCDBInputs) { + std::vector> fields; + schemaMetadata->Append("outputRoute", DataSpecUtils::describe(input)); + schemaMetadata->Append("outputBinding", input.binding); - for (auto& m : input.metadata) { - // Save the list of input tables - if (m.name.starts_with("input:")) { - auto name = m.name.substr(6); - schemaMetadata->Append("sourceTable", name); - schemaMetadata->Append("sourceMatcher", DataSpecUtils::describe(std::get(DataSpecUtils::fromMetadataString(m.defaultValue.get()).matcher))); - continue; - } - // Ignore the non ccdb: entries - if (!m.name.starts_with("ccdb:")) { - continue; - } - // Create the schema of the output - auto metadata = std::make_shared(); - metadata->Append("url", m.defaultValue.asString()); - auto columnName = m.name.substr(strlen("ccdb:")); - fields.emplace_back(std::make_shared(columnName, arrow::binary_view(), false, metadata)); + for (auto& m : input.metadata) { + // Save the list of input tables + if (m.name.starts_with("input:")) { + auto name = m.name.substr(6); + schemaMetadata->Append("sourceTable", name); + schemaMetadata->Append("sourceMatcher", DataSpecUtils::describe(std::get(DataSpecUtils::fromMetadataString(m.defaultValue.get()).matcher))); + continue; + } + // Ignore the non ccdb: entries + if (!m.name.starts_with("ccdb:")) { + continue; } - schemas.emplace_back(std::make_shared(fields, schemaMetadata)); + // Create the schema of the output + auto metadata = std::make_shared(); + metadata->Append("url", m.defaultValue.asString()); + auto columnName = m.name.substr(strlen("ccdb:")); + fields.emplace_back(std::make_shared(columnName, arrow::binary_view(), false, metadata)); } - + schemas.emplace_back(std::make_shared(fields, schemaMetadata)); + } + return adaptStateful([schemas](CallbackService& callbacks, ConfigParamRegistry const& options, DeviceSpec const& spec) { std::shared_ptr helper = std::make_shared(); CCDBFetcherHelper::initialiseHelper(*helper, options); std::unordered_map bindings; @@ -130,11 +129,11 @@ AlgorithmSpec AnalysisCCDBHelpers::fetchFromCCDB(ConfigContext const& /*ctx*/) int outputRouteIndex = bindings.at(outRouteDesc); auto& spec = helper->routes[outputRouteIndex].matcher; std::vector> builders; - for (auto const& _ : schema->fields()) { + for (auto& _ : schema->fields()) { builders.emplace_back(std::make_shared()); } - for (auto ci = 0; ci < timestampColumn->num_chunks(); ++ci) { + for (size_t ci = 0; ci < timestampColumn->num_chunks(); ++ci) { std::shared_ptr chunk = timestampColumn->chunk(ci); auto const* timestamps = chunk->data()->GetValuesSafe(1); diff --git a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.h b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.h index 3be2138bd2b5c..f8175034da0ba 100644 --- a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.h +++ b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.h @@ -17,7 +17,7 @@ namespace o2::framework { struct AnalysisCCDBHelpers { - static AlgorithmSpec fetchFromCCDB(ConfigContext const&); + static AlgorithmSpec fetchFromCCDB(ConfigContext const& ctx); }; } // namespace o2::framework diff --git a/Framework/Core/include/Framework/AnalysisTask.h b/Framework/Core/include/Framework/AnalysisTask.h index 4f8a9e719e4b9..c50b5358990de 100644 --- a/Framework/Core/include/Framework/AnalysisTask.h +++ b/Framework/Core/include/Framework/AnalysisTask.h @@ -521,7 +521,7 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) std::vector expressionInfos; /// make sure options and configurables are set before expression infos are created - homogeneous_apply_refs([&options](auto& element) { return analysis_task_parsers::appendOption(options, element); }, *task.get()); + homogeneous_apply_refs([&options, &hash](auto& element) { return analysis_task_parsers::appendOption(options, element); }, *task.get()); /// extract conditions and append them as inputs homogeneous_apply_refs([&inputs](auto& element) { return analysis_task_parsers::appendCondition(inputs, element); }, *task.get()); @@ -620,7 +620,7 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) } // reset pre-slice for the next dataframe auto slices = pc.services().get(); - homogeneous_apply_refs([&slices](auto& element) { + homogeneous_apply_refs([&pc, &slices](auto& element) { return analysis_task_parsers::updateSliceInfo(element, slices); }, *(task.get())); diff --git a/Framework/Core/src/AnalysisSupportHelpers.cxx b/Framework/Core/src/AnalysisSupportHelpers.cxx index 7edf9a2d8d27f..15b56f9afbff5 100644 --- a/Framework/Core/src/AnalysisSupportHelpers.cxx +++ b/Framework/Core/src/AnalysisSupportHelpers.cxx @@ -98,7 +98,7 @@ std::shared_ptr AnalysisSupportHelpers::getDataOutputDirecto if (!keepString.empty()) { dod->reset(); std::string d("dangling"); - if (d.starts_with(keepString) == 0) { + if (d.find(keepString) == 0) { // use the dangling outputs std::vector danglingOutputs; for (auto ii = 0u; ii < OutputsInputs.size(); ii++) { @@ -144,7 +144,7 @@ void AnalysisSupportHelpers::addMissingOutputsToSpawner(std::vector sinks::append_to{publisher.outputs}; // append them to the publisher outputs std::vector additionalInputs; - for (auto const& input : requestedSpecials | views::filter_not_matching(providedSpecials)) { + for (auto& input : requestedSpecials | views::filter_not_matching(providedSpecials)) { input.metadata | views::filter_string_params_with("input:") | views::params_to_input_specs() | From 538f355832f7b5e353704b09c123dd603521e7df Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 22 Jan 2026 10:09:02 +0100 Subject: [PATCH 008/133] DPL: do not do the new early forwarding for some of the data --- Framework/Core/src/DataProcessingDevice.cxx | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index fb54af9402079..3eaab36fb7908 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -1054,11 +1054,22 @@ void DataProcessingDevice::fillContext(DataProcessorContext& context, DeviceCont } auto decideEarlyForward = [&context, &deviceContext, &spec, this]() -> ForwardPolicy { - //ForwardPolicy defaultEarlyForwardPolicy = getenv("DPL_OLD_EARLY_FORWARD") ? ForwardPolicy::AtCompletionPolicySatisified : ForwardPolicy::AtInjection; - // Make the new policy optional until we handle some of the corner cases - // with custom policies which expect the early forward to happen only when - // all the data is available, like in the TPC case. - ForwardPolicy defaultEarlyForwardPolicy = getenv("DPL_NEW_EARLY_FORWARD") ? ForwardPolicy::AtInjection : ForwardPolicy::AtCompletionPolicySatisified; + ForwardPolicy defaultEarlyForwardPolicy = getenv("DPL_OLD_EARLY_FORWARD") ? ForwardPolicy::AtCompletionPolicySatisified : ForwardPolicy::AtInjection; + // FIXME: try again with the new policy by default. + // + // Make the new policy optional until we handle some of the corner cases + // with custom policies which expect the early forward to happen only when + // all the data is available, like in the TPC case. + // ForwardPolicy defaultEarlyForwardPolicy = getenv("DPL_NEW_EARLY_FORWARD") ? ForwardPolicy::AtInjection : ForwardPolicy::AtCompletionPolicySatisified; + for (auto& forward : spec.forwards) { + if (DataSpecUtils::match(forward.matcher, ConcreteDataTypeMatcher{"TPC", "DIGITSMCTR"}) || + DataSpecUtils::match(forward.matcher, ConcreteDataTypeMatcher{"TPC", "CLNATIVEMCLBL"}) || + DataSpecUtils::match(forward.matcher, ConcreteDataTypeMatcher{o2::header::gDataOriginTPC, "DIGITS"}) || + DataSpecUtils::match(forward.matcher, ConcreteDataTypeMatcher{o2::header::gDataOriginTPC, "CLUSTERNATIVE"})) { + defaultEarlyForwardPolicy = ForwardPolicy::AtCompletionPolicySatisified; + break; + } + } /// We must make sure there is no optional /// if we want to optimize the forwarding From 5376bb861a3a935a2a8211917d6d2e52e51c535e Mon Sep 17 00:00:00 2001 From: shahoian Date: Fri, 23 Jan 2026 17:01:44 +0100 Subject: [PATCH 009/133] PVertexer::refitVertexFull for refitting with different geom. In in case the residuals monitoring is running with geometry different from the one used for initial reconstruction pass a --configKeyValues option for the vertex refit as: ;pvertexer.useMeanVertexConstraint=false;pvertexer.iniScale2=100;pvertexer.acceptableScale2=10.; It will be used by the PVertexer::refitVertexFull. --- .../GlobalTrackingStudy/CheckResidConfig.h | 5 ++- .../study/src/CheckResid.cxx | 40 ++++++++++++------- .../include/DetectorsVertexing/PVertexer.h | 4 +- Detectors/Vertexing/src/PVertexer.cxx | 31 ++++++++++++++ 4 files changed, 62 insertions(+), 18 deletions(-) diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidConfig.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidConfig.h index 53dffeed7ad69..2a07eaf87930f 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidConfig.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidConfig.h @@ -26,11 +26,14 @@ struct CheckResidConfig : o2::conf::ConfigurableParamHelper { bool pvcontribOnly = true; bool addPVAsCluster = true; - bool refitPV = true; bool useStableRef = true; bool doIBOB = true; bool doResid = true; + bool refitPV = true; + float refitPVMV = false; + float refitPVIniScale = 100.f; + O2ParamDef(CheckResidConfig, "checkresid"); }; } // namespace o2::checkresid diff --git a/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx b/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx index d665af5747c60..e6584a7055446 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx @@ -38,14 +38,17 @@ #include "CommonUtils/TreeStreamRedirector.h" #include "ReconstructionDataFormats/VtxTrackRef.h" #include "DetectorsVertexing/PVertexer.h" - #ifdef WITH_OPENMP #include #endif +// Attention: in case the residuals are checked with geometry different from the one used for initial reconstruction, +// pass a --configKeyValues option for vertex refit as: +// ;pvertexer.useMeanVertexConstraint=false;pvertexer.iniScale2=100;pvertexer.acceptableScale2=10.; +// In any case, it is better to pass ;pvertexer.useMeanVertexConstraint=false; + namespace o2::checkresid { - using namespace o2::framework; using DetID = o2::detectors::DetID; using DataRequest = o2::globaltracking::DataRequest; @@ -83,6 +86,7 @@ class CheckResidSpec : public Task o2::globaltracking::RecoContainer* mRecoData = nullptr; int mNThreads = 1; + bool mMeanVertexUpdated = false; float mITSROFrameLengthMUS = 0.f; o2::dataformats::MeanVertexObject mMeanVtx{}; std::vector> mITSClustersArray; ///< ITS clusters created in run() method from compact clusters @@ -131,6 +135,7 @@ void CheckResidSpec::updateTimeDependentParams(ProcessingContext& pc) // mTPCCorrMapsLoader.extractCCDBInputs(pc); static bool initOnceDone = false; if (!initOnceDone) { // this params need to be queried only once + const auto& params = o2::checkresid::CheckResidConfig::Instance(); initOnceDone = true; // Note: reading of the ITS AlpideParam needed for ITS timing is done by the RecoContainer auto grp = o2::base::GRPGeomHelper::instance().getGRPECS(); @@ -142,9 +147,13 @@ void CheckResidSpec::updateTimeDependentParams(ProcessingContext& pc) } auto geom = o2::its::GeometryTGeo::Instance(); geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G, o2::math_utils::TransformType::T2G)); - o2::conf::ConfigurableParam::updateFromString("pvertexer.useMeanVertexConstraint=false"); + o2::conf::ConfigurableParam::updateFromString("pvertexer.useTimeInChi2=false;"); mVertexer.init(); } + if (mMeanVertexUpdated) { + mMeanVertexUpdated = false; + mVertexer.initMeanVertexConstraint(); + } bool updateMaps = false; /* if (mTPCCorrMapsLoader.isUpdated()) { @@ -200,6 +209,7 @@ void CheckResidSpec::process() } nvGood++; if (params.refitPV) { + LOGP(debug, "Refitting PV#{} of {} tracks", iv, pve.getNContributors()); auto tStartPVF = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); bool res = refitPV(pve, iv); pvFitDuration += std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count() - tStartPVF; @@ -315,6 +325,7 @@ bool CheckResidSpec::processITSTrack(const o2::its::TrackITS& iTrack, const o2:: resTrack.points.clear(); if (!prop->propagateToDCA(pv, trFitOut, bz)) { + LOGP(debug, "Failed to propagateToDCA, {}", trFitOut.asString()); return false; } float cosAlp, sinAlp; @@ -418,7 +429,7 @@ bool CheckResidSpec::refitPV(o2::dataformats::PrimaryVertex& pv, int vid) std::vector tracks; std::vector useTrack; std::vector gidsITS; - int ntr = pv.getNContributors(); + int ntr = pv.getNContributors(), ntrIni = ntr; tracks.reserve(ntr); useTrack.reserve(ntr); gidsITS.reserve(ntr); @@ -447,20 +458,18 @@ bool CheckResidSpec::refitPV(o2::dataformats::PrimaryVertex& pv, int vid) ntr++; } if (ntr < params.minPVContributors || !mVertexer.prepareVertexRefit(tracks, pv)) { + LOGP(warn, "Abandon vertex refit: NcontribNew = {} vs NcontribOld = {}", ntr, ntrIni); return false; } - // readjust vertexZ - const auto& pool = mVertexer.getTracksPool(); - float zUpd = 0; - for (const auto& t : pool) { - zUpd += t.z; - } - if (pool.size()) { - pv.setZ(zUpd / pool.size()); - mVertexer.prepareVertexRefit(tracks, pv); + LOGP(debug, "Original vtx: Nc:{} {}, chi2={}", pv.getNContributors(), pv.asString(), pv.getChi2()); + auto pvSave = pv; + pv = mVertexer.refitVertexFull(useTrack, pv); + LOGP(debug, "Refitted vtx: Nc:{} {}, chi2={}", ntr, pv.asString(), pv.getChi2()); + if (pv.getChi2() < 0.f) { + LOGP(warn, "Failed to refit PV {}", pvSave.asString()); + return false; } - pv = mVertexer.refitVertex(useTrack, pv); - return pv.getChi2() > 0.f; + return true; } bool CheckResidSpec::refitITStrack(o2::track::TrackParCov& track, GTrackID gid) @@ -515,6 +524,7 @@ void CheckResidSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (matcher == ConcreteDataMatcher("GLO", "MEANVERTEX", 0)) { LOG(info) << "Imposing new MeanVertex: " << ((const o2::dataformats::MeanVertexObject*)obj)->asString(); mMeanVtx = *(const o2::dataformats::MeanVertexObject*)obj; + mMeanVertexUpdated = true; return; } if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { diff --git a/Detectors/Vertexing/include/DetectorsVertexing/PVertexer.h b/Detectors/Vertexing/include/DetectorsVertexing/PVertexer.h index 9967cbfcd5642..c06c2119b0cd1 100644 --- a/Detectors/Vertexing/include/DetectorsVertexing/PVertexer.h +++ b/Detectors/Vertexing/include/DetectorsVertexing/PVertexer.h @@ -113,7 +113,7 @@ class PVertexer bool prepareVertexRefit(const TR& tracks, const o2d::VertexBase& vtxSeed); PVertex refitVertex(const std::vector useTrack, const o2d::VertexBase& vtxSeed); - + PVertex refitVertexFull(const std::vector useTrack, const o2d::VertexBase& vtxSeed); auto getNTZClusters() const { return mNTZClustersIni; } auto getTotTrials() const { return mTotTrials; } auto getMaxTrialsPerCluster() const { return mMaxTrialPerCluster; } @@ -135,6 +135,7 @@ class PVertexer void setPoolDumpDirectory(const std::string& d) { mPoolDumpDirectory = d; } void printInpuTracksStatus(const VertexingInput& input) const; + void initMeanVertexConstraint(); private: static constexpr int DBS_UNDEF = -2, DBS_NOISE = -1, DBS_INCHECK = -10; @@ -152,7 +153,6 @@ class PVertexer FitStatus evalIterations(VertexSeed& vtxSeed, PVertex& vtx) const; TimeEst timeEstimate(const VertexingInput& input) const; float findZSeedHistoPeak() const; - void initMeanVertexConstraint(); void applyConstraint(VertexSeed& vtxSeed) const; bool upscaleSigma(VertexSeed& vtxSeed) const; bool relateTrackToMeanVertex(o2::track::TrackParCov& trc, float vtxErr2); diff --git a/Detectors/Vertexing/src/PVertexer.cxx b/Detectors/Vertexing/src/PVertexer.cxx index 5fea1943ac762..10e504bba0772 100644 --- a/Detectors/Vertexing/src/PVertexer.cxx +++ b/Detectors/Vertexing/src/PVertexer.cxx @@ -1333,6 +1333,37 @@ PVertex PVertexer::refitVertex(const std::vector useTrack, const o2d::Vert return vtxRes; } +//______________________________________________ +PVertex PVertexer::refitVertexFull(const std::vector useTrack, const o2d::VertexBase& vtxSeed) +{ + // Use this method if because of e.g. different alingnment the new vertex is supposed to be shifted from the original one. + // Refit the tracks prepared by the successful prepareVertexRefit, possible skipping those tracks wich have useTrack value false + // (useTrack is ignored if empty). + // The vtxSeed is the originally found vertex, assumed to be the same original PV used for the prepareVertexRefit. + // Refitted PrimaryVertex is returned, negative chi2 means failure of the refit. + // ATTENTION: only the position is refitted, the vertex time and IRMin/IRMax info is dummy. + + if (vtxSeed != mVtxRefitOrig) { + throw std::runtime_error("refitVertex must be preceded by successful prepareVertexRefit"); + } + VertexingInput inp; + inp.scaleSigma2 = mPVParams->iniScale2; + inp.idRange = gsl::span(mRefitTrackIDs); + if (useTrack.size()) { + for (uint32_t i = 0; i < mTracksPool.size(); i++) { + mTracksPool[i].vtxID = useTrack[mTracksPool[i].entry] ? TrackVF::kNoVtx : TrackVF::kDiscarded; + } + } + PVertex vtxRes{}; + vtxRes.VertexBase::operator=(vtxSeed); + if (findVertex(inp, vtxRes)) { + vtxRes.setTimeStamp({0.f, -1.}); // time is not refitter + } else { + vtxRes.setChi2(-1.); + } + return vtxRes; +} + //______________________________________________ void PVertexer::printInpuTracksStatus(const VertexingInput& input) const { From 5b572ed12a5b3ca085a18d9ece546a84a98c3c30 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Fri, 11 Jul 2025 14:01:38 +0200 Subject: [PATCH 010/133] ITSMFT: staggered digitization Signed-off-by: Felix Schlepper --- .../Detectors/ITSMFT/common/src/ROFRecord.cxx | 14 +- .../ITS/base/include/ITSBase/GeometryTGeo.h | 8 +- .../MFT/base/include/MFTBase/GeometryTGeo.h | 6 +- .../base/include/ITSMFTBase/DPLAlpideParam.h | 41 +- .../base/include/ITSMFTBase/GeometryTGeo.h | 5 +- .../include/ITSMFTSimulation/DigiParams.h | 45 +- .../include/ITSMFTSimulation/Digitizer.h | 24 +- .../common/simulation/src/DigiParams.cxx | 44 +- .../common/simulation/src/Digitizer.cxx | 98 ++--- .../include/ITSMFTWorkflow/DigitReaderSpec.h | 54 +-- .../common/workflow/src/DigitReaderSpec.cxx | 223 +++++----- .../common/workflow/src/DigitWriterSpec.cxx | 84 +++- .../src/ITSMFTDigitizerSpec.cxx | 413 ++++++++++-------- 13 files changed, 614 insertions(+), 445 deletions(-) diff --git a/DataFormats/Detectors/ITSMFT/common/src/ROFRecord.cxx b/DataFormats/Detectors/ITSMFT/common/src/ROFRecord.cxx index 83b46f8798fc9..8dbde0d580efc 100644 --- a/DataFormats/Detectors/ITSMFT/common/src/ROFRecord.cxx +++ b/DataFormats/Detectors/ITSMFT/common/src/ROFRecord.cxx @@ -9,20 +9,22 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "DataFormatsITSMFT/ROFRecord.h" #include -#include "fmt/format.h" +#include + +#include "DataFormatsITSMFT/ROFRecord.h" +#include "Framework/Logger.h" using namespace o2::itsmft; std::string ROFRecord::asString() const { - return fmt::format("ROF: {} | {} entries starting from {}", mROFrame, getNEntries(), getFirstEntry()); + return std::format("ROF: {} | {} entries starting from {} | IR: {}", mROFrame, getNEntries(), getFirstEntry(), mBCData.asString()); } void ROFRecord::print() const { - std::cout << this << "\n\t" << mBCData << std::endl; + LOG(info) << asString(); } std::ostream& operator<<(std::ostream& stream, ROFRecord const& rec) @@ -33,12 +35,12 @@ std::ostream& operator<<(std::ostream& stream, ROFRecord const& rec) std::string MC2ROFRecord::asString() const { - return fmt::format("MCEventID: {} ROFs: {}-{} Entry in ROFRecords: {}", eventRecordID, minROF, maxROF, rofRecordID); + return std::format("MCEventID: {} ROFs: {}-{} Entry in ROFRecords: {}", eventRecordID, minROF, maxROF, rofRecordID); } void MC2ROFRecord::print() const { - std::cout << this << std::endl; + LOG(info) << asString(); } std::ostream& operator<<(std::ostream& stream, MC2ROFRecord const& rec) diff --git a/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h b/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h index 934c927ac3059..e236c898851f5 100644 --- a/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h +++ b/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h @@ -176,7 +176,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo bool getChipId(int index, int& lay, int& hba, int& sta, int& ssta, int& mod, int& chip) const; /// Get chip layer, from 0 - int getLayer(int index) const; + int getLayer(int index) const final; /// Get chip half barrel, from 0 int getHalfBarrel(int index) const; @@ -216,7 +216,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo return getSymbolicName(getChipIndex(lay, hba, sta, det)); } - /// Get the transformation matrix for a given chip (NOT A SENSOR!!!) 'index' by quering the TGeoManager + /// Get the transformation matrix for a given chip (NOT A SENSOR!!!) 'index' by querying the TGeoManager TGeoHMatrix* getMatrix(int index) const { return o2::base::GeometryManager::getMatrix(getDetID(), index); } TGeoHMatrix* getMatrix(int lay, int hba, int sta, int sens) const { return getMatrix(getChipIndex(lay, hba, sta, sens)); } bool getOriginalMatrix(int index, TGeoHMatrix& m) const @@ -336,7 +336,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo TString getMatrixPath(int index) const; /// Get the transformation matrix of the SENSOR (not necessary the same as the chip) - /// for a given chip 'index' by quering the TGeoManager + /// for a given chip 'index' by querying the TGeoManager TGeoHMatrix* extractMatrixSensor(int index) const; // create matrix for transformation from sensor local frame to global one @@ -407,7 +407,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo std::vector mNumberOfChipsPerStave; ///< number of chips per stave std::vector mNumberOfChipsPerHalfBarrel; ///< number of chips per halfbarrel std::vector mNumberOfChipsPerLayer; ///< number of chips per stave - std::vector mLastChipIndex; ///< max ID of the detctor in the layer + std::vector mLastChipIndex; ///< max ID of the detector in the layer std::array mIsLayerITS3; ///< flag with the information of the ITS version (ITS2 or ITS3) std::array mLayerToWrapper; ///< Layer to wrapper correspondence diff --git a/Detectors/ITSMFT/MFT/base/include/MFTBase/GeometryTGeo.h b/Detectors/ITSMFT/MFT/base/include/MFTBase/GeometryTGeo.h index 503e8332c4cf5..20b5407d614c5 100644 --- a/Detectors/ITSMFT/MFT/base/include/MFTBase/GeometryTGeo.h +++ b/Detectors/ITSMFT/MFT/base/include/MFTBase/GeometryTGeo.h @@ -95,7 +95,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo Int_t getSensorIndex(Int_t half, Int_t disk, Int_t ladder, Int_t sensor) const; /// get layer index (0:9) from the chip index - Int_t getLayer(Int_t index) const; + Int_t getLayer(Int_t index) const final; /// This routine computes the half, disk, ladder and sensor number /// given the sensor index number @@ -122,7 +122,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo { return extractNumberOfDisks(half); } - /// Returns the number of halfs MFT + /// Returns the number of halves MFT Int_t getNumberOfHalfs() { return extractNumberOfHalves(); @@ -181,7 +181,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo Int_t extractVolumeCopy(const Char_t* name, const Char_t* prefix) const; /// Get the transformation matrix of the sensor [...] - /// for a given sensor 'index' by quering the TGeoManager + /// for a given sensor 'index' by querying the TGeoManager TGeoHMatrix* extractMatrixSensor(Int_t index) const; // Create matrix for transformation from sensor local frame to global one diff --git a/Detectors/ITSMFT/common/base/include/ITSMFTBase/DPLAlpideParam.h b/Detectors/ITSMFT/common/base/include/ITSMFTBase/DPLAlpideParam.h index bc3b3dbde53b0..de39bed299634 100644 --- a/Detectors/ITSMFT/common/base/include/ITSMFTBase/DPLAlpideParam.h +++ b/Detectors/ITSMFT/common/base/include/ITSMFTBase/DPLAlpideParam.h @@ -26,17 +26,44 @@ constexpr float DEFStrobeDelay = o2::constants::lhc::LHCBunchSpacingNS * 4; // ~ template struct DPLAlpideParam : public o2::conf::ConfigurableParamHelper> { + static constexpr int getNLayers() + { + return N == o2::detectors::DetID::ITS ? 7 : 10; + } static constexpr std::string_view getParamName() { return N == o2::detectors::DetID::ITS ? ParamName[0] : ParamName[1]; } - int roFrameLengthInBC = DEFROFLengthBC(); ///< ROF length in BC for continuos mode - float roFrameLengthTrig = DEFROFLengthTrig(); ///< length of RO frame in ns for triggered mode - float strobeDelay = DEFStrobeDelay; ///< strobe start (in ns) wrt ROF start - float strobeLengthCont = -1.; ///< if < 0, full ROF length - delay - float strobeLengthTrig = 100.; ///< length of the strobe in ns (sig. over threshold checked in this window only) - int roFrameBiasInBC = DEFROFBiasInBC(); ///< bias of the start of ROF wrt orbit start: t_irof = (irof*roFrameLengthInBC + roFrameBiasInBC)*BClengthMUS + + int roFrameLengthInBC = DEFROFLengthBC(); ///< ROF length in BC for continuous mode + float roFrameLengthTrig = DEFROFLengthTrig(); ///< length of RO frame in ns for triggered mode + float strobeDelay = DEFStrobeDelay; ///< strobe start (in ns) wrt ROF start + float strobeLengthCont = -1.; ///< if < 0, full ROF length - delay + float strobeLengthTrig = 100.; ///< length of the strobe in ns (sig. over threshold checked in this window only) + int roFrameBiasInBC = DEFROFBiasInBC(); ///< bias of the start of ROF wrt orbit start: t_irof = (irof*roFrameLengthInBC + roFrameBiasInBC)*BClengthMUS + int roFrameLayerLengthInBC[getNLayers()] = {}; ///< staggering ROF length in BC for continuous mode per layer + int roFrameLayerBiasInBC[getNLayers()] = {}; ///< staggering ROF bias in BC for continuous mode per layer + int roFrameLayerDelayInBC[getNLayers()] = {}; ///< staggering ROF delay in BC for continuous mode per layer + + static constexpr bool supportsStaggering() noexcept { return (N == o2::detectors::DetID::ITS) ? false : false; } + // test if staggering is on + bool withStaggering() const noexcept + { + if constexpr (!supportsStaggering()) { + return false; + } + for (int i{0}; i < getNLayers(); ++i) { + if (roFrameLayerLengthInBC[i] != 0) { + return true; + } + } + return false; + } + // get ROF length for any layer + int getROFLengthInBC(int layer) const noexcept { return (withStaggering()) ? roFrameLayerLengthInBC[layer] : roFrameLengthInBC; } + int getROFBiasInBC(int layer) const noexcept { return (withStaggering()) ? roFrameLayerBiasInBC[layer] : roFrameBiasInBC; } + int getROFDelayInBC(int layer) const noexcept { return (withStaggering()) ? roFrameLayerDelayInBC[layer] : 0; } // boilerplate stuff + make principal key O2ParamDef(DPLAlpideParam, getParamName().data()); @@ -46,7 +73,7 @@ struct DPLAlpideParam : public o2::conf::ConfigurableParamHelper +#include #include -#include +#include "ITSMFTSimulation/AlpideSignalTrapezoid.h" #include "ITSMFTBase/DPLAlpideParam.h" //////////////////////////////////////////////////////////// @@ -51,24 +53,24 @@ class DigiParams void setContinuous(bool v) { mIsContinuous = v; } bool isContinuous() const { return mIsContinuous; } - int getROFrameLengthInBC() const { return mROFrameLengthInBC; } - void setROFrameLengthInBC(int n) { mROFrameLengthInBC = n; } + int getROFrameLengthInBC(int layer = -1) const { return layer < 0 ? mROFrameLengthInBC : mROFrameLayerLengthInBC[layer]; } + void setROFrameLengthInBC(int n, int layer = -1) { layer < 0 ? mROFrameLengthInBC = n : mROFrameLayerLengthInBC[layer] = n; } - void setROFrameLength(float ns); - float getROFrameLength() const { return mROFrameLength; } - float getROFrameLengthInv() const { return mROFrameLengthInv; } + void setROFrameLength(float ns, int layer = -1); + float getROFrameLength(int layer = -1) const { return layer < 0 ? mROFrameLength : mROFrameLayerLength[layer]; } + float getROFrameLengthInv(int layer = -1) const { return layer < 0 ? mROFrameLengthInv : mROFrameLayerLengthInv[layer]; } void setStrobeDelay(float ns) { mStrobeDelay = ns; } - float getStrobeDelay() const { return mStrobeDelay; } + float getStrobeDelay(int layer = -1) const { return layer < 0 ? mStrobeDelay : mStrobeLayerDelay[layer]; } void setStrobeLength(float ns) { mStrobeLength = ns; } - float getStrobeLength() const { return mStrobeLength; } + float getStrobeLength(int layer = -1) const { return layer < 0 ? mStrobeLength : mStrobeLayerLength[layer]; } void setTimeOffset(double sec) { mTimeOffset = sec; } double getTimeOffset() const { return mTimeOffset; } - void setROFrameBiasInBC(int n) { mROFrameBiasInBC = n; } - int getROFrameBiasInBC() const { return mROFrameBiasInBC; } + void setROFrameBiasInBC(int n, int layer = -1) { layer < 0 ? mROFrameBiasInBC = n : mROFrameLayerBiasInBC[layer] = n; } + int getROFrameBiasInBC(int layer = -1) const { return layer < 0 ? mROFrameBiasInBC : mROFrameLayerBiasInBC[layer]; } void setChargeThreshold(int v, float frac2Account = 0.1); void setNSimSteps(int v); @@ -96,13 +98,19 @@ class DigiParams const SignalShape& getSignalShape() const { return mSignalShape; } SignalShape& getSignalShape() { return (SignalShape&)mSignalShape; } + bool withStaggering() const noexcept { return !mROFrameLayerLength.empty(); } + void addROFrameLayerLengthInBC(int len) { mROFrameLayerLengthInBC.push_back(len); } + void addROFrameLayerBiasInBC(int len) { mROFrameLayerBiasInBC.push_back(len); } + void addStrobeLength(float ns) { mStrobeLayerLength.push_back(ns); } + void addStrobeDelay(float ns) { mStrobeLayerDelay.push_back(ns); } + virtual void print() const; private: static constexpr double infTime = 1e99; bool mIsContinuous = false; ///< flag for continuous simulation float mNoisePerPixel = 1.e-8; ///< ALPIDE Noise per chip - int mROFrameLengthInBC = 0; ///< ROF length in BC for continuos mode + int mROFrameLengthInBC = 0; ///< ROF length in BC for continuous mode float mROFrameLength = 0; ///< length of RO frame in ns float mStrobeDelay = 0.; ///< strobe start (in ns) wrt ROF start float mStrobeLength = 0; ///< length of the strobe in ns (sig. over threshold checked in this window only) @@ -115,17 +123,24 @@ class DigiParams float mVbb = 0.0; ///< back bias absolute value for MFT (in Volt) float mIBVbb = 0.0; ///< back bias absolute value for ITS Inner Barrel (in Volt) - float mOBVbb = 0.0; ///< back bias absolute value for ITS Outter Barrel (in Volt) + float mOBVbb = 0.0; ///< back bias absolute value for ITS Outer Barrel (in Volt) + + std::vector mROFrameLayerLengthInBC; ///< staggering ROF length in BC for continuous mode per layer + std::vector mROFrameLayerBiasInBC; ///< staggering ROF bias in BC for continuous mode per layer + std::vector mROFrameLayerLength; ///< staggering ROF length in ns for continuous mode per layer + std::vector mStrobeLayerLength; ///< staggering length of the strobe in ns (sig. over threshold checked in this window only) + std::vector mStrobeLayerDelay; ///< staggering delay of the strobe in ns o2::itsmft::AlpideSignalTrapezoid mSignalShape; ///< signal timeshape parameterization const o2::itsmft::AlpideSimResponse* mAlpSimResponse = nullptr; //!< pointer on external response // auxiliary precalculated parameters - float mROFrameLengthInv = 0; ///< inverse length of RO frame in ns - float mNSimStepsInv = 0; ///< its inverse + float mROFrameLengthInv = 0; ///< inverse length of RO frame in ns + std::vector mROFrameLayerLengthInv; // inverse length of RO frame in ns per layer + float mNSimStepsInv = 0; ///< its inverse - ClassDef(DigiParams, 2); + ClassDef(DigiParams, 3); }; } // namespace itsmft } // namespace o2 diff --git a/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/Digitizer.h b/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/Digitizer.h index 670dd32bf9f46..c81e2d9476644 100644 --- a/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/Digitizer.h +++ b/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/Digitizer.h @@ -49,6 +49,8 @@ class Digitizer : public TObject public: Digitizer() = default; + Digitizer(Digitizer&&) = delete; + Digitizer& operator=(Digitizer&&) = delete; ~Digitizer() override = default; Digitizer(const Digitizer&) = delete; Digitizer& operator=(const Digitizer&) = delete; @@ -56,7 +58,7 @@ class Digitizer : public TObject void setDigits(std::vector* dig) { mDigits = dig; } void setMCLabels(o2::dataformats::MCTruthContainer* mclb) { mMCLabels = mclb; } void setROFRecords(std::vector* rec) { mROFRecords = rec; } - o2::itsmft::DigiParams& getParams() { return (o2::itsmft::DigiParams&)mParams; } + o2::itsmft::DigiParams& getParams() { return mParams; } const o2::itsmft::DigiParams& getParams() const { return mParams; } void setNoiseMap(const o2::itsmft::NoiseMap* mp) { mNoiseMap = mp; } void setDeadChannelsMap(const o2::itsmft::NoiseMap* mp) { mDeadChanMap = mp; } @@ -67,17 +69,17 @@ class Digitizer : public TObject auto getChipResponse(int chipID); /// Steer conversion of hits to digits - void process(const std::vector* hits, int evID, int srcID); - void setEventTime(const o2::InteractionTimeRecord& irt); + void process(const std::vector* hits, int evID, int srcID, int layer = -1); + void setEventTime(const o2::InteractionTimeRecord& irt, int layer = -1); double getEndTimeOfROFMax() const { ///< return the time corresponding to end of the last reserved ROFrame : mROFrameMax - return mParams.getROFrameLength() * (mROFrameMax + 1) + mParams.getTimeOffset(); + return (mParams.getROFrameLength() * (double)(mROFrameMax + 1)) + mParams.getTimeOffset(); } void setContinuous(bool v) { mParams.setContinuous(v); } bool isContinuous() const { return mParams.isContinuous(); } - void fillOutputContainer(uint32_t maxFrame = 0xffffffff); + void fillOutputContainer(uint32_t maxFrame = 0xffffffff, int layer = -1); void setDigiParams(const o2::itsmft::DigiParams& par) { mParams = par; } const o2::itsmft::DigiParams& getDigitParams() const { return mParams; } @@ -92,11 +94,17 @@ class Digitizer : public TObject mEventROFrameMin = 0xffffffff; mEventROFrameMax = 0; } + void resetROFrameBounds() + { + mROFrameMin = 0; + mROFrameMax = 0; + mNewROFrame = 0; + } private: - void processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID, int srcID); + void processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID, int srcID, int lay); void registerDigits(ChipDigitsContainer& chip, uint32_t roFrame, float tInROF, int nROF, - uint16_t row, uint16_t col, int nEle, o2::MCCompLabel& lbl); + uint16_t row, uint16_t col, int nEle, o2::MCCompLabel& lbl, int lay); ExtraDig* getExtraDigBuffer(uint32_t roFrame) { @@ -115,7 +123,7 @@ class Digitizer : public TObject o2::itsmft::DigiParams mParams; ///< digitization parameters o2::InteractionTimeRecord mEventTime; ///< global event time and interaction record o2::InteractionRecord mIRFirstSampledTF; ///< IR of the 1st sampled IR, noise-only ROFs will be inserted till this IR only - double mCollisionTimeWrtROF; + double mCollisionTimeWrtROF{}; uint32_t mROFrameMin = 0; ///< lowest RO frame of current digits uint32_t mROFrameMax = 0; ///< highest RO frame of current digits uint32_t mNewROFrame = 0; ///< ROFrame corresponding to provided time diff --git a/Detectors/ITSMFT/common/simulation/src/DigiParams.cxx b/Detectors/ITSMFT/common/simulation/src/DigiParams.cxx index ffba627265cc7..a7c5c32b6351d 100644 --- a/Detectors/ITSMFT/common/simulation/src/DigiParams.cxx +++ b/Detectors/ITSMFT/common/simulation/src/DigiParams.cxx @@ -26,12 +26,17 @@ DigiParams::DigiParams() setNSimSteps(mNSimSteps); } -void DigiParams::setROFrameLength(float lNS) +void DigiParams::setROFrameLength(float lNS, int layer) { // set ROFrame length in nanosecongs - mROFrameLength = lNS; - assert(mROFrameLength > 1.); - mROFrameLengthInv = 1. / mROFrameLength; + assert(lNS > 1.f); + if (layer < 0) { + mROFrameLength = lNS; + mROFrameLengthInv = 1.f / mROFrameLength; + } else { + mROFrameLayerLength.push_back(lNS); + mROFrameLayerLengthInv.push_back(1.f / lNS); + } } void DigiParams::setNSimSteps(int v) @@ -58,17 +63,24 @@ void DigiParams::setChargeThreshold(int v, float frac2Account) //______________________________________________ void DigiParams::print() const { - // print settings - printf("Alpide digitization params:\n"); - printf("Continuous readout : %s\n", mIsContinuous ? "ON" : "OFF"); - printf("Readout Frame Length(ns) : %f\n", mROFrameLength); - printf("Strobe delay (ns) : %f\n", mStrobeDelay); - printf("Strobe length (ns) : %f\n", mStrobeLength); - printf("Threshold (N electrons) : %d\n", mChargeThreshold); - printf("Min N electrons to account : %d\n", mMinChargeToAccount); - printf("Number of charge sharing steps : %d\n", mNSimSteps); - printf("ELoss to N electrons factor : %e\n", mEnergyToNElectrons); - printf("Noise level per pixel : %e\n", mNoisePerPixel); - printf("Charge time-response:\n"); + LOGF(info, "Alpide digitization params:"); + LOGF(info, "Continuous readout : %s", mIsContinuous ? "ON" : "OFF"); + if (withStaggering()) { + for (int i{0}; i < (int)mROFrameLayerLengthInBC.size(); ++i) { + LOGF(info, " Readout Frame Layer:%d Length(ns)[BC] : %f [%d]", i, mROFrameLayerLength[i], mROFrameLayerLengthInBC[i]); + LOGF(info, "Strobe delay Layer %d (ns) : %f", i, mStrobeDelay); + LOGF(info, "Strobe length Layer %d (ns) : %f", i, mStrobeLength); + } + } else { + LOGF(info, "Readout Frame Length(ns) : %f", mROFrameLength); + LOGF(info, "Strobe delay (ns) : %f", mStrobeDelay); + LOGF(info, "Strobe length (ns) : %f", mStrobeLength); + } + LOGF(info, "Threshold (N electrons) : %d", mChargeThreshold); + LOGF(info, "Min N electrons to account : %d", mMinChargeToAccount); + LOGF(info, "Number of charge sharing steps : %d", mNSimSteps); + LOGF(info, "ELoss to N electrons factor : %e", mEnergyToNElectrons); + LOGF(info, "Noise level per pixel : %e", mNoisePerPixel); + LOGF(info, "Charge time-response:"); mSignalShape.print(); } diff --git a/Detectors/ITSMFT/common/simulation/src/Digitizer.cxx b/Detectors/ITSMFT/common/simulation/src/Digitizer.cxx index 4a8af0cbe9737..b1a92e988968b 100644 --- a/Detectors/ITSMFT/common/simulation/src/Digitizer.cxx +++ b/Detectors/ITSMFT/common/simulation/src/Digitizer.cxx @@ -13,6 +13,7 @@ /// \brief Implementation of the ITS/MFT digitizer #include "DataFormatsITSMFT/Digit.h" +#include "Framework/Logger.h" #include "ITSMFTBase/SegmentationAlpide.h" #include "ITSMFTSimulation/DPLDigitizerParam.h" #include "ITSMFTSimulation/Digitizer.h" @@ -21,10 +22,11 @@ #include "DetectorsRaw/HBFUtils.h" #include +#include #include #include +#include #include -#include // for LOG using o2::itsmft::Digit; using o2::itsmft::Hit; @@ -73,14 +75,14 @@ void Digitizer::init() } else { LOG(fatal) << "Invalid ITS Inner Barrel back-bias value"; } - if (doptITS.OBVbb == 0.0) { // for ITS Outter Barrel + if (doptITS.OBVbb == 0.0) { // for ITS Outer Barrel mAlpSimRespOB = mAlpSimResp[0]; LOG(info) << "Choosing Vbb=0V for ITS OB"; } else if (doptITS.OBVbb == 3.0) { mAlpSimRespOB = mAlpSimResp[1]; LOG(info) << "Choosing Vbb=-3V for ITS OB"; } else { - LOG(fatal) << "Invalid ITS Outter Barrel back-bias value"; + LOG(fatal) << "Invalid ITS Outer Barrel back-bias value"; } mParams.print(); mIRFirstSampledTF = o2::raw::HBFUtils::Instance().getFirstSampledTFIR(); @@ -98,47 +100,53 @@ auto Digitizer::getChipResponse(int chipID) if (chipID < 432) { // in ITS Inner Barrel return mAlpSimRespIB; - } else { // in ITS Outter Barrel + } else { // in ITS Outer Barrel return mAlpSimRespOB; } } //_______________________________________________________________________ -void Digitizer::process(const std::vector* hits, int evID, int srcID) +void Digitizer::process(const std::vector* hits, int evID, int srcID, int layer) { // digitize single event, the time must have been set beforehand + // opt. apply a filter on the layer of the processed hits - LOG(debug) << "Digitizing " << mGeometry->getName() << " hits of entry " << evID << " from source " + LOG(debug) << "Digitizing " << mGeometry->getName() << ":" << layer << " hits of entry " << evID << " from source " << srcID << " at time " << mEventTime << " ROFrame= " << mNewROFrame << ")" << " cont.mode: " << isContinuous() << " Min/Max ROFrames " << mROFrameMin << "/" << mROFrameMax; // is there something to flush ? if (mNewROFrame > mROFrameMin) { - fillOutputContainer(mNewROFrame - 1); // flush out all frame preceding the new one + fillOutputContainer(mNewROFrame - 1, layer); // flush out all frame preceding the new one } int nHits = hits->size(); std::vector hitIdx(nHits); std::iota(std::begin(hitIdx), std::end(hitIdx), 0); // sort hits to improve memory access - std::sort(hitIdx.begin(), hitIdx.end(), - [hits](auto lhs, auto rhs) { - return (*hits)[lhs].GetDetectorID() < (*hits)[rhs].GetDetectorID(); - }); - for (int i : hitIdx) { - processHit((*hits)[i], mROFrameMax, evID, srcID); + std::sort(hitIdx.begin(), hitIdx.end(), [hits](auto lhs, auto rhs) { + return (*hits)[lhs].GetDetectorID() < (*hits)[rhs].GetDetectorID(); + }); + for (int i : hitIdx | std::views::filter([&](int idx) { + if (layer < 0) { + return true; + } + return mGeometry->getLayer((*hits)[idx].GetDetectorID()) == layer; + })) { + processHit((*hits)[i], mROFrameMax, evID, srcID, layer); } + // in the triggered mode store digits after every MC event // TODO: in the real triggered mode this will not be needed, this is actually for the // single event processing only if (!mParams.isContinuous()) { - fillOutputContainer(mROFrameMax); + fillOutputContainer(mROFrameMax, layer); } } //_______________________________________________________________________ -void Digitizer::setEventTime(const o2::InteractionTimeRecord& irt) +void Digitizer::setEventTime(const o2::InteractionTimeRecord& irt, int layer) { // assign event time in ns mEventTime = irt; @@ -161,13 +169,13 @@ void Digitizer::setEventTime(const o2::InteractionTimeRecord& irt) // this event is before the first RO mIsBeforeFirstRO = true; } else { - mNewROFrame = nbc / mParams.getROFrameLengthInBC(); + mNewROFrame = nbc / mParams.getROFrameLengthInBC(layer); mIsBeforeFirstRO = false; } LOG(debug) << " NewROFrame " << mNewROFrame << " nbc " << nbc; // in continuous mode depends on starts of periodic readout frame - mCollisionTimeWrtROF += (nbc % mParams.getROFrameLengthInBC()) * o2::constants::lhc::LHCBunchSpacingNS; + mCollisionTimeWrtROF += (nbc % mParams.getROFrameLengthInBC(layer)) * o2::constants::lhc::LHCBunchSpacingNS; } else { mNewROFrame = 0; } @@ -183,16 +191,14 @@ void Digitizer::setEventTime(const o2::InteractionTimeRecord& irt) } //_______________________________________________________________________ -void Digitizer::fillOutputContainer(uint32_t frameLast) +void Digitizer::fillOutputContainer(uint32_t frameLast, int layer) { // fill output with digits from min.cached up to requested frame, generating the noise beforehand - if (frameLast > mROFrameMax) { - frameLast = mROFrameMax; - } + frameLast = std::min(frameLast, mROFrameMax); // make sure all buffers for extra digits are created up to the maxFrame getExtraDigBuffer(mROFrameMax); - LOG(info) << "Filling " << mGeometry->getName() << " digits output for RO frames " << mROFrameMin << ":" + LOG(info) << "Filling " << mGeometry->getName() << " digits:" << layer << " output for RO frames " << mROFrameMin << ":" << frameLast; o2::itsmft::ROFRecord rcROF; @@ -204,7 +210,7 @@ void Digitizer::fillOutputContainer(uint32_t frameLast) auto& extra = *(mExtraBuff.front().get()); for (auto& chip : mChips) { - if (chip.isDisabled()) { + if (chip.isDisabled() || (layer >= 0 && mGeometry->getLayer(chip.getChipIndex()) != layer)) { continue; } chip.addNoise(mROFrameMin, mROFrameMin, &mParams); @@ -236,7 +242,7 @@ void Digitizer::fillOutputContainer(uint32_t frameLast) // finalize ROF record rcROF.setNEntries(mDigits->size() - rcROF.getFirstEntry()); // number of digits if (isContinuous()) { - rcROF.getBCData().setFromLong(mIRFirstSampledTF.toLong() + mROFrameMin * mParams.getROFrameLengthInBC()); + rcROF.getBCData().setFromLong(mIRFirstSampledTF.toLong() + mROFrameMin * mParams.getROFrameLengthInBC(layer)); } else { rcROF.getBCData() = mEventTime; // RSTODO do we need to add trigger delay? } @@ -251,7 +257,7 @@ void Digitizer::fillOutputContainer(uint32_t frameLast) } //_______________________________________________________________________ -void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID, int srcID) +void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID, int srcID, int lay) { // convert single hit to digits auto chipID = hit.GetDetectorID(); @@ -284,14 +290,12 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID } float tTot = mParams.getSignalShape().getMaxDuration(); // frame of the hit signal start wrt event ROFrame - int roFrameRel = int(timeInROF * mParams.getROFrameLengthInv()); + int roFrameRel = int(timeInROF * mParams.getROFrameLengthInv(lay)); // frame of the hit signal end wrt event ROFrame: in the triggered mode we read just 1 frame - uint32_t roFrameRelMax = mParams.isContinuous() ? (timeInROF + tTot) * mParams.getROFrameLengthInv() : roFrameRel; + uint32_t roFrameRelMax = mParams.isContinuous() ? (timeInROF + tTot) * mParams.getROFrameLengthInv(lay) : roFrameRel; int nFrames = roFrameRelMax + 1 - roFrameRel; uint32_t roFrameMax = mNewROFrame + roFrameRelMax; - if (roFrameMax > maxFr) { - maxFr = roFrameMax; // if signal extends beyond current maxFrame, increase the latter - } + maxFr = std::max(roFrameMax, maxFr); // if signal extends beyond current maxFrame, increase the latter // here we start stepping in the depth of the sensor to generate charge diffusion float nStepsInv = mParams.getNSimStepsInv(); @@ -332,17 +336,13 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID } rowS -= AlpideRespSimMat::NPix / 2; rowE += AlpideRespSimMat::NPix / 2; - if (rowS < 0) { - rowS = 0; - } + rowS = std::max(rowS, 0); if (rowE >= Segmentation::NRows) { rowE = Segmentation::NRows - 1; } colS -= AlpideRespSimMat::NPix / 2; colE += AlpideRespSimMat::NPix / 2; - if (colS < 0) { - colS = 0; - } + colS = std::max(colS, 0); if (colE >= Segmentation::NCols) { colE = Segmentation::NCols - 1; } @@ -362,7 +362,7 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID const o2::itsmft::AlpideSimResponse* resp = getChipResponse(chipID); - // take into account that the AlpideSimResponse depth defintion has different min/max boundaries + // take into account that the AlpideSimResponse depth definition has different min/max boundaries // although the max should coincide with the surface of the epitaxial layer, which in the chip // local coordinates has Y = +SensorLayerThickness/2 @@ -379,7 +379,7 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID rowPrev = row; colPrev = col; } - bool flipCol, flipRow; + bool flipCol = false, flipRow = false; // note that response needs coordinates along column row (locX) (locZ) then depth (locY) auto rspmat = resp->getResponse(xyzLocS.X() - cRowPix, xyzLocS.Z() - cColPix, xyzLocS.Y(), flipRow, flipCol); @@ -389,12 +389,12 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID } for (int irow = AlpideRespSimMat::NPix; irow--;) { - int rowDest = row + irow - AlpideRespSimMat::NPix / 2 - rowS; // destination row in the respMatrix + int rowDest = row + irow - (AlpideRespSimMat::NPix / 2) - rowS; // destination row in the respMatrix if (rowDest < 0 || rowDest >= rowSpan) { continue; } for (int icol = AlpideRespSimMat::NPix; icol--;) { - int colDest = col + icol - AlpideRespSimMat::NPix / 2 - colS; // destination column in the respMatrix + int colDest = col + icol - (AlpideRespSimMat::NPix / 2) - colS; // destination column in the respMatrix if (colDest < 0 || colDest >= colSpan) { continue; } @@ -426,35 +426,31 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID continue; } // - registerDigits(chip, roFrameAbs, timeInROF, nFrames, rowIS, colIS, nEle, lbl); + registerDigits(chip, roFrameAbs, timeInROF, nFrames, rowIS, colIS, nEle, lbl, lay); } } } //________________________________________________________________________________ void Digitizer::registerDigits(ChipDigitsContainer& chip, uint32_t roFrame, float tInROF, int nROF, - uint16_t row, uint16_t col, int nEle, o2::MCCompLabel& lbl) + uint16_t row, uint16_t col, int nEle, o2::MCCompLabel& lbl, int lay) { // Register digits for given pixel, accounting for the possible signal contribution to // multiple ROFrame. The signal starts at time tInROF wrt the start of provided roFrame // In every ROFrame we check the collected signal during strobe - float tStrobe = mParams.getStrobeDelay() - tInROF; // strobe start wrt signal start + float tStrobe = mParams.getStrobeDelay(lay) - tInROF; // strobe start wrt signal start for (int i = 0; i < nROF; i++) { uint32_t roFr = roFrame + i; - int nEleROF = mParams.getSignalShape().getCollectedCharge(nEle, tStrobe, tStrobe + mParams.getStrobeLength()); - tStrobe += mParams.getROFrameLength(); // for the next ROF + int nEleROF = mParams.getSignalShape().getCollectedCharge(nEle, tStrobe, tStrobe + mParams.getStrobeLength(lay)); + tStrobe += mParams.getROFrameLength(lay); // for the next ROF // discard too small contributions, they have no chance to produce a digit if (nEleROF < mParams.getMinChargeToAccount()) { continue; } - if (roFr > mEventROFrameMax) { - mEventROFrameMax = roFr; - } - if (roFr < mEventROFrameMin) { - mEventROFrameMin = roFr; - } + mEventROFrameMax = std::max(roFr, mEventROFrameMax); + mEventROFrameMin = std::min(roFr, mEventROFrameMin); auto key = chip.getOrderingKey(roFr, row, col); PreDigit* pd = chip.findDigit(key); if (!pd) { diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/DigitReaderSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/DigitReaderSpec.h index e655e05842d71..348ba76468144 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/DigitReaderSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/DigitReaderSpec.h @@ -16,6 +16,7 @@ #include "TFile.h" #include "TTree.h" +#include "ITSMFTBase/DPLAlpideParam.h" #include "DataFormatsITSMFT/Digit.h" #include "DataFormatsITSMFT/GBTCalibData.h" #include "DataFormatsITSMFT/ROFRecord.h" @@ -34,64 +35,67 @@ namespace o2 namespace itsmft { +template class DigitReader : public Task { public: + static constexpr o2::detectors::DetID ID{N == o2::detectors::DetID::ITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT}; + static constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + static constexpr int NLayers{o2::itsmft::DPLAlpideParam::getNLayers()}; + static constexpr int RLayers = o2::itsmft::DPLAlpideParam::supportsStaggering() ? NLayers : 1; + DigitReader() = delete; - DigitReader(o2::detectors::DetID id, bool useMC, bool useCalib, bool triggerOut); + DigitReader(bool useMC, bool useCalib, bool triggerOut); ~DigitReader() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; protected: void connectTree(const std::string& filename); + template + void setBranchAddress(const std::string& base, Ptr& addr, int layer = -1); + std::string getBranchName(const std::string& base, int index); - std::vector mDigits, *mDigitsPtr = &mDigits; + std::array*, NLayers> mDigits; std::vector mCalib, *mCalibPtr = &mCalib; - std::vector mDigROFRec, *mDigROFRecPtr = &mDigROFRec; - std::vector mDigMC2ROFs, *mDigMC2ROFsPtr = &mDigMC2ROFs; - o2::dataformats::ConstMCTruthContainer mConstLabels; - o2::header::DataOrigin mOrigin = o2::header::gDataOriginInvalid; + std::array*, NLayers> mDigROFRec; + std::array*, NLayers> mDigMC2ROFs; + std::array, NLayers> mConstLabels; + std::array mPLabels; std::unique_ptr mFile; std::unique_ptr mTree; - bool mUseMC = true; // use MC truth - bool mUseCalib = true; // send calib data - bool mTriggerOut = true; // send dummy triggers vector + bool mUseMC = true; // use MC truth + bool mUseCalib = true; // send calib data + bool mTriggerOut = true; // send dummy triggers vector bool mUseIRFrames = false; // selected IRFrames modes int mROFBiasInBC = 0; int mROFLengthInBC = 0; int mNRUs = 0; - std::string mDetName = ""; - std::string mDetNameLC = ""; - std::string mFileName = ""; + std::string mDetName; + std::string mDetNameLC; + std::string mFileName; std::string mDigTreeName = "o2sim"; std::string mDigitBranchName = "Digit"; - std::string mDigROFBranchName = "DigitROF"; + std::string mDigitROFBranchName = "DigitROF"; std::string mCalibBranchName = "Calib"; - std::string mDigtMCTruthBranchName = "DigitMCTruth"; - std::string mDigtMC2ROFBranchName = "DigitMC2ROF"; + std::string mDigitMCTruthBranchName = "DigitMCTruth"; + std::string mDigitMC2ROFBranchName = "DigitMC2ROF"; }; -class ITSDigitReader : public DigitReader +class ITSDigitReader : public DigitReader { public: ITSDigitReader(bool useMC = true, bool useCalib = false, bool useTriggers = true) - : DigitReader(o2::detectors::DetID::ITS, useMC, useCalib, useTriggers) - { - mOrigin = o2::header::gDataOriginITS; - } + : DigitReader(useMC, useCalib, useTriggers) {} }; -class MFTDigitReader : public DigitReader +class MFTDigitReader : public DigitReader { public: MFTDigitReader(bool useMC = true, bool useCalib = false, bool useTriggers = true) - : DigitReader(o2::detectors::DetID::MFT, useMC, useCalib, useTriggers) - { - mOrigin = o2::header::gDataOriginMFT; - } + : DigitReader(useMC, useCalib, useTriggers) {} }; /// create a processor spec diff --git a/Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.cxx index 3c7a86fe173d6..ec86da4833a0d 100644 --- a/Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.cxx @@ -11,6 +11,7 @@ /// @file DigitReaderSpec.cxx +#include #include #include "TTree.h" @@ -39,25 +40,28 @@ namespace o2 namespace itsmft { -DigitReader::DigitReader(o2::detectors::DetID id, bool useMC, bool useCalib, bool triggerOut) +template +DigitReader::DigitReader(bool useMC, bool useCalib, bool triggerOut) : mUseMC(useMC), mUseCalib(useCalib), mTriggerOut(triggerOut), mDetNameLC(mDetName = ID.getName()), mDigTreeName("o2sim") { - assert(id == o2::detectors::DetID::ITS || id == o2::detectors::DetID::MFT); - mDetNameLC = mDetName = id.getName(); - mDigTreeName = "o2sim"; - mDigitBranchName = mDetName + mDigitBranchName; - mDigROFBranchName = mDetName + mDigROFBranchName; + mDigitROFBranchName = mDetName + mDigitROFBranchName; mCalibBranchName = mDetName + mCalibBranchName; - mDigtMCTruthBranchName = mDetName + mDigtMCTruthBranchName; - mDigtMC2ROFBranchName = mDetName + mDigtMC2ROFBranchName; - mTriggerOut = triggerOut; - mUseMC = useMC; - mUseCalib = useCalib; + mDigitMCTruthBranchName = mDetName + mDigitMCTruthBranchName; + mDigitMC2ROFBranchName = mDetName + mDigitMC2ROFBranchName; + std::transform(mDetNameLC.begin(), mDetNameLC.end(), mDetNameLC.begin(), ::tolower); + + for (uint32_t i = 0; i < NLayers; ++i) { + mDigits[i] = nullptr; + mDigROFRec[i] = nullptr; + mDigMC2ROFs[i] = nullptr; + mPLabels[i] = nullptr; + } } -void DigitReader::init(InitContext& ic) +template +void DigitReader::init(InitContext& ic) { mFileName = o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(ic.options().get("input-dir")), ic.options().get((mDetNameLC + "-digit-infile").c_str())); @@ -67,23 +71,23 @@ void DigitReader::init(InitContext& ic) connectTree(mFileName); } -void DigitReader::run(ProcessingContext& pc) +template +void DigitReader::run(ProcessingContext& pc) { const auto& tinfo = pc.services().get(); + const auto& alpideParam = o2::itsmft::DPLAlpideParam::Instance(); if (tinfo.globalRunNumberChanged && mUseIRFrames) { // new run is starting: 1st call // TODO: we have to find a way define CCDBInput for IRFrames mode only using DPL fetcher auto& ccdb = o2::ccdb::BasicCCDBManager::instance(); auto rlim = ccdb.getRunDuration(tinfo.runNumber); long ts = (rlim.first + rlim.second) / 2; - if (mOrigin == o2::header::gDataOriginITS) { + if constexpr (N == o2::detectors::DetID::ITS) { ccdb.getForTimeStamp>("ITS/Config/AlpideParam", ts); - const auto& alpideParam = o2::itsmft::DPLAlpideParam::Instance(); mROFBiasInBC = alpideParam.roFrameBiasInBC; mROFLengthInBC = alpideParam.roFrameLengthInBC; mNRUs = o2::itsmft::ChipMappingITS::getNRUs(); } else { ccdb.getForTimeStamp>("MFT/Config/AlpideParam", ts); - const auto& alpideParam = o2::itsmft::DPLAlpideParam::Instance(); mROFBiasInBC = alpideParam.roFrameBiasInBC; mROFLengthInBC = alpideParam.roFrameLengthInBC; mNRUs = o2::itsmft::ChipMappingMFT::getNRUs(); @@ -93,38 +97,37 @@ void DigitReader::run(ProcessingContext& pc) if (mUseIRFrames) { irFrames = pc.inputs().get>("driverInfo"); } - static o2::dataformats::IOMCTruthContainerView* plabels = nullptr; - if (mUseMC && !plabels) { - mTree->SetBranchAddress(mDigtMCTruthBranchName.c_str(), &plabels); - } - auto ent = mTree->GetReadEntry(); + auto ent = mTree->GetReadEntry(); if (!mUseIRFrames) { ent++; assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); - LOG(info) << mDetName << "DigitReader pushes " << mDigROFRec.size() << " ROFRecords, " << mDigits.size() << " digits at entry " << ent; - pc.outputs().snapshot(Output{mOrigin, "DIGITSROF", 0}, mDigROFRec); - pc.outputs().snapshot(Output{mOrigin, "DIGITS", 0}, mDigits); + for (uint32_t iLayer = 0; iLayer < RLayers; ++iLayer) { + LOG(info) << mDetName << "DigitReader:" << iLayer << " pushes " << mDigROFRec[iLayer]->size() << " ROFRecords, " << mDigits[iLayer]->size() << " digits at entry " << ent; + pc.outputs().snapshot(Output{Origin, "DIGITSROF", iLayer}, *mDigROFRec[iLayer]); + pc.outputs().snapshot(Output{Origin, "DIGITS", iLayer}, *mDigits[iLayer]); + if (mUseMC) { + auto& sharedlabels = pc.outputs().make>(Output{Origin, "DIGITSMCTR", iLayer}); + mPLabels[iLayer]->copyandflatten(sharedlabels); + delete mPLabels[iLayer]; + mPLabels[iLayer] = nullptr; + pc.outputs().snapshot(Output{Origin, "DIGITSMC2ROF", iLayer}, *mDigMC2ROFs[iLayer]); + } + } if (mUseCalib) { - pc.outputs().snapshot(Output{mOrigin, "GBTCALIB", 0}, mCalib); + pc.outputs().snapshot(Output{Origin, "GBTCALIB", 0}, mCalib); } if (mTriggerOut) { std::vector dummyTrig; - pc.outputs().snapshot(Output{mOrigin, "PHYSTRIG", 0}, dummyTrig); - } - if (mUseMC) { - auto& sharedlabels = pc.outputs().make>(Output{mOrigin, "DIGITSMCTR", 0}); - plabels->copyandflatten(sharedlabels); - delete plabels; - plabels = nullptr; - pc.outputs().snapshot(Output{mOrigin, "DIGITSMC2ROF", 0}, mDigMC2ROFs); + pc.outputs().snapshot(Output{Origin, "PHYSTRIG", 0}, dummyTrig); } if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); pc.services().get().readyToQuit(QuitRequest::Me); } } else { // need to select particulars IRs range, presumably from the same tree entry + // TODO implement for staggering std::vector digitsSel; std::vector calibSel; std::vector digROFRecSel; @@ -144,33 +147,33 @@ void DigitReader::run(ProcessingContext& pc) // do we need to read a new entry? if (ent > mTree->GetReadEntry()) { if (mUseMC) { - delete plabels; - plabels = nullptr; - mConstLabels.clear(); - mTree->SetBranchAddress(mDigtMCTruthBranchName.c_str(), &plabels); + delete mPLabels[0]; + mPLabels[0] = nullptr; + mConstLabels[0].clear(); + mTree->SetBranchAddress(mDigitMCTruthBranchName.c_str(), &mPLabels[0]); } mTree->GetEntry(ent); if (mUseMC) { - plabels->copyandflatten(mConstLabels); - delete plabels; - plabels = nullptr; + mPLabels[0]->copyandflatten(mConstLabels[0]); + delete mPLabels[0]; + mPLabels[0] = nullptr; } } std::vector rofOld2New; - rofOld2New.resize(mDigROFRec.size(), -1); + rofOld2New.resize(mDigROFRec[0]->size(), -1); - if (mDigROFRec.front().getBCData() <= irMax && (mDigROFRec.back().getBCData() + mROFLengthInBC - 1) >= irMin) { // there is an overlap - for (int irof = 0; irof < (int)mDigROFRec.size(); irof++) { - const auto& rof = mDigROFRec[irof]; + if (mDigROFRec[0]->front().getBCData() <= irMax && (mDigROFRec[0]->back().getBCData() + mROFLengthInBC - 1) >= irMin) { // there is an overlap + for (int irof = 0; irof < (int)mDigROFRec[0]->size(); irof++) { + const auto& rof = mDigROFRec[0]->at(irof); if (irfSel.check({rof.getBCData(), rof.getBCData() + mROFLengthInBC - 1}) != -1) { rofOld2New[irof] = (int)digROFRecSel.size(); LOGP(debug, "Adding selected ROF {}", rof.getBCData().asString()); digROFRecSel.push_back(rof); int offs = digitsSel.size(); digROFRecSel.back().setFirstEntry(offs); - std::copy(mDigits.begin() + rof.getFirstEntry(), mDigits.begin() + rof.getFirstEntry() + rof.getNEntries(), std::back_inserter(digitsSel)); + std::copy(mDigits[0]->begin() + rof.getFirstEntry(), mDigits[0]->begin() + rof.getFirstEntry() + rof.getNEntries(), std::back_inserter(digitsSel)); for (int id = 0; id < rof.getNEntries(); id++) { // copy MC info - digitLabelsSel.addElements(id + offs, mConstLabels.getLabels(id + rof.getFirstEntry())); + digitLabelsSel.addElements(id + offs, mConstLabels[0].getLabels(id + rof.getFirstEntry())); } if (mCalib.size() >= size_t((irof + 1) * mNRUs)) { std::copy(mCalib.begin() + irof * mNRUs, mCalib.begin() + (irof + 1) * mNRUs, std::back_inserter(calibSel)); @@ -179,7 +182,7 @@ void DigitReader::run(ProcessingContext& pc) } } if (mUseMC) { - digMC2ROFsSel = mDigMC2ROFs; + digMC2ROFsSel = *mDigMC2ROFs[0]; for (auto& mc2rof : digMC2ROFsSel) { if (mc2rof.rofRecordID < 0) { continue; // did not contribute even to the original data @@ -198,26 +201,26 @@ void DigitReader::run(ProcessingContext& pc) mc2rof.maxROF = mx; } } - if (mDigROFRec.back().getBCData() + mROFLengthInBC - 1 < irMax) { // need to check the next entry + if (mDigROFRec[0]->back().getBCData() + mROFLengthInBC - 1 < irMax) { // need to check the next entry ent++; continue; } break; // push collected data } } - pc.outputs().snapshot(Output{mOrigin, "DIGITSROF", 0}, digROFRecSel); - pc.outputs().snapshot(Output{mOrigin, "DIGITS", 0}, digitsSel); + pc.outputs().snapshot(Output{Origin, "DIGITSROF", 0}, digROFRecSel); + pc.outputs().snapshot(Output{Origin, "DIGITS", 0}, digitsSel); if (mUseCalib) { - pc.outputs().snapshot(Output{mOrigin, "GBTCALIB", 0}, calibSel); + pc.outputs().snapshot(Output{Origin, "GBTCALIB", 0}, calibSel); } if (mTriggerOut) { std::vector dummyTrig; - pc.outputs().snapshot(Output{mOrigin, "PHYSTRIG", 0}, dummyTrig); + pc.outputs().snapshot(Output{Origin, "PHYSTRIG", 0}, dummyTrig); } if (mUseMC) { - auto& sharedlabels = pc.outputs().make>(Output{mOrigin, "DIGITSMCTR", 0}); + auto& sharedlabels = pc.outputs().make>(Output{Origin, "DIGITSMCTR", 0}); digitLabelsSel.flatten_to(sharedlabels); - pc.outputs().snapshot(Output{mOrigin, "DIGITSMC2ROF", 0}, digMC2ROFsSel); + pc.outputs().snapshot(Output{Origin, "DIGITSMC2ROF", 0}, digMC2ROFsSel); } if (!irFrames.size() || irFrames.back().isLast()) { @@ -227,77 +230,99 @@ void DigitReader::run(ProcessingContext& pc) } } -void DigitReader::connectTree(const std::string& filename) +template +void DigitReader::connectTree(const std::string& filename) { mTree.reset(nullptr); // in case it was already loaded mFile.reset(TFile::Open(filename.c_str())); assert(mFile && !mFile->IsZombie()); mTree.reset((TTree*)mFile->Get(mDigTreeName.c_str())); assert(mTree); - - mTree->SetBranchAddress(mDigROFBranchName.c_str(), &mDigROFRecPtr); - mTree->SetBranchAddress(mDigitBranchName.c_str(), &mDigitsPtr); + for (uint32_t iLayer = 0; iLayer < RLayers; ++iLayer) { + setBranchAddress(mDigitROFBranchName, mDigROFRec[iLayer], iLayer); + setBranchAddress(mDigitBranchName, mDigits[iLayer], iLayer); + if (mUseMC) { + if (!mTree->GetBranch(getBranchName(mDigitMC2ROFBranchName, iLayer).c_str()) || !mTree->GetBranch(getBranchName(mDigitMCTruthBranchName, iLayer).c_str())) { + throw std::runtime_error("MC data requested but not found in the tree"); + } + setBranchAddress(mDigitMC2ROFBranchName, mDigMC2ROFs[iLayer], iLayer); + if (!mPLabels[iLayer]) { + setBranchAddress(mDigitMCTruthBranchName, mPLabels[iLayer], iLayer); + } + } + } if (mUseCalib) { if (!mTree->GetBranch(mCalibBranchName.c_str())) { throw std::runtime_error("GBT calibration data requested but not found in the tree"); } - mTree->SetBranchAddress(mCalibBranchName.c_str(), &mCalibPtr); - } - if (mUseMC) { - if (!mTree->GetBranch(mDigtMC2ROFBranchName.c_str()) || !mTree->GetBranch(mDigtMCTruthBranchName.c_str())) { - throw std::runtime_error("MC data requested but not found in the tree"); - } - mTree->SetBranchAddress(mDigtMC2ROFBranchName.c_str(), &mDigMC2ROFsPtr); + setBranchAddress(mCalibBranchName, mCalibPtr); } LOG(info) << "Loaded tree from " << filename << " with " << mTree->GetEntries() << " entries"; } -DataProcessorSpec getITSDigitReaderSpec(bool useMC, bool useCalib, bool useTriggers, std::string defname) +template +std::string DigitReader::getBranchName(const std::string& base, int index) { - std::vector outputSpec; - outputSpec.emplace_back("ITS", "DIGITS", 0, Lifetime::Timeframe); - outputSpec.emplace_back("ITS", "DIGITSROF", 0, Lifetime::Timeframe); - if (useCalib) { - outputSpec.emplace_back("ITS", "GBTCALIB", 0, Lifetime::Timeframe); + if constexpr (!o2::itsmft::DPLAlpideParam::supportsStaggering()) { + return base; } - if (useMC) { - outputSpec.emplace_back("ITS", "DIGITSMCTR", 0, Lifetime::Timeframe); - outputSpec.emplace_back("ITS", "DIGITSMC2ROF", 0, Lifetime::Timeframe); + return base + "_" + std::to_string(index); +} + +template +template +void DigitReader::setBranchAddress(const std::string& base, Ptr& addr, int layer) +{ + const auto name = getBranchName(base, layer); + if (Int_t ret = mTree->SetBranchAddress(name.c_str(), &addr); ret != 0) { + LOGP(fatal, "failed to set branch address for {} ret={}", name, ret); } - if (useTriggers) { - outputSpec.emplace_back("ITS", "PHYSTRIG", 0, Lifetime::Timeframe); +} + +namespace +{ +template +std::vector makeOutChannels(bool mctruth, bool useCalib) +{ + constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + std::vector outputs; + static constexpr int RLayers = o2::itsmft::DPLAlpideParam::supportsStaggering() ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + for (int iLayer = 0; iLayer < RLayers; ++iLayer) { + outputs.emplace_back(Origin, "DIGITS", iLayer, Lifetime::Timeframe); + outputs.emplace_back(Origin, "DIGITSROF", iLayer, Lifetime::Timeframe); + if (mctruth) { + outputs.emplace_back(Origin, "DIGITSMC2ROF", iLayer, Lifetime::Timeframe); + outputs.emplace_back(Origin, "DIGITSMCTR", iLayer, Lifetime::Timeframe); + } } + if (useCalib) { + outputs.emplace_back(Origin, "GBTCALIB", 0, Lifetime::Timeframe); + } + outputs.emplace_back(Origin, "PHYSTRIG", 0, Lifetime::Timeframe); + return outputs; +} +} // namespace + +DataProcessorSpec getITSDigitReaderSpec(bool useMC, bool useCalib, bool useTriggers, std::string defname) +{ return DataProcessorSpec{ - "its-digit-reader", - Inputs{}, - outputSpec, - AlgorithmSpec{adaptFromTask(useMC, useCalib)}, - Options{ + .name = "its-digit-reader", + .inputs = Inputs{}, + .outputs = makeOutChannels(useMC, useCalib), + .algorithm = AlgorithmSpec{adaptFromTask(useMC, useCalib)}, + .options = Options{ {"its-digit-infile", VariantType::String, defname, {"Name of the input digit file"}}, {"input-dir", VariantType::String, "none", {"Input directory"}}}}; } DataProcessorSpec getMFTDigitReaderSpec(bool useMC, bool useCalib, bool useTriggers, std::string defname) { - std::vector outputSpec; - outputSpec.emplace_back("MFT", "DIGITS", 0, Lifetime::Timeframe); - outputSpec.emplace_back("MFT", "DIGITSROF", 0, Lifetime::Timeframe); - if (useCalib) { - outputSpec.emplace_back("MFT", "GBTCALIB", 0, Lifetime::Timeframe); - } - if (useMC) { - outputSpec.emplace_back("MFT", "DIGITSMCTR", 0, Lifetime::Timeframe); - outputSpec.emplace_back("MFT", "DIGITSMC2ROF", 0, Lifetime::Timeframe); - } - if (useTriggers) { - outputSpec.emplace_back("MFT", "PHYSTRIG", 0, Lifetime::Timeframe); - } return DataProcessorSpec{ - "mft-digit-reader", - Inputs{}, - outputSpec, - AlgorithmSpec{adaptFromTask(useMC, useCalib)}, - Options{ + .name = "mft-digit-reader", + .inputs = Inputs{}, + .outputs = makeOutChannels(useMC, useCalib), + .algorithm = AlgorithmSpec{adaptFromTask(useMC, useCalib)}, + .options = Options{ {"mft-digit-infile", VariantType::String, defname, {"Name of the input digit file"}}, {"input-dir", VariantType::String, "none", {"Input directory"}}}}; } diff --git a/Detectors/ITSMFT/common/workflow/src/DigitWriterSpec.cxx b/Detectors/ITSMFT/common/workflow/src/DigitWriterSpec.cxx index 3a06d106ceb1f..c4f1e336180c7 100644 --- a/Detectors/ITSMFT/common/workflow/src/DigitWriterSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/DigitWriterSpec.cxx @@ -12,6 +12,9 @@ /// @brief Processor spec for a ROOT file writer for ITSMFT digits #include "ITSMFTWorkflow/DigitWriterSpec.h" +#include "Framework/ConcreteDataMatcher.h" +#include "Framework/DataRef.h" +#include "ITSMFTBase/DPLAlpideParam.h" #include "DPLUtils/MakeRootTreeWriterSpec.h" #include "DataFormatsITSMFT/Digit.h" #include "DataFormatsITSMFT/GBTCalibData.h" @@ -39,14 +42,24 @@ using MCCont = o2::dataformats::ConstMCTruthContainer; /// create the processor spec /// describing a processor receiving digits for ITS/MFT and writing them to file -DataProcessorSpec getDigitWriterSpec(bool mctruth, bool dec, bool calib, o2::header::DataOrigin detOrig, o2::detectors::DetID detId) +template +DataProcessorSpec getDigitWriterSpec(bool mctruth, bool dec, bool calib) { - std::string detStr = o2::detectors::DetID::getName(detId); + static constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + constexpr int NLayers = o2::itsmft::DPLAlpideParam::supportsStaggering() ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + std::string detStr = o2::detectors::DetID::getName(N); std::string detStrL = dec ? "o2_" : ""; // for decoded digits prepend by o2 detStrL += detStr; std::transform(detStrL.begin(), detStrL.end(), detStrL.begin(), ::tolower); - auto logger = [](std::vector const& inDigits) { - LOG(info) << "RECEIVED DIGITS SIZE " << inDigits.size(); + auto digitSizes = std::make_shared>(); + auto digitSizeGetter = [digitSizes](std::vector const& inDigits, DataRef const& ref) { + auto const* dh = DataRefUtils::getHeader(ref); + (*digitSizes)[dh->subSpecification] = inDigits.size(); + }; + auto rofSizes = std::make_shared>(); + auto rofSizeGetter = [rofSizes](std::vector const& inROFs, DataRef const& ref) { + auto const* dh = DataRefUtils::getHeader(ref); + (*rofSizes)[dh->subSpecification] = inROFs.size(); }; // the callback to be set as hook for custom action when the writer is closed @@ -71,9 +84,11 @@ DataProcessorSpec getDigitWriterSpec(bool mctruth, bool dec, bool calib, o2::hea // handler for labels // This is necessary since we can't store the original label buffer in a ROOT entry -- as is -- if it exceeds a certain size. // We therefore convert it to a special split class. - auto fillLabels = [](TBranch& branch, std::vector const& labelbuffer, DataRef const& /*ref*/) { + auto fillLabels = [digitSizes, rofSizes](TBranch& branch, std::vector const& labelbuffer, DataRef const& ref) { o2::dataformats::ConstMCTruthContainerView labels(labelbuffer); - LOG(info) << "WRITING " << labels.getNElements() << " LABELS "; + auto const* dh = DataRefUtils::getHeader(ref); + auto layer = static_cast(dh->subSpecification); + LOG(info) << "WRITING " << labels.getNElements() << " LABELS FOR " << layer << " WITH " << (*digitSizes)[layer] << " DIGITS IN " << (*rofSizes)[layer] << " ROFS"; o2::dataformats::IOMCTruthContainerView outputcontainer; auto ptr = &outputcontainer; @@ -83,35 +98,56 @@ DataProcessorSpec getDigitWriterSpec(bool mctruth, bool dec, bool calib, o2::hea br->ResetAddress(); }; + auto getIndex = [](DataRef const& ref) -> size_t { + auto const* dh = DataRefUtils::getHeader(ref); + return static_cast(dh->subSpecification); + }; + auto getName = [](std::string base, size_t index) -> std::string { + if constexpr (o2::itsmft::DPLAlpideParam::supportsStaggering()) { + return base += "_" + std::to_string(index); + } + return base; + }; return MakeRootTreeWriterSpec((detStr + "DigitWriter" + (dec ? "_dec" : "")).c_str(), (detStrL + "digits.root").c_str(), - MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Digits tree"}, + MakeRootTreeWriterSpec::TreeAttributes{.name = "o2sim", .title = detStr + " Digits tree"}, MakeRootTreeWriterSpec::CustomClose(finishWriting), - // in case of labels we first read them as std::vector and process them correctly in the fillLabels hook - BranchDefinition>{InputSpec{(detStr + "_digitsMCTR").c_str(), detOrig, "DIGITSMCTR", 0}, - (detStr + "DigitMCTruth").c_str(), - (mctruth ? 1 : 0), fillLabels}, - BranchDefinition>{InputSpec{(detStr + "_digitsMC2ROF").c_str(), detOrig, "DIGITSMC2ROF", 0}, - (detStr + "DigitMC2ROF").c_str(), - (mctruth ? 1 : 0)}, - BranchDefinition>{InputSpec{(detStr + "digits").c_str(), detOrig, "DIGITS", 0}, - (detStr + "Digit").c_str(), - logger}, - BranchDefinition>{InputSpec{(detStr + "calib").c_str(), detOrig, "GBTCALIB", 0}, - (detStr + "Calib").c_str(), - (calib ? 1 : 0)}, - BranchDefinition>{InputSpec{(detStr + "digitsROF").c_str(), detOrig, "DIGITSROF", 0}, - (detStr + "DigitROF").c_str()})(); + BranchDefinition>{InputSpec{detStr + "digits", ConcreteDataTypeMatcher{Origin, "DIGITS"}}, + detStr + "Digit", "digit-branch", + NLayers, + digitSizeGetter, + getIndex, + getName}, + BranchDefinition>{InputSpec{detStr + "digitsROF", ConcreteDataTypeMatcher{Origin, "DIGITSROF"}}, + detStr + "DigitROF", "digit-rof-branch", + NLayers, + rofSizeGetter, + getIndex, + getName}, + BranchDefinition>{InputSpec{detStr + "_digitsMCTR", ConcreteDataTypeMatcher{Origin, "DIGITSMCTR"}}, + detStr + "DigitMCTruth", "digit-mctruth-branch", + (mctruth ? NLayers : 0), + fillLabels, + getIndex, + getName}, + BranchDefinition>{InputSpec{detStr + "_digitsMC2ROF", ConcreteDataTypeMatcher{Origin, "DIGITSMC2ROF"}}, + detStr + "DigitMC2ROF", "digit-mc2rof-branch", + (mctruth ? NLayers : 0), + getIndex, + getName}, + BranchDefinition>{InputSpec{detStr + "calib", ConcreteDataTypeMatcher{Origin, "GBTCALIB"}}, + detStr + "Calib", "digit-calib-branch", + (calib ? 1 : 0)})(); } DataProcessorSpec getITSDigitWriterSpec(bool mctruth, bool dec, bool calib) { - return getDigitWriterSpec(mctruth, dec, calib, o2::header::gDataOriginITS, o2::detectors::DetID::ITS); + return getDigitWriterSpec(mctruth, dec, calib); } DataProcessorSpec getMFTDigitWriterSpec(bool mctruth, bool dec, bool calib) { - return getDigitWriterSpec(mctruth, dec, calib, o2::header::gDataOriginMFT, o2::detectors::DetID::MFT); + return getDigitWriterSpec(mctruth, dec, calib); } } // end namespace itsmft diff --git a/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx index b40e377d58ca2..6809c8dee3f19 100644 --- a/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx @@ -17,12 +17,13 @@ #include "Framework/Lifetime.h" #include "Framework/Task.h" #include "Framework/CCDBParamSpec.h" -#include "Steer/HitProcessingManager.h" // for DigitizationContext +#include "SimulationDataFormat/DigitizationContext.h" #include "DataFormatsITSMFT/Digit.h" #include "DataFormatsITSMFT/NoiseMap.h" #include "DataFormatsITSMFT/TimeDeadMap.h" #include "SimulationDataFormat/ConstMCTruthContainer.h" #include "DetectorsBase/BaseDPLDigitizer.h" +#include "DetectorsRaw/HBFUtils.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsCommonDataFormats/SimTraits.h" #include "DetectorsCommonDataFormats/DetectorNameConf.h" @@ -36,20 +37,25 @@ #include #include #include +#include using namespace o2::framework; using SubSpecificationType = o2::framework::DataAllocator::SubSpecificationType; -namespace o2 -{ -namespace itsmft +namespace o2::itsmft { using namespace o2::base; +template class ITSMFTDPLDigitizerTask : BaseDPLDigitizer { public: + static constexpr o2::detectors::DetID ID{N == o2::detectors::DetID::ITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT}; + static constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + static constexpr int NLayers{o2::itsmft::DPLAlpideParam::getNLayers()}; + using BaseDPLDigitizer::init; + void initDigitizerTask(framework::InitContext& ic) override { mDisableQED = ic.options().get("disable-qed"); @@ -60,121 +66,174 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer if (mFinished) { return; } + mFirstOrbitTF = pc.services().get().firstTForbit; - mID == o2::detectors::DetID::ITS ? updateTimeDependentParams(pc) : updateTimeDependentParams(pc); - std::string detStr = mID.getName(); + const o2::InteractionRecord firstIR(0, mFirstOrbitTF); + updateTimeDependentParams(pc); + + TStopwatch timer; + timer.Start(); + LOG(info) << " CALLING ITS DIGITIZATION "; + // read collision context from input auto context = pc.inputs().get("collisioncontext"); - context->initSimChains(mID, mSimChains); + context->initSimChains(ID, mSimChains); const bool withQED = context->isQEDProvided() && !mDisableQED; auto& timesview = context->getEventRecords(withQED); LOG(info) << "GOT " << timesview.size() << " COLLISSION TIMES"; - LOG(info) << "SIMCHAINS " << mSimChains.size(); + LOG(info) << "SIMCHAINS: " << mSimChains.size(); // if there is nothing to do ... return if (timesview.size() == 0) { return; } - TStopwatch timer; - timer.Start(); - LOG(info) << " CALLING ITS DIGITIZATION "; - mDigitizer.setDigits(&mDigits); - mDigitizer.setROFRecords(&mROFRecords); - mDigitizer.setMCLabels(&mLabels); + uint64_t nDigits{0}; + constexpr uint32_t nLayers = (DPLAlpideParam::supportsStaggering()) ? NLayers : 1; + for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer) { + const int layer = (DPLAlpideParam::supportsStaggering()) ? iLayer : -1; + mDigitizer.setDigits(&mDigits[iLayer]); + mDigitizer.setROFRecords(&mROFRecords[iLayer]); + mDigitizer.setMCLabels(&mLabels[iLayer]); + mDigitizer.resetROFrameBounds(); + + // digits are directly put into DPL owned resource + auto& digitsAccum = pc.outputs().make>(Output{Origin, "DIGITS", iLayer}); + + // rofs are accumulated first and the copied + const int nROFsPerOrbit = o2::constants::lhc::LHCMaxBunches / DPLAlpideParam::Instance().getROFLengthInBC(iLayer); + const int nROFsTF = nROFsPerOrbit * raw::HBFUtils::Instance().getNOrbitsPerTF(); + mROFRecordsAccum[iLayer].reserve(nROFsTF); + + auto accumulate = [this, &digitsAccum, &iLayer]() { + // accumulate result of single event processing on a specific layer, called after processing every event supplied + // AND after the final flushing via digitizer::fillOutputContainer + if (!mDigits[iLayer].size()) { + return; // no digits were flushed, nothing to accumulate + } + auto ndigAcc = digitsAccum.size(); + std::copy(mDigits[iLayer].begin(), mDigits[iLayer].end(), std::back_inserter(digitsAccum)); + + // fix ROFrecords references on ROF entries + auto nROFRecsOld = mROFRecordsAccum[iLayer].size(); + + for (int i = 0; i < mROFRecords[iLayer].size(); i++) { + auto& rof = mROFRecords[iLayer][i]; + rof.setFirstEntry(ndigAcc + rof.getFirstEntry()); + rof.print(); + + if (mFixMC2ROF[iLayer] < mMC2ROFRecordsAccum[iLayer].size()) { // fix ROFRecord entry in MC2ROF records + for (int m2rid = mFixMC2ROF[iLayer]; m2rid < mMC2ROFRecordsAccum[iLayer].size(); m2rid++) { + // need to register the ROFRecors entry for MC event starting from this entry + auto& mc2rof = mMC2ROFRecordsAccum[iLayer][m2rid]; + if (rof.getROFrame() == mc2rof.minROF) { + mFixMC2ROF[iLayer]++; + mc2rof.rofRecordID = nROFRecsOld + i; + mc2rof.print(); + } + } + } + } + + std::copy(mROFRecords[iLayer].begin(), mROFRecords[iLayer].end(), std::back_inserter(mROFRecordsAccum[iLayer])); + if (mWithMCTruth) { + mLabelsAccum[iLayer].mergeAtBack(mLabels[iLayer]); + } + LOG(info) << "Added " << mDigits[iLayer].size() << " digits:" << iLayer; + // clean containers from already accumulated stuff + mLabels[iLayer].clear(); + mDigits[iLayer].clear(); + mROFRecords[iLayer].clear(); + }; // and accumulate lambda + + const auto& eventParts = context->getEventParts(withQED); + const int64_t bcShift = mDigitizer.getParams().getROFrameBiasInBC(layer); // this accounts the misalignment and the opt. imposed rof delay + // loop over all composite collisions given from context (aka loop over all the interaction records) + for (int collID = 0; collID < timesview.size(); ++collID) { + auto irt = timesview[collID]; + if (irt.toLong() < bcShift) { // due to the ROF misalignment (+opt. delay) the collision would go to negative ROF ID, discard + continue; + } + irt -= bcShift; // account for the ROF start shift + + mDigitizer.setEventTime(irt, layer); + mDigitizer.resetEventROFrames(); // to estimate min/max ROF for this collID + // for each collision, loop over the constituents event and source IDs + // (background signal merging is basically taking place here) + for (const auto& part : eventParts[collID]) { - // digits are directly put into DPL owned resource - auto& digitsAccum = pc.outputs().make>(Output{mOrigin, "DIGITS", 0}); + // get the hits for this event and this source + mHits.clear(); + context->retrieveHits(mSimChains, o2::detectors::SimTraits::DETECTORBRANCHNAMES[ID][0].c_str(), part.sourceID, part.entryID, &mHits); - auto accumulate = [this, &digitsAccum]() { - // accumulate result of single event processing, called after processing every event supplied - // AND after the final flushing via digitizer::fillOutputContainer - if (!mDigits.size()) { - return; // no digits were flushed, nothing to accumulate + if (mHits.size() > 0) { + LOG(debug) << "For collision " << collID << " eventID " << part.entryID << " found " << mHits.size() << " hits "; + mDigitizer.process(&mHits, part.entryID, part.sourceID, layer); // call actual digitization procedure + } + } + mMC2ROFRecordsAccum[iLayer].emplace_back(collID, -1, mDigitizer.getEventROFrameMin(), mDigitizer.getEventROFrameMax()); + accumulate(); } - auto ndigAcc = digitsAccum.size(); - std::copy(mDigits.begin(), mDigits.end(), std::back_inserter(digitsAccum)); - - // fix ROFrecords references on ROF entries - auto nROFRecsOld = mROFRecordsAccum.size(); - - for (int i = 0; i < mROFRecords.size(); i++) { - auto& rof = mROFRecords[i]; - rof.setFirstEntry(ndigAcc + rof.getFirstEntry()); - rof.print(); - - if (mFixMC2ROF < mMC2ROFRecordsAccum.size()) { // fix ROFRecord entry in MC2ROF records - for (int m2rid = mFixMC2ROF; m2rid < mMC2ROFRecordsAccum.size(); m2rid++) { - // need to register the ROFRecors entry for MC event starting from this entry - auto& mc2rof = mMC2ROFRecordsAccum[m2rid]; - if (rof.getROFrame() == mc2rof.minROF) { - mFixMC2ROF++; - mc2rof.rofRecordID = nROFRecsOld + i; - mc2rof.print(); - } + mDigitizer.fillOutputContainer(0xffffffff, layer); + accumulate(); + nDigits += digitsAccum.size(); + + // here we have all digits and labels and we can send them to consumer (aka snapshot it onto output) + // ensure that the rof output is continuous + if (nROFsTF != mROFRecordsAccum[iLayer].size()) { + // it can happen that in the digitization rofs without contributing hits are skipped + // however downstream consumers of the clusters cannot know apriori the time structure + // the cluster rofs do not account for the bias so it will start always at BC=0 + std::vector expDigitRofVec(nROFsTF); + for (int iROF{0}; iROF < nROFsTF; ++iROF) { + auto& rof = expDigitRofVec[iROF]; + int orb = iROF * DPLAlpideParam::Instance().getROFLengthInBC(iLayer) / o2::constants::lhc::LHCMaxBunches + mFirstOrbitTF; + int bc = iROF * DPLAlpideParam::Instance().getROFLengthInBC(iLayer) % o2::constants::lhc::LHCMaxBunches; + o2::InteractionRecord ir(bc, orb); + rof.setBCData(ir); + rof.setROFrame(iROF); + rof.setNEntries(0); + rof.setFirstEntry(-1); + } + uint32_t prevEntry{0}; + for (const auto& rof : mROFRecordsAccum[iLayer]) { + const auto& ir = rof.getBCData(); + const auto irToFirst = ir - firstIR; + const int irROF = irToFirst.toLong() / DPLAlpideParam::Instance().getROFLengthInBC(iLayer); + auto& expROF = expDigitRofVec[irROF]; + expROF.setFirstEntry(rof.getFirstEntry()); + expROF.setNEntries(rof.getNEntries()); + if (expROF.getBCData() != rof.getBCData()) { + LOGP(fatal, "detected mismatch between expected ROF:{} and received ROF:{}", expROF.asString(), rof.asString()); + } + } + int prevFirst{0}; + for (auto& rof : expDigitRofVec) { + if (rof.getFirstEntry() < 0) { + rof.setFirstEntry(prevFirst); } + prevFirst = rof.getFirstEntry(); } + pc.outputs().snapshot(Output{Origin, "DIGITSROF", iLayer}, expDigitRofVec); + } else { + pc.outputs().snapshot(Output{Origin, "DIGITSROF", iLayer}, mROFRecordsAccum[iLayer]); } - - std::copy(mROFRecords.begin(), mROFRecords.end(), std::back_inserter(mROFRecordsAccum)); if (mWithMCTruth) { - mLabelsAccum.mergeAtBack(mLabels); - } - LOG(info) << "Added " << mDigits.size() << " digits "; - // clean containers from already accumulated stuff - mLabels.clear(); - mDigits.clear(); - mROFRecords.clear(); - }; // and accumulate lambda - - auto& eventParts = context->getEventParts(withQED); - int bcShift = mDigitizer.getParams().getROFrameBiasInBC(); - // loop over all composite collisions given from context (aka loop over all the interaction records) - for (int collID = 0; collID < timesview.size(); ++collID) { - auto irt = timesview[collID]; - if (irt.toLong() < bcShift) { // due to the ROF misalignment the collision would go to negative ROF ID, discard - continue; + pc.outputs().snapshot(Output{Origin, "DIGITSMC2ROF", iLayer}, mMC2ROFRecordsAccum[iLayer]); + auto& sharedlabels = pc.outputs().make>(Output{Origin, "DIGITSMCTR", iLayer}); + mLabelsAccum[iLayer].flatten_to(sharedlabels); + // free space of existing label containers + mLabels[iLayer].clear_andfreememory(); + mLabelsAccum[iLayer].clear_andfreememory(); } - irt -= bcShift; // account for the ROF start shift - - mDigitizer.setEventTime(irt); - mDigitizer.resetEventROFrames(); // to estimate min/max ROF for this collID - // for each collision, loop over the constituents event and source IDs - // (background signal merging is basically taking place here) - for (auto& part : eventParts[collID]) { - - // get the hits for this event and this source - mHits.clear(); - context->retrieveHits(mSimChains, o2::detectors::SimTraits::DETECTORBRANCHNAMES[mID][0].c_str(), part.sourceID, part.entryID, &mHits); - - if (mHits.size() > 0) { - LOG(debug) << "For collision " << collID << " eventID " << part.entryID - << " found " << mHits.size() << " hits "; - mDigitizer.process(&mHits, part.entryID, part.sourceID); // call actual digitization procedure - } - } - mMC2ROFRecordsAccum.emplace_back(collID, -1, mDigitizer.getEventROFrameMin(), mDigitizer.getEventROFrameMax()); - accumulate(); } - mDigitizer.fillOutputContainer(); - accumulate(); - - // here we have all digits and labels and we can send them to consumer (aka snapshot it onto output) - - pc.outputs().snapshot(Output{mOrigin, "DIGITSROF", 0}, mROFRecordsAccum); - if (mWithMCTruth) { - pc.outputs().snapshot(Output{mOrigin, "DIGITSMC2ROF", 0}, mMC2ROFRecordsAccum); - auto& sharedlabels = pc.outputs().make>(Output{mOrigin, "DIGITSMCTR", 0}); - mLabelsAccum.flatten_to(sharedlabels); - // free space of existing label containers - mLabels.clear_andfreememory(); - mLabelsAccum.clear_andfreememory(); - } - LOG(info) << mID.getName() << ": Sending ROMode= " << mROMode << " to GRPUpdater"; - pc.outputs().snapshot(Output{mOrigin, "ROMode", 0}, mROMode); + + LOG(info) << ID.getName() << ": Sending ROMode= " << mROMode << " to GRPUpdater"; + pc.outputs().snapshot(Output{Origin, "ROMode", 0}, mROMode); timer.Stop(); LOG(info) << "Digitization took " << timer.CpuTime() << "s"; + LOG(info) << "Produced " << nDigits << " digits"; // we should be only called once; tell DPL that this process is ready to exit pc.services().get().readyToQuit(QuitRequest::Me); @@ -184,18 +243,18 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) { - if (matcher == ConcreteDataMatcher(mOrigin, "NOISEMAP", 0)) { - LOG(info) << mID.getName() << " noise map updated"; + if (matcher == ConcreteDataMatcher(Origin, "NOISEMAP", 0)) { + LOG(info) << ID.getName() << " noise map updated"; mDigitizer.setNoiseMap((const o2::itsmft::NoiseMap*)obj); return; } - if (matcher == ConcreteDataMatcher(mOrigin, "DEADMAP", 0)) { - LOG(info) << mID.getName() << " static dead map updated"; + if (matcher == ConcreteDataMatcher(Origin, "DEADMAP", 0)) { + LOG(info) << ID.getName() << " static dead map updated"; mDeadMap = (o2::itsmft::NoiseMap*)obj; mDigitizer.setDeadChannelsMap(mDeadMap); return; } - if (matcher == ConcreteDataMatcher(mOrigin, "TimeDeadMap", 0)) { + if (matcher == ConcreteDataMatcher(Origin, "TimeDeadMap", 0)) { o2::itsmft::TimeDeadMap* timedeadmap = (o2::itsmft::TimeDeadMap*)obj; if (!timedeadmap->isDefault()) { timedeadmap->decodeMap(mFirstOrbitTF, *mDeadMap, true); @@ -204,30 +263,25 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer } mTimeDeadMapUpdated = true; mDigitizer.setDeadChannelsMap(mDeadMap); - LOG(info) << mID.getName() << " time-dependent dead map updated"; + LOG(info) << ID.getName() << " time-dependent dead map updated"; } else { - LOG(info) << mID.getName() << " time-dependent dead map is default/empty"; + LOG(info) << ID.getName() << " time-dependent dead map is default/empty"; } return; } - if (matcher == ConcreteDataMatcher(mOrigin, "ALPIDEPARAM", 0)) { - LOG(info) << mID.getName() << " Alpide param updated"; - if (mID == o2::detectors::DetID::ITS) { - const auto& par = o2::itsmft::DPLAlpideParam::Instance(); - par.printKeyValues(); - } else { - const auto& par = o2::itsmft::DPLAlpideParam::Instance(); - par.printKeyValues(); - } + if (matcher == ConcreteDataMatcher(Origin, "ALPIDEPARAM", 0)) { + LOG(info) << ID.getName() << " Alpide param updated"; + const auto& par = o2::itsmft::DPLAlpideParam::Instance(); + par.printKeyValues(); return; } - if (matcher == ConcreteDataMatcher(mOrigin, "ALPIDERESPVbb0", 0)) { - LOG(info) << mID.getName() << " loaded AlpideResponseData for Vbb=0V"; + if (matcher == ConcreteDataMatcher(Origin, "ALPIDERESPVbb0", 0)) { + LOG(info) << ID.getName() << " loaded AlpideResponseData for Vbb=0V"; mDigitizer.setAlpideResponse((o2::itsmft::AlpideSimResponse*)obj, 0); } - if (matcher == ConcreteDataMatcher(mOrigin, "ALPIDERESPVbbM3", 0)) { - LOG(info) << mID.getName() << " loaded AlpideResponseData for Vbb=-3V"; + if (matcher == ConcreteDataMatcher(Origin, "ALPIDERESPVbbM3", 0)) { + LOG(info) << ID.getName() << " loaded AlpideResponseData for Vbb=-3V"; mDigitizer.setAlpideResponse((o2::itsmft::AlpideSimResponse*)obj, 1); } } @@ -235,20 +289,19 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer protected: ITSMFTDPLDigitizerTask(bool mctruth = true) : BaseDPLDigitizer(InitServices::FIELD | InitServices::GEOM), mWithMCTruth(mctruth) {} - template void updateTimeDependentParams(ProcessingContext& pc) { - std::string detstr(o2::detectors::DetID::getName(DETID)); + std::string detstr(o2::detectors::DetID::getName(ID)); pc.inputs().get(detstr + "_noise"); pc.inputs().get(detstr + "_dead"); // TODO: the code should run even if this object does not exist. Or: create default object pc.inputs().get(detstr + "_time_dead"); - pc.inputs().get*>(detstr + "_alppar"); + pc.inputs().get*>(detstr + "_alppar"); pc.inputs().get(detstr + "_alpiderespvbb0"); pc.inputs().get(detstr + "_alpiderespvbbm3"); - auto& dopt = o2::itsmft::DPLDigitizerParam::Instance(); - auto& aopt = o2::itsmft::DPLAlpideParam::Instance(); + auto& dopt = o2::itsmft::DPLDigitizerParam::Instance(); + auto& aopt = o2::itsmft::DPLAlpideParam::Instance(); auto& digipar = mDigitizer.getParams(); digipar.setContinuous(dopt.continuous); digipar.setROFrameBiasInBC(aopt.roFrameBiasInBC); @@ -272,15 +325,29 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer digipar.setIBVbb(dopt.IBVbb); digipar.setOBVbb(dopt.OBVbb); digipar.setVbb(dopt.Vbb); + // staggering parameters + if constexpr (o2::itsmft::DPLAlpideParam::supportsStaggering()) { + const bool withStag = aopt.withStaggering(); + for (int iLayer{0}; iLayer < o2::itsmft::DPLAlpideParam::getNLayers(); ++iLayer) { + const int nLayer = (withStag) ? iLayer : -1; + auto frameNS = aopt.getROFLengthInBC(nLayer) * o2::constants::lhc::LHCBunchSpacingNS; + digipar.addROFrameLayerLengthInBC(aopt.getROFLengthInBC(nLayer)); + // NOTE: the rof delay looks from the digitizer like an additional bias + digipar.addROFrameLayerBiasInBC(aopt.getROFBiasInBC(nLayer) + aopt.getROFDelayInBC(nLayer)); + digipar.addStrobeDelay(aopt.strobeDelay); + digipar.addStrobeLength(aopt.strobeLengthCont > 0 ? aopt.strobeLengthCont : frameNS - aopt.strobeDelay); + digipar.setROFrameLength(aopt.getROFLengthInBC(nLayer) * o2::constants::lhc::LHCBunchSpacingNS, iLayer); + } + } mROMode = digipar.isContinuous() ? o2::parameters::GRPObject::CONTINUOUS : o2::parameters::GRPObject::PRESENT; - LOG(info) << mID.getName() << " simulated in " + LOG(info) << detstr << " simulated in " << ((mROMode == o2::parameters::GRPObject::CONTINUOUS) ? "CONTINUOUS" : "TRIGGERED") << " RO mode"; // configure digitizer o2::itsmft::GeometryTGeo* geom = nullptr; - if (mID == o2::detectors::DetID::ITS) { + if constexpr (N == o2::detectors::DetID::ITS) { geom = o2::its::GeometryTGeo::Instance(); } else { geom = o2::mft::GeometryTGeo::Instance(); @@ -294,81 +361,61 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer bool mFinished = false; bool mDisableQED = false; unsigned long mFirstOrbitTF = 0x0; - o2::detectors::DetID mID; - o2::header::DataOrigin mOrigin = o2::header::gDataOriginInvalid; o2::itsmft::Digitizer mDigitizer; - std::vector mDigits; - std::vector mROFRecords; - std::vector mROFRecordsAccum; + std::array, NLayers> mDigits; + std::array, NLayers> mROFRecords; + std::array, NLayers> mROFRecordsAccum; std::vector mHits; std::vector* mHitsP = &mHits; - o2::dataformats::MCTruthContainer mLabels; - o2::dataformats::MCTruthContainer mLabelsAccum; - std::vector mMC2ROFRecordsAccum; + std::array, NLayers> mLabels; + std::array, NLayers> mLabelsAccum; + std::array, NLayers> mMC2ROFRecordsAccum; std::vector mSimChains; o2::itsmft::NoiseMap* mDeadMap = nullptr; - int mFixMC2ROF = 0; // 1st entry in mc2rofRecordsAccum to be fixed for ROFRecordID + std::array mFixMC2ROF{}; // 1st entry in mc2rofRecordsAccum to be fixed for ROFRecordID bool mTimeDeadMapUpdated = false; o2::parameters::GRPObject::ROMode mROMode = o2::parameters::GRPObject::PRESENT; // readout mode }; //_______________________________________________ -class ITSDPLDigitizerTask : public ITSMFTDPLDigitizerTask +class ITSDPLDigitizerTask : public ITSMFTDPLDigitizerTask { public: - // FIXME: origin should be extractable from the DetID, the problem is 3d party header dependencies - static constexpr o2::detectors::DetID::ID DETID = o2::detectors::DetID::ITS; - static constexpr o2::header::DataOrigin DETOR = o2::header::gDataOriginITS; - ITSDPLDigitizerTask(bool mctruth = true) : ITSMFTDPLDigitizerTask(mctruth) - { - mID = DETID; - mOrigin = DETOR; - } + ITSDPLDigitizerTask(bool mctruth = true) : ITSMFTDPLDigitizerTask(mctruth) {} }; -constexpr o2::detectors::DetID::ID ITSDPLDigitizerTask::DETID; -constexpr o2::header::DataOrigin ITSDPLDigitizerTask::DETOR; - //_______________________________________________ -class MFTDPLDigitizerTask : public ITSMFTDPLDigitizerTask +class MFTDPLDigitizerTask : public ITSMFTDPLDigitizerTask { public: - // FIXME: origina should be extractable from the DetID, the problem is 3d party header dependencies - static constexpr o2::detectors::DetID::ID DETID = o2::detectors::DetID::MFT; - static constexpr o2::header::DataOrigin DETOR = o2::header::gDataOriginMFT; - MFTDPLDigitizerTask(bool mctruth) : ITSMFTDPLDigitizerTask(mctruth) - { - mID = DETID; - mOrigin = DETOR; - } + MFTDPLDigitizerTask(bool mctruth = true) : ITSMFTDPLDigitizerTask(mctruth) {} }; -constexpr o2::detectors::DetID::ID MFTDPLDigitizerTask::DETID; -constexpr o2::header::DataOrigin MFTDPLDigitizerTask::DETOR; - +namespace +{ +template std::vector makeOutChannels(o2::header::DataOrigin detOrig, bool mctruth) { std::vector outputs; - outputs.emplace_back(detOrig, "DIGITS", 0, Lifetime::Timeframe); - outputs.emplace_back(detOrig, "DIGITSROF", 0, Lifetime::Timeframe); - if (mctruth) { - outputs.emplace_back(detOrig, "DIGITSMC2ROF", 0, Lifetime::Timeframe); - outputs.emplace_back(detOrig, "DIGITSMCTR", 0, Lifetime::Timeframe); + constexpr uint32_t nLayers = (DPLAlpideParam::supportsStaggering()) ? DPLAlpideParam::getNLayers() : 1; + for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer) { + outputs.emplace_back(detOrig, "DIGITS", iLayer, Lifetime::Timeframe); + outputs.emplace_back(detOrig, "DIGITSROF", iLayer, Lifetime::Timeframe); + if (mctruth) { + outputs.emplace_back(detOrig, "DIGITSMC2ROF", iLayer, Lifetime::Timeframe); + outputs.emplace_back(detOrig, "DIGITSMCTR", iLayer, Lifetime::Timeframe); + } } outputs.emplace_back(detOrig, "ROMode", 0, Lifetime::Timeframe); return outputs; } +} // namespace DataProcessorSpec getITSDigitizerSpec(int channel, bool mctruth) { - std::string detStr = o2::detectors::DetID::getName(ITSDPLDigitizerTask::DETID); - auto detOrig = ITSDPLDigitizerTask::DETOR; - std::stringstream parHelper; - parHelper << "Params as " << o2::itsmft::DPLDigitizerParam::getParamName().data() << ".=value;... with" - << o2::itsmft::DPLDigitizerParam::Instance() - << "\n or " << o2::itsmft::DPLAlpideParam::getParamName().data() << ".=value;... with" - << o2::itsmft::DPLAlpideParam::Instance(); + std::string detStr = o2::detectors::DetID::getName(ITSDPLDigitizerTask::ID); + auto detOrig = ITSDPLDigitizerTask::Origin; std::vector inputs; inputs.emplace_back("collisioncontext", "SIM", "COLLISIONCONTEXT", static_cast(channel), Lifetime::Timeframe); inputs.emplace_back("ITS_noise", "ITS", "NOISEMAP", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/NoiseMap")); @@ -377,19 +424,18 @@ DataProcessorSpec getITSDigitizerSpec(int channel, bool mctruth) inputs.emplace_back("ITS_alppar", "ITS", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam")); inputs.emplace_back("ITS_alpiderespvbb0", "ITS", "ALPIDERESPVbb0", 0, Lifetime::Condition, ccdbParamSpec("ITSMFT/Calib/ALPIDEResponseVbb0")); inputs.emplace_back("ITS_alpiderespvbbm3", "ITS", "ALPIDERESPVbbM3", 0, Lifetime::Condition, ccdbParamSpec("ITSMFT/Calib/ALPIDEResponseVbbM3")); - - return DataProcessorSpec{(detStr + "Digitizer").c_str(), - inputs, makeOutChannels(detOrig, mctruth), - AlgorithmSpec{adaptFromTask(mctruth)}, - Options{ + return DataProcessorSpec{.name = detStr + "Digitizer", + .inputs = inputs, + .outputs = makeOutChannels(detOrig, mctruth), + .algorithm = AlgorithmSpec{adaptFromTask(mctruth)}, + .options = Options{ {"disable-qed", o2::framework::VariantType::Bool, false, {"disable QED handling"}}}}; } DataProcessorSpec getMFTDigitizerSpec(int channel, bool mctruth) { - std::string detStr = o2::detectors::DetID::getName(MFTDPLDigitizerTask::DETID); - auto detOrig = MFTDPLDigitizerTask::DETOR; - std::stringstream parHelper; + std::string detStr = o2::detectors::DetID::getName(MFTDPLDigitizerTask::ID); + auto detOrig = MFTDPLDigitizerTask::Origin; std::vector inputs; inputs.emplace_back("collisioncontext", "SIM", "COLLISIONCONTEXT", static_cast(channel), Lifetime::Timeframe); inputs.emplace_back("MFT_noise", "MFT", "NOISEMAP", 0, Lifetime::Condition, ccdbParamSpec("MFT/Calib/NoiseMap")); @@ -398,15 +444,12 @@ DataProcessorSpec getMFTDigitizerSpec(int channel, bool mctruth) inputs.emplace_back("MFT_alppar", "MFT", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("MFT/Config/AlpideParam")); inputs.emplace_back("MFT_alpiderespvbb0", "MFT", "ALPIDERESPVbb0", 0, Lifetime::Condition, ccdbParamSpec("ITSMFT/Calib/ALPIDEResponseVbb0")); inputs.emplace_back("MFT_alpiderespvbbm3", "MFT", "ALPIDERESPVbbM3", 0, Lifetime::Condition, ccdbParamSpec("ITSMFT/Calib/ALPIDEResponseVbbM3")); - parHelper << "Params as " << o2::itsmft::DPLDigitizerParam::getParamName().data() << ".=value;... with" - << o2::itsmft::DPLDigitizerParam::Instance() - << " or " << o2::itsmft::DPLAlpideParam::getParamName().data() << ".=value;... with" - << o2::itsmft::DPLAlpideParam::Instance(); - return DataProcessorSpec{(detStr + "Digitizer").c_str(), - inputs, makeOutChannels(detOrig, mctruth), - AlgorithmSpec{adaptFromTask(mctruth)}, - Options{{"disable-qed", o2::framework::VariantType::Bool, false, {"disable QED handling"}}}}; + return DataProcessorSpec{.name = detStr + "Digitizer", + .inputs = inputs, + .outputs = makeOutChannels(detOrig, mctruth), + .algorithm = AlgorithmSpec{adaptFromTask(mctruth)}, + .options = Options{{"disable-qed", o2::framework::VariantType::Bool, false, {"disable QED handling"}}}}; } -} // end namespace itsmft -} // end namespace o2 +} // namespace o2::itsmft + // end namespace o2 From b18b96ab60f829b2cb853b6a4553a47167948165 Mon Sep 17 00:00:00 2001 From: Roman Lietava Date: Sun, 25 Jan 2026 10:38:46 +0100 Subject: [PATCH 011/133] Ctpdev: task for populating BK with ctp config/scalers (#14993) * dev: code for repopulating BK with old configs/scalers * clang * fixes * fixes * clang * fix --- Detectors/CTP/workflowScalers/CMakeLists.txt | 8 + .../CTPWorkflowScalers/ctpCCDBManager.h | 5 +- .../CTP/workflowScalers/src/ctp-bk-write.cxx | 170 ++++++++++++++++++ .../workflowScalers/src/ctpCCDBManager.cxx | 27 +++ 4 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 Detectors/CTP/workflowScalers/src/ctp-bk-write.cxx diff --git a/Detectors/CTP/workflowScalers/CMakeLists.txt b/Detectors/CTP/workflowScalers/CMakeLists.txt index a31774ac66d69..f02a7f33e2abd 100644 --- a/Detectors/CTP/workflowScalers/CMakeLists.txt +++ b/Detectors/CTP/workflowScalers/CMakeLists.txt @@ -34,3 +34,11 @@ o2_add_executable( SOURCES src/ctp-ccdb-orbit.cxx PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP Boost::program_options) +o2_add_executable( + bk-write + COMPONENT_NAME ctp + SOURCES src/ctp-bk-write.cxx + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CTPWorkflowScalers + AliceO2::BookkeepingApi + Boost::program_options) diff --git a/Detectors/CTP/workflowScalers/include/CTPWorkflowScalers/ctpCCDBManager.h b/Detectors/CTP/workflowScalers/include/CTPWorkflowScalers/ctpCCDBManager.h index 4237ad4501fcc..df2aa79d18697 100644 --- a/Detectors/CTP/workflowScalers/include/CTPWorkflowScalers/ctpCCDBManager.h +++ b/Detectors/CTP/workflowScalers/include/CTPWorkflowScalers/ctpCCDBManager.h @@ -31,8 +31,9 @@ class ctpCCDBManager int saveOrbitReset(long timeStamp); int saveCtpCfg(uint32_t runNumber, long timeStamp); static CTPConfiguration getConfigFromCCDB(long timestamp, std::string run, bool& ok); - static CTPConfiguration getConfigFromCCDB(long timestamp, std::string run); - CTPRunScalers getScalersFromCCDB(long timestamp, std::string, bool& ok); + CTPConfiguration getConfigFromCCDB(long timestamp, std::string run); + CTPRunScalers getScalersFromCCDB(long timestamp, std::string run, bool& ok); + static CTPRunScalers getScalersFromCCDB(long timestamp, std::string, std::string path, bool& ok); static void setCCDBHost(std::string host) { mCCDBHost = host; }; static void setQCDBHost(std::string host) { mQCDBHost = host; }; void setCtpCfgDir(std::string& ctpcfgdir) { mCtpCfgDir = ctpcfgdir; }; diff --git a/Detectors/CTP/workflowScalers/src/ctp-bk-write.cxx b/Detectors/CTP/workflowScalers/src/ctp-bk-write.cxx new file mode 100644 index 0000000000000..8460c07dcc896 --- /dev/null +++ b/Detectors/CTP/workflowScalers/src/ctp-bk-write.cxx @@ -0,0 +1,170 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// example to run: +// +#include +#include +#include +#include +#include "CommonUtils/StringUtils.h" +#include +#include "CTPWorkflowScalers/ctpCCDBManager.h" +#include "BookkeepingApi/BkpClientFactory.h" +#include "BookkeepingApi/BkpClient.h" +#include +#include +#include +#include +namespace bpo = boost::program_options; +// +// Test in the lab +// o2-ctp-bk-write -r 37 -s 1 -c 1 --ccdb='http://acsl-ccdb.cern.ch:8083' -b 'acsl-aliecs.cern.ch:4001' -t 1753185071753 +// +int main(int argc, char** argv) +{ + const std::string testCCDB = "http://ccdb-test.cern.ch:8080"; + // std::string prodCCDB = "http://o2-ccdb.internal"; + const std::string aliceCCDB = "http://alice-ccdb.cern.ch"; + bpo::variables_map vm; + bpo::options_description opt_general("Usage:\n " + std::string(argv[0]) + + " Write ctp config or scalers to BK\n"); + bpo::options_description opt_hidden(""); + bpo::options_description opt_all; + bpo::positional_options_description opt_pos; + try { + auto add_option = opt_general.add_options(); + add_option("help,h", "Print this help message"); + add_option("input-file,f", bpo::value()->default_value("none"), "input file name, none - do not read file"); + add_option("bkhost,b", bpo::value()->default_value("none"), "bk web address"); + add_option("ccdb", bpo::value()->default_value("alice"), "choose databse: test- test ccdb; prod - production ccdb; alice - alice ccdb; else ccdb parameter"); + add_option("run-number,r", bpo::value()->default_value(0), "run number"); + add_option("timestamp,t", bpo::value()->default_value(0), "timestamp; if 0 timestamp is calulated inside this code"); + add_option("cfg,c", bpo::value()->default_value(0), "Do cfg"); + add_option("scalers,s", bpo::value()->default_value(0), "Do scalers"); + // + opt_all.add(opt_general).add(opt_hidden); + bpo::store(bpo::command_line_parser(argc, argv).options(opt_all).positional(opt_pos).run(), vm); + if (vm.count("help")) { + std::cout << opt_general << std::endl; + exit(0); + } + bpo::notify(vm); + } catch (bpo::error& e) { + std::cerr << "ERROR: " << e.what() << std::endl + << std::endl; + std::cerr << opt_general << std::endl; + exit(1); + } catch (std::exception& e) { + std::cerr << e.what() << ", application will now exit" << std::endl; + exit(2); + } + uint64_t timestamp = vm["timestamp"].as(); + // + int ret = 0; + std::vector runs; + int32_t run = vm["run-number"].as(); + std::cout << "run:" << run << std::endl; + if (run) { + std::cout << "pushing" << std::endl; + runs.push_back(std::to_string(run)); + } + // read input file + std::string filename = vm["input-file"].as(); + if (filename != "none") { + std::ifstream file(filename); + if (!file.is_open()) { + LOG(fatal) << "Cannot open file:" << filename << std::endl; + } else { + std::string line; + while (std::getline(file, line)) { + std::cout << line << "\n"; + std::vector tokens = o2::utils::Str::tokenize(line, ' '); + // int run = std::stoi(tokens[0]); + runs.push_back(tokens[0]); + } + } + } + bool cfg = vm["cfg"].as(); + bool scalers = vm["scalers"].as(); + std::cout << "Doing: cfg:" << cfg << " scal:" << scalers << std::endl; + if (cfg || scalers) { + std::string bkhost = vm["bkhost"].as(); + std::unique_ptr mBKClient = o2::bkp::api::BkpClientFactory::create(bkhost); + // get from ccdb + std::string ccdbAddress; + if (vm["ccdb"].as() == "prod") { + // ccdbAddress = prodCCDB; + } else if (vm["ccdb"].as() == "test") { + ccdbAddress = testCCDB; + } else if (vm["ccdb"].as() == "alice") { + ccdbAddress = aliceCCDB; + } else { + ccdbAddress = vm["ccdb"].as(); + } + o2::ctp::ctpCCDBManager::setCCDBHost(ccdbAddress); + std::cout << "CCDB: " << vm["ccdb"].as() << " " << ccdbAddress << std::endl; + std::map metadata; + for (auto const& run : runs) { + metadata["runNumber"] = run; + bool ok; + int runNumber = std::stoi(run); + auto ctpcfg = o2::ctp::ctpCCDBManager::getConfigFromCCDB(timestamp, run, ok); + + if (cfg) { + std::string ctpcfgstr = ctpcfg.getConfigString(); + try { + mBKClient->run()->setRawCtpTriggerConfiguration(runNumber, ctpcfgstr); + } catch (std::runtime_error& error) { + std::cerr << "An error occurred: " << error.what() << std::endl; + // return 1; + } + LOG(info) << "Run BK:" << run << " CFG:" << cfg; + } + if (scalers) { + auto ctpcnts = o2::ctp::ctpCCDBManager::getScalersFromCCDB(timestamp, run, "CTP/Calib/Scalers", ok); + ctpcnts.convertRawToO2(); + std::vector clsinds = ctpcnts.getClassIndexes(); + long ts = ctpcnts.getTimeLimit().second; + int i = 0; + for (auto const& ind : clsinds) { + std::array cntsbk = ctpcnts.getIntegralForClass(i); + std::string clsname = ctpcfg.getClassNameFromHWIndex(cntsbk[0]); + try { + mBKClient->ctpTriggerCounters()->createOrUpdateForRun(runNumber, clsname, ts, cntsbk[1], cntsbk[2], cntsbk[3], cntsbk[4], cntsbk[5], cntsbk[6]); + std::cout << runNumber << " clsname: " << cntsbk[0] << " " << clsname << " t:" << ts << " cnts:" << cntsbk[1] << " " << cntsbk[2] << " " << cntsbk[3] << " " << cntsbk[4] << " " << cntsbk[5] << " " << cntsbk[6] << std::endl; + ; + + } catch (std::runtime_error& error) { + std::cerr << "An error occurred: " << error.what() << std::endl; + // return 1; + } + LOG(debug) << "Run BK scalers ok"; + i++; + } + } + } + // add to bk + } + std::cout << "o2-ctp-bk-write done" << std::endl; + return ret; +} diff --git a/Detectors/CTP/workflowScalers/src/ctpCCDBManager.cxx b/Detectors/CTP/workflowScalers/src/ctpCCDBManager.cxx index df75b21c2effd..74d4a905c93e2 100644 --- a/Detectors/CTP/workflowScalers/src/ctpCCDBManager.cxx +++ b/Detectors/CTP/workflowScalers/src/ctpCCDBManager.cxx @@ -204,10 +204,16 @@ int ctpCCDBManager::saveCtpCfg(uint32_t runNumber, long timeStart) } CTPConfiguration ctpCCDBManager::getConfigFromCCDB(long timestamp, std::string run, bool& ok) { + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); mgr.setURL(mCCDBHost); std::map metadata; // can be empty metadata["runNumber"] = run; + if (timestamp == 0) { + // Timestamp + auto soreor = mgr.getRunDuration(std::stoi(run)); + timestamp = (soreor.second - soreor.first) / 2 + soreor.first; + } auto ctpconfigdb = mgr.getSpecific(CCDBPathCTPConfig, timestamp, metadata); if (ctpconfigdb == nullptr) { LOG(info) << "CTP config not in database, timestamp:" << timestamp; @@ -245,3 +251,24 @@ CTPRunScalers ctpCCDBManager::getScalersFromCCDB(long timestamp, std::string run } return *ctpscalers; } +CTPRunScalers ctpCCDBManager::getScalersFromCCDB(long timestamp, std::string run, std::string path, bool& ok) +{ + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); + mgr.setURL(mCCDBHost); + std::map metadata; // can be empty + metadata["runNumber"] = run; + if (timestamp == 0) { + // Timestamp + auto soreor = mgr.getRunDuration(std::stoi(run)); + timestamp = (soreor.second - soreor.first) / 2 + soreor.first; + } + auto ctpscalers = mgr.getSpecific(path, timestamp, metadata); + if (ctpscalers == nullptr) { + LOG(info) << "CTPRunScalers not in database, timestamp:" << timestamp; + ok = 0; + } else { + // ctpscalers->printStream(std::cout); + ok = 1; + } + return *ctpscalers; +} \ No newline at end of file From b33261326dc4203ba6e537df64e04b4bdb3716d5 Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Fri, 23 Jan 2026 14:55:22 +0100 Subject: [PATCH 012/133] Implement AO2D file checks for full_system_test Performed during ASYNC stage, where the AO2D is created --- prodtests/full_system_test.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/prodtests/full_system_test.sh b/prodtests/full_system_test.sh index bf235a500cd8b..6408588d46e68 100755 --- a/prodtests/full_system_test.sh +++ b/prodtests/full_system_test.sh @@ -295,6 +295,25 @@ for STAGE in $STAGES; do # boolean flag indicating if workflow completed successfully at all RC=$? SUCCESS=0 + # Check AOD production for ASYNC stage + if [[ "$STAGE" = "ASYNC" ]]; then + if [[ -f "AO2D.root" ]]; then + aod_size=`stat -c%s AO2D.root` + if [[ $aod_size -gt 0 ]]; then + echo "AO2D file produced: AO2D.root (size: ${aod_size} bytes)" + echo "aod_size_${STAGE},${TAG} value=${aod_size}" >> ${METRICFILE} + else + echo "ERROR: AO2D file (AO2D.root) exists but is empty" + echo "aod_size_${STAGE},${TAG} value=0" >> ${METRICFILE} + exit 1 + fi + else + echo "ERROR: AO2D file (AO2D.root) was not produced in ASYNC stage" + echo "aod_size_${STAGE},${TAG} value=0" >> ${METRICFILE} + exit 1 + fi + fi + [[ -f "${logfile}_done" ]] && [[ "$RC" = 0 ]] && SUCCESS=1 echo "success_${STAGE},${TAG} value=${SUCCESS}" >> ${METRICFILE} From 1fba29618f1f469fb918c725234f1d9f45dfc183 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Mon, 26 Jan 2026 10:23:20 +0100 Subject: [PATCH 013/133] DPL Analysis: modernize and cleanup some code (#14975) --- .../AnalysisSupport/src/AODReaderHelpers.cxx | 55 ++++------- Framework/Core/include/Framework/ASoA.h | 1 + .../Core/include/Framework/Expressions.h | 2 +- Framework/Core/src/ASoA.cxx | 96 +++++++++---------- Framework/Core/src/AnalysisHelpers.cxx | 37 +++---- Framework/Core/src/ArrowSupport.cxx | 26 ++--- Framework/Core/src/ArrowTableSlicingCache.cxx | 6 +- Framework/Core/src/Expressions.cxx | 4 +- Framework/Core/src/WorkflowHelpers.cxx | 74 +++++++------- 9 files changed, 129 insertions(+), 172 deletions(-) diff --git a/Framework/AnalysisSupport/src/AODReaderHelpers.cxx b/Framework/AnalysisSupport/src/AODReaderHelpers.cxx index 485f3fa69edad..4c1c065000186 100644 --- a/Framework/AnalysisSupport/src/AODReaderHelpers.cxx +++ b/Framework/AnalysisSupport/src/AODReaderHelpers.cxx @@ -37,7 +37,7 @@ struct Buildable { std::vector records; std::shared_ptr outputSchema; - Buildable(InputSpec const& spec) + explicit Buildable(InputSpec const& spec) : binding{spec.binding} { auto&& [origin_, description_, version_] = DataSpecUtils::asConcreteDataMatcher(spec); @@ -58,9 +58,8 @@ struct Buildable { } outputSchema = std::make_shared([](std::vector const& recs) { std::vector> fields; - for (auto& r : recs) { - fields.push_back(r.field()); - } + fields.reserve(recs.size()); + std::ranges::transform(recs, std::back_inserter(fields), [](auto& r) { return r.field(); }); return fields; }(records)) ->WithMetadata(std::make_shared(std::vector{std::string{"label"}}, std::vector{std::string{binding}})); @@ -87,19 +86,12 @@ AlgorithmSpec AODReaderHelpers::indexBuilderCallback(ConfigContext const& /*ctx* { return AlgorithmSpec::InitCallback{[](InitContext& ic) { auto const& requested = ic.services().get().requestedIDXs; - std::vector buildables; - for (auto const& i : requested) { - buildables.emplace_back(i); - } std::vector builders; - for (auto& b : buildables) { - builders.push_back(b.createBuilder()); - } + builders.reserve(requested.size()); + std::ranges::transform(requested, std::back_inserter(builders), [](auto const& i) { return Buildable{i}.createBuilder(); }); return [builders](ProcessingContext& pc) mutable { auto outputs = pc.outputs(); - for (auto& builder : builders) { - outputs.adopt(Output{builder.origin, builder.description, builder.version}, builder.materialize(pc)); - } + std::ranges::for_each(builders, [&pc, &outputs](auto& builder) { outputs.adopt(Output{builder.origin, builder.description, builder.version}, builder.materialize(pc)); }); }; }}; } @@ -119,7 +111,7 @@ struct Spawnable { header::DataDescription description; header::DataHeader::SubSpecificationType version; - Spawnable(InputSpec const& spec) + explicit Spawnable(InputSpec const& spec) : binding{spec.binding} { auto&& [origin_, description_, version_] = DataSpecUtils::asConcreteDataMatcher(spec); @@ -144,16 +136,19 @@ struct Spawnable { iws.str(json); schemas.emplace_back(ArrowJSONHelpers::read(iws)); } - for (auto const& i : spec.metadata | views::filter_string_params_starts_with("input:") | std::ranges::views::transform([](auto const& param) { - return DataSpecUtils::fromMetadataString(param.defaultValue.template get()); - })) { - matchers.emplace_back(std::get(i.matcher)); - } + std::ranges::transform(spec.metadata | + views::filter_string_params_starts_with("input:") | + std::ranges::views::transform( + [](auto const& param) { + return DataSpecUtils::fromMetadataString(param.defaultValue.template get()); + }), + std::back_inserter(matchers), [](auto const& i) { return std::get(i.matcher); }); std::vector> fields; - for (auto& s : schemas) { - std::copy(s->fields().begin(), s->fields().end(), std::back_inserter(fields)); - } + std::ranges::for_each(schemas, + [&fields](auto const& s) { + std::ranges::copy(s->fields(), std::back_inserter(fields)); + }); inputSchema = std::make_shared(fields); expressions = expressions::materializeProjectors(projectors, inputSchema, outputSchema->fields()); @@ -194,20 +189,12 @@ AlgorithmSpec AODReaderHelpers::aodSpawnerCallback(ConfigContext const& /*ctx*/) { return AlgorithmSpec::InitCallback{[](InitContext& ic) { auto const& requested = ic.services().get().spawnerInputs; - std::vector spawnables; - for (auto const& i : requested) { - spawnables.emplace_back(i); - } std::vector spawners; - for (auto& s : spawnables) { - spawners.push_back(s.createMaker()); - } - + spawners.reserve(requested.size()); + std::ranges::transform(requested, std::back_inserter(spawners), [](auto const& i) { return Spawnable{i}.createMaker(); }); return [spawners](ProcessingContext& pc) mutable { auto outputs = pc.outputs(); - for (auto& spawner : spawners) { - outputs.adopt(Output{spawner.origin, spawner.description, spawner.version}, spawner.materialize(pc)); - } + std::ranges::for_each(spawners, [&pc, &outputs](auto& spawner) { outputs.adopt(Output{spawner.origin, spawner.description, spawner.version}, spawner.materialize(pc)); }); }; }}; } diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index ec02c7e47132b..4fd35e0dc5065 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -1283,6 +1283,7 @@ struct TableIterator : IP, C... { }; struct ArrowHelpers { + static std::shared_ptr joinTables(std::vector>&& tables); static std::shared_ptr joinTables(std::vector>&& tables, std::span labels); static std::shared_ptr joinTables(std::vector>&& tables, std::span labels); static std::shared_ptr concatTables(std::vector>&& tables); diff --git a/Framework/Core/include/Framework/Expressions.h b/Framework/Core/include/Framework/Expressions.h index 0be19954f1faa..c5f50311a7d19 100644 --- a/Framework/Core/include/Framework/Expressions.h +++ b/Framework/Core/include/Framework/Expressions.h @@ -712,7 +712,7 @@ std::shared_ptr createProjectorHelper(size_t nColumns, expre std::shared_ptr schema, std::vector> const& fields); -std::vector> materializeProjectors(std::vector const& projectors, std::shared_ptr const& inputSchema, std::vector> outputFields); +std::vector> materializeProjectors(std::vector const& projectors, std::shared_ptr const& inputSchema, std::vector> const& outputFields); template std::shared_ptr createProjectors(framework::pack, std::vector> const& fields, gandiva::SchemaPtr schema) diff --git a/Framework/Core/src/ASoA.cxx b/Framework/Core/src/ASoA.cxx index 6a846c3d45b6c..1c73b257f81e4 100644 --- a/Framework/Core/src/ASoA.cxx +++ b/Framework/Core/src/ASoA.cxx @@ -62,71 +62,71 @@ SelectionVector sliceSelection(std::span const& mSelectedRows, in auto start_iterator = std::lower_bound(mSelectedRows.begin(), mSelectedRows.end(), start); auto stop_iterator = std::lower_bound(start_iterator, mSelectedRows.end(), end); SelectionVector slicedSelection{start_iterator, stop_iterator}; - std::transform(slicedSelection.begin(), slicedSelection.end(), slicedSelection.begin(), - [&start](int64_t idx) { - return idx - static_cast(start); - }); + std::ranges::transform(slicedSelection.begin(), slicedSelection.end(), slicedSelection.begin(), + [&start](int64_t idx) { + return idx - static_cast(start); + }); return slicedSelection; } -std::shared_ptr ArrowHelpers::joinTables(std::vector>&& tables, std::span labels) +std::shared_ptr ArrowHelpers::joinTables(std::vector>&& tables) { - if (tables.size() == 1) { - return tables[0]; - } - for (auto i = 0U; i < tables.size() - 1; ++i) { - if (tables[i]->num_rows() != tables[i + 1]->num_rows()) { - throw o2::framework::runtime_error_f("Tables %s and %s have different sizes (%d vs %d) and cannot be joined!", - labels[i], labels[i + 1], tables[i]->num_rows(), tables[i + 1]->num_rows()); - } - } std::vector> fields; std::vector> columns; - - for (auto& t : tables) { - auto tf = t->fields(); - std::copy(tf.begin(), tf.end(), std::back_inserter(fields)); - } - - auto schema = std::make_shared(fields); - - if (tables[0]->num_rows() != 0) { - for (auto& t : tables) { - auto tc = t->columns(); - std::copy(tc.begin(), tc.end(), std::back_inserter(columns)); + bool notEmpty = (tables[0]->num_rows() != 0); + std::ranges::for_each(tables, [&fields, &columns, notEmpty](auto const& t) { + std::ranges::copy(t->fields(), std::back_inserter(fields)); + if (notEmpty) { + std::ranges::copy(t->columns(), std::back_inserter(columns)); } - } + }); + auto schema = std::make_shared(fields); return arrow::Table::Make(schema, columns); } -std::shared_ptr ArrowHelpers::joinTables(std::vector>&& tables, std::span labels) +namespace +{ +template + requires(std::same_as) +auto makeString(T const& str) +{ + return str.c_str(); +} +template + requires(std::same_as) +auto makeString(T const& str) +{ + return str; +} + +template +void canNotJoin(std::vector> const& tables, std::span labels) { - if (tables.size() == 1) { - return tables[0]; - } for (auto i = 0U; i < tables.size() - 1; ++i) { if (tables[i]->num_rows() != tables[i + 1]->num_rows()) { throw o2::framework::runtime_error_f("Tables %s and %s have different sizes (%d vs %d) and cannot be joined!", - labels[i].c_str(), labels[i + 1].c_str(), tables[i]->num_rows(), tables[i + 1]->num_rows()); + makeString(labels[i]), makeString(labels[i + 1]), tables[i]->num_rows(), tables[i + 1]->num_rows()); } } - std::vector> fields; - std::vector> columns; +} +} // namespace - for (auto& t : tables) { - auto tf = t->fields(); - std::copy(tf.begin(), tf.end(), std::back_inserter(fields)); +std::shared_ptr ArrowHelpers::joinTables(std::vector>&& tables, std::span labels) +{ + if (tables.size() == 1) { + return tables[0]; } + canNotJoin(tables, labels); + return joinTables(std::forward>>(tables)); +} - auto schema = std::make_shared(fields); - - if (tables[0]->num_rows() != 0) { - for (auto& t : tables) { - auto tc = t->columns(); - std::copy(tc.begin(), tc.end(), std::back_inserter(columns)); - } +std::shared_ptr ArrowHelpers::joinTables(std::vector>&& tables, std::span labels) +{ + if (tables.size() == 1) { + return tables[0]; } - return arrow::Table::Make(schema, columns); + canNotJoin(tables, labels); + return joinTables(std::forward>>(tables)); } std::shared_ptr ArrowHelpers::concatTables(std::vector>&& tables) @@ -135,7 +135,6 @@ std::shared_ptr ArrowHelpers::concatTables(std::vector> columns; - assert(tables.size() > 1); std::vector> resultFields = tables[0]->schema()->fields(); auto compareFields = [](std::shared_ptr const& f1, std::shared_ptr const& f2) { // Let's do this with stable sorting. @@ -165,13 +164,12 @@ std::shared_ptr ArrowHelpers::concatTables(std::vector(chunks)); } - auto result = arrow::Table::Make(std::make_shared(resultFields), columns); - return result; + return arrow::Table::Make(std::make_shared(resultFields), columns); } arrow::ChunkedArray* getIndexFromLabel(arrow::Table* table, std::string_view label) { - auto field = std::find_if(table->schema()->fields().begin(), table->schema()->fields().end(), [&](std::shared_ptr const& f) { + auto field = std::ranges::find_if(table->schema()->fields(), [&](std::shared_ptr const& f) { auto caseInsensitiveCompare = [](const std::string_view& str1, const std::string& str2) { return std::ranges::equal( str1, str2, diff --git a/Framework/Core/src/AnalysisHelpers.cxx b/Framework/Core/src/AnalysisHelpers.cxx index f2ecb2d68ce28..b7eac692d3859 100644 --- a/Framework/Core/src/AnalysisHelpers.cxx +++ b/Framework/Core/src/AnalysisHelpers.cxx @@ -46,14 +46,12 @@ void IndexBuilder::resetBuilders(std::vector& bui std::shared_ptr IndexBuilder::materialize(std::vector& builders, std::vector>&& tables, std::vector const& records, std::shared_ptr const& schema, bool exclusive) { auto size = tables[0]->num_rows(); - if (builders.empty()) { + if (O2_BUILTIN_UNLIKELY(builders.empty())) { builders = makeBuilders(std::move(tables), records); } else { resetBuilders(builders, std::move(tables)); } - std::vector finds; - finds.resize(builders.size()); for (int64_t counter = 0; counter < size; ++counter) { int64_t idx = -1; if (std::get(builders[0].builder).keyIndex == nullptr) { @@ -61,29 +59,19 @@ std::shared_ptr IndexBuilder::materialize(std::vector(builders[0].builder).keyIndex->valueAt(counter); } - for (auto i = 0U; i < builders.size(); ++i) { - finds[i] = builders[i].find(idx); - } - if (exclusive) { - if (std::none_of(finds.begin(), finds.end(), [](bool const x) { return x == false; })) { - builders[0].fill(counter); - for (auto i = 1U; i < builders.size(); ++i) { - builders[i].fill(idx); - } - } - } else { + + bool found = true; + std::ranges::for_each(builders, [&idx, &found](auto& builder) { found &= builder.find(idx); }); + + if (!exclusive || found) { builders[0].fill(counter); - for (auto i = 1U; i < builders.size(); ++i) { - builders[i].fill(idx); - } + std::ranges::for_each(builders.begin() + 1, builders.end(), [&idx](auto& builder) { builder.fill(idx); }); } } std::vector> arrays; arrays.reserve(builders.size()); - for (auto& builder : builders) { - arrays.push_back(builder.result()); - } + std::ranges::transform(builders, std::back_inserter(arrays), [](auto& builder) { return builder.result(); }); return arrow::Table::Make(schema, arrays); } @@ -142,9 +130,7 @@ std::shared_ptr spawnerHelper(std::shared_ptr const& } arrays.reserve(nColumns); - for (auto i = 0U; i < nColumns; ++i) { - arrays.push_back(std::make_shared(chunks[i])); - } + std::ranges::transform(chunks, std::back_inserter(arrays), [](auto&& chunk) { return std::make_shared(chunk); }); return arrow::Table::Make(newSchema, arrays); } @@ -188,9 +174,8 @@ std::string serializeIndexRecords(std::vector& irs) std::vector> extractSources(ProcessingContext& pc, std::vector const& matchers) { std::vector> tables; - for (auto const& matcher : matchers) { - tables.emplace_back(pc.inputs().get(matcher)->asArrowTable()); - } + tables.reserve(matchers.size()); + std::ranges::transform(matchers, std::back_inserter(tables), [&pc](auto const& matcher) { return pc.inputs().get(matcher)->asArrowTable(); }); return tables; } diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index 95e763343671a..60277dfc38a74 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -531,13 +531,7 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() dh->dataOrigin.str, dh->dataDescription.str); continue; } - bool forwarded = false; - for (auto const& forward : ctx.services().get().forwards) { - if (DataSpecUtils::match(forward.matcher, *dh)) { - forwarded = true; - break; - } - } + bool forwarded = std::ranges::any_of(ctx.services().get().forwards, [&dh](auto const& forward) { return DataSpecUtils::match(forward.matcher, *dh); }); if (forwarded) { O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", "Message %{public}.4s/%{public}.16s is forwarded so we are not returning its memory.", @@ -584,11 +578,11 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() } }, .adjustTopology = [](WorkflowSpecNode& node, ConfigContext const& ctx) { auto& workflow = node.specs; - auto spawner = std::find_if(workflow.begin(), workflow.end(), [](DataProcessorSpec const& spec) { return spec.name == "internal-dpl-aod-spawner"; }); - auto analysisCCDB = std::find_if(workflow.begin(), workflow.end(), [](DataProcessorSpec const& spec) { return spec.name == "internal-dpl-aod-ccdb"; }); - auto builder = std::find_if(workflow.begin(), workflow.end(), [](DataProcessorSpec const& spec) { return spec.name == "internal-dpl-aod-index-builder"; }); - auto reader = std::find_if(workflow.begin(), workflow.end(), [](DataProcessorSpec const& spec) { return spec.name == "internal-dpl-aod-reader"; }); - auto writer = std::find_if(workflow.begin(), workflow.end(), [](DataProcessorSpec const& spec) { return spec.name == "internal-dpl-aod-writer"; }); + auto spawner = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { return spec.name.starts_with("internal-dpl-aod-spawner"); }); + auto analysisCCDB = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { return spec.name.starts_with("internal-dpl-aod-ccdb"); }); + auto builder = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { return spec.name.starts_with("internal-dpl-aod-index-builder"); }); + auto reader = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { return spec.name.starts_with("internal-dpl-aod-reader"); }); + auto writer = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { return spec.name.starts_with("internal-dpl-aod-writer"); }); auto& dec = ctx.services().get(); dec.requestedAODs.clear(); dec.requestedDYNs.clear(); @@ -626,8 +620,8 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() views::partial_match_filter(header::DataOrigin{"DYN"}) | sinks::append_to{dec.providedDYNs}; } - std::sort(dec.requestedDYNs.begin(), dec.requestedDYNs.end(), inputSpecLessThan); - std::sort(dec.providedDYNs.begin(), dec.providedDYNs.end(), outputSpecLessThan); + std::ranges::sort(dec.requestedDYNs, inputSpecLessThan); + std::ranges::sort(dec.providedDYNs, outputSpecLessThan); dec.spawnerInputs.clear(); dec.requestedDYNs | views::filter_not_matching(dec.providedDYNs) | @@ -646,8 +640,8 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() d.inputs | views::partial_match_filter(header::DataOrigin{"ATIM"}) | sinks::update_input_list{dec.requestedTIMs}; d.outputs | views::partial_match_filter(header::DataOrigin{"ATIM"}) | sinks::append_to{dec.providedTIMs}; } - std::sort(dec.requestedTIMs.begin(), dec.requestedTIMs.end(), inputSpecLessThan); - std::sort(dec.providedTIMs.begin(), dec.providedTIMs.end(), outputSpecLessThan); + std::ranges::sort(dec.requestedTIMs, inputSpecLessThan); + std::ranges::sort(dec.providedTIMs, outputSpecLessThan); // Use ranges::to> in C++23... dec.analysisCCDBInputs.clear(); dec.requestedTIMs | views::filter_not_matching(dec.providedTIMs) | sinks::append_to{dec.analysisCCDBInputs}; diff --git a/Framework/Core/src/ArrowTableSlicingCache.cxx b/Framework/Core/src/ArrowTableSlicingCache.cxx index 634c51f71f5a6..5162c698a1d66 100644 --- a/Framework/Core/src/ArrowTableSlicingCache.cxx +++ b/Framework/Core/src/ArrowTableSlicingCache.cxx @@ -210,7 +210,7 @@ std::pair ArrowTableSlicingCache::getCachePos(const Entry& bindingKey int ArrowTableSlicingCache::getCachePosSortedFor(Entry const& bindingKey) const { - auto locate = std::find(bindingsKeys.begin(), bindingsKeys.end(), bindingKey); + auto locate = std::ranges::find(bindingsKeys, bindingKey); if (locate != bindingsKeys.end()) { return std::distance(bindingsKeys.begin(), locate); } @@ -219,7 +219,7 @@ int ArrowTableSlicingCache::getCachePosSortedFor(Entry const& bindingKey) const int ArrowTableSlicingCache::getCachePosUnsortedFor(Entry const& bindingKey) const { - auto locate_unsorted = std::find(bindingsKeysUnsorted.begin(), bindingsKeysUnsorted.end(), bindingKey); + auto locate_unsorted = std::ranges::find(bindingsKeysUnsorted, bindingKey); if (locate_unsorted != bindingsKeysUnsorted.end()) { return std::distance(bindingsKeysUnsorted.begin(), locate_unsorted); } @@ -275,7 +275,7 @@ void ArrowTableSlicingCache::validateOrder(Entry const& bindingKey, const std::s } auto column = o2::framework::GetColumnByNameCI(input, key); auto array0 = static_cast>(column->chunk(0)->data()); - int32_t prev = 0; + int32_t prev; int32_t cur = array0.Value(0); int32_t lastNeg = cur < 0 ? cur : 0; int32_t lastPos = cur < 0 ? -1 : cur; diff --git a/Framework/Core/src/Expressions.cxx b/Framework/Core/src/Expressions.cxx index 43143f781ddf4..02a862d30032b 100644 --- a/Framework/Core/src/Expressions.cxx +++ b/Framework/Core/src/Expressions.cxx @@ -1348,11 +1348,11 @@ OpNode Parser::opFromToken(std::string const& token) return OpNode{static_cast(std::distance(mapping.begin(), locate))}; } -std::vector> materializeProjectors(std::vector const& projectors, std::shared_ptr const& inputSchema, std::vector> outputFields) +std::vector> materializeProjectors(std::vector const& projectors, std::shared_ptr const& inputSchema, std::vector> const& outputFields) { std::vector> expressions; int i = 0; - for (auto& p : projectors) { + for (auto const& p : projectors) { expressions.push_back( expressions::makeExpression( expressions::createExpressionTree( diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index fd9099e1aa24e..ff1ff1f4cf13d 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -100,7 +100,7 @@ std::vector // which have the current node as incoming. // nextEdges will contain all the edges which are not related // to the current node. - for (auto& ei : remainingEdgesIndex) { + for (auto const& ei : remainingEdgesIndex) { if (*(edgeIn + ei * stride) == node.index) { nextVertex.insert({*(edgeOut + ei * stride), node.layer + 1}); } else { @@ -112,7 +112,7 @@ std::vector // Of all the vertices which have node as incoming, // check if there is any other incoming node. std::set hasPredecessors; - for (auto& ei : remainingEdgesIndex) { + for (auto const& ei : remainingEdgesIndex) { for (auto& m : nextVertex) { if (m.index == *(edgeOut + ei * stride)) { hasPredecessors.insert({m.index, m.layer}); @@ -240,7 +240,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext for (size_t wi = 0; wi < workflow.size(); ++wi) { auto& processor = workflow[wi]; auto name = processor.name; - auto hash = runtime_hash(name.c_str()); + uint32_t hash = runtime_hash(name.c_str()); dec.outTskMap.push_back({hash, name}); std::string prefix = "internal-dpl-"; @@ -252,8 +252,8 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext processor.options.push_back(ConfigParamSpec{"end-value-enumeration", VariantType::Int64, -1ll, {"final value for the enumeration"}}); processor.options.push_back(ConfigParamSpec{"step-value-enumeration", VariantType::Int64, 1ll, {"step between one value and the other"}}); } - bool hasTimeframeInputs = std::any_of(processor.inputs.begin(), processor.inputs.end(), [](auto const& input) { return input.lifetime == Lifetime::Timeframe; }); - bool hasTimeframeOutputs = std::any_of(processor.outputs.begin(), processor.outputs.end(), [](auto const& output) { return output.lifetime == Lifetime::Timeframe; }); + bool hasTimeframeInputs = std::ranges::any_of(processor.inputs, [](auto const& input) { return input.lifetime == Lifetime::Timeframe; }); + bool hasTimeframeOutputs = std::ranges::any_of(processor.outputs, [](auto const& output) { return output.lifetime == Lifetime::Timeframe; }); // A timeframeSink consumes timeframes without creating new // timeframe data. @@ -261,10 +261,9 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext if (rateLimitingIPCID != -1) { if (timeframeSink && processor.name.find("internal-dpl-injected-dummy-sink") == std::string::npos) { O2_SIGNPOST_ID_GENERATE(sid, workflow_helpers); - uint32_t hash = runtime_hash(processor.name.c_str()); bool hasMatch = false; ConcreteDataMatcher summaryMatcher = ConcreteDataMatcher{"DPL", "SUMMARY", static_cast(hash)}; - auto summaryOutput = std::find_if(processor.outputs.begin(), processor.outputs.end(), [&summaryMatcher](auto const& output) { return DataSpecUtils::match(output, summaryMatcher); }); + auto summaryOutput = std::ranges::find_if(processor.outputs, [&summaryMatcher](auto const& output) { return DataSpecUtils::match(output, summaryMatcher); }); if (summaryOutput != processor.outputs.end()) { O2_SIGNPOST_EVENT_EMIT(workflow_helpers, sid, "output enumeration", "%{public}s already there in %{public}s", DataSpecUtils::describe(*summaryOutput).c_str(), processor.name.c_str()); @@ -283,7 +282,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext switch (input.lifetime) { case Lifetime::Timer: { auto concrete = DataSpecUtils::asConcreteDataMatcher(input); - auto hasOption = std::any_of(processor.options.begin(), processor.options.end(), [&input](auto const& option) { return (option.name == "period-" + input.binding); }); + auto hasOption = std::ranges::any_of(processor.options, [&input](auto const& option) { return (option.name == "period-" + input.binding); }); if (hasOption == false) { processor.options.push_back(ConfigParamSpec{"period-" + input.binding, VariantType::Int, 1000, {"period of the timer in milliseconds"}}); } @@ -299,7 +298,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext } break; case Lifetime::Condition: { requestedCCDBs.emplace_back(input); - if ((hasConditionOption == false) && std::none_of(processor.options.begin(), processor.options.end(), [](auto const& option) { return (option.name.compare("condition-backend") == 0); })) { + if ((hasConditionOption == false) && std::ranges::none_of(processor.options, [](auto const& option) { return (option.name.compare("condition-backend") == 0); })) { processor.options.emplace_back(ConfigParamSpec{"condition-backend", VariantType::String, defaultConditionBackend(), {"URL for CCDB"}}); processor.options.emplace_back(ConfigParamSpec{"condition-timestamp", VariantType::Int64, 0ll, {"Force timestamp for CCDB lookup"}}); hasConditionOption = true; @@ -307,7 +306,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext } break; case Lifetime::OutOfBand: { auto concrete = DataSpecUtils::asConcreteDataMatcher(input); - auto hasOption = std::any_of(processor.options.begin(), processor.options.end(), [&input](auto const& option) { return (option.name == "out-of-band-channel-name-" + input.binding); }); + auto hasOption = std::ranges::any_of(processor.options, [&input](auto const& option) { return (option.name == "out-of-band-channel-name-" + input.binding); }); if (hasOption == false) { processor.options.push_back(ConfigParamSpec{"out-of-band-channel-name-" + input.binding, VariantType::String, "out-of-band", {"channel to listen for out of band data"}}); } @@ -333,7 +332,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext } } - std::stable_sort(timer.outputs.begin(), timer.outputs.end(), [](OutputSpec const& a, OutputSpec const& b) { return *DataSpecUtils::getOptionalSubSpec(a) < *DataSpecUtils::getOptionalSubSpec(b); }); + std::ranges::stable_sort(timer.outputs, [](OutputSpec const& a, OutputSpec const& b) { return *DataSpecUtils::getOptionalSubSpec(a) < *DataSpecUtils::getOptionalSubSpec(b); }); for (auto& output : processor.outputs) { if (DataSpecUtils::partialMatch(output, AODOrigins)) { @@ -344,7 +343,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext dec.providedTIMs.emplace_back(output); } else if (DataSpecUtils::partialMatch(output, header::DataOrigin{"ATSK"})) { dec.providedOutputObjHist.emplace_back(output); - auto it = std::find_if(dec.outObjHistMap.begin(), dec.outObjHistMap.end(), [&](auto&& x) { return x.id == hash; }); + auto it = std::ranges::find_if(dec.outObjHistMap, [&](auto&& x) { return x.id == hash; }); if (it == dec.outObjHistMap.end()) { dec.outObjHistMap.push_back({hash, {output.binding.value}}); } else { @@ -359,10 +358,10 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext auto inputSpecLessThan = [](InputSpec const& lhs, InputSpec const& rhs) { return DataSpecUtils::describe(lhs) < DataSpecUtils::describe(rhs); }; auto outputSpecLessThan = [](OutputSpec const& lhs, OutputSpec const& rhs) { return DataSpecUtils::describe(lhs) < DataSpecUtils::describe(rhs); }; - std::sort(dec.requestedDYNs.begin(), dec.requestedDYNs.end(), inputSpecLessThan); - std::sort(dec.requestedTIMs.begin(), dec.requestedTIMs.end(), inputSpecLessThan); - std::sort(dec.providedDYNs.begin(), dec.providedDYNs.end(), outputSpecLessThan); - std::sort(dec.providedTIMs.begin(), dec.providedTIMs.end(), outputSpecLessThan); + std::ranges::sort(dec.requestedDYNs, inputSpecLessThan); + std::ranges::sort(dec.requestedTIMs, inputSpecLessThan); + std::ranges::sort(dec.providedDYNs, outputSpecLessThan); + std::ranges::sort(dec.providedTIMs, outputSpecLessThan); DataProcessorSpec indexBuilder{ "internal-dpl-aod-index-builder", @@ -389,8 +388,8 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext AnalysisSupportHelpers::addMissingOutputsToSpawner({}, dec.spawnerInputs, dec.requestedAODs, aodSpawner); AnalysisSupportHelpers::addMissingOutputsToReader(dec.providedAODs, dec.requestedAODs, aodReader); - std::sort(requestedCCDBs.begin(), requestedCCDBs.end(), inputSpecLessThan); - std::sort(providedCCDBs.begin(), providedCCDBs.end(), outputSpecLessThan); + std::ranges::sort(requestedCCDBs, inputSpecLessThan); + std::ranges::sort(providedCCDBs, outputSpecLessThan); AnalysisSupportHelpers::addMissingOutputsToReader(providedCCDBs, requestedCCDBs, ccdbBackend); std::vector extraSpecs; @@ -412,7 +411,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext // add the reader if (aodReader.outputs.empty() == false) { - auto mctracks2aod = std::find_if(workflow.begin(), workflow.end(), [](auto const& x) { return x.name == "mctracks-to-aod"; }); + auto mctracks2aod = std::ranges::find_if(workflow, [](auto const& x) { return x.name == "mctracks-to-aod"; }); if (mctracks2aod == workflow.end()) { // add normal reader aodReader.outputs.emplace_back(OutputSpec{"TFN", "TFNumber"}); @@ -440,22 +439,22 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext auto& dstf = std::get(matcher.matcher); // Check if any of the provided outputs is a DISTSTF // Check if any of the requested inputs is for a 0xccdb message - bool providesDISTSTF = std::any_of(workflow.begin(), workflow.end(), - [&matcher](auto const& dp) { - return std::any_of(dp.outputs.begin(), dp.outputs.end(), [&matcher](auto const& output) { - return DataSpecUtils::match(matcher, output); - }); - }); + bool providesDISTSTF = std::ranges::any_of(workflow, + [&matcher](auto const& dp) { + return std::any_of(dp.outputs.begin(), dp.outputs.end(), [&matcher](auto const& output) { + return DataSpecUtils::match(matcher, output); + }); + }); // If there is no CCDB requested, but we still ask for a FLP/DISTSUBTIMEFRAME/0xccdb // we add to the first data processor which has no inputs (apart from // enumerations / timers) the responsibility to provide the DISTSUBTIMEFRAME - bool requiresDISTSUBTIMEFRAME = std::any_of(workflow.begin(), workflow.end(), - [&dstf](auto const& dp) { - return std::any_of(dp.inputs.begin(), dp.inputs.end(), [&dstf](auto const& input) { - return DataSpecUtils::match(input, dstf); - }); - }); + bool requiresDISTSUBTIMEFRAME = std::ranges::any_of(workflow, + [&dstf](auto const& dp) { + return std::any_of(dp.inputs.begin(), dp.inputs.end(), [&dstf](auto const& input) { + return DataSpecUtils::match(input, dstf); + }); + }); // We find the first device which has either just enumerations or // just timers, and we will add the DISTSUBTIMEFRAME to it. @@ -560,7 +559,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext auto fileSink = AnalysisSupportHelpers::getGlobalAODSink(ctx); extraSpecs.push_back(fileSink); - auto it = std::find_if(dec.outputsInputs.begin(), dec.outputsInputs.end(), [](InputSpec& spec) -> bool { + auto it = std::ranges::find_if(dec.outputsInputs, [](InputSpec& spec) -> bool { return DataSpecUtils::partialMatch(spec, o2::header::DataOrigin("TFN")); }); size_t ii = std::distance(dec.outputsInputs.begin(), it); @@ -694,15 +693,8 @@ void WorkflowHelpers::adjustTopology(WorkflowSpec& workflow, ConfigContext const } if (distSTFCount > 0) { - bool found = false; for (auto& spec : workflow) { - for (auto& output : spec.outputs) { - if (DataSpecUtils::match(output, ConcreteDataMatcher{"FLP", "DISTSUBTIMEFRAME", 0})) { - found = true; - break; - } - } - if (found) { + if (std::ranges::any_of(spec.outputs, [](auto const& output) { return DataSpecUtils::match(output, ConcreteDataMatcher{"FLP", "DISTSUBTIMEFRAME", 0}); })) { for (unsigned int i = 1; i < distSTFCount; ++i) { spec.outputs.emplace_back(OutputSpec{ConcreteDataMatcher{"FLP", "DISTSUBTIMEFRAME", i}, Lifetime::Timeframe}); } @@ -1005,7 +997,7 @@ std::tuple, std::vector> WorkflowHelpers::analyzeOu input.binding = (snprintf(buf, 63, "output_%zu_%zu", output.workflowId, output.id), buf); // make sure that entries are unique - if (std::find(results.begin(), results.end(), input) == results.end()) { + if (std::ranges::find(results, input) == results.end()) { results.emplace_back(input); isDangling.emplace_back(matched == false); } From 7cc3f1c550e70f8c6b919c7168f505ee13f75dc5 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Mon, 26 Jan 2026 11:22:10 +0100 Subject: [PATCH 014/133] DPL: move snapshot code to use concepts Should be faster to compile and have better debug information. It will also work seamlessly for gsl::span (assuming you have ms_gsl 4.2.1) and std::span. --- .../Core/include/Framework/DataAllocator.h | 243 ++++++++++-------- .../include/Framework/SerializationMethods.h | 13 +- 2 files changed, 143 insertions(+), 113 deletions(-) diff --git a/Framework/Core/include/Framework/DataAllocator.h b/Framework/Core/include/Framework/DataAllocator.h index 287513ec85845..ed9a31ca2857c 100644 --- a/Framework/Core/include/Framework/DataAllocator.h +++ b/Framework/Core/include/Framework/DataAllocator.h @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -29,9 +29,9 @@ #include "Headers/DataHeader.h" #include -#include #include +#include #include #include #include @@ -127,6 +127,10 @@ template concept VectorOfMessageableTypes = is_specialization_v && is_messageable::value; +template +concept ContiguousMessageablesRange = std::ranges::contiguous_range && + is_messageable::value; + /// This allocator is responsible to make sure that the messages created match /// the provided spec and that depending on how many pipelined reader we /// have, messages get created on the channel for the reader of the current @@ -296,8 +300,9 @@ class DataAllocator /// /// Supported types: /// - messageable types (trivially copyable, non-polymorphic - /// - std::vector of messageable types - /// - std::vector of pointers of messageable type + /// - contiguous_range of messageable types + /// - random_access_ranges of pointers of messageable type + /// - sized range of messageable type /// - types with ROOT dictionary and implementing the ROOT ClassDef interface /// /// Note: for many use cases, especially for the messageable types, the `make` interface @@ -308,116 +313,140 @@ class DataAllocator /// Use @a ROOTSerialized type wrapper to force ROOT serialization. Same applies to /// types which do not implement the ClassDef interface but have a dictionary. template + requires(!std::ranges::contiguous_range && is_messageable::value) + void snapshot(const Output& spec, T const& object) + { + return snapshot(spec, std::span(&object, &object + 1)); + } + + void snapshot(const Output& spec, std::string_view const& object) + { + return snapshot(spec, std::span(object.data(), object.size())); + } + + // This is for snapshotting a range of contiguous messageable types + template + requires(ContiguousMessageablesRange && !std::is_pointer_v) void snapshot(const Output& spec, T const& object) { auto& proxy = mRegistry.get().proxy(); - fair::mq::MessagePtr payloadMessage; - auto serializationType = o2::header::gSerializationMethodNone; RouteIndex routeIndex = matchDataHeader(spec, mRegistry.get().timeslice); - if constexpr (is_messageable::value == true) { - // Serialize a snapshot of a trivially copyable, non-polymorphic object, - payloadMessage = proxy.createOutputMessage(routeIndex, sizeof(T)); - memcpy(payloadMessage->GetData(), &object, sizeof(T)); - - serializationType = o2::header::gSerializationMethodNone; - } else if constexpr (is_specialization_v == true || - (gsl::details::is_span::value && has_messageable_value_type::value)) { - using ElementType = typename std::remove_pointer::type; - if constexpr (is_messageable::value) { - // Serialize a snapshot of a std::vector of trivially copyable, non-polymorphic elements - // Note: in most cases it is better to use the `make` function und work with the provided - // reference object - constexpr auto elementSizeInBytes = sizeof(ElementType); - auto sizeInBytes = elementSizeInBytes * object.size(); - payloadMessage = proxy.createOutputMessage(routeIndex, sizeInBytes); - - if constexpr (std::is_pointer::value == false) { - // vector of elements - if (object.data() && sizeInBytes) { - memcpy(payloadMessage->GetData(), object.data(), sizeInBytes); - } - } else { - // serialize vector of pointers to elements - auto target = reinterpret_cast(payloadMessage->GetData()); - for (auto const& pointer : object) { - memcpy(target, pointer, elementSizeInBytes); - target += elementSizeInBytes; - } - } - - serializationType = o2::header::gSerializationMethodNone; - } else if constexpr (has_root_dictionary::value) { - return snapshot(spec, ROOTSerialized(object)); - } else { - static_assert(always_static_assert_v, - "value type of std::vector not supported by API, supported types:" - "\n - messageable tyeps (trivially copyable, non-polymorphic structures)" - "\n - pointers to those" - "\n - types with ROOT dictionary and implementing ROOT ClassDef interface"); - } - } else if constexpr (is_container::value == true && has_messageable_value_type::value == true) { - // Serialize a snapshot of a std::container of trivially copyable, non-polymorphic elements - // Note: in most cases it is better to use the `make` function und work with the provided - // reference object - constexpr auto elementSizeInBytes = sizeof(typename T::value_type); - auto sizeInBytes = elementSizeInBytes * object.size(); - payloadMessage = proxy.createOutputMessage(routeIndex, sizeInBytes); - - // serialize vector of pointers to elements - auto target = reinterpret_cast(payloadMessage->GetData()); - for (auto const& entry : object) { - memcpy(target, (void*)&entry, elementSizeInBytes); - target += elementSizeInBytes; - } - serializationType = o2::header::gSerializationMethodNone; - } else if constexpr (has_root_dictionary::value == true || is_specialization_v == true) { - // Serialize a snapshot of an object with root dictionary - payloadMessage = proxy.createOutputMessage(routeIndex); - payloadMessage->Rebuild(4096, {64}); - if constexpr (is_specialization_v == true) { - // Explicitely ROOT serialize a snapshot of object. - // An object wrapped into type `ROOTSerialized` is explicitely marked to be ROOT serialized - // and is expected to have a ROOT dictionary. Availability can not be checked at compile time - // for all cases. - using WrappedType = typename T::wrapped_type; - static_assert(std::is_same::value || - std::is_same::value || - std::is_void::value, - "class hint must be of type TClass or const char"); - - const TClass* cl = nullptr; - if (object.getHint() == nullptr) { - // get TClass info by wrapped type - cl = TClass::GetClass(typeid(WrappedType)); - } else if (std::is_same::value) { - // the class info has been passed directly - cl = reinterpret_cast(object.getHint()); - } else if (std::is_same::value) { - // get TClass info by optional name - cl = TClass::GetClass(reinterpret_cast(object.getHint())); - } - if (has_root_dictionary::value == false && cl == nullptr) { - if (std::is_same::value) { - throw runtime_error_f("ROOT serialization not supported, dictionary not found for type %s", - reinterpret_cast(object.getHint())); - } else { - throw runtime_error_f("ROOT serialization not supported, dictionary not found for type %s", - typeid(WrappedType).name()); - } - } - typename root_serializer::serializer().Serialize(*payloadMessage, &object(), cl); + using ElementType = typename std::remove_pointer::type; + // Serialize a snapshot of a std::vector of trivially copyable, non-polymorphic elements + // Note: in most cases it is better to use the `make` function und work with the provided + // reference object + constexpr auto elementSizeInBytes = sizeof(ElementType); + auto sizeInBytes = elementSizeInBytes * object.size(); + fair::mq::MessagePtr payloadMessage = proxy.createOutputMessage(routeIndex, sizeInBytes); + + // vector of elements + if (object.data() && sizeInBytes) { + memcpy(payloadMessage->GetData(), object.data(), sizeInBytes); + } + + addPartToContext(routeIndex, std::move(payloadMessage), spec, o2::header::gSerializationMethodNone); + } + + // A random access range of pointers we can serialise by storing the contens one after the other. + // On the receiving side you will have to retrieve it via a span + template + requires(std::ranges::random_access_range && is_messageable>::value && std::is_pointer_v) + void snapshot(const Output& spec, T const& object) + { + auto& proxy = mRegistry.get().proxy(); + RouteIndex routeIndex = matchDataHeader(spec, mRegistry.get().timeslice); + using ElementType = typename std::remove_pointer_t; + // Serialize a snapshot of a std::vector of trivially copyable, non-polymorphic elements + // Note: in most cases it is better to use the `make` function und work with the provided + // reference object + constexpr auto elementSizeInBytes = sizeof(ElementType); + auto sizeInBytes = elementSizeInBytes * object.size(); + fair::mq::MessagePtr payloadMessage = proxy.createOutputMessage(routeIndex, sizeInBytes); + + // serialize vector of pointers to elements + auto target = reinterpret_cast(payloadMessage->GetData()); + for (auto const& pointer : object) { + memcpy(target, pointer, elementSizeInBytes); + target += elementSizeInBytes; + } + + addPartToContext(routeIndex, std::move(payloadMessage), spec, o2::header::gSerializationMethodNone); + } + + // This is for a range where we can know upfront how many elements there are, + // so that we can preallocate the final size by simply multipling sizeof(T) x N elements + template + requires(!std::ranges::contiguous_range && std::ranges::sized_range && has_messageable_value_type::value) + void snapshot(const Output& spec, T const& object) + { + auto& proxy = mRegistry.get().proxy(); + RouteIndex routeIndex = matchDataHeader(spec, mRegistry.get().timeslice); + // Serialize a snapshot of a std::container of trivially copyable, non-polymorphic elements + // Note: in most cases it is better to use the `make` function und work with the provided + // reference object + constexpr auto elementSizeInBytes = sizeof(typename T::value_type); + auto sizeInBytes = elementSizeInBytes * object.size(); + fair::mq::MessagePtr payloadMessage = proxy.createOutputMessage(routeIndex, sizeInBytes); + + // serialize vector of pointers to elements + auto target = reinterpret_cast(payloadMessage->GetData()); + for (auto const& entry : object) { + memcpy(target, (void*)&entry, elementSizeInBytes); + target += elementSizeInBytes; + } + addPartToContext(routeIndex, std::move(payloadMessage), spec, o2::header::gSerializationMethodNone); + } + + template + requires(is_specialization_v) + void snapshot(const Output& spec, T const& object) + { + auto& proxy = mRegistry.get().proxy(); + RouteIndex routeIndex = matchDataHeader(spec, mRegistry.get().timeslice); + // Serialize a snapshot of an object with root dictionary + fair::mq::MessagePtr payloadMessage = proxy.createOutputMessage(routeIndex); + payloadMessage->Rebuild(4096, {64}); + const TClass* cl = nullptr; + // Explicitely ROOT serialize a snapshot of object. + // An object wrapped into type `ROOTSerialized` is explicitely marked to be ROOT serialized + // and is expected to have a ROOT dictionary. Availability can not be checked at compile time + // for all cases. + using WrappedType = typename T::wrapped_type; + + if (object.getHint() == nullptr) { + // get TClass info by wrapped type + cl = TClass::GetClass(typeid(WrappedType)); + } else if (std::is_same::value) { + // the class info has been passed directly + cl = reinterpret_cast(object.getHint()); + } else if (std::is_same::value) { + // get TClass info by optional name + cl = TClass::GetClass(reinterpret_cast(object.getHint())); + } + if (has_root_dictionary::value == false && cl == nullptr) { + if (std::is_same::value) { + throw runtime_error_f("ROOT serialization not supported, dictionary not found for type %s", + reinterpret_cast(object.getHint())); } else { - typename root_serializer::serializer().Serialize(*payloadMessage, &object, TClass::GetClass(typeid(T))); + throw runtime_error_f("ROOT serialization not supported, dictionary not found for type %s", + typeid(WrappedType).name()); } - serializationType = o2::header::gSerializationMethodROOT; - } else { - static_assert(always_static_assert_v, - "data type T not supported by API, \n specializations available for" - "\n - trivially copyable, non-polymorphic structures" - "\n - std::vector of messageable structures or pointers to those" - "\n - types with ROOT dictionary and implementing ROOT ClassDef interface"); } - addPartToContext(routeIndex, std::move(payloadMessage), spec, serializationType); + typename root_serializer::serializer().Serialize(*payloadMessage, &object(), cl); + addPartToContext(routeIndex, std::move(payloadMessage), spec, o2::header::gSerializationMethodROOT); + } + + template + requires(!is_messageable::value && !ContiguousMessageablesRange && has_root_dictionary::value && !is_specialization_v) + void snapshot(const Output& spec, T const& object) + { + auto& proxy = mRegistry.get().proxy(); + RouteIndex routeIndex = matchDataHeader(spec, mRegistry.get().timeslice); + // Serialize a snapshot of an object with root dictionary + fair::mq::MessagePtr payloadMessage = proxy.createOutputMessage(routeIndex); + payloadMessage->Rebuild(4096, {64}); + typename root_serializer::serializer().Serialize(*payloadMessage, &object, TClass::GetClass(typeid(T))); + addPartToContext(routeIndex, std::move(payloadMessage), spec, o2::header::gSerializationMethodROOT); } /// Take a snapshot of a raw data array which can be either POD or may contain a serialized diff --git a/Framework/Core/include/Framework/SerializationMethods.h b/Framework/Core/include/Framework/SerializationMethods.h index 31b9d24013ab4..68c509d36905f 100644 --- a/Framework/Core/include/Framework/SerializationMethods.h +++ b/Framework/Core/include/Framework/SerializationMethods.h @@ -15,6 +15,7 @@ /// @brief Type wrappers for enfording a specific serialization method #include "Framework/TypeTraits.h" +#include namespace o2::framework { @@ -43,6 +44,9 @@ namespace o2::framework /// - or - /// ROOTSerialized(object, "classname")); template + requires(!std::is_pointer_v && (std::same_as || + std::same_as || + std::is_void_v)) class ROOTSerialized { public: @@ -50,9 +54,6 @@ class ROOTSerialized using wrapped_type = T; using hint_type = HintType; - static_assert(std::is_pointer::value == false, "wrapped type can not be a pointer"); - static_assert(std::is_pointer::value == false, "hint type can not be a pointer"); - ROOTSerialized() = delete; ROOTSerialized(wrapped_type& ref, hint_type* hint = nullptr) : mRef(ref), mHint(hint) {} @@ -67,6 +68,9 @@ class ROOTSerialized }; template + requires(!std::is_pointer_v && (std::same_as || + std::same_as || + std::is_void_v)) class CCDBSerialized { public: @@ -74,9 +78,6 @@ class CCDBSerialized using wrapped_type = T; using hint_type = HintType; - static_assert(std::is_pointer::value == false, "wrapped type can not be a pointer"); - static_assert(std::is_pointer::value == false, "hint type can not be a pointer"); - CCDBSerialized() = delete; CCDBSerialized(wrapped_type& ref, hint_type* hint = nullptr) : mRef(ref), mHint(hint) {} From c6b7d8bf9c997a631449da7a98ad7e37d67bc151 Mon Sep 17 00:00:00 2001 From: atriolo Date: Mon, 19 Jan 2026 16:33:45 +0100 Subject: [PATCH 015/133] ALICE3-TRK: added possibility to use a local response file during digitization --- .../ALICE3/TRK/base/include/TRKBase/Specs.h | 8 ++++++ .../include/TRKSimulation/Digitizer.h | 5 ++++ .../ALICE3/TRK/simulation/src/DigiParams.cxx | 3 +++ .../ALICE3/TRK/simulation/src/Digitizer.cxx | 25 +++++++++++++----- .../src/TRKDigitizerSpec.cxx | 26 +++++++++++++++++-- 5 files changed, 59 insertions(+), 8 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h index a5a60422f77eb..95f9f9b00d7f3 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h @@ -131,6 +131,14 @@ constexpr double responseYShift{15.5 * mu}; constexpr double thickness{45 * mu}; } // namespace apts +namespace alice3resp /// parameters for the alice3 chip response +{ +constexpr double pitchX{10.0 * mu}; +constexpr double pitchZ{10.0 * mu}; +constexpr double responseYShift{5 * mu}; /// center of the epitaxial layer +constexpr double thickness{20 * mu}; +} // namespace alice3resp + } // namespace o2::trk::constants #endif diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Digitizer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Digitizer.h index 221d7b342bf59..362de63fb8cb6 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Digitizer.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Digitizer.h @@ -45,6 +45,7 @@ class Digitizer void setDigits(std::vector* dig) { mDigits = dig; } void setMCLabels(o2::dataformats::MCTruthContainer* mclb) { mMCLabels = mclb; } void setROFRecords(std::vector* rec) { mROFRecords = rec; } + void setResponseName(const std::string& name) { mRespName = name; } o2::trk::DigiParams& getParams() { return (o2::trk::DigiParams&)mParams; } const o2::trk::DigiParams& getParams() const { return mParams; } @@ -136,6 +137,8 @@ class Digitizer uint32_t mROFrameMax = 0; ///< highest RO frame of current digits uint32_t mNewROFrame = 0; ///< ROFrame corresponding to provided time + bool mIsBeforeFirstRO = false; + uint32_t mEventROFrameMin = 0xffffffff; ///< lowest RO frame for processed events (w/o automatic noise ROFs) uint32_t mEventROFrameMax = 0; ///< highest RO frame forfor processed events (w/o automatic noise ROFs) @@ -145,6 +148,8 @@ class Digitizer const o2::trk::ChipSimResponse* mChipSimRespVD = nullptr; // simulated response for VD chips const o2::trk::ChipSimResponse* mChipSimRespMLOT = nullptr; // simulated response for ML/OT chips + std::string mRespName; /// APTS or ALICE3, depending on the response to be used + bool mSimRespOrientation{false}; // wether the orientation in the response function is flipped float mSimRespVDShift{0.f}; // adjusting the Y-shift in the APTS response function to match sensor local coord. float mSimRespVDScaleX{1.f}; // scale x-local coordinate to response function x-coordinate diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/DigiParams.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/DigiParams.cxx index ca4685d53de2a..e2a78702204e5 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/DigiParams.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/DigiParams.cxx @@ -74,6 +74,9 @@ void DigiParams::print() const void DigiParams::setAlpSimResponse(const o2::itsmft::AlpideSimResponse* resp) { + LOG(debug) << "Response function data path: " << resp->getDataPath(); + LOG(debug) << "Response function info: "; + // resp->print(); if (!resp) { LOGP(fatal, "cannot set response function from null"); } diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx index 7c988faebf2df..0fd8c7820ce28 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx @@ -49,7 +49,7 @@ void Digitizer::init() } } - // setting the correct response function (for the moment, for both VD and MLOT the APTS response function is udes) + // setting the correct response function (for the moment, for both VD and MLOT the same response function is used) mChipSimResp = mParams.getAlpSimResponse(); mChipSimRespVD = mChipSimResp; /// for the moment considering the same response mChipSimRespMLOT = mChipSimResp; /// for the moment considering the same response @@ -65,11 +65,24 @@ void Digitizer::init() float thicknessVD = 0.0095; // cm --- hardcoded based on geometry currently present float thicknessMLOT = o2::trk::SegmentationChip::SiliconThicknessMLOT; // 0.01 cm = 100 um --- based on geometry currently present - mSimRespVDScaleX = o2::trk::constants::apts::pitchX / o2::trk::SegmentationChip::PitchRowVD; - mSimRespVDScaleZ = o2::trk::constants::apts::pitchZ / o2::trk::SegmentationChip::PitchColVD; - mSimRespVDShift = mChipSimRespVD->getDepthMax(); // the curved, rescaled, sensors have a width from 0 to -45. Must add 10 um (= max depth) to match the APTS response. - mSimRespMLOTScaleX = o2::trk::constants::apts::pitchX / o2::trk::SegmentationChip::PitchRowMLOT; - mSimRespMLOTScaleZ = o2::trk::constants::apts::pitchZ / o2::trk::SegmentationChip::PitchColMLOT; + LOG(info) << "Using response name: " << mRespName; + + if (mRespName == "APTS") { // default + mSimRespVDScaleX = o2::trk::constants::apts::pitchX / o2::trk::SegmentationChip::PitchRowVD; + mSimRespVDScaleZ = o2::trk::constants::apts::pitchZ / o2::trk::SegmentationChip::PitchColVD; + mSimRespVDShift = mChipSimRespVD->getDepthMax(); // the curved, rescaled, sensors have a width from 0 to -45. Must add ~10 um (= max depth) to match the APTS response. + mSimRespMLOTScaleX = o2::trk::constants::apts::pitchX / o2::trk::SegmentationChip::PitchRowMLOT; + mSimRespMLOTScaleZ = o2::trk::constants::apts::pitchZ / o2::trk::SegmentationChip::PitchColMLOT; + } else if (mRespName == "ALICE3") { + mSimRespVDScaleX = o2::trk::constants::alice3resp::pitchX / o2::trk::SegmentationChip::PitchRowVD; + mSimRespVDScaleZ = o2::trk::constants::alice3resp::pitchZ / o2::trk::SegmentationChip::PitchColVD; + mSimRespVDShift = mChipSimRespVD->getDepthMax(); // the curved, rescaled, sensors have a width from 0 to -95 um. Must align the start of epi layer with the response function. + mSimRespMLOTScaleX = o2::trk::constants::alice3resp::pitchX / o2::trk::SegmentationChip::PitchRowMLOT; + mSimRespMLOTScaleZ = o2::trk::constants::alice3resp::pitchZ / o2::trk::SegmentationChip::PitchColMLOT; + } else { + LOG(fatal) << "Unknown response name: " << mRespName; + } + mSimRespMLOTShift = mChipSimRespMLOT->getDepthMax() - thicknessMLOT / 2.f; // the shift should be done considering the rescaling done to adapt to the wrong silicon thickness. TODO: remove the scaling factor for the depth when the silicon thickness match the simulated response mSimRespOrientation = false; diff --git a/Steer/DigitizerWorkflow/src/TRKDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/TRKDigitizerSpec.cxx index a3d4d1f245fc5..30f9d33983712 100644 --- a/Steer/DigitizerWorkflow/src/TRKDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/TRKDigitizerSpec.cxx @@ -34,6 +34,7 @@ #include #include +#include #include using namespace o2::framework; @@ -68,6 +69,7 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer void initDigitizerTask(framework::InitContext& ic) override { mDisableQED = ic.options().get("disable-qed"); + mLocalRespFile = ic.options().get("local-response-file"); } void run(framework::ProcessingContext& pc) @@ -200,6 +202,15 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer mFinished = true; } + void setLocalResponseFunction() + { + std::unique_ptr file(TFile::Open(mLocalRespFile.data(), "READ")); + if (!file) { + LOG(fatal) << "Cannot open response file " << mLocalRespFile; + } + mDigitizer.getParams().setAlpSimResponse((const o2::itsmft::AlpideSimResponse*)file->Get("response1")); + } + void updateTimeDependentParams(ProcessingContext& pc) { static bool initOnce{false}; @@ -267,7 +278,15 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer // } if (matcher == ConcreteDataMatcher(mOrigin, "APTSRESP", 0)) { LOG(info) << mID.getName() << " loaded APTSResponseData"; - mDigitizer.getParams().setAlpSimResponse((const o2::itsmft::AlpideSimResponse*)obj); + if (mLocalRespFile.empty()) { + LOG(info) << "Using CCDB/APTS response file"; + mDigitizer.getParams().setAlpSimResponse((const o2::itsmft::AlpideSimResponse*)obj); + mDigitizer.setResponseName("APTS"); + } else { + LOG(info) << "Response function will be loaded from local file: " << mLocalRespFile; + setLocalResponseFunction(); + mDigitizer.setResponseName("ALICE3"); + } } } @@ -275,6 +294,7 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer bool mWithMCTruth{true}; bool mFinished{false}; bool mDisableQED{false}; + std::string mLocalRespFile{""}; const o2::detectors::DetID mID{o2::detectors::DetID::TRK}; const o2::header::DataOrigin mOrigin{o2::header::gDataOriginTRK}; o2::trk::Digitizer mDigitizer{}; @@ -307,7 +327,9 @@ DataProcessorSpec getTRKDigitizerSpec(int channel, bool mctruth) return DataProcessorSpec{detStr + "Digitizer", inputs, makeOutChannels(detOrig, mctruth), AlgorithmSpec{adaptFromTask(mctruth)}, - Options{{"disable-qed", o2::framework::VariantType::Bool, false, {"disable QED handling"}}}}; + Options{ + {"disable-qed", o2::framework::VariantType::Bool, false, {"disable QED handling"}}, + {"local-response-file", o2::framework::VariantType::String, "", {"use response file saved locally at this path/filename"}}}}; } } // namespace o2::trk From a09a567d02caea8d4d10219dd82243f72144c4a5 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Tue, 27 Jan 2026 14:50:19 +0100 Subject: [PATCH 016/133] DPL Analysis: Use dangling edges context in more places (#14988) --- .../AnalysisSupport/src/AODWriterHelpers.cxx | 21 +++-- .../CCDBSupport/src/AnalysisCCDBHelpers.cxx | 58 +++++++------- .../CCDBSupport/src/AnalysisCCDBHelpers.h | 2 +- .../Core/include/Framework/AnalysisTask.h | 4 +- Framework/Core/src/AnalysisSupportHelpers.cxx | 4 +- Framework/Core/src/ArrowSupport.cxx | 27 +------ Framework/Core/src/WorkflowHelpers.cxx | 79 +++++++++---------- Framework/Core/src/WorkflowHelpers.h | 3 + 8 files changed, 87 insertions(+), 111 deletions(-) diff --git a/Framework/AnalysisSupport/src/AODWriterHelpers.cxx b/Framework/AnalysisSupport/src/AODWriterHelpers.cxx index d868b7498fb76..b76ffca13977e 100644 --- a/Framework/AnalysisSupport/src/AODWriterHelpers.cxx +++ b/Framework/AnalysisSupport/src/AODWriterHelpers.cxx @@ -62,13 +62,13 @@ const static std::unordered_map ROOTfileNa AlgorithmSpec AODWriterHelpers::getOutputTTreeWriter(ConfigContext const& ctx) { - auto& ac = ctx.services().get(); auto dod = AnalysisSupportHelpers::getDataOutputDirector(ctx); int compressionLevel = 505; if (ctx.options().hasOption("aod-writer-compression")) { compressionLevel = ctx.options().get("aod-writer-compression"); } - return AlgorithmSpec{[dod, outputInputs = ac.outputsInputsAOD, compressionLevel](InitContext& ic) -> std::function { + return AlgorithmSpec{[dod, compressionLevel](InitContext& ic) -> std::function { + auto outputInputs = ic.services().get().outputsInputsAOD; LOGP(debug, "======== getGlobalAODSink::Init =========="); // find out if any table needs to be saved @@ -241,14 +241,13 @@ AlgorithmSpec AODWriterHelpers::getOutputTTreeWriter(ConfigContext const& ctx) }; } -AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& ctx) +AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& /*ctx*/) { - using namespace monitoring; - auto& ac = ctx.services().get(); - auto tskmap = ac.outTskMap; - auto objmap = ac.outObjHistMap; - - return AlgorithmSpec{[objmap, tskmap](InitContext& ic) -> std::function { + return AlgorithmSpec{[](InitContext& ic) -> std::function { + using namespace monitoring; + auto& dec = ic.services().get(); + auto tskmap = dec.outTskMap; + auto objmap = dec.outObjHistMap; auto& callbacks = ic.services().get(); auto inputObjects = std::make_shared>>(); @@ -278,7 +277,7 @@ AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& ctx) callbacks.set(endofdatacb); return [inputObjects, objmap, tskmap](ProcessingContext& pc) mutable -> void { - auto mergePart = [&inputObjects, &objmap, &tskmap, &pc](DataRef const& ref) { + auto mergePart = [&inputObjects, &objmap, &tskmap](DataRef const& ref) { O2_SIGNPOST_ID_GENERATE(hid, histogram_registry); O2_SIGNPOST_START(histogram_registry, hid, "mergePart", "Merging histogram"); if (!ref.header) { @@ -474,7 +473,7 @@ AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& ctx) }; O2_SIGNPOST_ID_GENERATE(rid, histogram_registry); O2_SIGNPOST_START(histogram_registry, rid, "processParts", "Start merging %zu parts received together.", pc.inputs().getNofParts(0)); - for (int pi = 0; pi < pc.inputs().getNofParts(0); ++pi) { + for (auto pi = 0U; pi < pc.inputs().getNofParts(0); ++pi) { mergePart(pc.inputs().get("x", pi)); } O2_SIGNPOST_END(histogram_registry, rid, "processParts", "Done histograms in multipart message."); diff --git a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx index 9ec911518f754..ea13d412cd0b8 100644 --- a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx +++ b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx @@ -67,38 +67,38 @@ void fillValidRoutes(CCDBFetcherHelper& helper, std::vector(); - std::vector> schemas; - auto schemaMetadata = std::make_shared(); + return adaptStateful([](ConfigParamRegistry const& options, DeviceSpec const& spec, InitContext& ic) { + auto& dec = ic.services().get(); + std::vector> schemas; + auto schemaMetadata = std::make_shared(); - for (auto& input : ac.analysisCCDBInputs) { - std::vector> fields; - schemaMetadata->Append("outputRoute", DataSpecUtils::describe(input)); - schemaMetadata->Append("outputBinding", input.binding); + for (auto& input : dec.analysisCCDBInputs) { + std::vector> fields; + schemaMetadata->Append("outputRoute", DataSpecUtils::describe(input)); + schemaMetadata->Append("outputBinding", input.binding); - for (auto& m : input.metadata) { - // Save the list of input tables - if (m.name.starts_with("input:")) { - auto name = m.name.substr(6); - schemaMetadata->Append("sourceTable", name); - schemaMetadata->Append("sourceMatcher", DataSpecUtils::describe(std::get(DataSpecUtils::fromMetadataString(m.defaultValue.get()).matcher))); - continue; - } - // Ignore the non ccdb: entries - if (!m.name.starts_with("ccdb:")) { - continue; + for (auto& m : input.metadata) { + // Save the list of input tables + if (m.name.starts_with("input:")) { + auto name = m.name.substr(6); + schemaMetadata->Append("sourceTable", name); + continue; + } + // Ignore the non ccdb: entries + if (!m.name.starts_with("ccdb:")) { + continue; + } + // Create the schema of the output + auto metadata = std::make_shared(); + metadata->Append("url", m.defaultValue.asString()); + auto columnName = m.name.substr(strlen("ccdb:")); + fields.emplace_back(std::make_shared(columnName, arrow::binary_view(), false, metadata)); } - // Create the schema of the output - auto metadata = std::make_shared(); - metadata->Append("url", m.defaultValue.asString()); - auto columnName = m.name.substr(strlen("ccdb:")); - fields.emplace_back(std::make_shared(columnName, arrow::binary_view(), false, metadata)); + schemas.emplace_back(std::make_shared(fields, schemaMetadata)); } - schemas.emplace_back(std::make_shared(fields, schemaMetadata)); - } - return adaptStateful([schemas](CallbackService& callbacks, ConfigParamRegistry const& options, DeviceSpec const& spec) { + std::shared_ptr helper = std::make_shared(); CCDBFetcherHelper::initialiseHelper(*helper, options); std::unordered_map bindings; @@ -129,11 +129,11 @@ AlgorithmSpec AnalysisCCDBHelpers::fetchFromCCDB(ConfigContext const& ctx) int outputRouteIndex = bindings.at(outRouteDesc); auto& spec = helper->routes[outputRouteIndex].matcher; std::vector> builders; - for (auto& _ : schema->fields()) { + for (auto const& _ : schema->fields()) { builders.emplace_back(std::make_shared()); } - for (size_t ci = 0; ci < timestampColumn->num_chunks(); ++ci) { + for (auto ci = 0; ci < timestampColumn->num_chunks(); ++ci) { std::shared_ptr chunk = timestampColumn->chunk(ci); auto const* timestamps = chunk->data()->GetValuesSafe(1); diff --git a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.h b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.h index f8175034da0ba..3be2138bd2b5c 100644 --- a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.h +++ b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.h @@ -17,7 +17,7 @@ namespace o2::framework { struct AnalysisCCDBHelpers { - static AlgorithmSpec fetchFromCCDB(ConfigContext const& ctx); + static AlgorithmSpec fetchFromCCDB(ConfigContext const&); }; } // namespace o2::framework diff --git a/Framework/Core/include/Framework/AnalysisTask.h b/Framework/Core/include/Framework/AnalysisTask.h index c50b5358990de..4f8a9e719e4b9 100644 --- a/Framework/Core/include/Framework/AnalysisTask.h +++ b/Framework/Core/include/Framework/AnalysisTask.h @@ -521,7 +521,7 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) std::vector expressionInfos; /// make sure options and configurables are set before expression infos are created - homogeneous_apply_refs([&options, &hash](auto& element) { return analysis_task_parsers::appendOption(options, element); }, *task.get()); + homogeneous_apply_refs([&options](auto& element) { return analysis_task_parsers::appendOption(options, element); }, *task.get()); /// extract conditions and append them as inputs homogeneous_apply_refs([&inputs](auto& element) { return analysis_task_parsers::appendCondition(inputs, element); }, *task.get()); @@ -620,7 +620,7 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) } // reset pre-slice for the next dataframe auto slices = pc.services().get(); - homogeneous_apply_refs([&pc, &slices](auto& element) { + homogeneous_apply_refs([&slices](auto& element) { return analysis_task_parsers::updateSliceInfo(element, slices); }, *(task.get())); diff --git a/Framework/Core/src/AnalysisSupportHelpers.cxx b/Framework/Core/src/AnalysisSupportHelpers.cxx index 15b56f9afbff5..7453751315626 100644 --- a/Framework/Core/src/AnalysisSupportHelpers.cxx +++ b/Framework/Core/src/AnalysisSupportHelpers.cxx @@ -98,7 +98,7 @@ std::shared_ptr AnalysisSupportHelpers::getDataOutputDirecto if (!keepString.empty()) { dod->reset(); std::string d("dangling"); - if (d.find(keepString) == 0) { + if (keepString.starts_with(d)) { // use the dangling outputs std::vector danglingOutputs; for (auto ii = 0u; ii < OutputsInputs.size(); ii++) { @@ -144,7 +144,7 @@ void AnalysisSupportHelpers::addMissingOutputsToSpawner(std::vector sinks::append_to{publisher.outputs}; // append them to the publisher outputs std::vector additionalInputs; - for (auto& input : requestedSpecials | views::filter_not_matching(providedSpecials)) { + for (auto const& input : requestedSpecials | views::filter_not_matching(providedSpecials)) { input.metadata | views::filter_string_params_with("input:") | views::params_to_input_specs() | diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index 60277dfc38a74..31cddc9803d69 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -685,33 +685,8 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() } } - // replace writer as some outputs may have become dangling and some are now consumed - auto [outputsInputs, isDangling] = WorkflowHelpers::analyzeOutputs(workflow); - - // create DataOutputDescriptor - std::shared_ptr dod = AnalysisSupportHelpers::getDataOutputDirector(ctx); - - // select outputs of type AOD which need to be saved - // ATTENTION: if there are dangling outputs the getGlobalAODSink - // has to be created in any case! - dec.outputsInputsAOD.clear(); - - for (auto ii = 0u; ii < outputsInputs.size(); ii++) { - if (DataSpecUtils::partialMatch(outputsInputs[ii], extendedAODOrigins)) { - auto ds = dod->getDataOutputDescriptors(outputsInputs[ii]); - if (!ds.empty() || isDangling[ii]) { - dec.outputsInputsAOD.emplace_back(outputsInputs[ii]); - } - } - } + WorkflowHelpers::injectAODWriter(workflow, ctx); - // file sink for any AOD output - if (!dec.outputsInputsAOD.empty()) { - // add TFNumber and TFFilename as input to the writer - dec.outputsInputsAOD.emplace_back("tfn", "TFN", "TFNumber"); - dec.outputsInputsAOD.emplace_back("tff", "TFF", "TFFilename"); - workflow.push_back(AnalysisSupportHelpers::getGlobalAODSink(ctx)); - } // Move the dummy sink at the end, if needed for (size_t i = 0; i < workflow.size(); ++i) { if (workflow[i].name == "internal-dpl-injected-dummy-sink") { diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index ff1ff1f4cf13d..714706952d26c 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -420,10 +420,10 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext // AODs are being injected on-the-fly, add error-handler reader aodReader.algorithm = AlgorithmSpec{ adaptStateful( - [outputs = aodReader.outputs](DeviceSpec const&) { + [](DeviceSpec const& spec) { LOGP(warn, "Workflow with injected AODs has unsatisfied inputs:"); - for (auto const& output : outputs) { - LOGP(warn, " {}", DataSpecUtils::describe(output)); + for (auto const& output : spec.outputs) { + LOGP(warn, " {}", DataSpecUtils::describe(output.matcher)); } LOGP(fatal, "Stopping."); // to ensure the output type for adaptStateful @@ -531,43 +531,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext workflow.insert(workflow.end(), extraSpecs.begin(), extraSpecs.end()); extraSpecs.clear(); - /// Analyze all ouputs - auto [outputsInputsTmp, isDanglingTmp] = analyzeOutputs(workflow); - dec.isDangling = isDanglingTmp; - dec.outputsInputs = outputsInputsTmp; - - // create DataOutputDescriptor - std::shared_ptr dod = AnalysisSupportHelpers::getDataOutputDirector(ctx); - - // select outputs of type AOD which need to be saved - // ATTENTION: if there are dangling outputs the getGlobalAODSink - // has to be created in any case! - for (auto ii = 0u; ii < dec.outputsInputs.size(); ii++) { - if (DataSpecUtils::partialMatch(dec.outputsInputs[ii], extendedAODOrigins)) { - auto ds = dod->getDataOutputDescriptors(dec.outputsInputs[ii]); - if (ds.size() > 0 || dec.isDangling[ii]) { - dec.outputsInputsAOD.emplace_back(dec.outputsInputs[ii]); - } - } - } - - // file sink for any AOD output - if (dec.outputsInputsAOD.size() > 0) { - // add TFNumber and TFFilename as input to the writer - dec.outputsInputsAOD.emplace_back(InputSpec{"tfn", "TFN", "TFNumber"}); - dec.outputsInputsAOD.emplace_back(InputSpec{"tff", "TFF", "TFFilename"}); - auto fileSink = AnalysisSupportHelpers::getGlobalAODSink(ctx); - extraSpecs.push_back(fileSink); - - auto it = std::ranges::find_if(dec.outputsInputs, [](InputSpec& spec) -> bool { - return DataSpecUtils::partialMatch(spec, o2::header::DataOrigin("TFN")); - }); - size_t ii = std::distance(dec.outputsInputs.begin(), it); - dec.isDangling[ii] = false; - } - - workflow.insert(workflow.end(), extraSpecs.begin(), extraSpecs.end()); - extraSpecs.clear(); + injectAODWriter(workflow, ctx); // Select dangling outputs which are not of type AOD std::vector redirectedOutputsInputs; @@ -704,6 +668,41 @@ void WorkflowHelpers::adjustTopology(WorkflowSpec& workflow, ConfigContext const } } +void WorkflowHelpers::injectAODWriter(WorkflowSpec& workflow, ConfigContext const& ctx) +{ + auto& dec = ctx.services().get(); + /// Analyze all ouputs + std::tie(dec.outputsInputs, dec.isDangling) = analyzeOutputs(workflow); + + // create DataOutputDescriptor + std::shared_ptr dod = AnalysisSupportHelpers::getDataOutputDirector(ctx); + + // select outputs of type AOD which need to be saved + dec.outputsInputsAOD.clear(); + for (auto ii = 0u; ii < dec.outputsInputs.size(); ii++) { + if (DataSpecUtils::partialMatch(dec.outputsInputs[ii], extendedAODOrigins)) { + auto ds = dod->getDataOutputDescriptors(dec.outputsInputs[ii]); + if (ds.size() > 0 || dec.isDangling[ii]) { + dec.outputsInputsAOD.emplace_back(dec.outputsInputs[ii]); + } + } + } + + // file sink for any AOD output + if (dec.outputsInputsAOD.size() > 0) { + // add TFNumber and TFFilename as input to the writer + DataSpecUtils::updateInputList(dec.outputsInputsAOD, InputSpec{"tfn", "TFN", "TFNumber"}); + DataSpecUtils::updateInputList(dec.outputsInputsAOD, InputSpec{"tff", "TFF", "TFFilename"}); + auto fileSink = AnalysisSupportHelpers::getGlobalAODSink(ctx); + workflow.push_back(fileSink); + + auto it = std::find_if(dec.outputsInputs.begin(), dec.outputsInputs.end(), [](InputSpec const& spec) -> bool { + return DataSpecUtils::partialMatch(spec, o2::header::DataOrigin("TFN")); + }); + dec.isDangling[std::distance(dec.outputsInputs.begin(), it)] = false; + } +} + void WorkflowHelpers::constructGraph(const WorkflowSpec& workflow, std::vector& logicalEdges, std::vector& outputs, diff --git a/Framework/Core/src/WorkflowHelpers.h b/Framework/Core/src/WorkflowHelpers.h index b2a4d4cab55df..5c0aa363c6d67 100644 --- a/Framework/Core/src/WorkflowHelpers.h +++ b/Framework/Core/src/WorkflowHelpers.h @@ -182,6 +182,9 @@ struct WorkflowHelpers { // @a ctx the context for the configuration phase static void injectServiceDevices(WorkflowSpec& workflow, ConfigContext& ctx); + // Function to correctly add AOD writer + static void injectAODWriter(WorkflowSpec& workflow, ConfigContext const& ctx); + // Final adjustments to @a workflow after service devices have been injected. static void adjustTopology(WorkflowSpec& workflow, ConfigContext const& ctx); From efad2290e1efb36f28e1c84c7e062525a919eb76 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Tue, 27 Jan 2026 13:39:03 +0100 Subject: [PATCH 017/133] ITSMFT: fix number of rofs per TF Signed-off-by: Felix Schlepper --- Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx index 6809c8dee3f19..eafb72c675a58 100644 --- a/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx @@ -184,8 +184,10 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer // it can happen that in the digitization rofs without contributing hits are skipped // however downstream consumers of the clusters cannot know apriori the time structure // the cluster rofs do not account for the bias so it will start always at BC=0 - std::vector expDigitRofVec(nROFsTF); - for (int iROF{0}; iROF < nROFsTF; ++iROF) { + // also have to account for spillage into next TF + const size_t nROFsLayer = std::max((size_t)nROFsTF, mROFRecordsAccum[iLayer].size()); + std::vector expDigitRofVec(nROFsLayer); + for (int iROF{0}; iROF < nROFsLayer; ++iROF) { auto& rof = expDigitRofVec[iROF]; int orb = iROF * DPLAlpideParam::Instance().getROFLengthInBC(iLayer) / o2::constants::lhc::LHCMaxBunches + mFirstOrbitTF; int bc = iROF * DPLAlpideParam::Instance().getROFLengthInBC(iLayer) % o2::constants::lhc::LHCMaxBunches; @@ -204,7 +206,7 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer expROF.setFirstEntry(rof.getFirstEntry()); expROF.setNEntries(rof.getNEntries()); if (expROF.getBCData() != rof.getBCData()) { - LOGP(fatal, "detected mismatch between expected ROF:{} and received ROF:{}", expROF.asString(), rof.asString()); + LOGP(fatal, "detected mismatch between expected {} and received {}", expROF.asString(), rof.asString()); } } int prevFirst{0}; @@ -214,6 +216,9 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer } prevFirst = rof.getFirstEntry(); } + // if more rofs where accumulated than ROFs possible in the TF, cut them away + // by construction expDigitRofVec is at least nROFsTF long + expDigitRofVec.resize(nROFsTF); pc.outputs().snapshot(Output{Origin, "DIGITSROF", iLayer}, expDigitRofVec); } else { pc.outputs().snapshot(Output{Origin, "DIGITSROF", iLayer}, mROFRecordsAccum[iLayer]); From 0cf7ec22173e7817599b997d189328ba65ecabc4 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Tue, 27 Jan 2026 21:10:07 +0100 Subject: [PATCH 018/133] GPU CMake: Improve architecture auto-detection --- dependencies/FindO2GPU.cmake | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/dependencies/FindO2GPU.cmake b/dependencies/FindO2GPU.cmake index 6ca311905e01c..21e2d7cad239a 100644 --- a/dependencies/FindO2GPU.cmake +++ b/dependencies/FindO2GPU.cmake @@ -10,7 +10,12 @@ # or submit itself to any jurisdiction. # NOTE!!!! - Whenever this file is changed, move it over to alidist/resources -# FindO2GPU.cmake Version 8 +# FindO2GPU.cmake Version 9 + +set(CUDA_COMPUTETARGET_DEFAULT_FULL 80-real 86-real 89-real 120-real 75-virtual) +set(HIP_AMDGPUTARGET_DEFAULT_FULL gfx906;gfx908) +set(CUDA_COMPUTETARGET_DEFAULT_MINIMAL 75-virtual) +set(HIP_AMDGPUTARGET_DEFAULT_MINIMAL gfx906) if(NOT DEFINED ENABLE_CUDA) set(ENABLE_CUDA "AUTO") @@ -32,11 +37,11 @@ if(CMAKE_BUILD_TYPE_UPPER STREQUAL "DEBUG") endif() if(CUDA_COMPUTETARGET AND CUDA_COMPUTETARGET STREQUAL "default") - set(CUDA_COMPUTETARGET 80-real 86-real 89-real 120-real 75-virtual) + set(CUDA_COMPUTETARGET ${CUDA_COMPUTETARGET_DEFAULT_FULL}) endif() if(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET STREQUAL "default") - set(HIP_AMDGPUTARGET gfx906;gfx908) + set(HIP_AMDGPUTARGET ${HIP_AMDGPUTARGET_DEFAULT_FULL}) endif() function(set_target_cuda_arch target) @@ -112,7 +117,7 @@ if(ENABLE_CUDA) if(CUDA_COMPUTETARGET) set(CMAKE_CUDA_ARCHITECTURES ${CUDA_COMPUTETARGET}) else() - set(CMAKE_CUDA_ARCHITECTURES 75-virtual) + set(O2_GPU_CUDA_UPDATE_NATIVE_ARCHITECTURE 1) endif() set(CMAKE_CUDA_STANDARD ${CMAKE_CXX_STANDARD}) set(CMAKE_CUDA_STANDARD_REQUIRED TRUE) @@ -156,6 +161,13 @@ if(ENABLE_CUDA) set(CMAKE_CUDA_COMPILER OFF) endif() endif() + if(NOT CMAKE_CUDA_ARCHITECTURES OR O2_GPU_CUDA_UPDATE_NATIVE_ARCHITECTURE) + if(NOT CMAKE_CUDA_ARCHITECTURES_NATIVE STREQUAL "") + set(CMAKE_CUDA_ARCHITECTURES ${CMAKE_CUDA_ARCHITECTURES_NATIVE}) + else() + set(CMAKE_CUDA_ARCHITECTURES ${CUDA_COMPUTETARGET_DEFAULT_MINIMAL}) + endif() + endif() if(CMAKE_CUDA_COMPILER) set(CMAKE_CUDA_FLAGS "-Xcompiler \"${O2_GPU_CMAKE_CXX_FLAGS_NOSTD}\" ${CMAKE_CUDA_FLAGS} --expt-relaxed-constexpr --extended-lambda -Xcompiler -Wno-attributes -Wno-deprecated-gpu-targets ${GPUCA_CUDA_DENORMALS_FLAGS}") set(CMAKE_CUDA_FLAGS_${CMAKE_BUILD_TYPE_UPPER} "-Xcompiler \"${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}\" ${CMAKE_CUDA_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}") @@ -184,7 +196,7 @@ if(ENABLE_CUDA) endif() set(CUDA_ENABLED ON) - message(STATUS "CUDA found (Version ${CMAKE_CUDA_COMPILER_VERSION})") + message(STATUS "CUDA found (Version ${CMAKE_CUDA_COMPILER_VERSION}, Architectures ${CMAKE_CUDA_ARCHITECTURES})") elseif(NOT ENABLE_CUDA STREQUAL "AUTO") message(FATAL_ERROR "CUDA not found (Compiler: ${CMAKE_CUDA_COMPILER})") else() @@ -305,7 +317,6 @@ if(ENABLE_HIP) if(hip_FOUND AND hipcub_FOUND AND rocthrust_FOUND AND rocprim_FOUND AND hip_HIPCC_EXECUTABLE AND hip_HIPIFY_PERL_EXECUTABLE) set(HIP_ENABLED ON) set_target_properties(roc::rocthrust PROPERTIES IMPORTED_GLOBAL TRUE) - message(STATUS "HIP Found (${hip_HIPCC_EXECUTABLE} version ${hip_VERSION})") set(CMAKE_HIP_FLAGS "${O2_GPU_CMAKE_CXX_FLAGS_NOSTD} ${CMAKE_HIP_FLAGS} ${GPUCA_HIP_DENORMALS_FLAGS}") set(CMAKE_HIP_FLAGS_${CMAKE_BUILD_TYPE_UPPER} "${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPER}} ${CMAKE_HIP_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}") string(APPEND CMAKE_HIP_FLAGS " -fgpu-defer-diag -mllvm -amdgpu-enable-lower-module-lds=false -mllvm -amdgpu-function-calls=true -Wno-invalid-command-line-argument -Wno-unused-command-line-argument -Wno-invalid-constexpr -Wno-ignored-optimization-argument -Wno-unused-private-field -Wno-pass-failed ") @@ -321,6 +332,7 @@ if(ENABLE_HIP) if(HIP_AMDGPUTARGET) set(CMAKE_HIP_ARCHITECTURES "${HIP_AMDGPUTARGET}") endif() + message(STATUS "HIP Found (${hip_HIPCC_EXECUTABLE} version ${hip_VERSION}, Architectures ${CMAKE_HIP_ARCHITECTURES})") else() set(HIP_ENABLED OFF) endif() From cb66b5edfc8322bc792b255368e52a897066c76a Mon Sep 17 00:00:00 2001 From: shahor02 Date: Wed, 28 Jan 2026 09:53:51 +0100 Subject: [PATCH 019/133] Add extra info with charge and timing and occupancy to unbinned residuals (#14969) * Add extra info with charge and timing to unbinned residuals * Store TOF time wrt t0 in DetInfoRes, diff to expectation in trackData.deltaTOF * Add per-stack TPC mult info to TrackData --- .../tpcinterpolationworkflow/CMakeLists.txt | 2 + .../TPCResidualAggregatorSpec.h | 6 +- .../TPCUnbinnedResidualReaderSpec.h | 1 + .../src/TPCInterpolationSpec.cxx | 15 +- .../src/TPCResidualWriterSpec.cxx | 1 + .../src/TPCUnbinnedResidualReaderSpec.cxx | 7 + .../calibration/SpacePoints/CMakeLists.txt | 3 +- .../include/SpacePoints/ResidualAggregator.h | 3 +- .../include/SpacePoints/TrackInterpolation.h | 95 ++++++++++- .../SpacePoints/src/ResidualAggregator.cxx | 10 +- .../SpacePoints/src/SpacePointCalibLinkDef.h | 2 + .../SpacePoints/src/TrackInterpolation.cxx | 154 ++++++++++++++++-- 12 files changed, 269 insertions(+), 30 deletions(-) diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/CMakeLists.txt b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/CMakeLists.txt index c8db0209d4471..09ec6081b06b8 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/CMakeLists.txt +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/CMakeLists.txt @@ -9,6 +9,8 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +# add_compile_options(-O0 -g -fPIC -fno-omit-frame-pointer) + o2_add_library(TPCInterpolationWorkflow SOURCES src/TPCInterpolationSpec.cxx src/TPCResidualWriterSpec.cxx diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCResidualAggregatorSpec.h b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCResidualAggregatorSpec.h index b9c99f9e65676..99f20e390a09a 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCResidualAggregatorSpec.h +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCResidualAggregatorSpec.h @@ -128,8 +128,9 @@ class ResidualAggregatorDevice : public o2::framework::Task updateTimeDependentParams(pc); std::chrono::duration ccdbUpdateTime = std::chrono::high_resolution_clock::now() - runStartTime; - // we always require the unbinned residuals and the associated track references + // we always require the unbinned residuals and the associated detector info and track references auto residualsData = pc.inputs().get>("unbinnedRes"); + auto residualsDataDet = pc.inputs().get>("detinfoRes"); auto trackRefs = pc.inputs().get>("trackRefs"); // track data input is optional @@ -151,7 +152,7 @@ class ResidualAggregatorDevice : public o2::framework::Task o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mAggregator->getCurrentTFInfo()); LOG(detail) << "Processing TF " << mAggregator->getCurrentTFInfo().tfCounter << " with " << trkData->size() << " tracks and " << residualsData.size() << " unbinned residuals associated to them"; - mAggregator->process(residualsData, trackRefs, trkDataPtr, lumi); + mAggregator->process(residualsData, residualsDataDet, trackRefs, trkDataPtr, lumi); std::chrono::duration runDuration = std::chrono::high_resolution_clock::now() - runStartTime; LOGP(debug, "Duration for run method: {} ms. From this taken for time dependent param update: {} ms", std::chrono::duration_cast(runDuration).count(), @@ -222,6 +223,7 @@ DataProcessorSpec getTPCResidualAggregatorSpec(bool trackInput, bool ctpInput, b auto& inputs = dataRequest->inputs; o2::tpc::VDriftHelper::requestCCDBInputs(inputs); inputs.emplace_back("unbinnedRes", "GLO", "UNBINNEDRES"); + inputs.emplace_back("detinfoRes", "GLO", "DETINFORES"); inputs.emplace_back("trackRefs", "GLO", "TRKREFS"); if (trackInput) { inputs.emplace_back("trkData", "GLO", "TRKDATA"); diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCUnbinnedResidualReaderSpec.h b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCUnbinnedResidualReaderSpec.h index 6c40bb355eb21..724151c90576f 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCUnbinnedResidualReaderSpec.h +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCUnbinnedResidualReaderSpec.h @@ -43,6 +43,7 @@ class TPCUnbinnedResidualReader : public o2::framework::Task std::string mInFileName; std::string mInTreeName; std::vector mUnbinnedResid, *mUnbinnedResidPtr = &mUnbinnedResid; + std::vector mDetInfoUnbRes, *mDetInfoUnbResPtr = &mDetInfoUnbRes; std::vector mTrackData, *mTrackDataPtr = &mTrackData; std::vector mTrackDataCompact, *mTrackDataCompactPtr = &mTrackDataCompact; }; diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx index da2fcaab913d7..4912a1df36a33 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx @@ -66,11 +66,12 @@ void TPCInterpolationDPL::updateTimeDependentParams(ProcessingContext& pc) initOnceDone = true; // other init-once stuff const auto& param = SpacePointsCalibConfParam::Instance(); + mInterpolation.setSqrtS(o2::base::GRPGeomHelper::instance().getGRPLHCIF()->getSqrtS()); + mInterpolation.setNHBPerTF(o2::base::GRPGeomHelper::getNHBFPerTF()); mInterpolation.init(mSources, mSourcesMap); if (mProcessITSTPConly) { mInterpolation.setProcessITSTPConly(); } - mInterpolation.setSqrtS(o2::base::GRPGeomHelper::instance().getGRPLHCIF()->getSqrtS()); int nTfs = mSlotLength / (o2::base::GRPGeomHelper::getNHBFPerTF() * o2::constants::lhc::LHCOrbitMUS * 1e-6); bool limitTracks = (param.maxTracksPerCalibSlot < 0) ? false : true; int nTracksPerTfMax = (nTfs > 0 && limitTracks) ? param.maxTracksPerCalibSlot / nTfs : -1; @@ -93,6 +94,11 @@ void TPCInterpolationDPL::updateTimeDependentParams(ProcessingContext& pc) mInterpolation.setProcessSeeds(); } o2::its::GeometryTGeo::Instance()->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2GRot) | o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L)); + mInterpolation.setExtDetResid(mExtDetResid); + mInterpolation.setITSClusterDictionary(mITSDict); + if (mDebugOutput) { + mInterpolation.setDumpTrackPoints(); + } } // we may have other params which need to be queried regularly if (mTPCVDriftHelper.isUpdated()) { @@ -103,11 +109,6 @@ void TPCInterpolationDPL::updateTimeDependentParams(ProcessingContext& pc) mInterpolation.setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); mTPCVDriftHelper.acknowledgeUpdate(); } - if (mDebugOutput) { - mInterpolation.setDumpTrackPoints(); - } - mInterpolation.setExtDetResid(mExtDetResid); - mInterpolation.setITSClusterDictionary(mITSDict); } void TPCInterpolationDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) @@ -143,6 +144,7 @@ void TPCInterpolationDPL::run(ProcessingContext& pc) } } pc.outputs().snapshot(Output{"GLO", "UNBINNEDRES", 0}, mInterpolation.getClusterResiduals()); + pc.outputs().snapshot(Output{"GLO", "DETINFORES", 0}, mInterpolation.getClusterResidualsDetInfo()); pc.outputs().snapshot(Output{"GLO", "TRKREFS", 0}, mInterpolation.getTrackDataCompact()); if (mSendTrackData) { pc.outputs().snapshot(Output{"GLO", "TRKDATA", 0}, mInterpolation.getReferenceTracks()); @@ -188,6 +190,7 @@ DataProcessorSpec getTPCInterpolationSpec(GTrackID::mask_t srcCls, GTrackID::mas } } outputs.emplace_back("GLO", "UNBINNEDRES", 0, Lifetime::Timeframe); + outputs.emplace_back("GLO", "DETINFORES", 0, Lifetime::Timeframe); outputs.emplace_back("GLO", "TRKREFS", 0, Lifetime::Timeframe); if (sendTrackData) { outputs.emplace_back("GLO", "TRKDATA", 0, Lifetime::Timeframe); diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCResidualWriterSpec.cxx b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCResidualWriterSpec.cxx index 5f6d7ad7b361c..8b06444bdb9b3 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCResidualWriterSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCResidualWriterSpec.cxx @@ -38,6 +38,7 @@ DataProcessorSpec getTPCResidualWriterSpec(bool writeTrackData, bool debugOutput BranchDefinition>{InputSpec{"tracksUnfiltered", "GLO", "TPCINT_TRK", 0}, "tracksUnfiltered", ((writeUnfiltered && writeTrackData) ? 1 : 0)}, BranchDefinition>{InputSpec{"residualsUnfiltered", "GLO", "TPCINT_RES", 0}, "residualsUnfiltered", (writeUnfiltered ? 1 : 0)}, BranchDefinition>{InputSpec{"residuals", "GLO", "UNBINNEDRES"}, "residuals"}, + BranchDefinition>{InputSpec{"detInfo", "GLO", "DETINFORES"}, "detInfo"}, BranchDefinition>{InputSpec{"trackRefs", "GLO", "TRKREFS"}, "trackRefs"}, BranchDefinition>{InputSpec{"tracks", "GLO", "TRKDATA"}, "tracks", (writeTrackData ? 1 : 0)}, BranchDefinition>{InputSpec{"trackExt", "GLO", "TRKDATAEXT"}, "trackExt", (debugOutput ? 1 : 0)})(); diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCUnbinnedResidualReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCUnbinnedResidualReaderSpec.cxx index 55da5a5e71e44..c2dae375731a4 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCUnbinnedResidualReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCUnbinnedResidualReaderSpec.cxx @@ -44,6 +44,11 @@ void TPCUnbinnedResidualReader::connectTree() assert(mTreeIn); mTreeIn->SetBranchAddress("residuals", &mUnbinnedResidPtr); mTreeIn->SetBranchAddress("trackRefs", &mTrackDataCompactPtr); + if (mTreeIn->GetBranch("detInfo")) { + mTreeIn->SetBranchAddress("detInfo", &mDetInfoUnbResPtr); + } else { + LOGP(warn, "No detInfo branch found in the unbinned residuals tree, empty vector will be sent"); + } if (mTrackInput) { mTreeIn->SetBranchAddress("tracks", &mTrackDataPtr); } @@ -58,6 +63,7 @@ void TPCUnbinnedResidualReader::run(ProcessingContext& pc) LOG(info) << "Pushing " << mUnbinnedResid.size() << " unbinned residuals at entry " << currEntry; pc.outputs().snapshot(Output{"GLO", "UNBINNEDRES", 0}, mUnbinnedResid); pc.outputs().snapshot(Output{"GLO", "TRKREFS", 0}, mTrackDataCompact); + pc.outputs().snapshot(Output{"GLO", "DETINFORES", 0}, mDetInfoUnbRes); if (mTrackInput) { LOG(info) << "Pushing " << mTrackData.size() << " reference tracks for these residuals"; pc.outputs().snapshot(Output{"GLO", "TRKDATA", 0}, mTrackData); @@ -73,6 +79,7 @@ DataProcessorSpec getUnbinnedTPCResidualsReaderSpec(bool trkInput) { std::vector outputs; outputs.emplace_back("GLO", "UNBINNEDRES", 0, Lifetime::Timeframe); + outputs.emplace_back("GLO", "DETINFORES", 0, Lifetime::Timeframe); outputs.emplace_back("GLO", "TRKREFS", 0, Lifetime::Timeframe); if (trkInput) { outputs.emplace_back("GLO", "TRKDATA", 0, Lifetime::Timeframe); diff --git a/Detectors/TPC/calibration/SpacePoints/CMakeLists.txt b/Detectors/TPC/calibration/SpacePoints/CMakeLists.txt index 510cff4f7760c..47bb9c09a9951 100644 --- a/Detectors/TPC/calibration/SpacePoints/CMakeLists.txt +++ b/Detectors/TPC/calibration/SpacePoints/CMakeLists.txt @@ -29,7 +29,8 @@ o2_add_library(SpacePoints O2::DataFormatsITSMFT O2::DataFormatsTRD O2::DataFormatsTOF - O2::DataFormatsGlobalTracking) + O2::DataFormatsGlobalTracking + O2::GPUTracking) o2_target_root_dictionary(SpacePoints HEADERS include/SpacePoints/TrackResiduals.h diff --git a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/ResidualAggregator.h b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/ResidualAggregator.h index a02d830cfe45d..00af697da3a9b 100644 --- a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/ResidualAggregator.h +++ b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/ResidualAggregator.h @@ -49,7 +49,7 @@ struct ResidualsContainer { void fillStatisticsBranches(); uint64_t getNEntries() const { return nResidualsTotal; } - void fill(const o2::dataformats::TFIDInfo& ti, const gsl::span resid, const gsl::span trkRefsIn, const gsl::span* trkDataIn, const o2::ctp::LumiInfo* lumiInput); + void fill(const o2::dataformats::TFIDInfo& ti, const gsl::span resid, const gsl::span detInfoRes, const gsl::span trkRefsIn, const gsl::span* trkDataIn, const o2::ctp::LumiInfo* lumiInput); void merge(ResidualsContainer* prev); void print(); void writeToFile(bool closeFileAfterwards); @@ -64,6 +64,7 @@ struct ResidualsContainer { std::vector sumUnbinnedResid, *sumUnbinnedResidPtr{&sumUnbinnedResid}; ///< sum of unbinned residuals for each TF std::vector lumi, *lumiPtr{&lumi}; ///< luminosity information from CTP per TF std::vector unbinnedRes, *unbinnedResPtr{&unbinnedRes}; ///< unbinned residuals which are sent to the aggregator + std::vector detInfoUnbRes, *detInfoUnbResPtr{&detInfoUnbRes}; ///< detector info associated to unbinned residuals which are sent to the aggregator std::vector trkData, *trkDataPtr{&trkData}; ///< track data and cluster ranges std::vector trackInfo, *trackInfoPtr{&trackInfo}; ///< allows to obtain track type for each unbinned residual downstream o2::ctp::LumiInfo lumiTF; ///< for each processed TF we store the lumi information in the tree of unbinned residuals diff --git a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackInterpolation.h b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackInterpolation.h index 992925179ffce..e7d0fb197ea42 100644 --- a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackInterpolation.h +++ b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackInterpolation.h @@ -47,6 +47,11 @@ class TTree; namespace o2 { +namespace gpu +{ +class GPUParam; +} + namespace tpc { class VDriftCorrFact; @@ -101,15 +106,72 @@ struct UnbinnedResid { ClassDefNV(UnbinnedResid, 2); }; +struct DetInfoResid { // detector info associated with residual + uint32_t word = 0; // container interpreted in a different way depending on the detector type + // + // TPC view: qTot and qMax of the cluster + uint16_t qTotTPC() const { return static_cast(word & 0xFFFFu); } + uint16_t qMaxTPC() const { return static_cast((word >> 16) & 0xFFFFu); } + void setTPC(uint16_t qTot, uint16_t qMax) { word = (static_cast(qMax) << 16) | static_cast(qTot); } + // + // TRD view: q0, q1, q2 + calibrated slope (truncated to in +-3.5 range) + static constexpr uint32_t TRDQ0NB = 7, TRDQ1NB = 7, TRDQ2NB = 6, TRDSlpNB = 12; + static constexpr uint32_t TRDQ0Msk = (1 << TRDQ0NB) - 1, TRDQ1Msk = (1 << TRDQ1NB) - 1, TRDQ2Msk = ((1 << TRDQ2NB) - 1), TRDSlpMsk = (1 << TRDSlpNB) - 1; + static constexpr float TRDMaxSlope = 3.5, TRDSlope2Int = ((1 << TRDSlpNB) - 1) / (2 * TRDMaxSlope), TRDInt2Slope = 1.f / TRDSlope2Int; + uint16_t q0TRD() const { return static_cast(word & TRDQ0Msk); } + uint16_t q1TRD() const { return static_cast((word >> TRDQ0NB) & TRDQ1Msk); } + uint16_t q2TRD() const { return static_cast((word >> (TRDQ0NB + TRDQ1NB)) & TRDQ2Msk); } + float slopeTRD() const { return ((word >> (TRDQ0NB + TRDQ1NB + TRDQ2NB)) & TRDSlpMsk) * TRDInt2Slope - TRDMaxSlope; } + void setTRD(uint8_t q0, uint8_t q1, uint8_t q2, float slope) + { + float rslope = (slope + TRDMaxSlope) * TRDSlope2Int; + if (rslope < 0.f) { + rslope = 0; + } else if (rslope > TRDSlpMsk) { + rslope = TRDSlpMsk; + } + uint32_t slpI = std::round(rslope); + word = (static_cast(slpI << (TRDQ0NB + TRDQ1NB + TRDQ2NB)) | + static_cast((q2 & TRDQ2Msk) << (TRDQ0NB + TRDQ1NB)) | + static_cast((q1 & TRDQ1Msk) << TRDQ0NB) | + static_cast(q0 & TRDQ0Msk)); + } + // + // TOF view (time difference in \mus wrt seeding ITS-TPC track) + float timeTOF() const { return std::bit_cast(word); } + void setTOF(float t) { word = std::bit_cast(t); } + // + // No info for ITS is stored + // + // PV view (time difference in \mus wrt contributing ITS-TPC track) + float timePV() const { return std::bit_cast(word); } + void setPV(float t) { word = std::bit_cast(t); } + + ClassDefNV(DetInfoResid, 1); +}; + /// 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, uint8_t nextraRes = 0) : idxFirstResidual(idx), nResiduals(nRes), sourceId(source), nExtDetResid(nextraRes) {} + TrackDataCompact(uint32_t idx, std::array mlt, uint8_t nRes, uint8_t source, uint8_t nextraRes = 0) : idxFirstResidual(idx), multStack{mlt}, nResiduals(nRes), sourceId(source), nExtDetResid(nextraRes) {} uint32_t idxFirstResidual; ///< the index of the first residual from this track + std::array multStack{}; // multiplicity in the stack packed as asinh(x*0.05)/0.05 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, 2); + + void setMultStack(float v, int stack) + { + uint32_t mltPacked = std::round(std::asinh(v * 0.05) / 0.05); + multStack[stack] = mltPacked < 0xff ? mltPacked : 0xff; + } + float getMultStack(int stack) const + { + return std::sinh(multStack[stack] * 0.05) / 0.05; + } + float getMultStackPacked(int stack) const { return multStack[stack]; } + + ClassDefNV(TrackDataCompact, 3); }; // TODO add to UnbinnedResid::sec flag if cluster was used or not @@ -149,11 +211,22 @@ struct TrackData { short TRDTrkltSlope[6] = {}; ///< TRD tracklet slope 0x7fff / param::MaxTRDSlope 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 - + std::array multStack{}; // multiplicity in the stack packed as asinh(x*0.05)/0.05 float getT0Error() const { return float(clAvailTOF); } bool isTOFAvail() const { return clAvailTOF != 0; } - ClassDefNV(TrackData, 9); + void setMultStack(float v, int stack) + { + uint32_t mltPacked = std::round(std::asinh(v * 0.05) / 0.05); + multStack[stack] = mltPacked < 0xff ? mltPacked : 0xff; + } + float getMultStack(int stack) const + { + return std::sinh(multStack[stack] * 0.05) / 0.05; + } + float getMultStackPacked(int stack) const { return multStack[stack]; } + + ClassDefNV(TrackData, 10); }; /// \class TrackInterpolation @@ -268,6 +341,8 @@ class TrackInterpolation void diffToMA(const int np, const std::array& y, std::array& diffMA) const; // -------------------------------------- settings -------------------------------------------------- + void setNHBPerTF(int n) { mNHBPerTF = n; } + void setTPCVDrift(const o2::tpc::VDriftCorrFact& v); /// Sets the flag if material correction should be applied when extrapolating the tracks @@ -296,10 +371,13 @@ class TrackInterpolation 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, TrackData* trkData = nullptr); + int processTRDLayer(const o2::trd::TrackTRD& trkTRD, int iLayer, o2::track::TrackParCov& trkWork, std::array* trkltTRDYZ = nullptr, + std::array* trkltTRDCov = nullptr, TrackData* trkData = nullptr, + o2::trd::Tracklet64* trk64 = nullptr, o2::trd::CalibratedTracklet* trkCalib = nullptr); // --------------------------------- output --------------------------------------------- std::vector& getClusterResiduals() { return mClRes; } + std::vector& getClusterResidualsDetInfo() { return mDetInfoRes; } std::vector& getTrackDataCompact() { return mTrackDataCompact; } std::vector& getTrackDataExtended() { return mTrackDataExtended; } std::vector& getReferenceTracks() { return mTrackData; } @@ -308,8 +386,14 @@ class TrackInterpolation private: static constexpr float sFloatEps{1.e-7f}; ///< float epsilon for robust linear fitting + static constexpr int NSTACKS = 4; + static constexpr std::array STACKROWS{0, 63, 97, 127, 152}; // parameters + settings const SpacePointsCalibConfParam* mParams = nullptr; + std::shared_ptr mTPCParam = nullptr; + int mNHBPerTF = 32; + int mNTPCOccBinLength = 16; ///< TPC occupancy bin length in TB + float mNTPCOccBinLengthInv = 1.f / 16; ///< its inverse float mTPCTimeBinMUS{.2f}; ///< TPC time bin duration in us float mTPCVDriftRef = -1.; ///< TPC nominal drift speed in cm/microseconds float mTPCDriftTimeOffsetRef = 0.; ///< TPC nominal (e.g. at the start of run) drift time bias in cm/mus @@ -348,6 +432,7 @@ class TrackInterpolation std::vector mTrackDataCompact{}; ///< required to connect each residual to a global track std::vector mTrackDataExtended{}; ///< full tracking information for debugging std::vector mClRes{}; ///< residuals for each available TPC cluster of all tracks + std::vector mDetInfoRes{}; ///< packed detector info associated with each residual std::vector mTrackDataUnfiltered{}; ///< same as mTrackData, but for all tracks before outlier filtering std::vector mClResUnfiltered{}; ///< same as mClRes, but for all residuals before outlier filtering diff --git a/Detectors/TPC/calibration/SpacePoints/src/ResidualAggregator.cxx b/Detectors/TPC/calibration/SpacePoints/src/ResidualAggregator.cxx index a120c0e4ae782..b916e14dbf741 100644 --- a/Detectors/TPC/calibration/SpacePoints/src/ResidualAggregator.cxx +++ b/Detectors/TPC/calibration/SpacePoints/src/ResidualAggregator.cxx @@ -124,6 +124,7 @@ void ResidualsContainer::init(const TrackResiduals* residualsEngine, std::string treeOutResidualsUnbinned->Branch("trackInfo", &trackInfoPtr); treeOutResidualsUnbinned->Branch("CTPLumi", &lumiTF); treeOutResidualsUnbinned->Branch("timeMS", &timeMS); + treeOutResidualsUnbinned->Branch("detInfo", &detInfoUnbResPtr); } if (writeTrackData) { treeOutTrackData = std::make_unique("trackData", "Track information incl cluster range ref"); @@ -170,7 +171,7 @@ void ResidualsContainer::fillStatisticsBranches() } } -void ResidualsContainer::fill(const o2::dataformats::TFIDInfo& ti, const gsl::span resid, const gsl::span trkRefsIn, const gsl::span* trkDataIn, const o2::ctp::LumiInfo* lumiInput) +void ResidualsContainer::fill(const o2::dataformats::TFIDInfo& ti, const gsl::span resid, const gsl::span detInfoRes, const gsl::span trkRefsIn, const gsl::span* trkDataIn, const o2::ctp::LumiInfo* lumiInput) { // receives large vector of unbinned residuals and fills the sector-wise vectors // with binned residuals and statistics @@ -185,13 +186,14 @@ void ResidualsContainer::fill(const o2::dataformats::TFIDInfo& ti, const gsl::sp firstSeenTF = ti.tfCounter; } for (const auto& residIn : resid) { - ++nUnbinnedResidualsInTF; bool counterIncremented = false; if (writeUnbinnedResiduals) { unbinnedRes.push_back(residIn); + detInfoUnbRes.push_back(detInfoRes.size() ? detInfoRes[nUnbinnedResidualsInTF] : DetInfoResid{}); ++nResidualsTotal; counterIncremented = true; } + ++nUnbinnedResidualsInTF; if (!writeBinnedResid) { continue; } @@ -247,6 +249,7 @@ void ResidualsContainer::fill(const o2::dataformats::TFIDInfo& ti, const gsl::sp timeMS = orbitReset + ti.tfCounter * o2::constants::lhc::LHCOrbitMUS * 1.e-3; treeOutResidualsUnbinned->Fill(); unbinnedRes.clear(); + detInfoUnbRes.clear(); trackInfo.clear(); } tfOrbits.push_back(ti.firstTForbit); @@ -338,6 +341,9 @@ void ResidualsContainer::merge(ResidualsContainer* prev) if (writeUnbinnedResiduals) { prev->treeOutResidualsUnbinned->SetBranchAddress("res", &unbinnedResPtr); prev->treeOutResidualsUnbinned->SetBranchAddress("trackInfo", &trackInfoPtr); + prev->treeOutResidualsUnbinned->SetBranchAddress("CTPLumi", &lumiTF); + prev->treeOutResidualsUnbinned->SetBranchAddress("timeMS", &timeMS); + prev->treeOutResidualsUnbinned->SetBranchAddress("detInfo", &detInfoUnbResPtr); for (int i = 0; i < treeOutResidualsUnbinned->GetEntries(); ++i) { treeOutResidualsUnbinned->GetEntry(i); prev->treeOutResidualsUnbinned->Fill(); diff --git a/Detectors/TPC/calibration/SpacePoints/src/SpacePointCalibLinkDef.h b/Detectors/TPC/calibration/SpacePoints/src/SpacePointCalibLinkDef.h index b109a610f60b5..a3f9f3fe2267c 100644 --- a/Detectors/TPC/calibration/SpacePoints/src/SpacePointCalibLinkDef.h +++ b/Detectors/TPC/calibration/SpacePoints/src/SpacePointCalibLinkDef.h @@ -29,7 +29,9 @@ #pragma link C++ class o2::tpc::TrackResiduals::VoxRes + ; #pragma link C++ class o2::tpc::TrackResiduals::VoxStats + ; #pragma link C++ class o2::tpc::UnbinnedResid + ; +#pragma link C++ class o2::tpc::DetInfoResid + ; #pragma link C++ class std::vector < o2::tpc::UnbinnedResid> + ; +#pragma link C++ class std::vector < o2::tpc::DetInfoResid> + ; #pragma link C++ class std::vector < o2::tpc::TrackResiduals::LocalResid> + ; #pragma link C++ class std::vector < o2::tpc::TrackResiduals::VoxStats> + ; #pragma link C++ class o2::tpc::ResidualAggregator + ; diff --git a/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx index 7db5b7455f1a7..6c37be9ddc1b1 100644 --- a/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx +++ b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx @@ -33,6 +33,11 @@ #include "DataFormatsTPC/VDriftCorrFact.h" #include "Framework/Logger.h" #include "CCDB/BasicCCDBManager.h" +#include "GPUO2InterfaceUtils.h" +#include "GPUO2InterfaceConfiguration.h" +#include "GPUO2InterfaceRefit.h" +#include "GPUParam.h" +#include "GPUParam.inc" #include #include #include @@ -135,7 +140,7 @@ void TrackInterpolation::init(o2::dataformats::GlobalTrackID::mask_t src, o2::da auto geom = o2::its::GeometryTGeo::Instance(); geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); - + mTPCParam = o2::gpu::GPUO2InterfaceUtils::getFullParamShared(0.f, mNHBPerTF); mInitDone = true; LOGP(info, "Done initializing TrackInterpolation. Configured track input: {}. Track input specifically for map: {}", GTrackID::getSourcesNames(mSourcesConfigured), mSingleSourcesConfigured ? "identical" : GTrackID::getSourcesNames(mSourcesConfiguredMap)); @@ -316,6 +321,10 @@ void TrackInterpolation::process() // set the input containers mTPCTracksClusIdx = mRecoCont->getTPCTracksClusterRefs(); mTPCClusterIdxStruct = &mRecoCont->getTPCClusters(); + int nbOccTOT = o2::gpu::GPUO2InterfaceRefit::fillOccupancyMapGetSize(mNHBPerTF, mTPCParam.get()); + o2::gpu::GPUO2InterfaceUtils::paramUseExternalOccupancyMap(mTPCParam.get(), mNHBPerTF, mRecoCont->occupancyMapTPC.data(), nbOccTOT); + mNTPCOccBinLength = mTPCParam->rec.tpc.occupancyMapTimeBins; + mNTPCOccBinLengthInv = 1.f / mNTPCOccBinLength; { if (!mITSDict) { LOG(error) << "No ITS dictionary available"; @@ -354,6 +363,7 @@ void TrackInterpolation::process() int maxOutputTracks = (mMaxTracksPerTF >= 0) ? mMaxTracksPerTF + mAddTracksForMapPerTF : nSeeds; mTrackData.reserve(maxOutputTracks); mClRes.reserve(maxOutputTracks * param::NPadRows); + mDetInfoRes.reserve(maxOutputTracks * param::NPadRows); bool maxTracksReached = false; for (int iSeed = 0; iSeed < nSeeds; ++iSeed) { if (mMaxTracksPerTF >= 0 && mTrackDataCompact.size() >= mMaxTracksPerTF + mAddTracksForMapPerTF) { @@ -435,6 +445,8 @@ void TrackInterpolation::interpolateTrack(int iSeed) { LOGP(debug, "Starting track interpolation for GID {}", mGIDs[iSeed].asString()); TrackData trackData; + o2::trd::Tracklet64 trkl64; + o2::trd::CalibratedTracklet trklCalib; std::unique_ptr trackDataExtended; std::vector clusterResiduals; auto propagator = o2::base::Propagator::Instance(); @@ -468,7 +480,9 @@ void TrackInterpolation::interpolateTrack(int iSeed) trackData.clIdx.setFirstEntry(mClRes.size()); // reference the first cluster residual belonging to this track float clusterTimeBinOffset = mTrackTimes[iSeed] / mTPCTimeBinMUS; - // store the TPC cluster positions in the cache + // store the TPC cluster positions in the cache, as well as dedx info + std::array, constants::MAXGLOBALPADROW> mCacheDEDX{}; + std::array multBins{}; for (int iCl = trkTPC.getNClusterReferences(); iCl--;) { uint8_t sector, row; uint32_t clusterIndexInRow; @@ -481,6 +495,12 @@ void TrackInterpolation::interpolateTrack(int iSeed) mCache[row].clY = clTPCYZ[0]; mCache[row].clZ = clTPCYZ[1]; mCache[row].clAngle = o2::math_utils::sector2Angle(sector); + mCacheDEDX[row].first = clTPC.getQtot(); + mCacheDEDX[row].second = clTPC.getQmax(); + int imb = int(clTPC.getTime() * mNTPCOccBinLengthInv); + if (imb < mTPCParam->occupancyMapSize) { + multBins[row] = 1 + std::max(0, imb); + } } // extrapolate seed through TPC and store track position at each pad row @@ -627,10 +647,19 @@ void TrackInterpolation::interpolateTrack(int iSeed) trackData.nClsTPC = trkTPC.getNClusterReferences(); trackData.nClsITS = trkITS.getNumberOfClusters(); trackData.nTrkltsTRD = gidTable[GTrackID::TRD].isIndexSet() ? mRecoCont->getITSTPCTRDTrack(gidTable[GTrackID::ITSTPCTRD]).getNtracklets() : 0; + + double t0forTOF = 0.; // to be set if TOF is matched + float t0forTOFwithinBC = 0.f; + float t0forTOFres = 9999.f; + if (gidTable[GTrackID::TOF].isIndexSet()) { const auto& tofMatch = mRecoCont->getTOFMatch(mGIDs[iSeed]); - trackData.deltaTOF = tofMatch.getSignal() - tofMatch.getFT0Best() - tofMatch.getLTIntegralOut().getTOF(trkTPC.getPID().getID()); - trackData.clAvailTOF = uint16_t(tofMatch.getFT0BestRes()); + ULong64_t bclongtof = (tofMatch.getSignal() - 10000) * o2::tof::Geo::BC_TIME_INPS_INV; + t0forTOF = tofMatch.getFT0Best(); // setting t0 for TOF + t0forTOFwithinBC = t0forTOF - bclongtof * o2::tof::Geo::BC_TIME_INPS; + t0forTOFres = tofMatch.getFT0BestRes(); + trackData.deltaTOF = tofMatch.getSignal() - t0forTOF - tofMatch.getLTIntegralOut().getTOF(trkTPC.getPID().getID()); + trackData.clAvailTOF = uint16_t(t0forTOFres); } else { trackData.clAvailTOF = 0; } @@ -655,6 +684,7 @@ void TrackInterpolation::interpolateTrack(int iSeed) 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); + mDetInfoRes.emplace_back().setTPC(mCacheDEDX[iRow].first, mCacheDEDX[iRow].second); // qtot, qmax ++nClValidated; } else { ++mRejectedResiduals; @@ -662,6 +692,30 @@ void TrackInterpolation::interpolateTrack(int iSeed) } trackData.clIdx.setEntries(nClValidated); + // store multiplicity info + for (int ist = 0; ist < NSTACKS; ist++) { + int mltBinMin = 0x7ffff, mltBinMax = -1, prevBin = -1; + for (int ir = STACKROWS[ist]; ir < STACKROWS[ist + 1]; ir++) { + if (multBins[ir] != prevBin && multBins[ir] > 0) { // there is a cluster different from previous one + prevBin = multBins[ir]; + if (multBins[ir] > mltBinMax) { + mltBinMax = multBins[ir]; + } + if (multBins[ir] < mltBinMin) { + mltBinMin = multBins[ir]; + } + } + } + if (--mltBinMin >= 0) { // we were offsetting bin IDs by 1! + float avMlt = 0; + for (int ib = mltBinMin; ib < mltBinMax; ib++) { + avMlt += mTPCParam->occupancyMap[ib]; + } + avMlt /= (mltBinMax - mltBinMin); + trackData.setMultStack(avMlt, ist); + } + } + bool stopPropagation = !mExtDetResid; if (!stopPropagation) { // do we have TRD residuals to add? @@ -670,7 +724,7 @@ void TrackInterpolation::interpolateTrack(int iSeed) 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, nullptr, &trackData); + int res = processTRDLayer(trkTRD, iLayer, trkWork, &trkltTRDYZ, nullptr, &trackData, &trkl64, &trklCalib); if (res == -1) { // no traklet on this layer continue; } @@ -684,6 +738,7 @@ void TrackInterpolation::interpolateTrack(int iSeed) 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); + mDetInfoRes.emplace_back().setTRD(trkl64.getQ0(), trkl64.getQ1(), trkl64.getQ2(), trklCalib.getDy()); // q0,q1,q2,slope trackData.nExtDetResid++; } } @@ -710,8 +765,16 @@ void TrackInterpolation::interpolateTrack(int iSeed) 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(); + // get seeding track time + 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()); + // get seeding track time + if (!gidTable[GTrackID::ITSTPC].isIndexSet()) { + LOGP(fatal, "ITS-TPC seed index is not set for TOF track"); + } + float tdif = static_cast(clTOF.getTime() - t0forTOF); // time in \mus wrt interaction time0 + mDetInfoRes.emplace_back().setTOF(tdif * 1e-6); trackData.nExtDetResid++; } break; @@ -738,6 +801,7 @@ void TrackInterpolation::interpolateTrack(int iSeed) 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()); + mDetInfoRes.emplace_back(); // empty placeholder trackData.nExtDetResid++; } } @@ -759,6 +823,11 @@ void TrackInterpolation::interpolateTrack(int iSeed) if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(trkWorkITS.getY()) < param::MaxY) && (std::abs(trkWorkITS.getZ()) < param::MaxZ) && abs(xv) < param::MaxVtxX) { short compXV = static_cast(xv * 0x7fff / param::MaxVtxX); mClRes.emplace_back(dy, dz, alpha / TMath::Pi(), trkWorkITS.getY(), trkWorkITS.getZ(), 190, -1, compXV); + if (!gidTable[GTrackID::ITSTPC].isIndexSet()) { + LOGP(fatal, "ITS-TPC seed index is not set for TOF track"); + } + float tdif = pv.getTimeStamp().getTimeStamp() - mRecoCont->getTPCITSTrack(gidTable[GTrackID::ITSTPC]).getTimeMUS().getTimeStamp(); + mDetInfoRes.emplace_back().setPV(tdif); // time in \mus wrt seeding ITS-TPC track trackData.nExtDetResid++; } } @@ -767,7 +836,7 @@ void TrackInterpolation::interpolateTrack(int iSeed) } mGIDsSuccess.push_back(mGIDs[iSeed]); - mTrackDataCompact.emplace_back(trackData.clIdx.getFirstEntry(), nClValidated, mGIDs[iSeed].getSource(), trackData.nExtDetResid); + mTrackDataCompact.emplace_back(trackData.clIdx.getFirstEntry(), trackData.multStack, nClValidated, mGIDs[iSeed].getSource(), trackData.nExtDetResid); mTrackData.push_back(std::move(trackData)); if (mDumpTrackPoints) { (*trackDataExtended).clIdx.setEntries(nClValidated); @@ -785,7 +854,8 @@ 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, TrackData* trkData) + std::array* trkltTRDYZ, std::array* trkltTRDCov, TrackData* trkData, + o2::trd::Tracklet64* trk64, o2::trd::CalibratedTracklet* trkCalib) { // 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); @@ -827,6 +897,12 @@ int TrackInterpolation::processTRDLayer(const o2::trd::TrackTRD& trkTRD, int iLa trkData->TRDTrkltSlope[iLayer] = slope * 0x7fff / param::MaxTRDSlope; } } + if (trk64) { + *trk64 = trdTrklt; + } + if (trkCalib) { + *trkCalib = trdSP; + } return trkltDet; } @@ -836,6 +912,8 @@ void TrackInterpolation::extrapolateTrack(int iSeed) LOGP(debug, "Starting track extrapolation for GID {}", mGIDs[iSeed].asString()); const auto& gidTable = mGIDtables[iSeed]; TrackData trackData; + o2::trd::Tracklet64 trkl64; + o2::trd::CalibratedTracklet trklCalib; std::unique_ptr trackDataExtended; std::vector clusterResiduals; trackData.clIdx.setFirstEntry(mClRes.size()); @@ -866,6 +944,8 @@ void TrackInterpolation::extrapolateTrack(int iSeed) unsigned short rowPrev = 0; // used to calculate dRow of two consecutive cluster residuals unsigned short nMeasurements = 0; uint8_t clRowPrev = constants::MAXGLOBALPADROW; // used to identify and skip split clusters on the same pad row + std::array, constants::MAXGLOBALPADROW> mCacheDEDX{}; + std::array multBins{}; for (int iCl = trkTPC.getNClusterReferences(); iCl--;) { uint8_t sector, row; uint32_t clusterIndexInRow; @@ -897,10 +977,14 @@ void TrackInterpolation::extrapolateTrack(int iSeed) const auto tz = trkWork.getZ(); const auto snp = trkWork.getSnp(); const auto sec = sector; - clusterResiduals.emplace_back(dY, dZ, ty, tz, snp, sec, row - rowPrev); - + mCacheDEDX[row].first = cl.getQtot(); + mCacheDEDX[row].second = cl.getQmax(); rowPrev = row; + int imb = int(cl.getTime() * mNTPCOccBinLengthInv); + if (imb < mTPCParam->occupancyMapSize) { + multBins[row] = 1 + std::max(0, imb); + } ++nMeasurements; } @@ -937,6 +1021,7 @@ void TrackInterpolation::extrapolateTrack(int iSeed) const auto z = clusterResiduals[iCl].z; 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, clusterResiduals[iCl].sec); + mDetInfoRes.emplace_back().setTPC(mCacheDEDX[iRow].first, mCacheDEDX[iRow].second); // qtot, qmax ++nClValidated; } else { ++mRejectedResiduals; @@ -944,6 +1029,30 @@ void TrackInterpolation::extrapolateTrack(int iSeed) } trackData.clIdx.setEntries(nClValidated); + // store multiplicity info + for (int ist = 0; ist < NSTACKS; ist++) { + int mltBinMin = 0x7ffff, mltBinMax = -1, prevBin = -1; + for (int ir = STACKROWS[ist]; ir < STACKROWS[ist + 1]; ir++) { + if (multBins[ir] != prevBin && multBins[ir] > 0) { // there is a cluster + prevBin = multBins[ir]; + if (multBins[ir] > mltBinMax) { + mltBinMax = multBins[ir]; + } + if (multBins[ir] < mltBinMin) { + mltBinMin = multBins[ir]; + } + } + } + if (--mltBinMin >= 0) { // we were offsetting bin IDs by 1! + float avMlt = 0; + for (int ib = mltBinMin; ib < mltBinMax; ib++) { + avMlt += mTPCParam->occupancyMap[ib]; + } + avMlt /= (mltBinMax - mltBinMin); + trackData.setMultStack(avMlt, ist); + } + } + bool stopPropagation = !mExtDetResid; if (!stopPropagation) { // do we have TRD residuals to add? @@ -955,7 +1064,7 @@ void TrackInterpolation::extrapolateTrack(int iSeed) trackData.nTrkltsTRD = trkTRD.getNtracklets(); for (int iLayer = 0; iLayer < o2::trd::constants::NLAYER; iLayer++) { std::array trkltTRDYZ{}; - int res = processTRDLayer(trkTRD, iLayer, trkWork, &trkltTRDYZ, nullptr, &trackData); + int res = processTRDLayer(trkTRD, iLayer, trkWork, &trkltTRDYZ, nullptr, &trackData, &trkl64, &trklCalib); if (res == -1) { // no traklet on this layer continue; } @@ -970,6 +1079,7 @@ void TrackInterpolation::extrapolateTrack(int iSeed) 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); + mDetInfoRes.emplace_back().setTRD(trkl64.getQ0(), trkl64.getQ1(), trkl64.getQ2(), trklCalib.getDy()); // q0,q1,q2,slope trackData.nExtDetResid++; } } @@ -979,8 +1089,12 @@ void TrackInterpolation::extrapolateTrack(int iSeed) trackData.clAvailTOF = 0; while (gidTableFull[GTrackID::TOF].isIndexSet() && !stopPropagation) { const auto& tofMatch = mRecoCont->getTOFMatch(gidFull); - trackData.deltaTOF = tofMatch.getSignal() - tofMatch.getFT0Best() - tofMatch.getLTIntegralOut().getTOF(trkTPC.getPID().getID()); - trackData.clAvailTOF = uint16_t(tofMatch.getFT0BestRes()); + ULong64_t bclongtof = (tofMatch.getSignal() - 10000) * o2::tof::Geo::BC_TIME_INPS_INV; + double t0forTOF = tofMatch.getFT0Best(); // setting t0 for TOF + float t0forTOFwithinBC = t0forTOF - bclongtof * o2::tof::Geo::BC_TIME_INPS; + float t0forTOFres = tofMatch.getFT0BestRes(); + trackData.deltaTOF = tofMatch.getSignal() - t0forTOF - tofMatch.getLTIntegralOut().getTOF(trkTPC.getPID().getID()); + trackData.clAvailTOF = uint16_t(t0forTOFres); 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()}; @@ -1002,6 +1116,13 @@ void TrackInterpolation::extrapolateTrack(int iSeed) 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()); + // get seeding track time + if (!gidTableFull[GTrackID::ITSTPC].isIndexSet()) { + LOGP(fatal, "ITS-TPC seed index is not set for TOF track"); + } + + float tdif = static_cast(clTOF.getTime() - t0forTOF); // time in \mus wrt interaction time0 + mDetInfoRes.emplace_back().setTOF(tdif * 1e-6); // time in \mus wrt seeding ITS-TPC track trackData.nExtDetResid++; } break; @@ -1028,6 +1149,7 @@ void TrackInterpolation::extrapolateTrack(int iSeed) 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()); + mDetInfoRes.emplace_back(); // empty placeholder trackData.nExtDetResid++; } } @@ -1049,6 +1171,11 @@ void TrackInterpolation::extrapolateTrack(int iSeed) if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(trkWorkITS.getY()) < param::MaxY) && (std::abs(trkWorkITS.getZ()) < param::MaxZ) && abs(xv) < param::MaxVtxX) { short compXV = static_cast(xv * 0x7fff / param::MaxVtxX); mClRes.emplace_back(dy, dz, alpha / TMath::Pi(), trkWorkITS.getY(), trkWorkITS.getZ(), 190, -1, compXV); + if (!gidTableFull[GTrackID::ITSTPC].isIndexSet()) { + LOGP(fatal, "ITS-TPC seed index is not set for TOF track"); + } + float tdif = pv.getTimeStamp().getTimeStamp() - mRecoCont->getTPCITSTrack(gidTableFull[GTrackID::ITSTPC]).getTimeMUS().getTimeStamp(); + mDetInfoRes.emplace_back().setPV(tdif); // time in \mus wrt seeding ITS-TPC track trackData.nExtDetResid++; } } @@ -1057,7 +1184,7 @@ void TrackInterpolation::extrapolateTrack(int iSeed) } mTrackData.push_back(std::move(trackData)); mGIDsSuccess.push_back(mGIDs[iSeed]); - mTrackDataCompact.emplace_back(trackData.clIdx.getFirstEntry(), nClValidated, mGIDs[iSeed].getSource(), trackData.nExtDetResid); + mTrackDataCompact.emplace_back(trackData.clIdx.getFirstEntry(), trackData.multStack, nClValidated, mGIDs[iSeed].getSource(), trackData.nExtDetResid); if (mDumpTrackPoints) { (*trackDataExtended).clIdx.setEntries(nClValidated); (*trackDataExtended).nExtDetResid = trackData.nExtDetResid; @@ -1445,6 +1572,7 @@ void TrackInterpolation::reset() mTrackDataCompact.clear(); mTrackDataExtended.clear(); mClRes.clear(); + mDetInfoRes.clear(); mTrackDataUnfiltered.clear(); mClResUnfiltered.clear(); mGIDsSuccess.clear(); From 0df45c42929034ad639a89a0618896090012270a Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Thu, 25 Sep 2025 15:57:50 +0200 Subject: [PATCH 020/133] Implementation of TPC loopers in O2 --- Generators/CMakeLists.txt | 10 + Generators/include/Generators/Generator.h | 13 + Generators/include/Generators/TPCLoopers.h | 148 ++++++ .../include/Generators/TPCLoopersParam.h | 54 ++ Generators/share/TPCLoopers/README.md | 79 +++ .../share/TPCLoopers/ScalerComptonParams.json | 28 + .../share/TPCLoopers/ScalerPairParams.json | 34 ++ .../share/TPCLoopers/gaussian_params.csv | 4 + .../share/TPCLoopers/poisson_params.csv | 3 + Generators/src/Generator.cxx | 184 +++++++ Generators/src/GeneratorsLinkDef.h | 4 + Generators/src/TPCLoopers.cxx | 486 ++++++++++++++++++ Generators/src/TPCLoopersParam.cxx | 15 + prodtests/full_system_test.sh | 37 +- 14 files changed, 1095 insertions(+), 4 deletions(-) create mode 100644 Generators/include/Generators/TPCLoopers.h create mode 100644 Generators/include/Generators/TPCLoopersParam.h create mode 100644 Generators/share/TPCLoopers/README.md create mode 100644 Generators/share/TPCLoopers/ScalerComptonParams.json create mode 100644 Generators/share/TPCLoopers/ScalerPairParams.json create mode 100644 Generators/share/TPCLoopers/gaussian_params.csv create mode 100644 Generators/share/TPCLoopers/poisson_params.csv create mode 100644 Generators/src/TPCLoopers.cxx create mode 100644 Generators/src/TPCLoopersParam.cxx diff --git a/Generators/CMakeLists.txt b/Generators/CMakeLists.txt index 02caa63df0d43..287536ff118f7 100644 --- a/Generators/CMakeLists.txt +++ b/Generators/CMakeLists.txt @@ -41,6 +41,8 @@ o2_add_library(Generators src/GeneratorTParticleParam.cxx src/GeneratorService.cxx src/FlowMapper.cxx + src/TPCLoopers.cxx + src/TPCLoopersParam.cxx $<$:src/GeneratorPythia8.cxx> $<$:src/DecayerPythia8.cxx> $<$:src/GeneratorPythia8Param.cxx> @@ -53,6 +55,7 @@ o2_add_library(Generators PUBLIC_LINK_LIBRARIES FairRoot::Base O2::SimConfig O2::CommonUtils O2::DetectorsBase O2::ZDCBase O2::SimulationDataFormat ${pythiaTarget} ${hepmcTarget} FairRoot::Gen + onnxruntime::onnxruntime TARGETVARNAME targetName) if(pythia_FOUND) @@ -63,6 +66,8 @@ if(HepMC3_FOUND) target_compile_definitions(${targetName} PUBLIC GENERATORS_WITH_HEPMC3) endif() +target_compile_definitions(${targetName} PUBLIC GENERATORS_WITH_TPCLOOPERS) + set(headers include/Generators/Generator.h include/Generators/Trigger.h @@ -88,6 +93,10 @@ set(headers include/Generators/FlowMapper.h ) +list(APPEND headers + include/Generators/TPCLoopers.h + include/Generators/TPCLoopersParam.h) + if(pythia_FOUND) list(APPEND headers include/Generators/GeneratorPythia8.h @@ -158,4 +167,5 @@ endif() o2_data_file(COPY share/external DESTINATION Generators) o2_data_file(COPY share/egconfig DESTINATION Generators) +o2_data_file(COPY share/TPCLoopers DESTINATION Generators) o2_data_file(COPY share/pythia8 DESTINATION Generators) diff --git a/Generators/include/Generators/Generator.h b/Generators/include/Generators/Generator.h index bd35a00793e2d..3484601aa42bb 100644 --- a/Generators/include/Generators/Generator.h +++ b/Generators/include/Generators/Generator.h @@ -17,6 +17,10 @@ #include "FairGenerator.h" #include "TParticle.h" #include "Generators/Trigger.h" +#ifdef GENERATORS_WITH_TPCLOOPERS +#include "Generators/TPCLoopers.h" +#include "Generators/TPCLoopersParam.h" +#endif #include #include #include @@ -73,6 +77,7 @@ class Generator : public FairGenerator /** methods to override **/ virtual Bool_t generateEvent() = 0; // generates event (in structure internal to generator) virtual Bool_t importParticles() = 0; // fills the mParticles vector (transfer from generator state) + Bool_t finalizeEvent(); // final part of event generation that can be customised using external macros virtual void updateHeader(o2::dataformats::MCEventHeader* eventHeader) {}; Bool_t triggerEvent(); @@ -154,6 +159,8 @@ class Generator : public FairGenerator private: void updateSubGeneratorInformation(o2::dataformats::MCEventHeader* header) const; + // loopers flag + Bool_t mAddTPCLoopers = kFALSE; // Flag is automatically set to true if TPC is in readout detectors, loopers are not vetoed and transport is enabled // collect an ID and a short description of sub-generator entities std::unordered_map mSubGeneratorsIdToDesc; // the current ID of the sub-generator used in the current event (if applicable) @@ -162,6 +169,12 @@ class Generator : public FairGenerator // global static information about (upper limit of) number of events to be generated static unsigned int gTotalNEvents; +#ifdef GENERATORS_WITH_TPCLOOPERS + // Loopers generator instance + std::unique_ptr mTPCLoopersGen = nullptr; + bool initTPCLoopersGen(); +#endif + ClassDefOverride(Generator, 2); }; /** class Generator **/ diff --git a/Generators/include/Generators/TPCLoopers.h b/Generators/include/Generators/TPCLoopers.h new file mode 100644 index 0000000000000..6a1d3ef262e22 --- /dev/null +++ b/Generators/include/Generators/TPCLoopers.h @@ -0,0 +1,148 @@ +// Copyright 2024-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \author M+Giacalone - September 2025 + +#ifndef ALICEO2_EVENTGEN_TPCLOOPERS_H_ +#define ALICEO2_EVENTGEN_TPCLOOPERS_H_ + +#ifdef GENERATORS_WITH_TPCLOOPERS +#include +#endif +#include +#include +#include "TRandom3.h" +#include +#include "TParticle.h" + +#ifdef GENERATORS_WITH_TPCLOOPERS +// Static Ort::Env instance for multiple onnx model loading +extern Ort::Env global_env; + +// This class is responsible for loading the scaler parameters from a JSON file +// and applying the inverse transformation to the generated data. +// Inferenced output is scaled (min-max normalization or robust scaling for outlier features) during training, +// so we need to revert this transformation to get physical values. +struct Scaler { + std::vector normal_min; + std::vector normal_max; + std::vector outlier_center; + std::vector outlier_scale; + + void load(const std::string& filename); + + std::vector inverse_transform(const std::vector& input); + + private: + std::vector jsonArrayToVector(const rapidjson::Value& jsonArray); +}; + +// This class loads the ONNX model and generates samples using it. +class ONNXGenerator +{ + public: + ONNXGenerator(Ort::Env& shared_env, const std::string& model_path); + + std::vector generate_sample(); + + private: + Ort::Env& env; + Ort::Session session; + TRandom3 rand_gen; +}; +#endif // GENERATORS_WITH_TPCLOOPERS + +namespace o2 +{ +namespace eventgen +{ + +#ifdef GENERATORS_WITH_TPCLOOPERS +/** + * Generator for TPC Loopers based on pre-trained ONNX models. + * Currently it generates loopers as electron-positron pairs and Compton electrons + * according to specified distributions and parameters. + * This can be extended to other types of background processes in the future (e.g. slow neutron spallation products, saturation tail). + * Multiple configuration options are available: + * - Flat gas: loopers are generated uniformly per event taking a reference value which can be either the LHC orbit time or the average interaction time record interval from the collision context. + * ==> Current automatic setup (default) sets the interaction rate automatically from the collision context and the reference value per orbit is calculated from an external file. + * ==> Number of loopers per orbit can be adjusted via a specific parameter. + * - Poisson + Gaussian sampling: number of loopers are sampled from Poissonian (for pairs) and Gaussian (for Compton electrons) distributions based on provided parameters. + * ==> flat gas must be disabled to use this option. + * - Fixed number of loopers per event + * ==> flat gas must be disabled to use this option and Poissonian/Gaussian parameters file should be set to None + */ +class GenTPCLoopers +{ + public: + GenTPCLoopers(std::string model_pairs = "tpcloopmodel.onnx", std::string model_compton = "tpcloopmodelcompton.onnx", + std::string poisson = "poisson.csv", std::string gauss = "gauss.csv", std::string scaler_pair = "scaler_pair.json", + std::string scaler_compton = "scaler_compton.json"); + + Bool_t generateEvent(); + + Bool_t generateEvent(double time_limit); + + std::vector importParticles(); + + unsigned int PoissonPairs(); + + unsigned int GaussianElectrons(); + + void SetNLoopers(unsigned int nsig_pair, unsigned int nsig_compton); + + void SetMultiplier(const std::array& mult); + + void setFlatGas(Bool_t flat, Int_t number = -1, Int_t nloopers_orbit = -1); + + void setFractionPairs(float fractionPairs); + + void SetRate(const std::string& rateFile, bool isPbPb, int intRate = 50000); + + void SetAdjust(float adjust = 0.f); + + unsigned int getNLoopers() const { return (mNLoopersPairs + mNLoopersCompton); } + + private: + std::unique_ptr mONNX_pair = nullptr; + std::unique_ptr mONNX_compton = nullptr; + std::unique_ptr mScaler_pair = nullptr; + std::unique_ptr mScaler_compton = nullptr; + double mPoisson[3] = {0.0, 0.0, 0.0}; // Mu, Min and Max of Poissonian + double mGauss[4] = {0.0, 0.0, 0.0, 0.0}; // Mean, Std, Min, Max + std::vector> mGenPairs; + std::vector> mGenElectrons; + unsigned int mNLoopersPairs = -1; + unsigned int mNLoopersCompton = -1; + std::array mMultiplier = {1., 1.}; + bool mPoissonSet = false; + bool mGaussSet = false; + // Random number generator + TRandom3 mRandGen; + int mCurrentEvent = 0; // Current event number, used for adaptive loopers + TFile* mContextFile = nullptr; // Input collision context file + o2::steer::DigitizationContext* mCollisionContext = nullptr; // Pointer to the digitization context + std::vector mInteractionTimeRecords; // Interaction time records from collision context + Bool_t mFlatGas = false; // Flag to indicate if flat gas loopers are used + Bool_t mFlatGasOrbit = false; // Flag to indicate if flat gas loopers are per orbit + Int_t mFlatGasNumber = -1; // Number of flat gas loopers per event + double mIntTimeRecMean = 1.0; // Average interaction time record used for the reference + double mTimeLimit = 0.0; // Time limit for the current event + double mTimeEnd = 0.0; // Time limit for the last event + float mLoopsFractionPairs = 0.08; // Fraction of loopers from Pairs + int mInteractionRate = 50000; // Interaction rate in Hz +}; +#endif // GENERATORS_WITH_TPCLOOPERS + +} // namespace eventgen +} // namespace o2 + +#endif // ALICEO2_EVENTGEN_TPCLOOPERS_H_ \ No newline at end of file diff --git a/Generators/include/Generators/TPCLoopersParam.h b/Generators/include/Generators/TPCLoopersParam.h new file mode 100644 index 0000000000000..87e4510d6e617 --- /dev/null +++ b/Generators/include/Generators/TPCLoopersParam.h @@ -0,0 +1,54 @@ +// Copyright 2024-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \author M+Giacalone - September 2025 + +#ifndef ALICEO2_EVENTGEN_TPCLOOPERSPARAM_H_ +#define ALICEO2_EVENTGEN_TPCLOOPERSPARAM_H_ + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2 +{ +namespace eventgen +{ + +/** + ** a parameter class/struct to keep the settings of + ** the TPC loopers event-generator and + ** allow the user to modify them + **/ +struct GenTPCLoopersParam : public o2::conf::ConfigurableParamHelper { + bool loopersVeto = false; // if true, no loopers are generated + // Current files are set to custom user CCDB paths, TO BE CHANGED + std::string model_pairs = "ccdb://Users/m/mgiacalo/WGAN_ExtGenPair"; // ONNX model for e+e- pair production + std::string model_compton = "ccdb://Users/m/mgiacalo/WGAN_ExtGenCompton"; // ONNX model for Compton scattering + std::string poisson = "${O2_ROOT}/share/Generators/TPCLoopers/poisson_params.csv"; // file with Poissonian parameters + std::string gauss = "${O2_ROOT}/share/Generators/TPCLoopers/gaussian_params.csv"; // file with Gaussian parameters + std::string scaler_pair = "${O2_ROOT}/share/Generators/TPCLoopers/ScalerPairParams.json"; // file with scaler parameters for e+e- pair production + std::string scaler_compton = "${O2_ROOT}/share/Generators/TPCLoopers/ScalerComptonParams.json"; // file with scaler parameters for Compton scattering + std::string nclxrate = "ccdb://Users/m/mgiacalo/ClustersTrackRatio"; // file with clusters/rate information per orbit + std::string colsys = "PbPb"; // collision system (PbPb or pp) + int intrate = -1; // Automatic IR from collision context if -1, else user-defined interaction rate in Hz + bool flat_gas = true; // if true, the gas density is considered flat in the TPC volume + unsigned int nFlatGasLoopers = 500; // number of loopers to be generated per event in case of flat gas [currently unused, kept for possible future debug developments] + float fraction_pairs = 0.08; // fraction of loopers [currently unused, kept for possible future debug developments] + float multiplier[2] = {1., 1.}; // multiplier for pairs and compton loopers for Poissonian and Gaussian sampling + unsigned int fixedNLoopers[2] = {1, 1}; // fixed number of loopers coming from pairs and compton electrons - valid if flat gas is false and both Poisson and Gaussian params files are empty + float adjust_flatgas = 0.f; // adjustment for the number of flat gas loopers per orbit (in percentage, e.g. -0.1 = -10%) [-1, inf)] + O2ParamDef(GenTPCLoopersParam, "GenTPCLoopers"); +}; + +} // end namespace eventgen +} // end namespace o2 + +#endif // ALICEO2_EVENTGEN_TPCLOOPERSPARAM_H_ diff --git a/Generators/share/TPCLoopers/README.md b/Generators/share/TPCLoopers/README.md new file mode 100644 index 0000000000000..0e0ac858b8809 --- /dev/null +++ b/Generators/share/TPCLoopers/README.md @@ -0,0 +1,79 @@ +# TPC Loopers Generator - Parameter Files + +This directory contains parameter files used by the TPC Loopers event generator in ALICE O2. + +## Overview + +The TPC Loopers generator uses pre-trained ONNX models to generate realistic looper particles based on machine learning models trained on full GEANT4 slow neutron transport simulations. The parameter files in this directory provide: +- Example statistical distribution parameters for sampling the number of loopers per event +- **Mandatory** scaling parameters for transforming the ONNX model outputs to physical values + +## Files Description + +### Statistical Sampling Parameters + +The files provided in the folder are examples based on the training dataset. + +#### `gaussian_params.csv` +Parameters for Gaussian distribution used to sample the number of Compton electrons per event. + +**Format:** Four values (one per line) +1. Mean (μ) +2. Standard deviation (σ) +3. Minimum value +4. Maximum value + +#### `poisson_params.csv` +Parameters for Poisson distribution used to sample the number of electron-positron pairs per event. + +**Format:** Three values (one per line) +1. Lambda (λ) parameter +2. Minimum value +3. Maximum value + +### Scaler Parameters + +These JSON files contain the parameters for inverse transformation of the ONNX models output. They should be kept as they are +unless a new version of the models is released. + +#### `ScalerComptonParams.json` +Scaler parameters for Compton electron generation model. + +**Structure:** +```json +{ + "normal": { + "min": [array of 5 min values for min-max normalization], + "max": [array of 5 max values for min-max normalization] + }, + "outlier": { + "center": [array of 2 center values for robust scaling], + "scale": [array of 2 scale values for robust scaling] + } +} +``` + +- **normal**: Min-max normalization parameters for standard features (`Px`, `Py`, `Pz`, `VertexCoordinatesX`, `VertexCoordinatesY`) +- **outlier**: Robust scaler parameters (center and scale) for outlier features (`VertexCoordinatesZ`,`time`) + +#### `ScalerPairParams.json` +Scaler parameters for electron-positron pair generation model. + +**Structure:** +```json +{ + "normal": { + "min": [array of 8 min values for min-max normalization], + "max": [array of 8 max values for min-max normalization] + }, + "outlier": { + "center": [array of 2 center values for robust scaling], + "scale": [array of 2 scale values for robust scaling] + } +} +``` + +- **normal**: Min-max normalization parameters for standard features (`Px_e`, `Py_e`, `Pz_e`,`Px_p`, `Py_p`, `Pz_p`, `VertexCoordinatesX`, `VertexCoordinatesY`) +- **outlier**: Robust scaler parameters (center and scale) for outlier features (`VertexCoordinatesZ`,`time`) +--- +*Author: M. Giacalone - September 2025* diff --git a/Generators/share/TPCLoopers/ScalerComptonParams.json b/Generators/share/TPCLoopers/ScalerComptonParams.json new file mode 100644 index 0000000000000..157647fee2db7 --- /dev/null +++ b/Generators/share/TPCLoopers/ScalerComptonParams.json @@ -0,0 +1,28 @@ +{ + "normal": { + "min": [ + -0.0108811147511005, + -0.0098758740350604, + -0.0103233363479375, + -260.0542297363281, + -259.80059814453125 + ], + "max": [ + 0.0108060473576188, + 0.0103057539090514, + 0.0106524610891938, + 260.0343933105469, + 259.62890625 + ] + }, + "outlier": { + "center": [ + -71.39387130737305, + 96791.23828125 + ], + "scale": [ + 265.9389114379883, + 230762.30981445312 + ] + } +} \ No newline at end of file diff --git a/Generators/share/TPCLoopers/ScalerPairParams.json b/Generators/share/TPCLoopers/ScalerPairParams.json new file mode 100644 index 0000000000000..57cdac421d3f6 --- /dev/null +++ b/Generators/share/TPCLoopers/ScalerPairParams.json @@ -0,0 +1,34 @@ +{ + "normal": { + "min": [ + -0.0073022879660129, + -0.0077305701561272, + -0.0076750442385673, + -0.0082916170358657, + -0.0079681202769279, + -0.0077468422241508, + -255.6164093017578, + -252.9441680908203 + ], + "max": [ + 0.007688719779253, + 0.0077241472899913, + 0.0075828479602932, + 0.00813714787364, + 0.0083825681358575, + 0.0073839174583554, + 256.2904968261719, + 253.4925842285156 + ] + }, + "outlier": { + "center": [ + -79.66580963134766, + 141535.640625 + ], + "scale": [ + 250.8921127319336, + 222363.16015625 + ] + } +} \ No newline at end of file diff --git a/Generators/share/TPCLoopers/gaussian_params.csv b/Generators/share/TPCLoopers/gaussian_params.csv new file mode 100644 index 0000000000000..8e07c22dd30bf --- /dev/null +++ b/Generators/share/TPCLoopers/gaussian_params.csv @@ -0,0 +1,4 @@ +9.611554230339172022e+01 +1.963570744941765867e+01 +4.300000000000000000e+01 +1.690000000000000000e+02 diff --git a/Generators/share/TPCLoopers/poisson_params.csv b/Generators/share/TPCLoopers/poisson_params.csv new file mode 100644 index 0000000000000..ef26bd973d34c --- /dev/null +++ b/Generators/share/TPCLoopers/poisson_params.csv @@ -0,0 +1,3 @@ +3.165383056343737511e+00 +1.000000000000000000e+00 +1.200000000000000000e+01 diff --git a/Generators/src/Generator.cxx b/Generators/src/Generator.cxx index 9204ede98215e..465a8ffb7ee22 100644 --- a/Generators/src/Generator.cxx +++ b/Generators/src/Generator.cxx @@ -17,11 +17,16 @@ #include "SimulationDataFormat/MCEventHeader.h" #include "SimulationDataFormat/ParticleStatus.h" #include "SimulationDataFormat/MCGenProperties.h" +#include #include "FairPrimaryGenerator.h" #include #include #include "TClonesArray.h" #include "TParticle.h" +#include "TSystem.h" +#include "TGrid.h" +#include "CCDB/BasicCCDBManager.h" +#include namespace o2 { @@ -39,6 +44,25 @@ Generator::Generator() : FairGenerator("ALICEo2", "ALICEo2 Generator"), /** default constructor **/ mThisInstanceID = Generator::InstanceCounter; Generator::InstanceCounter++; +#ifdef GENERATORS_WITH_TPCLOOPERS + const auto& simConfig = o2::conf::SimConfig::Instance(); + const auto& loopersParam = o2::eventgen::GenTPCLoopersParam::Instance(); + if (!loopersParam.loopersVeto) { + bool transport = (simConfig.getMCEngine() != "O2TrivialMCEngine"); + if (transport) { + bool tpcActive = (std::find(simConfig.getReadoutDetectors().begin(), simConfig.getReadoutDetectors().end(), "TPC") != simConfig.getReadoutDetectors().end()); + if (tpcActive) { + if (initTPCLoopersGen()) { + mAddTPCLoopers = kTRUE; + } + } else { + LOG(info) << "TPC not active in readout detectors: loopers fast generator disabled."; + } + } + } else { + LOG(info) << "Loopers fast generator turned OFF with veto flag."; + } +#endif } /*****************************************************************/ @@ -49,7 +73,126 @@ Generator::Generator(const Char_t* name, const Char_t* title) : FairGenerator(na /** constructor **/ mThisInstanceID = Generator::InstanceCounter; Generator::InstanceCounter++; +#ifdef GENERATORS_WITH_TPCLOOPERS + const auto& simConfig = o2::conf::SimConfig::Instance(); + const auto& loopersParam = o2::eventgen::GenTPCLoopersParam::Instance(); + if (!loopersParam.loopersVeto) { + bool transport = (simConfig.getMCEngine() != "O2TrivialMCEngine"); + if (transport) { + bool tpcActive = (std::find(simConfig.getReadoutDetectors().begin(), simConfig.getReadoutDetectors().end(), "TPC") != simConfig.getReadoutDetectors().end()); + if (tpcActive) { + if (initTPCLoopersGen()) { + mAddTPCLoopers = kTRUE; + } + } else { + LOG(info) << "TPC not active in readout detectors: loopers fast generator disabled."; + } + } + } else { + LOG(info) << "Loopers fast generator turned OFF with veto flag."; + } +#endif +} + +/*****************************************************************/ +#ifdef GENERATORS_WITH_TPCLOOPERS +bool Generator::initTPCLoopersGen() +{ + // Expand all environment paths + const auto& loopersParam = o2::eventgen::GenTPCLoopersParam::Instance(); + std::string model_pairs = gSystem->ExpandPathName(loopersParam.model_pairs.c_str()); + std::string model_compton = gSystem->ExpandPathName(loopersParam.model_compton.c_str()); + std::string nclxrate = gSystem->ExpandPathName(loopersParam.nclxrate.c_str()); + const auto& scaler_pair = gSystem->ExpandPathName(loopersParam.scaler_pair.c_str()); + const auto& scaler_compton = gSystem->ExpandPathName(loopersParam.scaler_compton.c_str()); + const auto& poisson = gSystem->ExpandPathName(loopersParam.poisson.c_str()); + const auto& gauss = gSystem->ExpandPathName(loopersParam.gauss.c_str()); + const auto& flat_gas = loopersParam.flat_gas; + const auto& colsys = loopersParam.colsys; + if (flat_gas) { + if (colsys != "PbPb" && colsys != "pp") { + LOG(warning) << "Automatic background loopers configuration supports only 'pp' and 'PbPb' systems."; + LOG(warning) << "Fast loopers generator will remain OFF."; + return kFALSE; + } + bool isContext = std::filesystem::exists("collisioncontext.root"); + if (!isContext) { + LOG(warning) << "Warning: No collisioncontext.root file found!"; + LOG(warning) << "Loopers will be kept OFF."; + return kFALSE; + } + } + std::array multiplier = {loopersParam.multiplier[0], loopersParam.multiplier[1]}; + unsigned int nLoopersPairs = loopersParam.fixedNLoopers[0]; + unsigned int nLoopersCompton = loopersParam.fixedNLoopers[1]; + const std::array models = {model_pairs, model_compton, nclxrate}; + const std::array local_names = {"WGANpair.onnx", "WGANcompton.onnx", "nclxrate.root"}; + const std::array isAlien = {models[0].starts_with("alien://"), models[1].starts_with("alien://"), models[2].starts_with("alien://")}; + const std::array isCCDB = {models[0].starts_with("ccdb://"), models[1].starts_with("ccdb://"), models[2].starts_with("ccdb://")}; + if (std::any_of(isAlien.begin(), isAlien.end(), [](bool v) { return v; })) { + if (!gGrid) { + TGrid::Connect("alien://"); + if (!gGrid) { + LOG(fatal) << "AliEn connection failed, check token."; + exit(1); + } + } + for (size_t i = 0; i < models.size(); ++i) { + if (isAlien[i] && !TFile::Cp(models[i].c_str(), local_names[i].c_str())) { + LOG(fatal) << "Error: Model file " << models[i] << " does not exist!"; + exit(1); + } + } + } + if (std::any_of(isCCDB.begin(), isCCDB.end(), [](bool v) { return v; })) { + auto& ccdb = o2::ccdb::BasicCCDBManager::instance(); + ccdb.setURL("http://alice-ccdb.cern.ch"); + // Get underlying CCDB API from BasicCCDBManager + auto& ccdb_api = ccdb.getCCDBAccessor(); + for (size_t i = 0; i < models.size(); ++i) { + if (isCCDB[i]) { + auto model_path = models[i].substr(7); // Remove "ccdb://" + // Treat filename if provided in the CCDB path + auto extension = model_path.find(".onnx"); + if (extension != std::string::npos) { + auto last_slash = model_path.find_last_of('/'); + model_path = model_path.substr(0, last_slash); + } + std::map filter; + if (!ccdb_api.retrieveBlob(model_path, "./", filter, o2::ccdb::getCurrentTimestamp(), false, local_names[i].c_str())) { + LOG(fatal) << "Error: issues in retrieving " << model_path << " from CCDB!"; + exit(1); + } + } + } + } + model_pairs = isAlien[0] || isCCDB[0] ? local_names[0] : model_pairs; + model_compton = isAlien[1] || isCCDB[1] ? local_names[1] : model_compton; + nclxrate = isAlien[2] || isCCDB[2] ? local_names[2] : nclxrate; + try { + // Create the TPC loopers generator with the provided parameters + mTPCLoopersGen = std::make_unique(model_pairs, model_compton, poisson, gauss, scaler_pair, scaler_compton); + const auto& intrate = loopersParam.intrate; + // Configure the generator with flat gas loopers defined per orbit with clusters/track info + // If intrate is negative (default), automatic IR from collisioncontext.root will be used + if (flat_gas) { + mTPCLoopersGen->SetRate(nclxrate, (colsys == "PbPb") ? true : false, intrate); + mTPCLoopersGen->SetAdjust(loopersParam.adjust_flatgas); + } else { + // Otherwise, Poisson+Gauss sampling or fixed number of loopers per event will be used + // Multiplier is applied only with distribution sampling + // This configuration can be used for testing purposes, in all other cases flat gas is recommended + mTPCLoopersGen->SetNLoopers(nLoopersPairs, nLoopersCompton); + mTPCLoopersGen->SetMultiplier(multiplier); + } + LOG(info) << "TPC Loopers generator initialized successfully"; + } catch (const std::exception& e) { + LOG(error) << "Failed to initialize TPC Loopers generator: " << e.what(); + mTPCLoopersGen.reset(); + } + return kTRUE; } +#endif /*****************************************************************/ @@ -64,6 +207,41 @@ Bool_t /*****************************************************************/ +Bool_t + Generator::finalizeEvent() +{ +#ifdef GENERATORS_WITH_TPCLOOPERS + if (mAddTPCLoopers) { + if (!mTPCLoopersGen) { + LOG(error) << "Loopers generator not initialized"; + return kFALSE; + } + + // Generate loopers using the initialized TPC loopers generator + if (!mTPCLoopersGen->generateEvent()) { + LOG(error) << "Failed to generate loopers event"; + return kFALSE; + } + if (mTPCLoopersGen->getNLoopers() == 0) { + LOG(warning) << "No loopers generated for this event"; + return kTRUE; + } + const auto& looperParticles = mTPCLoopersGen->importParticles(); + if (looperParticles.empty()) { + LOG(error) << "Failed to import loopers particles"; + return kFALSE; + } + // Append the generated looper particles to the main particle list + mParticles.insert(mParticles.end(), looperParticles.begin(), looperParticles.end()); + + LOG(debug) << "Added " << looperParticles.size() << " looper particles"; + } +#endif + return kTRUE; +} + +/*****************************************************************/ + Bool_t Generator::ReadEvent(FairPrimaryGenerator* primGen) { @@ -91,6 +269,12 @@ Bool_t return kFALSE; } + /** Event finalization**/ + if (!finalizeEvent()) { + LOG(error) << "ReadEvent failed in finalizeEvent"; + return kFALSE; + } + if (mSubGeneratorsIdToDesc.empty() && mSubGeneratorId > -1) { LOG(fatal) << "ReadEvent failed because no SubGenerator description given"; } diff --git a/Generators/src/GeneratorsLinkDef.h b/Generators/src/GeneratorsLinkDef.h index 2b8d42f86bf9b..24b3f2e452498 100644 --- a/Generators/src/GeneratorsLinkDef.h +++ b/Generators/src/GeneratorsLinkDef.h @@ -35,6 +35,10 @@ #pragma link C++ class o2::eventgen::GeneratorFromEventPool + ; #pragma link C++ class o2::eventgen::GeneratorEventPoolParam + ; #pragma link C++ class o2::eventgen::EventPoolGenConfig + ; +#ifdef GENERATORS_WITH_TPCLOOPERS +#pragma link C++ class o2::eventgen::GenTPCLoopers + ; +#pragma link C++ class o2::eventgen::GenTPCLoopersParam + ; +#endif #pragma link C++ class o2::conf::ConfigurableParamPromoter < o2::eventgen::GeneratorEventPoolParam, o2::eventgen::EventPoolGenConfig> + ; #ifdef GENERATORS_WITH_HEPMC3 #pragma link C++ class o2::eventgen::GeneratorHepMC + ; diff --git a/Generators/src/TPCLoopers.cxx b/Generators/src/TPCLoopers.cxx new file mode 100644 index 0000000000000..6e5af7c0c84d8 --- /dev/null +++ b/Generators/src/TPCLoopers.cxx @@ -0,0 +1,486 @@ +// Copyright 2024-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \author M+Giacalone - September 2025 + +#include "Generators/TPCLoopers.h" +#include "CCDB/CCDBTimeStampUtils.h" +#include "CCDB/CcdbApi.h" +#include "DetectorsRaw/HBFUtils.h" +#include "TF1.h" +#include +#include +#include "SimulationDataFormat/MCGenProperties.h" +#include +#include +#include "TDatabasePDG.h" + +// Static Ort::Env instance for multiple onnx model loading +Ort::Env global_env(ORT_LOGGING_LEVEL_WARNING, "GlobalEnv"); + +// This class is responsible for loading the scaler parameters from a JSON file +// and applying the inverse transformation to the generated data. + +void Scaler::load(const std::string& filename) +{ + std::ifstream file(filename); + if (!file.is_open()) { + throw std::runtime_error("Error: Could not open scaler file!"); + } + + std::string json_str((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + file.close(); + + rapidjson::Document doc; + doc.Parse(json_str.c_str()); + + if (doc.HasParseError()) { + throw std::runtime_error("Error: JSON parsing failed!"); + } + + normal_min = jsonArrayToVector(doc["normal"]["min"]); + normal_max = jsonArrayToVector(doc["normal"]["max"]); + outlier_center = jsonArrayToVector(doc["outlier"]["center"]); + outlier_scale = jsonArrayToVector(doc["outlier"]["scale"]); +} + +std::vector Scaler::inverse_transform(const std::vector& input) +{ + std::vector output; + for (int i = 0; i < input.size(); ++i) { + if (i < input.size() - 2) { + output.push_back(input[i] * (normal_max[i] - normal_min[i]) + normal_min[i]); + } else { + output.push_back(input[i] * outlier_scale[i - (input.size() - 2)] + outlier_center[i - (input.size() - 2)]); + } + } + + return output; +} + +std::vector Scaler::jsonArrayToVector(const rapidjson::Value& jsonArray) +{ + std::vector vec; + for (int i = 0; i < jsonArray.Size(); ++i) { + vec.push_back(jsonArray[i].GetDouble()); + } + return vec; +} + +// This class loads the ONNX model and generates samples using it. + +ONNXGenerator::ONNXGenerator(Ort::Env& shared_env, const std::string& model_path) + : env(shared_env), session(env, model_path.c_str(), Ort::SessionOptions{}) +{ + // Create session options + Ort::SessionOptions session_options; + session = Ort::Session(env, model_path.c_str(), session_options); +} + +std::vector ONNXGenerator::generate_sample() +{ + Ort::AllocatorWithDefaultOptions allocator; + + // Generate a latent vector (z) + std::vector z(100); + for (auto& v : z) { + v = rand_gen.Gaus(0.0, 1.0); + } + + // Prepare input tensor + std::vector input_shape = {1, 100}; + // Get memory information + Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault); + + // Create input tensor correctly + Ort::Value input_tensor = Ort::Value::CreateTensor( + memory_info, z.data(), z.size(), input_shape.data(), input_shape.size()); + // Run inference + const char* input_names[] = {"z"}; + const char* output_names[] = {"output"}; + auto output_tensors = session.Run(Ort::RunOptions{nullptr}, input_names, &input_tensor, 1, output_names, 1); + + // Extract output + float* output_data = output_tensors.front().GetTensorMutableData(); + // Get the size of the output tensor + auto output_tensor_info = output_tensors.front().GetTensorTypeAndShapeInfo(); + size_t output_data_size = output_tensor_info.GetElementCount(); // Total number of elements in the tensor + std::vector output; + for (int i = 0; i < output_data_size; ++i) { + output.push_back(output_data[i]); + } + + return output; +} + +namespace o2 +{ +namespace eventgen +{ + +GenTPCLoopers::GenTPCLoopers(std::string model_pairs, std::string model_compton, + std::string poisson, std::string gauss, std::string scaler_pair, + std::string scaler_compton) +{ + // Checking if the model files exist and are not empty + std::ifstream model_file[2]; + model_file[0].open(model_pairs); + model_file[1].open(model_compton); + if (!model_file[0].is_open() || model_file[0].peek() == std::ifstream::traits_type::eof()) { + LOG(fatal) << "Error: Pairs model file is empty or does not exist!"; + exit(1); + } + if (!model_file[1].is_open() || model_file[1].peek() == std::ifstream::traits_type::eof()) { + LOG(fatal) << "Error: Compton model file is empty or does not exist!"; + exit(1); + } + model_file[0].close(); + model_file[1].close(); + // Checking if the scaler files exist and are not empty + std::ifstream scaler_file[2]; + scaler_file[0].open(scaler_pair); + scaler_file[1].open(scaler_compton); + if (!scaler_file[0].is_open() || scaler_file[0].peek() == std::ifstream::traits_type::eof()) { + LOG(fatal) << "Error: Pairs scaler file is empty or does not exist!"; + exit(1); + } + if (!scaler_file[1].is_open() || scaler_file[1].peek() == std::ifstream::traits_type::eof()) { + LOG(fatal) << "Error: Compton scaler file is empty or does not exist!"; + exit(1); + } + scaler_file[0].close(); + scaler_file[1].close(); + // Checking if the poisson file exists and it's not empty + if (poisson != "" && poisson != "None" && poisson != "none") { + std::ifstream poisson_file(poisson); + if (!poisson_file.is_open() || poisson_file.peek() == std::ifstream::traits_type::eof()) { + LOG(fatal) << "Error: Poisson file is empty or does not exist!"; + exit(1); + } else { + poisson_file >> mPoisson[0] >> mPoisson[1] >> mPoisson[2]; + poisson_file.close(); + mPoissonSet = true; + } + } + // Checking if the gauss file exists and it's not empty + if (gauss != "" && gauss != "None" && gauss != "none") { + std::ifstream gauss_file(gauss); + if (!gauss_file.is_open() || gauss_file.peek() == std::ifstream::traits_type::eof()) { + LOG(fatal) << "Error: Gauss file is empty or does not exist!"; + exit(1); + } else { + gauss_file >> mGauss[0] >> mGauss[1] >> mGauss[2] >> mGauss[3]; + gauss_file.close(); + mGaussSet = true; + } + } + mONNX_pair = std::make_unique(global_env, model_pairs); + mScaler_pair = std::make_unique(); + mScaler_pair->load(scaler_pair); + mONNX_compton = std::make_unique(global_env, model_compton); + mScaler_compton = std::make_unique(); + mScaler_compton->load(scaler_compton); +} + +Bool_t GenTPCLoopers::generateEvent() +{ + // Clear the vector of pairs + mGenPairs.clear(); + // Clear the vector of compton electrons + mGenElectrons.clear(); + if (mFlatGas) { + unsigned int nLoopers, nLoopersPairs, nLoopersCompton; + LOG(debug) << "mCurrentEvent is " << mCurrentEvent; + LOG(debug) << "Current event time: " << ((mCurrentEvent < mInteractionTimeRecords.size() - 1) ? std::to_string(mInteractionTimeRecords[mCurrentEvent + 1].bc2ns() - mInteractionTimeRecords[mCurrentEvent].bc2ns()) : std::to_string(mTimeEnd - mInteractionTimeRecords[mCurrentEvent].bc2ns())) << " ns"; + LOG(debug) << "Current time offset wrt BC: " << mInteractionTimeRecords[mCurrentEvent].getTimeOffsetWrtBC() << " ns"; + mTimeLimit = (mCurrentEvent < mInteractionTimeRecords.size() - 1) ? mInteractionTimeRecords[mCurrentEvent + 1].bc2ns() - mInteractionTimeRecords[mCurrentEvent].bc2ns() : mTimeEnd - mInteractionTimeRecords[mCurrentEvent].bc2ns(); + // With flat gas the number of loopers are adapted based on time interval widths + // The denominator is either the LHC orbit (if mFlatGasOrbit is true) or the mean interaction time record interval + nLoopers = mFlatGasOrbit ? (mFlatGasNumber * (mTimeLimit / o2::constants::lhc::LHCOrbitNS)) : (mFlatGasNumber * (mTimeLimit / mIntTimeRecMean)); + nLoopersPairs = static_cast(std::round(nLoopers * mLoopsFractionPairs)); + nLoopersCompton = nLoopers - nLoopersPairs; + SetNLoopers(nLoopersPairs, nLoopersCompton); + LOG(info) << "Flat gas loopers: " << nLoopers << " (pairs: " << nLoopersPairs << ", compton: " << nLoopersCompton << ")"; + generateEvent(mTimeLimit); + mCurrentEvent++; + } else { + // Set number of loopers if poissonian params are available + if (mPoissonSet) { + mNLoopersPairs = static_cast(std::round(mMultiplier[0] * PoissonPairs())); + LOG(debug) << "Generated loopers pairs (Poisson): " << mNLoopersPairs; + } + if (mGaussSet) { + mNLoopersCompton = static_cast(std::round(mMultiplier[1] * GaussianElectrons())); + LOG(debug) << "Generated compton electrons (Gauss): " << mNLoopersCompton; + } + // Generate pairs + for (int i = 0; i < mNLoopersPairs; ++i) { + std::vector pair = mONNX_pair->generate_sample(); + // Apply the inverse transformation using the scaler + std::vector transformed_pair = mScaler_pair->inverse_transform(pair); + mGenPairs.push_back(transformed_pair); + } + // Generate compton electrons + for (int i = 0; i < mNLoopersCompton; ++i) { + std::vector electron = mONNX_compton->generate_sample(); + // Apply the inverse transformation using the scaler + std::vector transformed_electron = mScaler_compton->inverse_transform(electron); + mGenElectrons.push_back(transformed_electron); + } + } + return true; +} + +Bool_t GenTPCLoopers::generateEvent(double time_limit) +{ + LOG(info) << "Time constraint for loopers: " << time_limit << " ns"; + // Generate pairs + for (int i = 0; i < mNLoopersPairs; ++i) { + std::vector pair = mONNX_pair->generate_sample(); + // Apply the inverse transformation using the scaler + std::vector transformed_pair = mScaler_pair->inverse_transform(pair); + transformed_pair[9] = gRandom->Uniform(0., time_limit); // Regenerate time, scaling is not needed because time_limit is already in nanoseconds + mGenPairs.push_back(transformed_pair); + } + // Generate compton electrons + for (int i = 0; i < mNLoopersCompton; ++i) { + std::vector electron = mONNX_compton->generate_sample(); + // Apply the inverse transformation using the scaler + std::vector transformed_electron = mScaler_compton->inverse_transform(electron); + transformed_electron[6] = gRandom->Uniform(0., time_limit); // Regenerate time, scaling is not needed because time_limit is already in nanoseconds + mGenElectrons.push_back(transformed_electron); + } + LOG(info) << "Generated Particles with time limit"; + return true; +} + +std::vector GenTPCLoopers::importParticles() +{ + std::vector particles; + const double mass_e = TDatabasePDG::Instance()->GetParticle(11)->Mass(); + const double mass_p = TDatabasePDG::Instance()->GetParticle(-11)->Mass(); + // Get looper pairs from the event + for (auto& pair : mGenPairs) { + double px_e, py_e, pz_e, px_p, py_p, pz_p; + double vx, vy, vz, time; + double e_etot, p_etot; + px_e = pair[0]; + py_e = pair[1]; + pz_e = pair[2]; + px_p = pair[3]; + py_p = pair[4]; + pz_p = pair[5]; + vx = pair[6]; + vy = pair[7]; + vz = pair[8]; + time = pair[9]; + e_etot = TMath::Sqrt(px_e * px_e + py_e * py_e + pz_e * pz_e + mass_e * mass_e); + p_etot = TMath::Sqrt(px_p * px_p + py_p * py_p + pz_p * pz_p + mass_p * mass_p); + // Push the electron + TParticle electron(11, 1, -1, -1, -1, -1, px_e, py_e, pz_e, e_etot, vx, vy, vz, time / 1e9); + electron.SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(electron.GetStatusCode(), 0).fullEncoding); + electron.SetBit(ParticleStatus::kToBeDone, // + o2::mcgenstatus::getHepMCStatusCode(electron.GetStatusCode()) == 1); + particles.push_back(electron); + // Push the positron + TParticle positron(-11, 1, -1, -1, -1, -1, px_p, py_p, pz_p, p_etot, vx, vy, vz, time / 1e9); + positron.SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(positron.GetStatusCode(), 0).fullEncoding); + positron.SetBit(ParticleStatus::kToBeDone, // + o2::mcgenstatus::getHepMCStatusCode(positron.GetStatusCode()) == 1); + particles.push_back(positron); + } + // Get compton electrons from the event + for (auto& compton : mGenElectrons) { + double px, py, pz; + double vx, vy, vz, time; + double etot; + px = compton[0]; + py = compton[1]; + pz = compton[2]; + vx = compton[3]; + vy = compton[4]; + vz = compton[5]; + time = compton[6]; + etot = TMath::Sqrt(px * px + py * py + pz * pz + mass_e * mass_e); + // Push the electron + TParticle electron(11, 1, -1, -1, -1, -1, px, py, pz, etot, vx, vy, vz, time / 1e9); + electron.SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(electron.GetStatusCode(), 0).fullEncoding); + electron.SetBit(ParticleStatus::kToBeDone, // + o2::mcgenstatus::getHepMCStatusCode(electron.GetStatusCode()) == 1); + particles.push_back(electron); + } + + return particles; +} + +unsigned int GenTPCLoopers::PoissonPairs() +{ + unsigned int poissonValue; + do { + // Generate a Poisson-distributed random number with mean mPoisson[0] + poissonValue = mRandGen.Poisson(mPoisson[0]); + } while (poissonValue < mPoisson[1] || poissonValue > mPoisson[2]); // Regenerate if out of range + + return poissonValue; +} + +unsigned int GenTPCLoopers::GaussianElectrons() +{ + unsigned int gaussValue; + do { + // Generate a Normal-distributed random number with mean mGass[0] and stddev mGauss[1] + gaussValue = mRandGen.Gaus(mGauss[0], mGauss[1]); + } while (gaussValue < mGauss[2] || gaussValue > mGauss[3]); // Regenerate if out of range + + return gaussValue; +} + +void GenTPCLoopers::SetNLoopers(unsigned int nsig_pair, unsigned int nsig_compton) +{ + if (mFlatGas) { + mNLoopersPairs = nsig_pair; + mNLoopersCompton = nsig_compton; + } else { + if (mPoissonSet) { + LOG(info) << "Poissonian parameters correctly loaded."; + } else { + mNLoopersPairs = nsig_pair; + } + if (mGaussSet) { + LOG(info) << "Gaussian parameters correctly loaded."; + } else { + mNLoopersCompton = nsig_compton; + } + } +} + +void GenTPCLoopers::SetMultiplier(const std::array& mult) +{ + // Multipliers will work only if the poissonian and gaussian parameters are set + // otherwise they will be ignored + if (mult[0] < 0 || mult[1] < 0) { + LOG(fatal) << "Error: Multiplier values must be non-negative!"; + exit(1); + } else { + LOG(info) << "Multiplier values set to: Pair = " << mult[0] << ", Compton = " << mult[1]; + mMultiplier[0] = mult[0]; + mMultiplier[1] = mult[1]; + } +} + +void GenTPCLoopers::setFlatGas(Bool_t flat, Int_t number, Int_t nloopers_orbit) +{ + mFlatGas = flat; + if (mFlatGas) { + if (nloopers_orbit > 0) { + mFlatGasOrbit = true; + mFlatGasNumber = nloopers_orbit; + LOG(info) << "Flat gas loopers will be generated using orbit reference."; + } else { + mFlatGasOrbit = false; + if (number < 0) { + LOG(warn) << "Warning: Number of loopers per event must be non-negative! Switching option off."; + mFlatGas = false; + mFlatGasNumber = -1; + } else { + mFlatGasNumber = number; + } + } + if (mFlatGas) { + mContextFile = std::filesystem::exists("collisioncontext.root") ? TFile::Open("collisioncontext.root") : nullptr; + mCollisionContext = mContextFile ? (o2::steer::DigitizationContext*)mContextFile->Get("DigitizationContext") : nullptr; + mInteractionTimeRecords = mCollisionContext ? mCollisionContext->getEventRecords() : std::vector{}; + if (mInteractionTimeRecords.empty()) { + LOG(error) << "Error: No interaction time records found in the collision context!"; + exit(1); + } else { + LOG(info) << "Interaction Time records has " << mInteractionTimeRecords.size() << " entries."; + mCollisionContext->printCollisionSummary(); + } + for (int c = 0; c < mInteractionTimeRecords.size() - 1; c++) { + mIntTimeRecMean += mInteractionTimeRecords[c + 1].bc2ns() - mInteractionTimeRecords[c].bc2ns(); + } + mIntTimeRecMean /= (mInteractionTimeRecords.size() - 1); // Average interaction time record used as reference + const auto& hbfUtils = o2::raw::HBFUtils::Instance(); + // Get the start time of the second orbit after the last interaction record + const auto& lastIR = mInteractionTimeRecords.back(); + o2::InteractionRecord finalOrbitIR(0, lastIR.orbit + 2); // Final orbit, BC = 0 + mTimeEnd = finalOrbitIR.bc2ns(); + LOG(debug) << "Final orbit start time: " << mTimeEnd << " ns while last interaction record time is " << mInteractionTimeRecords.back().bc2ns() << " ns"; + } + } else { + mFlatGasNumber = -1; + } + LOG(info) << "Flat gas loopers: " << (mFlatGas ? "ON" : "OFF") << ", Reference loopers number per " << (mFlatGasOrbit ? "orbit " : "event ") << mFlatGasNumber; +} + +void GenTPCLoopers::setFractionPairs(float fractionPairs) +{ + if (fractionPairs < 0 || fractionPairs > 1) { + LOG(fatal) << "Error: Loops fraction for pairs must be in the range [0, 1]."; + exit(1); + } + mLoopsFractionPairs = fractionPairs; + LOG(info) << "Pairs fraction set to: " << mLoopsFractionPairs; +} + +void GenTPCLoopers::SetRate(const std::string& rateFile, bool isPbPb = true, int intRate) +{ + // Checking if the rate file exists and is not empty + TFile rate_file(rateFile.c_str(), "READ"); + if (!rate_file.IsOpen() || rate_file.IsZombie()) { + LOG(fatal) << "Error: Rate file is empty or does not exist!"; + exit(1); + } + const char* fitName = isPbPb ? "fitPbPb" : "fitpp"; + auto fit = (TF1*)rate_file.Get(fitName); + if (!fit) { + LOG(fatal) << "Error: Could not find fit function '" << fitName << "' in rate file!"; + exit(1); + } + mInteractionRate = intRate; + if (mInteractionRate < 0) { + mContextFile = std::filesystem::exists("collisioncontext.root") ? TFile::Open("collisioncontext.root") : nullptr; + if (!mContextFile || mContextFile->IsZombie()) { + LOG(fatal) << "Error: Interaction rate not provided and collision context file not found!"; + exit(1); + } + mCollisionContext = (o2::steer::DigitizationContext*)mContextFile->Get("DigitizationContext"); + mInteractionRate = std::floor(mCollisionContext->getDigitizerInteractionRate()); + LOG(info) << "Interaction rate retrieved from collision context: " << mInteractionRate << " Hz"; + if (mInteractionRate < 0) { + LOG(fatal) << "Error: Invalid interaction rate retrieved from collision context!"; + exit(1); + } + } + auto ref = static_cast(std::floor(fit->Eval(mInteractionRate / 1000.))); // fit expects rate in kHz + rate_file.Close(); + if (ref <= 0) { + LOG(fatal) << "Computed flat gas number reference per orbit is <=0"; + exit(1); + } else { + LOG(info) << "Set flat gas number to " << ref << " loopers per orbit using " << fitName << " from " << mInteractionRate << " Hz interaction rate."; + auto flat = true; + setFlatGas(flat, -1, ref); + } +} + +void GenTPCLoopers::SetAdjust(float adjust) +{ + if (mFlatGas && mFlatGasOrbit && adjust >= -1.f && adjust != 0.f) { + LOG(info) << "Adjusting flat gas number per orbit by " << adjust * 100.f << "%"; + mFlatGasNumber = static_cast(std::round(mFlatGasNumber * (1.f + adjust))); + LOG(info) << "New flat gas number per orbit: " << mFlatGasNumber; + } +} + +} // namespace eventgen +} // namespace o2 \ No newline at end of file diff --git a/Generators/src/TPCLoopersParam.cxx b/Generators/src/TPCLoopersParam.cxx new file mode 100644 index 0000000000000..0202a8ced0535 --- /dev/null +++ b/Generators/src/TPCLoopersParam.cxx @@ -0,0 +1,15 @@ +// Copyright 2024-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \author M+Giacalone - September 2025 + +#include "Generators/TPCLoopersParam.h" +O2ParamImpl(o2::eventgen::GenTPCLoopersParam); diff --git a/prodtests/full_system_test.sh b/prodtests/full_system_test.sh index 6408588d46e68..e2ecca590140f 100755 --- a/prodtests/full_system_test.sh +++ b/prodtests/full_system_test.sh @@ -54,8 +54,8 @@ O2SIMSEED=${O2SIMSEED:-0} SPLITTRDDIGI=${SPLITTRDDIGI:-1} DIGITDOWNSCALINGTRD=${DIGITDOWNSCALINGTRD:-1000} NHBPERTF=${NHBPERTF:-128} -RUNFIRSTORBIT=${RUNFIRSTORBIT:-0} -FIRSTSAMPLEDORBIT=${FIRSTSAMPLEDORBIT:-0} +RUNFIRSTORBIT=${RUNFIRSTORBIT:-256} +FIRSTSAMPLEDORBIT=${FIRSTSAMPLEDORBIT:-256} OBLIGATORYSOR=${OBLIGATORYSOR:-false} FST_TPC_ZSVERSION=${FST_TPC_ZSVERSION:-4} TPC_SLOW_REALISITC_FULL_SIM=${TPC_SLOW_REALISITC_FULL_SIM:-0} @@ -137,11 +137,40 @@ if [[ $TPC_SLOW_REALISITC_FULL_SIM == 1 ]]; then DIGITOPTKEY+="TPCEleParam.doCommonModePerPad=0;TPCEleParam.doIonTailPerPad=1;TPCEleParam.commonModeCoupling=0;TPCEleParam.doNoiseEmptyPads=1;TPCEleParam.doSaturationTail=0;TPCDetParam.TPCRecoWindowSim=10;" fi -taskwrapper sim.log o2-sim ${FST_BFIELD+--field=}${FST_BFIELD} --seed $O2SIMSEED -n $NEvents --configKeyValues "\"$SIMOPTKEY\"" -g ${FST_GENERATOR} -e ${FST_MC_ENGINE} -j $NJOBS --run ${RUNNUMBER} -o o2sim +# Create collision context +SIGNALSPEC="o2sim,${FST_COLRATE},1000000:1000000" +QEDSPEC="" +if [[ $FST_QED == 1 ]]; then + PbPbXSec="8." + QEDXSECRATIO=$(awk "BEGIN {printf \"%.2f\",`grep xSectionQED qed/qedgenparam.ini | cut -d'=' -f 2`/$PbPbXSec}") + QEDRATE=$(awk "BEGIN {printf \"%.2f\",${FST_COLRATE}*${QEDXSECRATIO}}") + QEDSPEC="--QEDinteraction qed,${QEDRATE},10000000:${NEventsQED}" +fi + +taskwrapper collcontext.log o2-steer-colcontexttool \ + -i ${SIGNALSPEC} \ + --show-context \ + --timeframeID 0 \ + --orbitsPerTF ${NHBPERTF} \ + --orbits $(( ${NTIMEFRAMES} * ${NHBPERTF} )) \ + --seed ${O2SIMSEED} \ + --noEmptyTF \ + --first-orbit ${RUNFIRSTORBIT} \ + --extract-per-timeframe tf:o2sim \ + --with-vertices kCCDB \ + --maxCollsPerTF ${NEvents} \ + --orbitsEarly 1 \ + --bcPatternFile ccdb \ + ${QEDSPEC} + +# Include collision system for TPC loopers generation +SIMOPTKEY+="GenTPCLoopers.colsys=${BEAMTYPE};" + +taskwrapper sim.log o2-sim ${FST_BFIELD+--field=}${FST_BFIELD} --vertexMode kCollContext --seed $O2SIMSEED -n $NEvents --configKeyValues "\"$SIMOPTKEY\"" -g ${FST_GENERATOR} -e ${FST_MC_ENGINE} -j $NJOBS --run ${RUNNUMBER} -o o2sim --fromCollContext collisioncontext.root:o2sim if [[ $DO_EMBEDDING == 1 ]]; then taskwrapper embed.log o2-sim ${FST_BFIELD+--field=}${FST_BFIELD} -j $NJOBS --run ${RUNNUMBER} -n $NEvents -g pythia8pp -e ${FST_MC_ENGINE} -o sig --configKeyValues ${FST_EMBEDDING_CONFIG} --embedIntoFile o2sim_MCHeader.root fi -taskwrapper digi.log o2-sim-digitizer-workflow -n $NEvents ${DIGIQED} ${NOMCLABELS} --sims ${SIM_SOURCES} --tpc-lanes $((NJOBS < 36 ? NJOBS : 36)) --shm-segment-size $SHMSIZE ${GLOBALDPLOPT} ${DIGITOPT} --configKeyValues "\"${DIGITOPTKEY}\"" --interactionRate $FST_COLRATE --early-forward-policy always +taskwrapper digi.log o2-sim-digitizer-workflow -n $NEvents ${DIGIQED} ${NOMCLABELS} --sims ${SIM_SOURCES} --tpc-lanes $((NJOBS < 36 ? NJOBS : 36)) --shm-segment-size $SHMSIZE ${GLOBALDPLOPT} ${DIGITOPT} --configKeyValues "\"${DIGITOPTKEY}\"" --interactionRate $FST_COLRATE --early-forward-policy always --incontext collisioncontext.root [[ $SPLITTRDDIGI == "1" ]] && taskwrapper digiTRD.log o2-sim-digitizer-workflow -n $NEvents ${NOMCLABELS} --sims ${SIM_SOURCES} --onlyDet TRD --trd-digit-downscaling ${DIGITDOWNSCALINGTRD} --shm-segment-size $SHMSIZE ${GLOBALDPLOPT} --incontext collisioncontext.root --configKeyValues "\"${DIGITOPTKEYTRD}\"" --early-forward-policy always touch digiTRD.log_done From 52b0e23ac5a60f3953749a91a29285b67d7d1558 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 28 Jan 2026 15:32:46 +0100 Subject: [PATCH 021/133] DPL: disable early forwarding for output proxies --- Framework/Core/src/DataProcessingDevice.cxx | 9 +++++++++ Framework/Core/src/ExternalFairMQDeviceProxy.cxx | 2 ++ 2 files changed, 11 insertions(+) diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 3eaab36fb7908..fd03b7725d847 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -1070,6 +1070,15 @@ void DataProcessingDevice::fillContext(DataProcessorContext& context, DeviceCont break; } } + // Output proxies should wait for the completion policy before forwarding. + // Because they actually do not do anything, that's equivalent to + // forwarding after the processing. + for (auto& label : spec.labels) { + if (label.value == "output-proxy") { + defaultEarlyForwardPolicy = ForwardPolicy::AfterProcessing; + break; + } + } /// We must make sure there is no optional /// if we want to optimize the forwarding diff --git a/Framework/Core/src/ExternalFairMQDeviceProxy.cxx b/Framework/Core/src/ExternalFairMQDeviceProxy.cxx index b4bfc991db9ae..3b0275879a158 100644 --- a/Framework/Core/src/ExternalFairMQDeviceProxy.cxx +++ b/Framework/Core/src/ExternalFairMQDeviceProxy.cxx @@ -1045,6 +1045,7 @@ DataProcessorSpec specifyFairMQDeviceOutputProxy(char const* name, spec.options = { ConfigParamSpec{"channel-config", VariantType::String, d, {"Out-of-band channel config"}}, }; + spec.labels.push_back(DataProcessorLabel{"output-proxy"}); return spec; } @@ -1180,6 +1181,7 @@ DataProcessorSpec specifyFairMQDeviceMultiOutputProxy(char const* name, spec.options = { ConfigParamSpec{"channel-config", VariantType::String, d, {"Out-of-band channel config"}}, }; + spec.labels.push_back(DataProcessorLabel{"output-proxy"}); return spec; } From 3669ad3516f3d4a7ced7685735c35d60a15277f7 Mon Sep 17 00:00:00 2001 From: Felix Weiglhofer Date: Thu, 22 Jan 2026 16:36:03 +0100 Subject: [PATCH 022/133] GPU: Parallelize TPC pad filter over pad rows instead of cachelines. --- GPU/GPUTracking/DataTypes/GPUTPCGeometry.h | 2 + .../Definitions/GPUDefParametersDefaults.h | 8 +- .../Global/GPUChainTrackingClusterizer.cxx | 2 +- .../GPUTPCCFCheckPadBaseline.cxx | 101 +++++++++++++----- .../GPUTPCCFCheckPadBaseline.h | 40 ++++++- 5 files changed, 118 insertions(+), 35 deletions(-) diff --git a/GPU/GPUTracking/DataTypes/GPUTPCGeometry.h b/GPU/GPUTracking/DataTypes/GPUTPCGeometry.h index 9ad83bff363ac..13cec6c752fbe 100644 --- a/GPU/GPUTracking/DataTypes/GPUTPCGeometry.h +++ b/GPU/GPUTracking/DataTypes/GPUTPCGeometry.h @@ -96,6 +96,7 @@ class GPUTPCGeometry // TODO: Make values constexpr GPUd() static constexpr int32_t EndIROC() { return 63; } GPUd() static constexpr int32_t EndOROC1() { return 97; } GPUd() static constexpr int32_t EndOROC2() { return 127; } + GPUd() static constexpr int32_t MaxNPadsPerRow() { return 138; } #else GPUd() static constexpr int32_t GetRegion(int32_t row) { return (row < 63 ? 0 : row < 63 + 64 ? 1 : 2); } GPUd() static constexpr int32_t GetRegionRows(int32_t region) { return 0; } // dummy @@ -104,6 +105,7 @@ class GPUTPCGeometry // TODO: Make values constexpr GPUd() static constexpr int32_t EndIROC() { return 63; } GPUd() static constexpr int32_t EndOROC1() { return 63 + 64; } GPUd() static constexpr int32_t EndOROC2() { return GPUCA_ROW_COUNT; } + GPUd() static constexpr int32_t MaxNPadsPerRow() { return 140; } #endif GPUd() static constexpr float TPCLength() { return 250.f - 0.275f; } diff --git a/GPU/GPUTracking/Definitions/GPUDefParametersDefaults.h b/GPU/GPUTracking/Definitions/GPUDefParametersDefaults.h index 01ae33dc3b4d8..1be881ee6323e 100644 --- a/GPU/GPUTracking/Definitions/GPUDefParametersDefaults.h +++ b/GPU/GPUTracking/Definitions/GPUDefParametersDefaults.h @@ -68,7 +68,7 @@ #define GPUCA_LB_GPUTPCCompressionKernels_step1unattached 512, 2 #define GPUCA_LB_GPUTPCDecompressionKernels_step0attached 128, 2 #define GPUCA_LB_GPUTPCDecompressionKernels_step1unattached 64, 2 - #define GPUCA_LB_GPUTPCCFCheckPadBaseline 64, 10 + #define GPUCA_LB_GPUTPCCFCheckPadBaseline 576, 2 #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillIndexMap 512 #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillFromDigits 512 #define GPUCA_LB_GPUTPCCFChargeMapFiller_findFragmentStart 512 @@ -133,7 +133,7 @@ #define GPUCA_LB_GPUTPCCompressionKernels_step1unattached 512, 2 #define GPUCA_LB_GPUTPCDecompressionKernels_step0attached 128, 2 #define GPUCA_LB_GPUTPCDecompressionKernels_step1unattached 64, 2 - #define GPUCA_LB_GPUTPCCFCheckPadBaseline 64, 2 + #define GPUCA_LB_GPUTPCCFCheckPadBaseline 576, 2 #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillIndexMap 512 #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillFromDigits 512 #define GPUCA_LB_GPUTPCCFChargeMapFiller_findFragmentStart 512 @@ -197,7 +197,7 @@ #define GPUCA_LB_GPUTPCCompressionKernels_step1unattached 512, 3 #define GPUCA_LB_GPUTPCDecompressionKernels_step0attached 32, 1 #define GPUCA_LB_GPUTPCDecompressionKernels_step1unattached 32, 1 - #define GPUCA_LB_GPUTPCCFCheckPadBaseline 64,8 + #define GPUCA_LB_GPUTPCCFCheckPadBaseline 576,2 #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillIndexMap 448 #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillFromDigits 448 #define GPUCA_LB_GPUTPCCFChargeMapFiller_findFragmentStart 448 @@ -447,7 +447,7 @@ #define GPUCA_LB_GPUTPCStartHitsSorter 256 #endif #ifndef GPUCA_LB_GPUTPCCFCheckPadBaseline - #define GPUCA_LB_GPUTPCCFCheckPadBaseline 64 + #define GPUCA_LB_GPUTPCCFCheckPadBaseline 576 #endif #ifndef GPUCA_LB_GPUTPCCFChargeMapFiller_fillIndexMap #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillIndexMap 512 diff --git a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx index 7629086272ed6..bf6577cfd929e 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx @@ -962,7 +962,7 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) checkForNoisyPads &= !GetProcessingSettings().disableTPCNoisyPadFilter; if (checkForNoisyPads) { - int32_t nBlocks = TPC_PADS_IN_SECTOR / GPUTPCCFCheckPadBaseline::PadsPerCacheline; + const int32_t nBlocks = GPUTPCCFCheckPadBaseline::GetNBlocks(doGPU); runKernel({GetGridBlk(nBlocks, lane), {iSector}}); getKernelTimer(RecoStep::TPCClusterFinding, iSector, TPC_PADS_IN_SECTOR * fragment.lengthWithoutOverlap() * sizeof(PackedCharge), false); diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.cxx index 3248e517ff465..33ed089890bc4 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.cxx @@ -9,13 +9,12 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file GPUTPCCFCheckPadBaseline.h +/// \file GPUTPCCFCheckPadBaseline.cxx /// \author Felix Weiglhofer #include "GPUTPCCFCheckPadBaseline.h" #include "CfArray2D.h" #include "PackedCharge.h" -#include "GPUTPCGeometry.h" #include "clusterFinderDefs.h" #ifndef GPUCA_GPUCODE @@ -28,51 +27,88 @@ using namespace o2::gpu::tpccf; template <> GPUd() void GPUTPCCFCheckPadBaseline::Thread<0>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer) { - const CfFragment& fragment = clusterer.mPmemory->fragment; - CfArray2D chargeMap(reinterpret_cast(clusterer.mPchargeMap)); - - int32_t basePad = iBlock * PadsPerCacheline; - CfChargePos basePos = padToCfChargePos(basePad, clusterer); +#ifdef GPUCA_GPUCODE + CheckBaselineGPU(nBlocks, nThreads, iBlock, iThread, smem, clusterer); +#else + CheckBaselineCPU(nBlocks, nThreads, iBlock, iThread, smem, clusterer); +#endif +} - if (not basePos.valid()) { +// Charges are stored in a 2D array (pad and time) using a tiling layout. +// Tiles are 8 pads x 4 timebins large stored in time-major layout and make up a single cacheline. +// +// This kernel processes one row per block. Threads cooperatively load chunks +// of 4 consecutive time bins for all pads into shared memory. Thread `i` then processes charges for pad `i` in shared memory. +// Blocks require `nextMultipleOf<64>(138 * 4) = 576` threads to process the largest TPC rows with 138 pads correctly. +GPUd() void GPUTPCCFCheckPadBaseline::CheckBaselineGPU(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer) +{ +#ifdef GPUCA_GPUCODE + if (iBlock >= GPUCA_ROW_COUNT) { return; } -#ifdef GPUCA_GPUCODE - static_assert(TPC_MAX_FRAGMENT_LEN_GPU % NumOfCachedTimebins == 0); + const CfFragment& fragment = clusterer.mPmemory->fragment; + CfArray2D chargeMap(reinterpret_cast(clusterer.mPchargeMap)); + + const auto iRow = iBlock; + const auto rowinfo = GetRowInfo(iRow); + const CfChargePos basePos{(Row)iRow, 0, 0}; int32_t totalCharges = 0; int32_t consecCharges = 0; int32_t maxConsecCharges = 0; Charge maxCharge = 0; - int16_t localPadId = iThread / NumOfCachedTimebins; - int16_t localTimeBin = iThread % NumOfCachedTimebins; - bool handlePad = localTimeBin == 0; + const int16_t iPadOffset = iThread % MaxNPadsPerRow; + const int16_t iTimeOffset = iThread / MaxNPadsPerRow; + const int16_t iPadHandle = iThread; + const bool handlePad = iPadHandle < rowinfo.nPads; + + const auto firstTB = fragment.firstNonOverlapTimeBin(); + const auto lastTB = fragment.lastNonOverlapTimeBin(); + + for (auto t = firstTB; t < lastTB; t += NumOfCachedTBs) { + + const TPCFragmentTime iTime = t + iTimeOffset; + + const CfChargePos pos = basePos.delta({iPadOffset, iTime}); + + smem.charges[iTimeOffset][iPadOffset] = iTime < lastTB && iPadOffset < rowinfo.nPads ? chargeMap[pos].unpack() : 0; - for (tpccf::TPCFragmentTime t = fragment.firstNonOverlapTimeBin(); t < fragment.lastNonOverlapTimeBin(); t += NumOfCachedTimebins) { - const CfChargePos pos = basePos.delta({localPadId, int16_t(t + localTimeBin)}); - smem.charges[localPadId][localTimeBin] = (pos.valid()) ? chargeMap[pos].unpack() : 0; GPUbarrier(); + if (handlePad) { - for (int32_t i = 0; i < NumOfCachedTimebins; i++) { - const Charge q = smem.charges[localPadId][i]; + for (int32_t i = 0; i < NumOfCachedTBs; i++) { + const Charge q = smem.charges[i][iPadHandle]; totalCharges += (q > 0); consecCharges = (q > 0) ? consecCharges + 1 : 0; maxConsecCharges = CAMath::Max(consecCharges, maxConsecCharges); maxCharge = CAMath::Max(q, maxCharge); } } + GPUbarrier(); } - GPUbarrier(); - if (handlePad) { - updatePadBaseline(basePad + localPadId, clusterer, totalCharges, maxConsecCharges, maxCharge); + updatePadBaseline(rowinfo.globalPadOffset + iPadOffset, clusterer, totalCharges, maxConsecCharges, maxCharge); } +#endif +} -#else // CPU CODE +GPUd() void GPUTPCCFCheckPadBaseline::CheckBaselineCPU(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer) +{ +#ifndef GPUCA_GPUCODE + const CfFragment& fragment = clusterer.mPmemory->fragment; + CfArray2D chargeMap(reinterpret_cast(clusterer.mPchargeMap)); + + int32_t basePad = iBlock * PadsPerCacheline; + int32_t padsPerRow; + CfChargePos basePos = padToCfChargePos(basePad, clusterer, padsPerRow); + + if (not basePos.valid()) { + return; + } constexpr size_t ElemsInTileRow = (size_t)TilingLayout>::WidthInTiles * TimebinsPerCacheline * PadsPerCacheline; @@ -122,7 +158,8 @@ GPUd() void GPUTPCCFCheckPadBaseline::Thread<0>(int32_t nBlocks, int32_t nThread #endif } -GPUd() CfChargePos GPUTPCCFCheckPadBaseline::padToCfChargePos(int32_t& pad, const GPUTPCClusterFinder& clusterer) +template +GPUd() CfChargePos GPUTPCCFCheckPadBaseline::padToCfChargePos(int32_t& pad, const GPUTPCClusterFinder& clusterer, int32_t& padsPerRow) { constexpr GPUTPCGeometry geo; @@ -130,17 +167,31 @@ GPUd() CfChargePos GPUTPCCFCheckPadBaseline::padToCfChargePos(int32_t& pad, cons for (Row r = 0; r < GPUCA_ROW_COUNT; r++) { int32_t npads = geo.NPads(r); int32_t padInRow = pad - padOffset; - if (0 <= padInRow && padInRow < CAMath::nextMultipleOf(npads)) { - int32_t cachelineOffset = padInRow % PadsPerCacheline; + if (0 <= padInRow && padInRow < npads) { + int32_t cachelineOffset = padInRow % PadsPerBlock; pad -= cachelineOffset; + padsPerRow = npads; return CfChargePos{r, Pad(padInRow - cachelineOffset), 0}; } padOffset += npads; } + padsPerRow = 0; return CfChargePos{0, 0, INVALID_TIME_BIN}; } +GPUd() GPUTPCCFCheckPadBaseline::RowInfo GPUTPCCFCheckPadBaseline::GetRowInfo(int16_t row) +{ + constexpr GPUTPCGeometry geo; + + int16_t padOffset = 0; + for (int16_t r = 0; r < row; r++) { + padOffset += geo.NPads(r); + } + + return RowInfo{padOffset, geo.NPads(row)}; +} + GPUd() void GPUTPCCFCheckPadBaseline::updatePadBaseline(int32_t pad, const GPUTPCClusterFinder& clusterer, int32_t totalCharges, int32_t consecCharges, Charge maxCharge) { const CfFragment& fragment = clusterer.mPmemory->fragment; diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.h index 25c93a4649662..a71f1358a73a6 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.h @@ -11,14 +11,20 @@ /// \file GPUTPCCFCheckPadBaseline.h /// \author Felix Weiglhofer +/// +/// Kernel identifies noisy TPC pads by analyzing charge patterns over time. +/// A pad is marked noisy if it exceeds thresholds for total or consecutive +/// time bins with charge, unless the charge exceeds a saturation threshold. #ifndef O2_GPU_GPU_TPC_CF_CHECK_PAD_BASELINE_H #define O2_GPU_GPU_TPC_CF_CHECK_PAD_BASELINE_H #include "GPUGeneralKernels.h" #include "GPUConstantMem.h" +#include "GPUTPCGeometry.h" #include "clusterFinderDefs.h" +#include "CfArray2D.h" namespace o2::gpu { @@ -28,13 +34,20 @@ class GPUTPCCFCheckPadBaseline : public GPUKernelTemplate public: enum { - PadsPerCacheline = 8, - TimebinsPerCacheline = 4, - NumOfCachedTimebins = GPUCA_GET_THREAD_COUNT(GPUCA_LB_GPUTPCCFCheckPadBaseline) / PadsPerCacheline, + PadsPerCacheline = TPCMapMemoryLayout::Width, + TimebinsPerCacheline = TPCMapMemoryLayout::Height, + EntriesPerCacheline = PadsPerCacheline * TimebinsPerCacheline, + NumOfCachedPads = GPUCA_WARP_SIZE / TimebinsPerCacheline, + NumCLsPerWarp = GPUCA_WARP_SIZE / EntriesPerCacheline, + NumOfCachedTBs = TimebinsPerCacheline, + // Threads index shared memory as [iThread / MaxNPadsPerRow][iThread % MaxNPadsPerRow]. + // Rounding up to a multiple of PadsPerCacheline ensures iThread / MaxNPadsPerRow < NumOfCachedTBs + // for all threads, avoiding out-of-bounds access. + MaxNPadsPerRow = CAMath::nextMultipleOf(GPUTPCGeometry::MaxNPadsPerRow()), }; struct GPUSharedMemory { - tpccf::Charge charges[PadsPerCacheline][NumOfCachedTimebins]; + tpccf::Charge charges[NumOfCachedTBs][MaxNPadsPerRow]; }; typedef GPUTPCClusterFinder processorType; @@ -48,11 +61,28 @@ class GPUTPCCFCheckPadBaseline : public GPUKernelTemplate return gpudatatypes::RecoStep::TPCClusterFinding; } + static int32_t GetNBlocks(bool isGPU) + { + const int32_t nBlocks = TPC_PADS_IN_SECTOR / PadsPerCacheline; + return isGPU ? GPUCA_ROW_COUNT : nBlocks; + } + template GPUd() static void Thread(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer); private: - GPUd() static CfChargePos padToCfChargePos(int32_t& pad, const GPUTPCClusterFinder&); + GPUd() static void CheckBaselineGPU(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer); + GPUd() static void CheckBaselineCPU(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer); + + template + GPUd() static CfChargePos padToCfChargePos(int32_t& pad, const GPUTPCClusterFinder&, int32_t& padsPerRow); + + struct RowInfo { + int16_t globalPadOffset; + int16_t nPads; + }; + GPUd() static RowInfo GetRowInfo(int16_t row); + GPUd() static void updatePadBaseline(int32_t pad, const GPUTPCClusterFinder&, int32_t totalCharges, int32_t consecCharges, tpccf::Charge maxCharge); }; From f86363afb697abe64b9bf593a6cd92dd45b49aae Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Mon, 26 Jan 2026 13:16:00 +0100 Subject: [PATCH 023/133] ITS: instaniate TRK classes Signed-off-by: Felix Schlepper --- Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx | 4 ++++ Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx | 13 +++++++++++-- Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx | 4 ++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx index 0d8b461181741..70f4e3d1d3fc7 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx @@ -667,5 +667,9 @@ void TimeFrame::wipe() } template class TimeFrame<7>; +// ALICE3 upgrade +#ifdef ENABLE_UPGRADES +template class TimeFrame<11>; +#endif } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx b/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx index 59459dcab17e8..658a90b37613f 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx @@ -145,8 +145,13 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& er } catch (const std::bad_alloc& err) { handleException(err); return; - } catch (...) { - error("Uncaught exception, all bets are off..."); + } catch (const std::exception& err) { + error(std::format("Uncaught exception, all bets are off... {}", err.what())); + // clear tracks explicitly since if not fatalising on exception this may contain partial output + for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { + mTimeFrame->getTracks(iROF).clear(); + } + return; } if (mTimeFrame->hasMCinformation()) { @@ -357,5 +362,9 @@ void Tracker::printSummary() const } template class Tracker<7>; +// ALICE3 upgrade +#ifdef ENABLE_UPGRADES +template class Tracker<11>; +#endif } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx index fe67eadaf6f72..15459576a1031 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx @@ -1327,5 +1327,9 @@ void TrackerTraits::setNThreads(int n, std::shared_ptr } template class TrackerTraits<7>; +// ALICE3 upgrade +#ifdef ENABLE_UPGRADES +template class TrackerTraits<11>; +#endif } // namespace o2::its From 2d37a89a8539690def207cc7fd55dbf8b7514b03 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Mon, 26 Jan 2026 13:53:46 +0100 Subject: [PATCH 024/133] ITS: enlarge StartLayerMask for TRK Signed-off-by: Felix Schlepper --- .../ITSMFT/ITS/tracking/include/ITStracking/Configuration.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h index 1019a3e3d45a9..1c4d604a629ed 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h @@ -68,7 +68,7 @@ struct TrackingParameters { float MaxChi2NDF = 30.f; int ReseedIfShorter = 6; // reseed for the final fit track with the length shorter than this std::vector MinPt = {0.f, 0.f, 0.f, 0.f}; - unsigned char StartLayerMask = 0x7F; + uint16_t StartLayerMask = 0x7F; bool RepeatRefitOut = true; // repeat outward refit using inward refit as a seed bool ShiftRefToCluster = true; // TrackFit: after update shift the linearization reference to cluster bool FindShortTracks = false; From 8eebfb5e616c670e42b7a41742b64c123a9e7b54 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Mon, 26 Jan 2026 17:30:26 +0100 Subject: [PATCH 025/133] ITS: GPU: reduce TrackITS allocation Signed-off-by: Felix Schlepper --- .../GPU/ITStrackingGPU/TimeFrameGPU.h | 8 +- .../GPU/ITStrackingGPU/TrackingKernels.h | 61 +++++-- .../ITS/tracking/GPU/cuda/TimeFrameGPU.cu | 23 +-- .../tracking/GPU/cuda/TrackerTraitsGPU.cxx | 65 ++++--- .../ITS/tracking/GPU/cuda/TrackingKernels.cu | 169 +++++++++++++----- 5 files changed, 234 insertions(+), 92 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h index 8095d690bbcc8..d6d87eb8c1143 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h @@ -80,8 +80,8 @@ class TimeFrameGPU final : public TimeFrame void createNeighboursIndexTablesDevice(const int); void createNeighboursDevice(const unsigned int layer); void createNeighboursLUTDevice(const int, const unsigned int); - void createTrackITSExtDevice(bounded_vector&); - void downloadTrackITSExtDevice(bounded_vector&); + void createTrackITSExtDevice(const size_t); + void downloadTrackITSExtDevice(); void downloadCellsNeighboursDevice(std::vector>>&, const int); void downloadNeighboursLUTDevice(bounded_vector&, const int); void downloadCellsDevice(); @@ -140,6 +140,8 @@ class TimeFrameGPU final : public TimeFrame int** getDeviceArrayNeighboursCellLUT() const { return mNeighboursCellLUTDeviceArray; } CellSeedN** getDeviceArrayCells() { return mCellsDeviceArray; } CellSeedN* getDeviceTrackSeeds() { return mTrackSeedsDevice; } + int* getDeviceTrackSeedsLUT() { return mTrackSeedsLUTDevice; } + auto getNTrackSeeds() const { return mNTracks; } o2::track::TrackParCovF** getDeviceArrayTrackSeeds() { return mCellSeedsDeviceArray; } float** getDeviceArrayTrackSeedsChi2() { return mCellSeedsChi2DeviceArray; } int* getDeviceNeighboursIndexTables(const int layer) { return mNeighboursIndexTablesDevice[layer]; } @@ -219,6 +221,8 @@ class TimeFrameGPU final : public TimeFrame CellSeedN** mCellsDeviceArray; std::array mNeighboursIndexTablesDevice; CellSeedN* mTrackSeedsDevice{nullptr}; + int* mTrackSeedsLUTDevice{nullptr}; + unsigned int mNTracks{0}; std::array mCellSeedsDevice; o2::track::TrackParCovF** mCellSeedsDeviceArray; std::array mCellSeedsChi2Device; diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h index 6e0427f5413ba..53992ccf3eb85 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h @@ -207,23 +207,48 @@ void processNeighboursHandler(const int startLayer, const int nThreads); template -void trackSeedHandler(CellSeed* trackSeeds, - const TrackingFrameInfo** foundTrackingFrameInfo, - const Cluster** unsortedClusters, - o2::its::TrackITSExt* tracks, - const std::vector& layerRadiiHost, - const std::vector& minPtsHost, - const unsigned int nSeeds, - const float Bz, - const int startLevel, - const float maxChi2ClusterAttachment, - const float maxChi2NDF, - const int reseedIfShorter, - const bool repeatRefitOut, - const bool shiftRefToCluster, - const o2::base::Propagator* propagator, - const o2::base::PropagatorF::MatCorrType matCorrType, - const int nBlocks, - const int nThreads); +void countTrackSeedHandler(CellSeed* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const unsigned int nSeeds, + const float Bz, + const int startLevel, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads); + +template +void computeTrackSeedHandler(CellSeed* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + o2::its::TrackITSExt* tracks, + const int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const unsigned int nSeeds, + const unsigned int nTracks, + const float Bz, + const int startLevel, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads); + } // namespace o2::its #endif // ITSTRACKINGGPU_TRACKINGKERNELS_H_ diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu index 6532165d9628a..c7d6e31ec771a 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu @@ -439,8 +439,10 @@ void TimeFrameGPU::loadTrackSeedsDevice(bounded_vector& seed GPUTimer timer("loading track seeds"); GPULog("gpu-transfer: loading {} track seeds, for {:.2f} MB.", seeds.size(), seeds.size() * sizeof(CellSeedN) / constants::MB); allocMem(reinterpret_cast(&mTrackSeedsDevice), seeds.size() * sizeof(CellSeedN), this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); - GPUChkErrS(cudaHostRegister(seeds.data(), seeds.size() * sizeof(CellSeedN), cudaHostRegisterPortable)); GPUChkErrS(cudaMemcpy(mTrackSeedsDevice, seeds.data(), seeds.size() * sizeof(CellSeedN), cudaMemcpyHostToDevice)); + GPULog("gpu-transfer: creating {} track seeds LUT, for {:.2f} MB.", seeds.size() + 1, (seeds.size() + 1) * sizeof(int) / constants::MB); + allocMem(reinterpret_cast(&mTrackSeedsLUTDevice), (seeds.size() + 1) * sizeof(int), this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemset(mTrackSeedsLUTDevice, 0, (seeds.size() + 1) * sizeof(int))); } template @@ -458,14 +460,15 @@ void TimeFrameGPU::createNeighboursDevice(const unsigned int layer) } template -void TimeFrameGPU::createTrackITSExtDevice(bounded_vector& seeds) +void TimeFrameGPU::createTrackITSExtDevice(const size_t nSeeds) { GPUTimer timer("reserving tracks"); - mTrackITSExt = bounded_vector(seeds.size(), {}, this->getMemoryPool().get()); - GPULog("gpu-allocation: reserving {} tracks, for {:.2f} MB.", seeds.size(), seeds.size() * sizeof(o2::its::TrackITSExt) / constants::MB); - allocMem(reinterpret_cast(&mTrackITSExtDevice), seeds.size() * sizeof(o2::its::TrackITSExt), this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); - GPUChkErrS(cudaMemset(mTrackITSExtDevice, 0, seeds.size() * sizeof(o2::its::TrackITSExt))); - GPUChkErrS(cudaHostRegister(mTrackITSExt.data(), seeds.size() * sizeof(o2::its::TrackITSExt), cudaHostRegisterPortable)); + mNTracks = 0; + GPUChkErrS(cudaMemcpy(&mNTracks, mTrackSeedsLUTDevice + nSeeds, sizeof(int), cudaMemcpyDeviceToHost)); + GPULog("gpu-allocation: reserving {} tracks, for {:.2f} MB.", mNTracks, mNTracks * sizeof(o2::its::TrackITSExt) / constants::MB); + mTrackITSExt = bounded_vector(mNTracks, {}, this->getMemoryPool().get()); + allocMem(reinterpret_cast(&mTrackITSExtDevice), mNTracks * sizeof(o2::its::TrackITSExt), this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemset(mTrackITSExtDevice, 0, mNTracks * sizeof(o2::its::TrackITSExt))); } template @@ -588,13 +591,11 @@ void TimeFrameGPU::downloadNeighboursLUTDevice(bounded_vector& lut } template -void TimeFrameGPU::downloadTrackITSExtDevice(bounded_vector& seeds) +void TimeFrameGPU::downloadTrackITSExtDevice() { GPUTimer timer("downloading tracks"); GPULog("gpu-transfer: downloading {} tracks, for {:.2f} MB.", mTrackITSExt.size(), mTrackITSExt.size() * sizeof(o2::its::TrackITSExt) / constants::MB); - GPUChkErrS(cudaMemcpy(mTrackITSExt.data(), mTrackITSExtDevice, seeds.size() * sizeof(o2::its::TrackITSExt), cudaMemcpyDeviceToHost)); - GPUChkErrS(cudaHostUnregister(mTrackITSExt.data())); - GPUChkErrS(cudaHostUnregister(seeds.data())); + GPUChkErrS(cudaMemcpy(mTrackITSExt.data(), mTrackITSExtDevice, mTrackITSExt.size() * sizeof(o2::its::TrackITSExt), cudaMemcpyDeviceToHost)); } template diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx index c4a5cfb4e26b3..60774e3313d7f 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx @@ -322,29 +322,52 @@ void TrackerTraitsGPU::findRoads(const int iteration) LOGP(debug, "No track seeds found, skipping track finding"); continue; } - mTimeFrameGPU->createTrackITSExtDevice(trackSeeds); mTimeFrameGPU->loadTrackSeedsDevice(trackSeeds); - trackSeedHandler(mTimeFrameGPU->getDeviceTrackSeeds(), // CellSeed* - mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), // TrackingFrameInfo** - mTimeFrameGPU->getDeviceArrayUnsortedClusters(), // Cluster** - mTimeFrameGPU->getDeviceTrackITSExt(), // o2::its::TrackITSExt* - this->mTrkParams[iteration].LayerRadii, // const std::vector& - this->mTrkParams[iteration].MinPt, // const std::vector& - trackSeeds.size(), // const size_t nSeeds - this->mBz, // const float Bz - startLevel, // const int startLevel, - this->mTrkParams[0].MaxChi2ClusterAttachment, // float maxChi2ClusterAttachment - this->mTrkParams[0].MaxChi2NDF, // float maxChi2NDF - this->mTrkParams[0].RepeatRefitOut, - this->mTrkParams[0].ReseedIfShorter, - this->mTrkParams[0].ShiftRefToCluster, - mTimeFrameGPU->getDevicePropagator(), // const o2::base::Propagator* propagator - this->mTrkParams[0].CorrType, // o2::base::PropagatorImpl::MatCorrType - conf.nBlocksTracksSeeds[iteration], - conf.nThreadsTracksSeeds[iteration]); - - mTimeFrameGPU->downloadTrackITSExtDevice(trackSeeds); + // Since TrackITSExt is an enourmous class it is better to first count how many + // successfull fits we do and only then allocate + countTrackSeedHandler(mTimeFrameGPU->getDeviceTrackSeeds(), + mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), + mTimeFrameGPU->getDeviceArrayUnsortedClusters(), + mTimeFrameGPU->getDeviceTrackSeedsLUT(), + this->mTrkParams[iteration].LayerRadii, + this->mTrkParams[iteration].MinPt, + trackSeeds.size(), + this->mBz, + startLevel, + this->mTrkParams[0].MaxChi2ClusterAttachment, + this->mTrkParams[0].MaxChi2NDF, + this->mTrkParams[0].RepeatRefitOut, + this->mTrkParams[0].ReseedIfShorter, + this->mTrkParams[0].ShiftRefToCluster, + mTimeFrameGPU->getDevicePropagator(), + this->mTrkParams[0].CorrType, + mTimeFrameGPU->getFrameworkAllocator(), + conf.nBlocksTracksSeeds[iteration], + conf.nThreadsTracksSeeds[iteration]); + mTimeFrameGPU->createTrackITSExtDevice(trackSeeds.size()); + computeTrackSeedHandler(mTimeFrameGPU->getDeviceTrackSeeds(), + mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), + mTimeFrameGPU->getDeviceArrayUnsortedClusters(), + mTimeFrameGPU->getDeviceTrackITSExt(), + mTimeFrameGPU->getDeviceTrackSeedsLUT(), + this->mTrkParams[iteration].LayerRadii, + this->mTrkParams[iteration].MinPt, + trackSeeds.size(), + mTimeFrameGPU->getNTrackSeeds(), + this->mBz, + startLevel, + this->mTrkParams[0].MaxChi2ClusterAttachment, + this->mTrkParams[0].MaxChi2NDF, + this->mTrkParams[0].RepeatRefitOut, + this->mTrkParams[0].ReseedIfShorter, + this->mTrkParams[0].ShiftRefToCluster, + mTimeFrameGPU->getDevicePropagator(), + this->mTrkParams[0].CorrType, + mTimeFrameGPU->getFrameworkAllocator(), + conf.nBlocksTracksSeeds[iteration], + conf.nThreadsTracksSeeds[iteration]); + mTimeFrameGPU->downloadTrackITSExtDevice(); auto& tracks = mTimeFrameGPU->getTrackITSExt(); diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index 85689488f5f6e..e5427c07cb52b 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -277,12 +277,13 @@ struct compare_track_chi2 { } }; -template +template GPUg() void __launch_bounds__(256, 1) fitTrackSeedsKernel( CellSeed* trackSeeds, const TrackingFrameInfo** foundTrackingFrameInfo, const Cluster** unsortedClusters, o2::its::TrackITSExt* tracks, + maybe_const* seedLUT, const float* layerRadii, const float* minPts, const unsigned int nSeeds, @@ -297,6 +298,13 @@ GPUg() void __launch_bounds__(256, 1) fitTrackSeedsKernel( const o2::base::PropagatorF::MatCorrType matCorrType) { for (int iCurrentTrackSeedIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentTrackSeedIndex < nSeeds; iCurrentTrackSeedIndex += blockDim.x * gridDim.x) { + + if constexpr (!initRun) { + if (seedLUT[iCurrentTrackSeedIndex] == seedLUT[iCurrentTrackSeedIndex + 1]) { + continue; + } + } + TrackITSExt temporaryTrack = seedTrackForRefit(trackSeeds[iCurrentTrackSeedIndex], foundTrackingFrameInfo, unsortedClusters, layerRadii, bz, reseedIfShorter); o2::track::TrackPar linRef{temporaryTrack}; bool fitSuccess = fitTrack(temporaryTrack, // TrackITSExt& track, @@ -366,7 +374,12 @@ GPUg() void __launch_bounds__(256, 1) fitTrackSeedsKernel( temporaryTrack.getParamIn() = saveInw; temporaryTrack.setChi2(saveChi2); } - tracks[iCurrentTrackSeedIndex] = temporaryTrack; + + if constexpr (initRun) { + seedLUT[iCurrentTrackSeedIndex] = 1; + } else { + tracks[seedLUT[iCurrentTrackSeedIndex]] = temporaryTrack; + } } } @@ -1191,32 +1204,84 @@ void processNeighboursHandler(const int startLayer, } template -void trackSeedHandler(CellSeed* trackSeeds, - const TrackingFrameInfo** foundTrackingFrameInfo, - const Cluster** unsortedClusters, - o2::its::TrackITSExt* tracks, - const std::vector& layerRadiiHost, - const std::vector& minPtsHost, - const unsigned int nSeeds, - const float bz, - const int startLevel, - const float maxChi2ClusterAttachment, - const float maxChi2NDF, - const int reseedIfShorter, - const bool repeatRefitOut, - const bool shiftRefToCluster, - const o2::base::Propagator* propagator, - const o2::base::PropagatorF::MatCorrType matCorrType, - const int nBlocks, - const int nThreads) +void countTrackSeedHandler(CellSeed* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const unsigned int nSeeds, + const float bz, + const int startLevel, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads) +{ + // TODO: the minPts&layerRadii is transfered twice + // we should allocate this in constant memory and stop these + // small transferes! + thrust::device_vector minPts(minPtsHost); + thrust::device_vector layerRadii(layerRadiiHost); + gpu::fitTrackSeedsKernel<<>>( + trackSeeds, // CellSeed* + foundTrackingFrameInfo, // TrackingFrameInfo** + unsortedClusters, // Cluster** + nullptr, // TrackITSExt* + seedLUT, // int* + thrust::raw_pointer_cast(&layerRadii[0]), // const float* + thrust::raw_pointer_cast(&minPts[0]), // const float* + nSeeds, // const unsigned int + bz, // const float + startLevel, // const int + maxChi2ClusterAttachment, // float + maxChi2NDF, // float + reseedIfShorter, // int + repeatRefitOut, // bool + shiftRefToCluster, // bool + propagator, // const o2::base::Propagator* + matCorrType); // o2::base::PropagatorF::MatCorrType + auto sync_policy = THRUST_NAMESPACE::par(gpu::TypedAllocator(alloc)); + thrust::exclusive_scan(sync_policy, seedLUT, seedLUT + nSeeds + 1, seedLUT); +} + +template +void computeTrackSeedHandler(CellSeed* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + o2::its::TrackITSExt* tracks, + const int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const unsigned int nSeeds, + const unsigned int nTracks, + const float bz, + const int startLevel, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads) { thrust::device_vector minPts(minPtsHost); thrust::device_vector layerRadii(layerRadiiHost); - gpu::fitTrackSeedsKernel<<>>( + gpu::fitTrackSeedsKernel<<>>( trackSeeds, // CellSeed* foundTrackingFrameInfo, // TrackingFrameInfo** unsortedClusters, // Cluster** tracks, // TrackITSExt* + seedLUT, // const int* thrust::raw_pointer_cast(&layerRadii[0]), // const float* thrust::raw_pointer_cast(&minPts[0]), // const float* nSeeds, // const unsigned int @@ -1229,8 +1294,9 @@ void trackSeedHandler(CellSeed* trackSeeds, shiftRefToCluster, // bool propagator, // const o2::base::Propagator* matCorrType); // o2::base::PropagatorF::MatCorrType + auto sync_policy = THRUST_NAMESPACE::par(gpu::TypedAllocator(alloc)); thrust::device_ptr tr_ptr(tracks); - thrust::sort(tr_ptr, tr_ptr + nSeeds, gpu::compare_track_chi2()); + thrust::sort(sync_policy, tr_ptr, tr_ptr + nTracks, gpu::compare_track_chi2()); } /// Explicit instantiation of ITS2 handlers @@ -1394,23 +1460,46 @@ template void processNeighboursHandler<7>(const int startLayer, const int nBlocks, const int nThreads); -template void trackSeedHandler(CellSeed<7>* trackSeeds, - const TrackingFrameInfo** foundTrackingFrameInfo, - const Cluster** unsortedClusters, - o2::its::TrackITSExt* tracks, - const std::vector& layerRadiiHost, - const std::vector& minPtsHost, - const unsigned int nSeeds, - const float bz, - const int startLevel, - const float maxChi2ClusterAttachment, - const float maxChi2NDF, - const int reseedIfShorter, - const bool repeatRefitOut, - const bool shiftRefToCluster, - const o2::base::Propagator* propagator, - const o2::base::PropagatorF::MatCorrType matCorrType, - const int nBlocks, - const int nThreads); +template void countTrackSeedHandler(CellSeed<7>* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const unsigned int nSeeds, + const float bz, + const int startLevel, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads); + +template void computeTrackSeedHandler(CellSeed<7>* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + o2::its::TrackITSExt* tracks, + const int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const unsigned int nSeeds, + const unsigned int nTracks, + const float bz, + const int startLevel, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads); } // namespace o2::its From c3f85c1d7cbbd4c9e16819f1f0d23b47d7004796 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Mon, 26 Jan 2026 17:31:50 +0100 Subject: [PATCH 026/133] ITS: fix correctForMaterial arg for actual layer Signed-off-by: Felix Schlepper --- Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu | 2 +- Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index e5427c07cb52b..7c257bc1d0ba1 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -505,7 +505,7 @@ GPUg() void __launch_bounds__(256, 1) computeLayerCellsKernel( break; } - if (!track.correctForMaterial(layerxX0[layer + iC], layerxX0[layer] * constants::Radl * constants::Rho, true)) { + if (!track.correctForMaterial(layerxX0[layer + iC], layerxX0[layer + iC] * constants::Radl * constants::Rho, true)) { break; } diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx index 15459576a1031..b4ac847863d51 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx @@ -357,7 +357,7 @@ void TrackerTraits::computeLayerCells(const int iteration) break; } - if (!track.correctForMaterial(mTrkParams[0].LayerxX0[iLayer + iC], mTrkParams[0].LayerxX0[iLayer] * constants::Radl * constants::Rho, true)) { + if (!track.correctForMaterial(mTrkParams[0].LayerxX0[iLayer + iC], mTrkParams[0].LayerxX0[iLayer + iC] * constants::Radl * constants::Rho, true)) { break; } From b62d3d6084dc219ffe1e99c8d42042d043c73773 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Wed, 28 Jan 2026 11:10:20 +0100 Subject: [PATCH 027/133] ITS: GPU: create compile time stack tags Signed-off-by: Felix Schlepper --- .../ITS/tracking/GPU/cuda/TimeFrameGPU.cu | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu index c7d6e31ec771a..da0cd51478945 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu @@ -11,7 +11,6 @@ /// #include -#include #include #include @@ -633,21 +632,37 @@ void TimeFrameGPU::unregisterHostMemory(const int maxLayers) checkedUnregisterArray(mPinnedROFramesClusters, mROFramesClustersDevice); } +namespace detail +{ +template +constexpr uint64_t makeIterTag() +{ + static_assert(I < 10); + constexpr char tag[] = {'I', 'T', 'S', 'I', 'T', 'E', 'R', char('0' + I), '\0'}; + return qStr2Tag(tag); +} +template +constexpr auto makeIterTags(std::index_sequence) +{ + return std::array{makeIterTag()...}; +} +// FIXME: we have to be careful that the MaxIter does not diverge from the 4 here! +constexpr auto kIterTags = makeIterTags(std::make_index_sequence<4>{}); +} // namespace detail + template void TimeFrameGPU::pushMemoryStack(const int iteration) { // mark the beginning of memory marked with MEMORY_STACK that can be discarded // after doing one iteration - const auto name = fmt::format("ITSITER{}", iteration); - (this->mExternalAllocator)->pushTagOnStack(qStr2Tag(name.c_str())); + (this->mExternalAllocator)->pushTagOnStack(detail::kIterTags[iteration]); } template void TimeFrameGPU::popMemoryStack(const int iteration) { // pop all memory on the stack from this iteration - const auto name = fmt::format("ITSITER{}", iteration); - (this->mExternalAllocator)->popTagOffStack(qStr2Tag(name.c_str())); + (this->mExternalAllocator)->popTagOffStack(detail::kIterTags[iteration]); } template From 5f4f95a4797a1f5082d3c388fbc05aff8438ba95 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Tue, 27 Jan 2026 19:25:24 +0100 Subject: [PATCH 028/133] GPU: add constexpr version of qStr2Tag Signed-off-by: Felix Schlepper --- GPU/GPUTracking/utils/strtag.h | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/GPU/GPUTracking/utils/strtag.h b/GPU/GPUTracking/utils/strtag.h index 24c527ca11a8c..a822083da8980 100644 --- a/GPU/GPUTracking/utils/strtag.h +++ b/GPU/GPUTracking/utils/strtag.h @@ -15,20 +15,21 @@ #ifndef STRTAG_H #define STRTAG_H -#include +#include +#include #include +#include -template -constexpr T qStr2Tag(const char* str) +template +constexpr T qStr2Tag(const char (&str)[N]) { - if (strlen(str) != sizeof(T)) { - throw std::runtime_error("Invalid tag length"); + static_assert(std::is_trivially_copyable_v); + static_assert(N - 1 == sizeof(T), "Invalid tag length"); + T value{}; + for (std::size_t i = 0; i < sizeof(T); ++i) { + value |= T(static_cast(str[i])) << (i * 8); } - T tmp; - for (uint32_t i = 0; i < sizeof(T); i++) { - ((char*)&tmp)[i] = str[i]; - } - return tmp; + return value; } template From e4a4f1a0029b3370a50410ee061b6acfa1a83ec1 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Tue, 27 Jan 2026 20:13:38 +0100 Subject: [PATCH 029/133] ITS: GPU: add skipping of parts where nothing was found Signed-off-by: Felix Schlepper --- .../ITS/tracking/GPU/cuda/TrackingKernels.cu | 25 +++++++++++++++---- .../ITS/tracking/GPU/hip/CMakeLists.txt | 2 ++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index 7c257bc1d0ba1..a12237358c8bd 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -399,6 +399,11 @@ GPUg() void __launch_bounds__(256, 1) computeLayerCellNeighboursKernel( const int maxCellNeighbours = 1e2) { for (int iCurrentCellIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentCellIndex < nCells; iCurrentCellIndex += blockDim.x * gridDim.x) { + if constexpr (!initRun) { + if (neighboursIndexTable[iCurrentCellIndex] == neighboursIndexTable[iCurrentCellIndex + 1]) { + continue; + } + } const auto& currentCellSeed{cellSeedArray[layerIndex][iCurrentCellIndex]}; const int nextLayerTrackletIndex{currentCellSeed.getSecondTrackletIndex()}; const int nextLayerFirstCellIndex{cellsLUTs[layerIndex + 1][nextLayerTrackletIndex]}; @@ -464,8 +469,13 @@ GPUg() void __launch_bounds__(256, 1) computeLayerCellsKernel( const float cellDeltaTanLambdaSigma, const float nSigmaCut) { - constexpr float layerxX0[7] = {5.e-3f, 5.e-3f, 5.e-3f, 1.e-2f, 1.e-2f, 1.e-2f, 1.e-2f}; // Hardcoded here for the moment. + constexpr float layerxX0[7] = {5.e-3f, 5.e-3f, 5.e-3f, 1.e-2f, 1.e-2f, 1.e-2f, 1.e-2f}; // FIXME: Hardcoded here for the moment. for (int iCurrentTrackletIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentTrackletIndex < nTrackletsCurrent; iCurrentTrackletIndex += blockDim.x * gridDim.x) { + if constexpr (!initRun) { + if (cellsLUTs[layer][iCurrentTrackletIndex] == cellsLUTs[layer][iCurrentTrackletIndex + 1]) { + continue; + } + } const Tracklet& currentTracklet = tracklets[layer][iCurrentTrackletIndex]; const int nextLayerClusterIndex{currentTracklet.secondClusterIndex}; const int nextLayerFirstTrackletIndex{trackletsLUT[layer + 1][nextLayerClusterIndex]}; @@ -526,11 +536,11 @@ GPUg() void __launch_bounds__(256, 1) computeLayerCellsKernel( new (cells + cellsLUTs[layer][iCurrentTrackletIndex] + foundCells) CellSeed{layer, clusId[0], clusId[1], clusId[2], iCurrentTrackletIndex, iNextTrackletIndex, track, chi2}; } ++foundCells; - if constexpr (initRun) { - cellsLUTs[layer][iCurrentTrackletIndex] = foundCells; - } } } + if constexpr (initRun) { + cellsLUTs[layer][iCurrentTrackletIndex] = foundCells; + } } } @@ -692,8 +702,13 @@ GPUg() void __launch_bounds__(256, 1) processNeighboursKernel( const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType) { - constexpr float layerxX0[7] = {5.e-3f, 5.e-3f, 5.e-3f, 1.e-2f, 1.e-2f, 1.e-2f, 1.e-2f}; // Hardcoded here for the moment. + constexpr float layerxX0[7] = {5.e-3f, 5.e-3f, 5.e-3f, 1.e-2f, 1.e-2f, 1.e-2f, 1.e-2f}; // FIXME: Hardcoded here for the moment. for (unsigned int iCurrentCell = blockIdx.x * blockDim.x + threadIdx.x; iCurrentCell < nCurrentCells; iCurrentCell += blockDim.x * gridDim.x) { + if constexpr (!dryRun) { + if (foundSeedsTable[iCurrentCell] == foundSeedsTable[iCurrentCell + 1]) { + continue; + } + } int foundSeeds{0}; const auto& currentCell{currentCellSeeds[iCurrentCell]}; if (currentCell.getLevel() != level) { diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt index e8e475f2232c8..a40aac491a386 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt @@ -13,6 +13,8 @@ if(HIP_ENABLED) message(STATUS "Building ITS HIP tracker") set(CMAKE_HIP_FLAGS "${CMAKE_HIP_FLAGS} -fgpu-rdc") # set(CMAKE_HIP_FLAGS "${CMAKE_HIP_FLAGS} -O0 -g -ggdb -fno-inline -fno-omit-frame-pointer -D__HIP_ENABLE_DEVICE_ASSERT__") + # add_compile_definitions(ITS_MEASURE_GPU_TIME) + # add_compile_definitions(ITS_GPU_LOG) o2_add_hipified_library(ITStrackingHIP SOURCES ../cuda/ClusterLinesGPU.cu ../cuda/TimeFrameGPU.cu From aa3ef3751f282ee477e0636d6bd5697c43103381 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Tue, 27 Jan 2026 20:14:15 +0100 Subject: [PATCH 030/133] ITS: GPU: more memory clearing in processNeighbours Signed-off-by: Felix Schlepper --- .../ITS/tracking/GPU/cuda/TrackingKernels.cu | 84 +++++++++++++------ 1 file changed, 58 insertions(+), 26 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index a12237358c8bd..50888c676df77 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -39,6 +39,7 @@ // O2 track model #include "ReconstructionDataFormats/Track.h" #include "DetectorsBase/Propagator.h" +#include "utils/strtag.h" using namespace o2::track; namespace o2::its @@ -1106,11 +1107,19 @@ void processNeighboursHandler(const int startLayer, const int nBlocks, const int nThreads) { + constexpr uint64_t Tag = qStr2Tag("ITS_PNH1"); + + // allocators used auto allocInt = gpu::TypedAllocator(alloc); auto allocCellSeed = gpu::TypedAllocator>(alloc); - thrust::device_vector> foundSeedsTable(nCells[startLayer] + 1, 0, allocInt); - auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(gpu::Stream::DefaultStream); + // use sync_policy, this part cannot be run async but tell thrust to use the allocator + auto sync_policy = THRUST_NAMESPACE::par(gpu::TypedAllocator(alloc)); + + // put initial computation on Tag1 + alloc->pushTagOnStack(Tag); + // start processing of cells + thrust::device_vector> foundSeedsTable(nCells[startLayer] + 1, 0, allocInt); gpu::processNeighboursKernel<<>>( startLayer, startLevel, @@ -1129,10 +1138,10 @@ void processNeighboursHandler(const int startLayer, maxChi2ClusterAttachment, propagator, matCorrType); - thrust::exclusive_scan(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); - - thrust::device_vector> updatedCellId(foundSeedsTable.back(), 0, allocInt); - thrust::device_vector, gpu::TypedAllocator>> updatedCellSeed(foundSeedsTable.back(), allocCellSeed); + thrust::exclusive_scan(sync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); + auto foundSeeds{foundSeedsTable.back()}; + thrust::device_vector> updatedCellId(foundSeeds, 0, allocInt); + thrust::device_vector, gpu::TypedAllocator>> updatedCellSeed(foundSeeds, allocCellSeed); gpu::processNeighboursKernel<<>>( startLayer, startLevel, @@ -1151,20 +1160,41 @@ void processNeighboursHandler(const int startLayer, maxChi2ClusterAttachment, propagator, matCorrType); - GPUChkErrS(cudaStreamSynchronize(gpu::Stream::DefaultStream)); + // now do inward steps until stop is reached int level = startLevel; - thrust::device_vector> lastCellId(allocInt); - thrust::device_vector, gpu::TypedAllocator>> lastCellSeed(allocCellSeed); + + // Host buffers to break dependency + // FIXME: these should be on our memory resource! + std::vector hostCellId; + std::vector> hostCellSeed; + + // inward loop for (int iLayer{startLayer - 1}; iLayer > 0 && level > 2; --iLayer) { - lastCellSeed.swap(updatedCellSeed); - lastCellId.swap(updatedCellId); + // copy current results to host + hostCellId.resize(updatedCellId.size()); + hostCellSeed.resize(updatedCellSeed.size()); + thrust::copy(updatedCellId.begin(), updatedCellId.end(), hostCellId.begin()); + thrust::copy(updatedCellSeed.begin(), updatedCellSeed.end(), hostCellSeed.begin()); + + auto lastCellSeedSize{hostCellSeed.size()}; + // but before we clear the memory, and immediately start a new block + alloc->popTagOffStack(Tag); + alloc->pushTagOnStack(Tag); + + // based on the previous step's result create new LUT and zero it + thrust::device_vector>(allocInt).swap(foundSeedsTable); + foundSeedsTable.resize(lastCellSeedSize + 1); + thrust::fill(sync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), 0); + + // recreate lastCell vectors from host + thrust::device_vector> lastCellId(hostCellId.begin(), hostCellId.end(), allocInt); + thrust::device_vector, gpu::TypedAllocator>> lastCellSeed(hostCellSeed.begin(), hostCellSeed.end(), allocCellSeed); + // also create new vectors on new block thrust::device_vector, gpu::TypedAllocator>>(allocCellSeed).swap(updatedCellSeed); thrust::device_vector>(allocInt).swap(updatedCellId); - auto lastCellSeedSize{lastCellSeed.size()}; - foundSeedsTable.resize(lastCellSeedSize + 1); - thrust::fill(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), 0); + // start step gpu::processNeighboursKernel<<>>( iLayer, --level, @@ -1183,14 +1213,13 @@ void processNeighboursHandler(const int startLayer, maxChi2ClusterAttachment, propagator, matCorrType); - thrust::exclusive_scan(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); - - auto foundSeeds{foundSeedsTable.back()}; + // how many new seeds where found + thrust::exclusive_scan(sync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); + foundSeeds = foundSeedsTable.back(); + // do a resize, we don't need to set the memory now since we know that all of these are written to + // Note though this does not clear the memory... updatedCellId.resize(foundSeeds); - thrust::fill(nosync_policy, updatedCellId.begin(), updatedCellId.end(), 0); updatedCellSeed.resize(foundSeeds); - thrust::fill(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), CellSeed()); - gpu::processNeighboursKernel<<>>( iLayer, level, @@ -1210,12 +1239,15 @@ void processNeighboursHandler(const int startLayer, propagator, matCorrType); } - GPUChkErrS(cudaStreamSynchronize(gpu::Stream::DefaultStream)); - thrust::device_vector, gpu::TypedAllocator>> outSeeds(updatedCellSeed.size(), allocCellSeed); - auto end = thrust::copy_if(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), outSeeds.begin(), gpu::seed_selector(1.e3, maxChi2NDF * ((startLevel + 2) * 2 - 5))); - auto s{end - outSeeds.begin()}; - seedsHost.reserve(seedsHost.size() + s); - thrust::copy(outSeeds.begin(), outSeeds.begin() + s, std::back_inserter(seedsHost)); + + // final copy of result + const auto selector = gpu::seed_selector(1.e3, maxChi2NDF * ((startLevel + 2) * 2 - 5)); + const auto count = thrust::count_if(sync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), selector); + thrust::device_vector, gpu::TypedAllocator>> outSeeds(count, allocCellSeed); + thrust::copy_if(sync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), outSeeds.begin(), selector); + seedsHost.reserve(seedsHost.size() + count); + thrust::copy(outSeeds.begin(), outSeeds.end(), std::back_inserter(seedsHost)); + alloc->popTagOffStack(Tag); } template From 0bb564d94f58153218e6cb421a5c666239c44428 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Wed, 28 Jan 2026 12:35:23 +0100 Subject: [PATCH 031/133] ITS: GPU: fix wrong argument parsing for outward refit Signed-off-by: Felix Schlepper --- Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx | 4 ++-- .../ITSMFT/ITS/tracking/include/ITStracking/Configuration.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx index 60774e3313d7f..42d2227de60f8 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx @@ -337,8 +337,8 @@ void TrackerTraitsGPU::findRoads(const int iteration) startLevel, this->mTrkParams[0].MaxChi2ClusterAttachment, this->mTrkParams[0].MaxChi2NDF, - this->mTrkParams[0].RepeatRefitOut, this->mTrkParams[0].ReseedIfShorter, + this->mTrkParams[0].RepeatRefitOut, this->mTrkParams[0].ShiftRefToCluster, mTimeFrameGPU->getDevicePropagator(), this->mTrkParams[0].CorrType, @@ -359,8 +359,8 @@ void TrackerTraitsGPU::findRoads(const int iteration) startLevel, this->mTrkParams[0].MaxChi2ClusterAttachment, this->mTrkParams[0].MaxChi2NDF, - this->mTrkParams[0].RepeatRefitOut, this->mTrkParams[0].ReseedIfShorter, + this->mTrkParams[0].RepeatRefitOut, this->mTrkParams[0].ShiftRefToCluster, mTimeFrameGPU->getDevicePropagator(), this->mTrkParams[0].CorrType, diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h index 1c4d604a629ed..10e1681c73e8d 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h @@ -69,7 +69,7 @@ struct TrackingParameters { int ReseedIfShorter = 6; // reseed for the final fit track with the length shorter than this std::vector MinPt = {0.f, 0.f, 0.f, 0.f}; uint16_t StartLayerMask = 0x7F; - bool RepeatRefitOut = true; // repeat outward refit using inward refit as a seed + bool RepeatRefitOut = false; // repeat outward refit using inward refit as a seed bool ShiftRefToCluster = true; // TrackFit: after update shift the linearization reference to cluster bool FindShortTracks = false; bool PerPrimaryVertexProcessing = false; From 63e5c6136f9eb261968541ae80cc589d2c1b0a7f Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Thu, 29 Jan 2026 13:44:05 +0100 Subject: [PATCH 032/133] DPL Analysis: cleanup AnalysisTask.h and ASoA.h (#14996) * remove unnecessary include; unnecessary mutability; too specific requires; unused function * use non-capturing lambdas; restrict template * we only expect void process functions * improve getIterators * improve homogeneous_apply_ref --------- Co-authored-by: ALICE Action Bot --- Framework/Core/include/Framework/ASoA.h | 62 +++---- .../Core/include/Framework/AnalysisManagers.h | 6 - .../Core/include/Framework/AnalysisTask.h | 163 +++++++++--------- .../include/Framework/StructToTuple.h | 46 ++--- 4 files changed, 128 insertions(+), 149 deletions(-) diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index 4fd35e0dc5065..7586d6a6d3c63 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -405,15 +405,15 @@ class Table; /// Type-checking index column binding struct Binding { void const* ptr = nullptr; - size_t hash = 0; - std::span refs; + uint32_t hash = 0; + // std::span refs; template void bind(T const* table) { ptr = table; hash = o2::framework::TypeIdHelpers::uniqueId(); - refs = std::span{T::originals}; + // refs = std::span{T::originals}; } template @@ -1293,6 +1293,9 @@ struct ArrowHelpers { template concept is_iterator = framework::base_of_template || framework::specialization_of_template; +template +concept is_table_or_iterator = is_table || is_iterator; + template concept with_originals = requires { T::originals.size(); @@ -2724,7 +2727,7 @@ consteval auto getIndexTargets() return !(*mColumnIterator).empty(); \ } \ \ - template \ + template \ auto _Getter_##_as() const \ { \ if (O2_BUILTIN_UNLIKELY(mBinding.ptr == nullptr)) { \ @@ -2734,10 +2737,15 @@ consteval auto getIndexTargets() if (O2_BUILTIN_UNLIKELY(t == nullptr)) { \ o2::soa::dereferenceWithWrongType(#_Getter_, #_Table_); \ } \ - return getIterators(); \ + auto result = std::vector(); \ + result.reserve((*mColumnIterator).size()); \ + for (auto& i : *mColumnIterator) { \ + result.emplace_back(t->rawIteratorAt(i)); \ + } \ + return result; \ } \ \ - template \ + template \ auto filtered_##_Getter_##_as() const \ { \ if (O2_BUILTIN_UNLIKELY(mBinding.ptr == nullptr)) { \ @@ -2747,35 +2755,15 @@ consteval auto getIndexTargets() if (O2_BUILTIN_UNLIKELY(t == nullptr)) { \ o2::soa::dereferenceWithWrongType(#_Getter_, #_Table_); \ } \ - return getFilteredIterators(); \ - } \ - \ - template \ - auto getIterators() const \ - { \ - auto result = std::vector(); \ - for (auto& i : *mColumnIterator) { \ - result.push_back(mBinding.get()->rawIteratorAt(i)); \ - } \ - return result; \ - } \ - \ - template \ - std::vector getFilteredIterators() const \ - { \ - if constexpr (o2::soa::is_filtered_table) { \ - auto result = std::vector(); \ - for (auto const& i : *mColumnIterator) { \ - auto pos = mBinding.get()->isInSelectedRows(i); \ - if (pos > 0) { \ - result.emplace_back(mBinding.get()->iteratorAt(pos)); \ - } \ + auto result = std::vector(); \ + result.reserve((*mColumnIterator).size()); \ + for (auto const& i : *mColumnIterator) { \ + auto pos = t->isInSelectedRows(i); \ + if (pos > 0) { \ + result.emplace_back(t->iteratorAt(pos)); \ } \ - return result; \ - } else { \ - static_assert(o2::framework::always_static_assert_v, "T is not a Filtered type"); \ } \ - return {}; \ + return result; \ } \ \ auto _Getter_() const \ @@ -3090,15 +3078,9 @@ consteval auto getIndexTargets() if (O2_BUILTIN_UNLIKELY(t == nullptr)) { \ o2::soa::dereferenceWithWrongType(#_Getter_, "self"); \ } \ - return getIterators(); \ - } \ - \ - template \ - auto getIterators() const \ - { \ auto result = std::vector(); \ for (auto& i : *mColumnIterator) { \ - result.push_back(mBinding.get()->rawIteratorAt(i)); \ + result.push_back(t->rawIteratorAt(i)); \ } \ return result; \ } \ diff --git a/Framework/Core/include/Framework/AnalysisManagers.h b/Framework/Core/include/Framework/AnalysisManagers.h index fd41a079c6570..121ce7f4b4a77 100644 --- a/Framework/Core/include/Framework/AnalysisManagers.h +++ b/Framework/Core/include/Framework/AnalysisManagers.h @@ -534,12 +534,6 @@ void bindExternalIndicesPartition(P& partition, T*... tables) } /// Cache handling -template -bool preInitializeCache(InitContext&, T&) -{ - return false; -} - template bool initializeCache(ProcessingContext&, T&) { diff --git a/Framework/Core/include/Framework/AnalysisTask.h b/Framework/Core/include/Framework/AnalysisTask.h index 4f8a9e719e4b9..365c6b1d41692 100644 --- a/Framework/Core/include/Framework/AnalysisTask.h +++ b/Framework/Core/include/Framework/AnalysisTask.h @@ -22,7 +22,6 @@ #include "Framework/EndOfStreamContext.h" #include "Framework/GroupSlicer.h" #include "Framework/StructToTuple.h" -#include "Framework/Traits.h" #include "Framework/TypeIdHelpers.h" #include "Framework/ArrowTableSlicingCache.h" #include "Framework/AnalysisDataModel.h" @@ -63,17 +62,20 @@ static constexpr bool is_enumeration_v> = true; template concept is_enumeration = is_enumeration_v>; +template +concept is_table_iterator_or_enumeration = soa::is_table_or_iterator || is_enumeration; + // Helper struct which builds a DataProcessorSpec from // the contents of an AnalysisTask... namespace { struct AnalysisDataProcessorBuilder { - template + template static void addGroupingCandidates(Cache& bk, Cache& bku, bool enabled) { - [&bk, &bku, enabled](framework::pack) mutable { + [](framework::pack, Cache& bk, Cache& bku, bool enabled) { auto key = std::string{"fIndex"} + o2::framework::cutString(soa::getLabelFromType>()); - ([&bk, &bku, &key, enabled]() mutable { + ([](Cache& bk, Cache& bku, bool enabled, std::string const& key) { if constexpr (soa::relatedByIndex, std::decay_t>()) { Entry e{soa::getLabelFromTypeForKey>(key), soa::getMatcherFromTypeForKey>(key), key, enabled}; if constexpr (o2::soa::is_smallgroups>) { @@ -82,9 +84,9 @@ struct AnalysisDataProcessorBuilder { framework::updatePairList(bk, e); } } - }(), + }(bk, bku, enabled, key), ...); - }(framework::pack{}); + }(framework::pack{}, bk, bku, enabled); } template @@ -168,8 +170,8 @@ struct AnalysisDataProcessorBuilder { return true; } /// 1. enumeration (must be the only argument) - template - static void inputsFromArgs(R (C::*)(A), const char* /*name*/, bool /*value*/, std::vector& inputs, std::vector&) //, Cache&, Cache&) + template + static void inputsFromArgs(void (C::*)(A), const char* /*name*/, bool /*value*/, std::vector& inputs, std::vector&) //, Cache&, Cache&) { std::vector inputMetadata; // FIXME: for the moment we do not support begin, end and step. @@ -177,37 +179,37 @@ struct AnalysisDataProcessorBuilder { } /// 2. 1st argument is an iterator - template - static void inputsFromArgs(R (C::*)(A, Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos) //, Cache& bk, Cache& bku) + template + static void inputsFromArgs(void (C::*)(A, Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos) //, Cache& bk, Cache& bku) requires(std::is_lvalue_reference_v && (std::is_lvalue_reference_v && ...)) { - constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); + constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); addInputsAndExpressions::parent_t, Args...>(hash, name, value, inputs, eInfos); } /// 3. generic case - template - static void inputsFromArgs(R (C::*)(Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos) //, Cache&, Cache&) + template + static void inputsFromArgs(void (C::*)(Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos) //, Cache&, Cache&) requires(std::is_lvalue_reference_v && ...) { - constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); + constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); addInputsAndExpressions(hash, name, value, inputs, eInfos); } /// 1. enumeration (no grouping) - template - static void cacheFromArgs(R (C::*)(A), bool, Cache&, Cache&) + template + static void cacheFromArgs(void (C::*)(A), bool, Cache&, Cache&) { } /// 2. iterator (the only grouping case) - template - static void cacheFromArgs(R (C::*)(A, Args...), bool value, Cache& bk, Cache& bku) + template + static void cacheFromArgs(void (C::*)(A, Args...), bool value, Cache& bk, Cache& bku) { addGroupingCandidates(bk, bku, value); } /// 3. generic case (no grouping) - template - static void cacheFromArgs(R (C::*)(A, Args...), bool, Cache&, Cache&) + template + static void cacheFromArgs(void (C::*)(A, Args...), bool, Cache&, Cache&) { } @@ -282,51 +284,53 @@ struct AnalysisDataProcessorBuilder { } } - template - static auto bindGroupingTable(InputRecord& record, R (C::*)(Grouping, Args...), std::vector& infos) + template + static auto bindGroupingTable(InputRecord& record, void (C::*)(Grouping, Args...), std::vector& infos) requires(!std::same_as) { - constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); + constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); return extract, 0>(record, infos, hash); } - template - static auto bindAssociatedTables(InputRecord& record, R (C::*)(Grouping, Args...), std::vector& infos) + template + static auto bindAssociatedTables(InputRecord& record, void (C::*)(Grouping, Args...), std::vector& infos) requires(!std::same_as && sizeof...(Args) > 0) { constexpr auto p = pack{}; - constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); + constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); return std::make_tuple(extract, has_type_at_v(p) + 1>(record, infos, hash)...); } - template + template static void overwriteInternalIndices(std::tuple& dest, std::tuple const& src) { (std::get(dest).bindInternalIndicesTo(&std::get(src)), ...); } - template - static void invokeProcess(Task& task, InputRecord& inputs, R (C::*processingFunction)(Grouping, Associated...), std::vector& infos, ArrowTableSlicingCache& slices) + template + static void invokeProcess(Task& task, InputRecord& inputs, void (Task::*processingFunction)(Grouping, Associated...), std::vector& infos, ArrowTableSlicingCache& slices) { using G = std::decay_t; auto groupingTable = AnalysisDataProcessorBuilder::bindGroupingTable(inputs, processingFunction, infos); + constexpr const int numElements = nested_brace_constructible_size>() / 10; + // set filtered tables for partitions with grouping - homogeneous_apply_refs([&groupingTable](auto& element) { + homogeneous_apply_refs_sized([&groupingTable](auto& element) { analysis_task_parsers::setPartition(element, groupingTable); analysis_task_parsers::bindInternalIndicesPartition(element, &groupingTable); return true; }, - task); + task); if constexpr (sizeof...(Associated) == 0) { // single argument to process - homogeneous_apply_refs([&groupingTable](auto& element) { + homogeneous_apply_refs_sized([&groupingTable](auto& element) { analysis_task_parsers::bindExternalIndicesPartition(element, &groupingTable); analysis_task_parsers::setGroupedCombination(element, groupingTable); return true; }, - task); + task); if constexpr (soa::is_iterator) { for (auto& element : groupingTable) { std::invoke(processingFunction, task, *element); @@ -344,7 +348,7 @@ struct AnalysisDataProcessorBuilder { // pre-bind self indices std::apply( [&task](auto&... t) mutable { - (homogeneous_apply_refs( + (homogeneous_apply_refs_sized( [&t](auto& p) { analysis_task_parsers::bindInternalIndicesPartition(p, &t); return true; @@ -356,12 +360,12 @@ struct AnalysisDataProcessorBuilder { auto binder = [&task, &groupingTable, &associatedTables](auto& x) mutable { x.bindExternalIndices(&groupingTable, &std::get>(associatedTables)...); - homogeneous_apply_refs([&x](auto& t) mutable { + homogeneous_apply_refs_sized([&x](auto& t) mutable { analysis_task_parsers::setPartition(t, x); analysis_task_parsers::bindExternalIndicesPartition(t, &x); return true; }, - task); + task); }; groupingTable.bindExternalIndices(&std::get>(associatedTables)...); @@ -373,11 +377,11 @@ struct AnalysisDataProcessorBuilder { associatedTables); // GroupedCombinations bound separately, as they should be set once for all associated tables - homogeneous_apply_refs([&groupingTable, &associatedTables](auto& t) { + homogeneous_apply_refs_sized([&groupingTable, &associatedTables](auto& t) { analysis_task_parsers::setGroupedCombination(t, groupingTable, associatedTables); return true; }, - task); + task); overwriteInternalIndices(associatedTables, associatedTables); if constexpr (soa::is_iterator>) { auto slicer = GroupSlicer(groupingTable, associatedTables, slices); @@ -391,28 +395,28 @@ struct AnalysisDataProcessorBuilder { associatedSlices); // bind partitions and grouping table - homogeneous_apply_refs([&groupingTable](auto& x) { + homogeneous_apply_refs_sized([&groupingTable](auto& x) { analysis_task_parsers::bindExternalIndicesPartition(x, &groupingTable); return true; }, - task); + task); invokeProcessWithArgs(task, processingFunction, slice.groupingElement(), associatedSlices); } } else { // bind partitions and grouping table - homogeneous_apply_refs([&groupingTable](auto& x) { + homogeneous_apply_refs_sized([&groupingTable](auto& x) { analysis_task_parsers::bindExternalIndicesPartition(x, &groupingTable); return true; }, - task); + task); invokeProcessWithArgs(task, processingFunction, groupingTable, associatedTables); } } } - template + template static void invokeProcessWithArgs(C& task, T processingFunction, G g, std::tuple& at) { std::invoke(processingFunction, task, g, std::get(at)...); @@ -520,16 +524,18 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) std::vector options; std::vector expressionInfos; + constexpr const int numElements = nested_brace_constructible_size>() / 10; + /// make sure options and configurables are set before expression infos are created - homogeneous_apply_refs([&options](auto& element) { return analysis_task_parsers::appendOption(options, element); }, *task.get()); + homogeneous_apply_refs_sized([&options](auto& element) { return analysis_task_parsers::appendOption(options, element); }, *task.get()); /// extract conditions and append them as inputs - homogeneous_apply_refs([&inputs](auto& element) { return analysis_task_parsers::appendCondition(inputs, element); }, *task.get()); + homogeneous_apply_refs_sized([&inputs](auto& element) { return analysis_task_parsers::appendCondition(inputs, element); }, *task.get()); /// parse process functions defined by corresponding configurables if constexpr (requires { &T::process; }) { AnalysisDataProcessorBuilder::inputsFromArgs(&T::process, "default", true, inputs, expressionInfos); } - homogeneous_apply_refs( + homogeneous_apply_refs_sized( [name = name_str, &expressionInfos, &inputs](auto& x) mutable { // this pushes (argumentIndex, processHash, schemaPtr, nullptr) into expressionInfos for arguments that are Filtered/filtered_iterators return AnalysisDataProcessorBuilder::requestInputsFromArgs(x, name, inputs, expressionInfos); @@ -538,39 +544,39 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) // request base tables for spawnable extended tables and indices to be built // this checks for duplications - homogeneous_apply_refs([&inputs](auto& element) { + homogeneous_apply_refs_sized([&inputs](auto& element) { return analysis_task_parsers::requestInputs(inputs, element); }, - *task.get()); + *task.get()); // no static way to check if the task defines any processing, we can only make sure it subscribes to at least something if (inputs.empty() == true) { LOG(warn) << "Task " << name_str << " has no inputs"; } - homogeneous_apply_refs([&outputs, &hash](auto& element) { return analysis_task_parsers::appendOutput(outputs, element, hash); }, *task.get()); + homogeneous_apply_refs_sized([&outputs, &hash](auto& element) { return analysis_task_parsers::appendOutput(outputs, element, hash); }, *task.get()); auto requiredServices = CommonServices::defaultServices(); auto arrowServices = CommonServices::arrowServices(); requiredServices.insert(requiredServices.end(), arrowServices.begin(), arrowServices.end()); - homogeneous_apply_refs([&requiredServices](auto& element) { return analysis_task_parsers::addService(requiredServices, element); }, *task.get()); + homogeneous_apply_refs_sized([&requiredServices](auto& element) { return analysis_task_parsers::addService(requiredServices, element); }, *task.get()); auto algo = AlgorithmSpec::InitCallback{[task = task, expressionInfos](InitContext& ic) mutable { Cache bindingsKeys; Cache bindingsKeysUnsorted; // add preslice declarations to slicing cache definition - homogeneous_apply_refs([&bindingsKeys, &bindingsKeysUnsorted](auto& element) { return analysis_task_parsers::registerCache(element, bindingsKeys, bindingsKeysUnsorted); }, *task.get()); + homogeneous_apply_refs_sized([&bindingsKeys, &bindingsKeysUnsorted](auto& element) { return analysis_task_parsers::registerCache(element, bindingsKeys, bindingsKeysUnsorted); }, *task.get()); - homogeneous_apply_refs([&ic](auto&& element) { return analysis_task_parsers::prepareOption(ic, element); }, *task.get()); - homogeneous_apply_refs([&ic](auto&& element) { return analysis_task_parsers::prepareService(ic, element); }, *task.get()); + homogeneous_apply_refs_sized([&ic](auto&& element) { return analysis_task_parsers::prepareOption(ic, element); }, *task.get()); + homogeneous_apply_refs_sized([&ic](auto&& element) { return analysis_task_parsers::prepareService(ic, element); }, *task.get()); auto& callbacks = ic.services().get(); auto eoscb = [task](EndOfStreamContext& eosContext) { - homogeneous_apply_refs([&eosContext](auto& element) { + homogeneous_apply_refs_sized([&eosContext](auto& element) { analysis_task_parsers::postRunService(eosContext, element); analysis_task_parsers::postRunOutput(eosContext, element); return true; }, - *task.get()); + *task.get()); eosContext.services().get().readyToQuit(QuitRequest::Me); }; @@ -582,84 +588,75 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) } /// update configurables in filters and partitions - homogeneous_apply_refs( + homogeneous_apply_refs_sized( [&ic](auto& element) -> bool { return analysis_task_parsers::updatePlaceholders(ic, element); }, *task.get()); /// create expression trees for filters gandiva trees matched to schemas and store the pointers into expressionInfos - homogeneous_apply_refs([&expressionInfos](auto& element) { + homogeneous_apply_refs_sized([&expressionInfos](auto& element) { return analysis_task_parsers::createExpressionTrees(expressionInfos, element); }, - *task.get()); + *task.get()); /// parse process functions to enable requested grouping caches - note that at this state process configurables have their final values if constexpr (requires { &T::process; }) { AnalysisDataProcessorBuilder::cacheFromArgs(&T::process, true, bindingsKeys, bindingsKeysUnsorted); } - homogeneous_apply_refs( - [&bindingsKeys, &bindingsKeysUnsorted](auto& x) mutable { + homogeneous_apply_refs_sized( + [&bindingsKeys, &bindingsKeysUnsorted](auto& x) { return AnalysisDataProcessorBuilder::requestCacheFromArgs(x, bindingsKeys, bindingsKeysUnsorted); }, *task.get()); ic.services().get().setCaches(std::move(bindingsKeys)); ic.services().get().setCachesUnsorted(std::move(bindingsKeysUnsorted)); - // initialize global caches - homogeneous_apply_refs([&ic](auto& element) { - return analysis_task_parsers::preInitializeCache(ic, element); - }, - *(task.get())); return [task, expressionInfos](ProcessingContext& pc) mutable { // load the ccdb object from their cache - homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::newDataframeCondition(pc.inputs(), element); }, *task.get()); + homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::newDataframeCondition(pc.inputs(), element); }, *task.get()); // reset partitions once per dataframe - homogeneous_apply_refs([](auto& element) { return analysis_task_parsers::newDataframePartition(element); }, *task.get()); + homogeneous_apply_refs_sized([](auto& element) { return analysis_task_parsers::newDataframePartition(element); }, *task.get()); // reset selections for the next dataframe - for (auto& info : expressionInfos) { - info.resetSelection = true; - } + std::ranges::for_each(expressionInfos, [](auto& info) { info.resetSelection = true; }); // reset pre-slice for the next dataframe auto slices = pc.services().get(); - homogeneous_apply_refs([&slices](auto& element) { + homogeneous_apply_refs_sized([&slices](auto& element) { return analysis_task_parsers::updateSliceInfo(element, slices); }, - *(task.get())); + *(task.get())); // initialize local caches - homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::initializeCache(pc, element); }, *(task.get())); + homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::initializeCache(pc, element); }, *(task.get())); // prepare outputs - homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::prepareOutput(pc, element); }, *task.get()); + homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::prepareOutput(pc, element); }, *task.get()); // execute run() if constexpr (requires { task->run(pc); }) { task->run(pc); } // execute process() - if constexpr (requires { AnalysisDataProcessorBuilder::invokeProcess(*(task.get()), pc.inputs(), &T::process, expressionInfos, slices); }) { + if constexpr (requires { &T::process; }) { AnalysisDataProcessorBuilder::invokeProcess(*(task.get()), pc.inputs(), &T::process, expressionInfos, slices); } // execute optional process() - homogeneous_apply_refs( - [&pc, &expressionInfos, &task, &slices](auto& x) mutable { - if constexpr (base_of_template>) { + homogeneous_apply_refs_sized( + [&pc, &expressionInfos, &task, &slices](auto& x) { + if constexpr (is_process_configurable) { if (x.value == true) { AnalysisDataProcessorBuilder::invokeProcess(*task.get(), pc.inputs(), x.process, expressionInfos, slices); return true; } + return false; } return false; }, *task.get()); // prepare delayed outputs - homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::prepareDelayedOutput(pc, element); }, *task.get()); + homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::prepareDelayedOutput(pc, element); }, *task.get()); // finalize outputs - homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::finalizeOutput(pc, element); }, *task.get()); + homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::finalizeOutput(pc, element); }, *task.get()); }; }}; return { name, - // FIXME: For the moment we hardcode this. We could build - // this list from the list of methods actually implemented in the - // task itself. inputs, outputs, algo, diff --git a/Framework/Foundation/include/Framework/StructToTuple.h b/Framework/Foundation/include/Framework/StructToTuple.h index 5748329f6a50d..1c7aa62260bd3 100644 --- a/Framework/Foundation/include/Framework/StructToTuple.h +++ b/Framework/Foundation/include/Framework/StructToTuple.h @@ -174,9 +174,9 @@ consteval int nested_brace_constructible_size() return brace_constructible_size() - nesting; } -template () / 10> +template () / 10, typename L> requires(D == 9) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -194,9 +194,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 8) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -214,9 +214,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 7) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -234,9 +234,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 6) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -254,9 +254,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 5) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -274,9 +274,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 4) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -294,9 +294,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 3) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -314,9 +314,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 2) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -334,9 +334,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 1) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -354,9 +354,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 0) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -373,6 +373,12 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } +template +constexpr auto homogeneous_apply_refs_sized(L l, T&& object) +{ + return homogeneous_apply_refs(l, object); +} + } // namespace o2::framework #endif // O2_FRAMEWORK_STRUCTTOTUPLE_H_ From a979c459f80ce84473fcc430700f82e5f352f433 Mon Sep 17 00:00:00 2001 From: Ernst Hellbar Date: Fri, 16 Jan 2026 14:05:07 +0100 Subject: [PATCH 033/133] dpl-workflow.sh: enable ALPIDE_ERR_DUMPS by default in online physics runs --- prodtests/full-system-test/dpl-workflow.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index f55605d1da485..f559fcdf91cf5 100755 --- a/prodtests/full-system-test/dpl-workflow.sh +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -106,7 +106,8 @@ EVE_OPT=" --jsons-folder $EDJSONS_DIR" : ${ITSTPC_CONFIG_KEY:=} : ${AOD_SOURCES:=$TRACK_SOURCES} : ${AODPROD_OPT:=} -: ${ALPIDE_ERR_DUMPS:=0} +: ${ALPIDE_ERR_DUMPS:=} +[[ -z $ALPIDE_ERR_DUMPS ]] && [[ $EPNSYNCMODE == 1 && $RUNTYPE == "PHYSICS" ]] && ALPIDE_ERR_DUMPS=1 || ALPIDE_ERR_DUMPS=0 [[ "0$DISABLE_ROOT_OUTPUT" == "00" ]] && DISABLE_ROOT_OUTPUT= From fd54f4a12076c934e1359a0da6dffed4a0649578 Mon Sep 17 00:00:00 2001 From: ehellbar Date: Fri, 30 Jan 2026 11:03:46 +0100 Subject: [PATCH 034/133] DPL: fix device signpost segfaults for o2-dpl-raw-proxy (#15003) --- Framework/Core/src/DataProcessingDevice.cxx | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index fd03b7725d847..8a306c7b96001 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -1509,9 +1509,8 @@ void DataProcessingDevice::doPrepare(ServiceRegistryRef ref) for (auto sci : pollOrder) { auto& info = state.inputChannelInfos[sci]; - auto& channelSpec = spec.inputChannels[sci]; O2_SIGNPOST_ID_FROM_POINTER(cid, device, &info); - O2_SIGNPOST_START(device, cid, "channels", "Processing channel %s", channelSpec.name.c_str()); + O2_SIGNPOST_START(device, cid, "channels", "Processing channel %s", info.channel->GetName().c_str()); if (info.state != InputChannelState::Completed && info.state != InputChannelState::Pull) { context.allDone = false; @@ -1523,18 +1522,18 @@ void DataProcessingDevice::doPrepare(ServiceRegistryRef ref) DataProcessingDevice::handleData(ref, info); } O2_SIGNPOST_END(device, cid, "channels", "Flushing channel %s which is in state %d and has %zu parts still pending.", - channelSpec.name.c_str(), (int)info.state, info.parts.Size()); + info.channel->GetName().c_str(), (int)info.state, info.parts.Size()); continue; } if (info.channel == nullptr) { O2_SIGNPOST_END(device, cid, "channels", "Channel %s which is in state %d is nullptr and has %zu parts still pending.", - channelSpec.name.c_str(), (int)info.state, info.parts.Size()); + info.channel->GetName().c_str(), (int)info.state, info.parts.Size()); continue; } // Only poll DPL channels for now. if (info.channelType != ChannelAccountingType::DPL) { O2_SIGNPOST_END(device, cid, "channels", "Channel %s which is in state %d is not a DPL channel and has %zu parts still pending.", - channelSpec.name.c_str(), (int)info.state, info.parts.Size()); + info.channel->GetName().c_str(), (int)info.state, info.parts.Size()); continue; } auto& socket = info.channel->GetSocket(); @@ -1546,7 +1545,7 @@ void DataProcessingDevice::doPrepare(ServiceRegistryRef ref) socket.Events(&info.hasPendingEvents); // If we do not read, we can continue. if ((info.hasPendingEvents & 1) == 0 && (info.parts.Size() == 0)) { - O2_SIGNPOST_END(device, cid, "channels", "No pending events and no remaining parts to process for channel %{public}s", channelSpec.name.c_str()); + O2_SIGNPOST_END(device, cid, "channels", "No pending events and no remaining parts to process for channel %{public}s", info.channel->GetName().c_str()); continue; } } @@ -1564,12 +1563,12 @@ void DataProcessingDevice::doPrepare(ServiceRegistryRef ref) bool newMessages = false; while (true) { O2_SIGNPOST_EVENT_EMIT(device, cid, "channels", "Receiving loop called for channel %{public}s (%d) with oldest possible timeslice %zu", - channelSpec.name.c_str(), info.id.value, info.oldestForChannel.value); + info.channel->GetName().c_str(), info.id.value, info.oldestForChannel.value); if (info.parts.Size() < 64) { fair::mq::Parts parts; info.channel->Receive(parts, 0); if (parts.Size()) { - O2_SIGNPOST_EVENT_EMIT(device, cid, "channels", "Received %zu parts from channel %{public}s (%d).", parts.Size(), channelSpec.name.c_str(), info.id.value); + O2_SIGNPOST_EVENT_EMIT(device, cid, "channels", "Received %zu parts from channel %{public}s (%d).", parts.Size(), info.channel->GetName().c_str(), info.id.value); } for (auto&& part : parts) { info.parts.fParts.emplace_back(std::move(part)); @@ -1598,7 +1597,7 @@ void DataProcessingDevice::doPrepare(ServiceRegistryRef ref) } } O2_SIGNPOST_END(device, cid, "channels", "Done processing channel %{public}s (%d).", - channelSpec.name.c_str(), info.id.value); + info.channel->GetName().c_str(), info.id.value); } } From 515ba3a699331dbd7a6c772ce8405ef49a492a99 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 30 Jan 2026 12:05:55 +0100 Subject: [PATCH 035/133] Revert "DPL Analysis: cleanup AnalysisTask.h and ASoA.h (#14996)" (#15005) This reverts commit 63e5c6136f9eb261968541ae80cc589d2c1b0a7f. --- Framework/Core/include/Framework/ASoA.h | 62 ++++--- .../Core/include/Framework/AnalysisManagers.h | 6 + .../Core/include/Framework/AnalysisTask.h | 163 +++++++++--------- .../include/Framework/StructToTuple.h | 46 +++-- 4 files changed, 149 insertions(+), 128 deletions(-) diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index 7586d6a6d3c63..4fd35e0dc5065 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -405,15 +405,15 @@ class Table; /// Type-checking index column binding struct Binding { void const* ptr = nullptr; - uint32_t hash = 0; - // std::span refs; + size_t hash = 0; + std::span refs; template void bind(T const* table) { ptr = table; hash = o2::framework::TypeIdHelpers::uniqueId(); - // refs = std::span{T::originals}; + refs = std::span{T::originals}; } template @@ -1293,9 +1293,6 @@ struct ArrowHelpers { template concept is_iterator = framework::base_of_template || framework::specialization_of_template; -template -concept is_table_or_iterator = is_table || is_iterator; - template concept with_originals = requires { T::originals.size(); @@ -2727,7 +2724,7 @@ consteval auto getIndexTargets() return !(*mColumnIterator).empty(); \ } \ \ - template \ + template \ auto _Getter_##_as() const \ { \ if (O2_BUILTIN_UNLIKELY(mBinding.ptr == nullptr)) { \ @@ -2737,15 +2734,10 @@ consteval auto getIndexTargets() if (O2_BUILTIN_UNLIKELY(t == nullptr)) { \ o2::soa::dereferenceWithWrongType(#_Getter_, #_Table_); \ } \ - auto result = std::vector(); \ - result.reserve((*mColumnIterator).size()); \ - for (auto& i : *mColumnIterator) { \ - result.emplace_back(t->rawIteratorAt(i)); \ - } \ - return result; \ + return getIterators(); \ } \ \ - template \ + template \ auto filtered_##_Getter_##_as() const \ { \ if (O2_BUILTIN_UNLIKELY(mBinding.ptr == nullptr)) { \ @@ -2755,17 +2747,37 @@ consteval auto getIndexTargets() if (O2_BUILTIN_UNLIKELY(t == nullptr)) { \ o2::soa::dereferenceWithWrongType(#_Getter_, #_Table_); \ } \ - auto result = std::vector(); \ - result.reserve((*mColumnIterator).size()); \ - for (auto const& i : *mColumnIterator) { \ - auto pos = t->isInSelectedRows(i); \ - if (pos > 0) { \ - result.emplace_back(t->iteratorAt(pos)); \ - } \ + return getFilteredIterators(); \ + } \ + \ + template \ + auto getIterators() const \ + { \ + auto result = std::vector(); \ + for (auto& i : *mColumnIterator) { \ + result.push_back(mBinding.get()->rawIteratorAt(i)); \ } \ return result; \ } \ \ + template \ + std::vector getFilteredIterators() const \ + { \ + if constexpr (o2::soa::is_filtered_table) { \ + auto result = std::vector(); \ + for (auto const& i : *mColumnIterator) { \ + auto pos = mBinding.get()->isInSelectedRows(i); \ + if (pos > 0) { \ + result.emplace_back(mBinding.get()->iteratorAt(pos)); \ + } \ + } \ + return result; \ + } else { \ + static_assert(o2::framework::always_static_assert_v, "T is not a Filtered type"); \ + } \ + return {}; \ + } \ + \ auto _Getter_() const \ { \ return _Getter_##_as(); \ @@ -3078,9 +3090,15 @@ consteval auto getIndexTargets() if (O2_BUILTIN_UNLIKELY(t == nullptr)) { \ o2::soa::dereferenceWithWrongType(#_Getter_, "self"); \ } \ + return getIterators(); \ + } \ + \ + template \ + auto getIterators() const \ + { \ auto result = std::vector(); \ for (auto& i : *mColumnIterator) { \ - result.push_back(t->rawIteratorAt(i)); \ + result.push_back(mBinding.get()->rawIteratorAt(i)); \ } \ return result; \ } \ diff --git a/Framework/Core/include/Framework/AnalysisManagers.h b/Framework/Core/include/Framework/AnalysisManagers.h index 121ce7f4b4a77..fd41a079c6570 100644 --- a/Framework/Core/include/Framework/AnalysisManagers.h +++ b/Framework/Core/include/Framework/AnalysisManagers.h @@ -534,6 +534,12 @@ void bindExternalIndicesPartition(P& partition, T*... tables) } /// Cache handling +template +bool preInitializeCache(InitContext&, T&) +{ + return false; +} + template bool initializeCache(ProcessingContext&, T&) { diff --git a/Framework/Core/include/Framework/AnalysisTask.h b/Framework/Core/include/Framework/AnalysisTask.h index 365c6b1d41692..4f8a9e719e4b9 100644 --- a/Framework/Core/include/Framework/AnalysisTask.h +++ b/Framework/Core/include/Framework/AnalysisTask.h @@ -22,6 +22,7 @@ #include "Framework/EndOfStreamContext.h" #include "Framework/GroupSlicer.h" #include "Framework/StructToTuple.h" +#include "Framework/Traits.h" #include "Framework/TypeIdHelpers.h" #include "Framework/ArrowTableSlicingCache.h" #include "Framework/AnalysisDataModel.h" @@ -62,20 +63,17 @@ static constexpr bool is_enumeration_v> = true; template concept is_enumeration = is_enumeration_v>; -template -concept is_table_iterator_or_enumeration = soa::is_table_or_iterator || is_enumeration; - // Helper struct which builds a DataProcessorSpec from // the contents of an AnalysisTask... namespace { struct AnalysisDataProcessorBuilder { - template + template static void addGroupingCandidates(Cache& bk, Cache& bku, bool enabled) { - [](framework::pack, Cache& bk, Cache& bku, bool enabled) { + [&bk, &bku, enabled](framework::pack) mutable { auto key = std::string{"fIndex"} + o2::framework::cutString(soa::getLabelFromType>()); - ([](Cache& bk, Cache& bku, bool enabled, std::string const& key) { + ([&bk, &bku, &key, enabled]() mutable { if constexpr (soa::relatedByIndex, std::decay_t>()) { Entry e{soa::getLabelFromTypeForKey>(key), soa::getMatcherFromTypeForKey>(key), key, enabled}; if constexpr (o2::soa::is_smallgroups>) { @@ -84,9 +82,9 @@ struct AnalysisDataProcessorBuilder { framework::updatePairList(bk, e); } } - }(bk, bku, enabled, key), + }(), ...); - }(framework::pack{}, bk, bku, enabled); + }(framework::pack{}); } template @@ -170,8 +168,8 @@ struct AnalysisDataProcessorBuilder { return true; } /// 1. enumeration (must be the only argument) - template - static void inputsFromArgs(void (C::*)(A), const char* /*name*/, bool /*value*/, std::vector& inputs, std::vector&) //, Cache&, Cache&) + template + static void inputsFromArgs(R (C::*)(A), const char* /*name*/, bool /*value*/, std::vector& inputs, std::vector&) //, Cache&, Cache&) { std::vector inputMetadata; // FIXME: for the moment we do not support begin, end and step. @@ -179,37 +177,37 @@ struct AnalysisDataProcessorBuilder { } /// 2. 1st argument is an iterator - template - static void inputsFromArgs(void (C::*)(A, Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos) //, Cache& bk, Cache& bku) + template + static void inputsFromArgs(R (C::*)(A, Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos) //, Cache& bk, Cache& bku) requires(std::is_lvalue_reference_v && (std::is_lvalue_reference_v && ...)) { - constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); + constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); addInputsAndExpressions::parent_t, Args...>(hash, name, value, inputs, eInfos); } /// 3. generic case - template - static void inputsFromArgs(void (C::*)(Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos) //, Cache&, Cache&) + template + static void inputsFromArgs(R (C::*)(Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos) //, Cache&, Cache&) requires(std::is_lvalue_reference_v && ...) { - constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); + constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); addInputsAndExpressions(hash, name, value, inputs, eInfos); } /// 1. enumeration (no grouping) - template - static void cacheFromArgs(void (C::*)(A), bool, Cache&, Cache&) + template + static void cacheFromArgs(R (C::*)(A), bool, Cache&, Cache&) { } /// 2. iterator (the only grouping case) - template - static void cacheFromArgs(void (C::*)(A, Args...), bool value, Cache& bk, Cache& bku) + template + static void cacheFromArgs(R (C::*)(A, Args...), bool value, Cache& bk, Cache& bku) { addGroupingCandidates(bk, bku, value); } /// 3. generic case (no grouping) - template - static void cacheFromArgs(void (C::*)(A, Args...), bool, Cache&, Cache&) + template + static void cacheFromArgs(R (C::*)(A, Args...), bool, Cache&, Cache&) { } @@ -284,53 +282,51 @@ struct AnalysisDataProcessorBuilder { } } - template - static auto bindGroupingTable(InputRecord& record, void (C::*)(Grouping, Args...), std::vector& infos) + template + static auto bindGroupingTable(InputRecord& record, R (C::*)(Grouping, Args...), std::vector& infos) requires(!std::same_as) { - constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); + constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); return extract, 0>(record, infos, hash); } - template - static auto bindAssociatedTables(InputRecord& record, void (C::*)(Grouping, Args...), std::vector& infos) + template + static auto bindAssociatedTables(InputRecord& record, R (C::*)(Grouping, Args...), std::vector& infos) requires(!std::same_as && sizeof...(Args) > 0) { constexpr auto p = pack{}; - constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); + constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); return std::make_tuple(extract, has_type_at_v(p) + 1>(record, infos, hash)...); } - template + template static void overwriteInternalIndices(std::tuple& dest, std::tuple const& src) { (std::get(dest).bindInternalIndicesTo(&std::get(src)), ...); } - template - static void invokeProcess(Task& task, InputRecord& inputs, void (Task::*processingFunction)(Grouping, Associated...), std::vector& infos, ArrowTableSlicingCache& slices) + template + static void invokeProcess(Task& task, InputRecord& inputs, R (C::*processingFunction)(Grouping, Associated...), std::vector& infos, ArrowTableSlicingCache& slices) { using G = std::decay_t; auto groupingTable = AnalysisDataProcessorBuilder::bindGroupingTable(inputs, processingFunction, infos); - constexpr const int numElements = nested_brace_constructible_size>() / 10; - // set filtered tables for partitions with grouping - homogeneous_apply_refs_sized([&groupingTable](auto& element) { + homogeneous_apply_refs([&groupingTable](auto& element) { analysis_task_parsers::setPartition(element, groupingTable); analysis_task_parsers::bindInternalIndicesPartition(element, &groupingTable); return true; }, - task); + task); if constexpr (sizeof...(Associated) == 0) { // single argument to process - homogeneous_apply_refs_sized([&groupingTable](auto& element) { + homogeneous_apply_refs([&groupingTable](auto& element) { analysis_task_parsers::bindExternalIndicesPartition(element, &groupingTable); analysis_task_parsers::setGroupedCombination(element, groupingTable); return true; }, - task); + task); if constexpr (soa::is_iterator) { for (auto& element : groupingTable) { std::invoke(processingFunction, task, *element); @@ -348,7 +344,7 @@ struct AnalysisDataProcessorBuilder { // pre-bind self indices std::apply( [&task](auto&... t) mutable { - (homogeneous_apply_refs_sized( + (homogeneous_apply_refs( [&t](auto& p) { analysis_task_parsers::bindInternalIndicesPartition(p, &t); return true; @@ -360,12 +356,12 @@ struct AnalysisDataProcessorBuilder { auto binder = [&task, &groupingTable, &associatedTables](auto& x) mutable { x.bindExternalIndices(&groupingTable, &std::get>(associatedTables)...); - homogeneous_apply_refs_sized([&x](auto& t) mutable { + homogeneous_apply_refs([&x](auto& t) mutable { analysis_task_parsers::setPartition(t, x); analysis_task_parsers::bindExternalIndicesPartition(t, &x); return true; }, - task); + task); }; groupingTable.bindExternalIndices(&std::get>(associatedTables)...); @@ -377,11 +373,11 @@ struct AnalysisDataProcessorBuilder { associatedTables); // GroupedCombinations bound separately, as they should be set once for all associated tables - homogeneous_apply_refs_sized([&groupingTable, &associatedTables](auto& t) { + homogeneous_apply_refs([&groupingTable, &associatedTables](auto& t) { analysis_task_parsers::setGroupedCombination(t, groupingTable, associatedTables); return true; }, - task); + task); overwriteInternalIndices(associatedTables, associatedTables); if constexpr (soa::is_iterator>) { auto slicer = GroupSlicer(groupingTable, associatedTables, slices); @@ -395,28 +391,28 @@ struct AnalysisDataProcessorBuilder { associatedSlices); // bind partitions and grouping table - homogeneous_apply_refs_sized([&groupingTable](auto& x) { + homogeneous_apply_refs([&groupingTable](auto& x) { analysis_task_parsers::bindExternalIndicesPartition(x, &groupingTable); return true; }, - task); + task); invokeProcessWithArgs(task, processingFunction, slice.groupingElement(), associatedSlices); } } else { // bind partitions and grouping table - homogeneous_apply_refs_sized([&groupingTable](auto& x) { + homogeneous_apply_refs([&groupingTable](auto& x) { analysis_task_parsers::bindExternalIndicesPartition(x, &groupingTable); return true; }, - task); + task); invokeProcessWithArgs(task, processingFunction, groupingTable, associatedTables); } } } - template + template static void invokeProcessWithArgs(C& task, T processingFunction, G g, std::tuple& at) { std::invoke(processingFunction, task, g, std::get(at)...); @@ -524,18 +520,16 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) std::vector options; std::vector expressionInfos; - constexpr const int numElements = nested_brace_constructible_size>() / 10; - /// make sure options and configurables are set before expression infos are created - homogeneous_apply_refs_sized([&options](auto& element) { return analysis_task_parsers::appendOption(options, element); }, *task.get()); + homogeneous_apply_refs([&options](auto& element) { return analysis_task_parsers::appendOption(options, element); }, *task.get()); /// extract conditions and append them as inputs - homogeneous_apply_refs_sized([&inputs](auto& element) { return analysis_task_parsers::appendCondition(inputs, element); }, *task.get()); + homogeneous_apply_refs([&inputs](auto& element) { return analysis_task_parsers::appendCondition(inputs, element); }, *task.get()); /// parse process functions defined by corresponding configurables if constexpr (requires { &T::process; }) { AnalysisDataProcessorBuilder::inputsFromArgs(&T::process, "default", true, inputs, expressionInfos); } - homogeneous_apply_refs_sized( + homogeneous_apply_refs( [name = name_str, &expressionInfos, &inputs](auto& x) mutable { // this pushes (argumentIndex, processHash, schemaPtr, nullptr) into expressionInfos for arguments that are Filtered/filtered_iterators return AnalysisDataProcessorBuilder::requestInputsFromArgs(x, name, inputs, expressionInfos); @@ -544,39 +538,39 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) // request base tables for spawnable extended tables and indices to be built // this checks for duplications - homogeneous_apply_refs_sized([&inputs](auto& element) { + homogeneous_apply_refs([&inputs](auto& element) { return analysis_task_parsers::requestInputs(inputs, element); }, - *task.get()); + *task.get()); // no static way to check if the task defines any processing, we can only make sure it subscribes to at least something if (inputs.empty() == true) { LOG(warn) << "Task " << name_str << " has no inputs"; } - homogeneous_apply_refs_sized([&outputs, &hash](auto& element) { return analysis_task_parsers::appendOutput(outputs, element, hash); }, *task.get()); + homogeneous_apply_refs([&outputs, &hash](auto& element) { return analysis_task_parsers::appendOutput(outputs, element, hash); }, *task.get()); auto requiredServices = CommonServices::defaultServices(); auto arrowServices = CommonServices::arrowServices(); requiredServices.insert(requiredServices.end(), arrowServices.begin(), arrowServices.end()); - homogeneous_apply_refs_sized([&requiredServices](auto& element) { return analysis_task_parsers::addService(requiredServices, element); }, *task.get()); + homogeneous_apply_refs([&requiredServices](auto& element) { return analysis_task_parsers::addService(requiredServices, element); }, *task.get()); auto algo = AlgorithmSpec::InitCallback{[task = task, expressionInfos](InitContext& ic) mutable { Cache bindingsKeys; Cache bindingsKeysUnsorted; // add preslice declarations to slicing cache definition - homogeneous_apply_refs_sized([&bindingsKeys, &bindingsKeysUnsorted](auto& element) { return analysis_task_parsers::registerCache(element, bindingsKeys, bindingsKeysUnsorted); }, *task.get()); + homogeneous_apply_refs([&bindingsKeys, &bindingsKeysUnsorted](auto& element) { return analysis_task_parsers::registerCache(element, bindingsKeys, bindingsKeysUnsorted); }, *task.get()); - homogeneous_apply_refs_sized([&ic](auto&& element) { return analysis_task_parsers::prepareOption(ic, element); }, *task.get()); - homogeneous_apply_refs_sized([&ic](auto&& element) { return analysis_task_parsers::prepareService(ic, element); }, *task.get()); + homogeneous_apply_refs([&ic](auto&& element) { return analysis_task_parsers::prepareOption(ic, element); }, *task.get()); + homogeneous_apply_refs([&ic](auto&& element) { return analysis_task_parsers::prepareService(ic, element); }, *task.get()); auto& callbacks = ic.services().get(); auto eoscb = [task](EndOfStreamContext& eosContext) { - homogeneous_apply_refs_sized([&eosContext](auto& element) { + homogeneous_apply_refs([&eosContext](auto& element) { analysis_task_parsers::postRunService(eosContext, element); analysis_task_parsers::postRunOutput(eosContext, element); return true; }, - *task.get()); + *task.get()); eosContext.services().get().readyToQuit(QuitRequest::Me); }; @@ -588,75 +582,84 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) } /// update configurables in filters and partitions - homogeneous_apply_refs_sized( + homogeneous_apply_refs( [&ic](auto& element) -> bool { return analysis_task_parsers::updatePlaceholders(ic, element); }, *task.get()); /// create expression trees for filters gandiva trees matched to schemas and store the pointers into expressionInfos - homogeneous_apply_refs_sized([&expressionInfos](auto& element) { + homogeneous_apply_refs([&expressionInfos](auto& element) { return analysis_task_parsers::createExpressionTrees(expressionInfos, element); }, - *task.get()); + *task.get()); /// parse process functions to enable requested grouping caches - note that at this state process configurables have their final values if constexpr (requires { &T::process; }) { AnalysisDataProcessorBuilder::cacheFromArgs(&T::process, true, bindingsKeys, bindingsKeysUnsorted); } - homogeneous_apply_refs_sized( - [&bindingsKeys, &bindingsKeysUnsorted](auto& x) { + homogeneous_apply_refs( + [&bindingsKeys, &bindingsKeysUnsorted](auto& x) mutable { return AnalysisDataProcessorBuilder::requestCacheFromArgs(x, bindingsKeys, bindingsKeysUnsorted); }, *task.get()); ic.services().get().setCaches(std::move(bindingsKeys)); ic.services().get().setCachesUnsorted(std::move(bindingsKeysUnsorted)); + // initialize global caches + homogeneous_apply_refs([&ic](auto& element) { + return analysis_task_parsers::preInitializeCache(ic, element); + }, + *(task.get())); return [task, expressionInfos](ProcessingContext& pc) mutable { // load the ccdb object from their cache - homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::newDataframeCondition(pc.inputs(), element); }, *task.get()); + homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::newDataframeCondition(pc.inputs(), element); }, *task.get()); // reset partitions once per dataframe - homogeneous_apply_refs_sized([](auto& element) { return analysis_task_parsers::newDataframePartition(element); }, *task.get()); + homogeneous_apply_refs([](auto& element) { return analysis_task_parsers::newDataframePartition(element); }, *task.get()); // reset selections for the next dataframe - std::ranges::for_each(expressionInfos, [](auto& info) { info.resetSelection = true; }); + for (auto& info : expressionInfos) { + info.resetSelection = true; + } // reset pre-slice for the next dataframe auto slices = pc.services().get(); - homogeneous_apply_refs_sized([&slices](auto& element) { + homogeneous_apply_refs([&slices](auto& element) { return analysis_task_parsers::updateSliceInfo(element, slices); }, - *(task.get())); + *(task.get())); // initialize local caches - homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::initializeCache(pc, element); }, *(task.get())); + homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::initializeCache(pc, element); }, *(task.get())); // prepare outputs - homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::prepareOutput(pc, element); }, *task.get()); + homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::prepareOutput(pc, element); }, *task.get()); // execute run() if constexpr (requires { task->run(pc); }) { task->run(pc); } // execute process() - if constexpr (requires { &T::process; }) { + if constexpr (requires { AnalysisDataProcessorBuilder::invokeProcess(*(task.get()), pc.inputs(), &T::process, expressionInfos, slices); }) { AnalysisDataProcessorBuilder::invokeProcess(*(task.get()), pc.inputs(), &T::process, expressionInfos, slices); } // execute optional process() - homogeneous_apply_refs_sized( - [&pc, &expressionInfos, &task, &slices](auto& x) { - if constexpr (is_process_configurable) { + homogeneous_apply_refs( + [&pc, &expressionInfos, &task, &slices](auto& x) mutable { + if constexpr (base_of_template>) { if (x.value == true) { AnalysisDataProcessorBuilder::invokeProcess(*task.get(), pc.inputs(), x.process, expressionInfos, slices); return true; } - return false; } return false; }, *task.get()); // prepare delayed outputs - homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::prepareDelayedOutput(pc, element); }, *task.get()); + homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::prepareDelayedOutput(pc, element); }, *task.get()); // finalize outputs - homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::finalizeOutput(pc, element); }, *task.get()); + homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::finalizeOutput(pc, element); }, *task.get()); }; }}; return { name, + // FIXME: For the moment we hardcode this. We could build + // this list from the list of methods actually implemented in the + // task itself. inputs, outputs, algo, diff --git a/Framework/Foundation/include/Framework/StructToTuple.h b/Framework/Foundation/include/Framework/StructToTuple.h index 1c7aa62260bd3..5748329f6a50d 100644 --- a/Framework/Foundation/include/Framework/StructToTuple.h +++ b/Framework/Foundation/include/Framework/StructToTuple.h @@ -174,9 +174,9 @@ consteval int nested_brace_constructible_size() return brace_constructible_size() - nesting; } -template () / 10, typename L> +template () / 10> requires(D == 9) -constexpr auto homogeneous_apply_refs(L l, T&& object) +auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -194,9 +194,9 @@ constexpr auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10, typename L> +template () / 10> requires(D == 8) -constexpr auto homogeneous_apply_refs(L l, T&& object) +auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -214,9 +214,9 @@ constexpr auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10, typename L> +template () / 10> requires(D == 7) -constexpr auto homogeneous_apply_refs(L l, T&& object) +auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -234,9 +234,9 @@ constexpr auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10, typename L> +template () / 10> requires(D == 6) -constexpr auto homogeneous_apply_refs(L l, T&& object) +auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -254,9 +254,9 @@ constexpr auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10, typename L> +template () / 10> requires(D == 5) -constexpr auto homogeneous_apply_refs(L l, T&& object) +auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -274,9 +274,9 @@ constexpr auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10, typename L> +template () / 10> requires(D == 4) -constexpr auto homogeneous_apply_refs(L l, T&& object) +auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -294,9 +294,9 @@ constexpr auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10, typename L> +template () / 10> requires(D == 3) -constexpr auto homogeneous_apply_refs(L l, T&& object) +auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -314,9 +314,9 @@ constexpr auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10, typename L> +template () / 10> requires(D == 2) -constexpr auto homogeneous_apply_refs(L l, T&& object) +auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -334,9 +334,9 @@ constexpr auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10, typename L> +template () / 10> requires(D == 1) -constexpr auto homogeneous_apply_refs(L l, T&& object) +auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -354,9 +354,9 @@ constexpr auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10, typename L> +template () / 10> requires(D == 0) -constexpr auto homogeneous_apply_refs(L l, T&& object) +auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -373,12 +373,6 @@ constexpr auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template -constexpr auto homogeneous_apply_refs_sized(L l, T&& object) -{ - return homogeneous_apply_refs(l, object); -} - } // namespace o2::framework #endif // O2_FRAMEWORK_STRUCTTOTUPLE_H_ From dd70bca748eca6084e14c34d048c7ad4b79c120b Mon Sep 17 00:00:00 2001 From: Pavel Larionov Date: Fri, 30 Jan 2026 17:02:29 +0100 Subject: [PATCH 036/133] ALICE 3: add fully cylindrical IRIS, correct Si thickness, add v3 building (#14979) * VD layer sensitive silicon 20 um, 80 um non-sens * Add an option for pure cylindrical IRIS * add v3 building option for FT3 * build pure cyl IRIS v3 by default --- .../include/FT3Simulation/Detector.h | 1 + .../ALICE3/FT3/simulation/src/Detector.cxx | 61 +++- .../ALICE3/TRK/base/include/TRKBase/Specs.h | 4 +- .../include/TRKSimulation/VDGeometryBuilder.h | 8 +- .../include/TRKSimulation/VDLayer.h | 13 +- .../ALICE3/TRK/simulation/src/Detector.cxx | 2 +- .../TRK/simulation/src/VDGeometryBuilder.cxx | 307 ++++++++++++++---- .../ALICE3/TRK/simulation/src/VDLayer.cxx | 280 ++++++++++++++-- 8 files changed, 585 insertions(+), 91 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/Detector.h b/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/Detector.h index a88ea5a351ad2..a68f8cf7788b6 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/Detector.h +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/Detector.h @@ -116,6 +116,7 @@ class Detector : public o2::base::DetImpl void buildFT3V3b(); void buildFT3Scoping(); void buildFT3NewVacuumVessel(); + void buildFT3ScopingV3(); void buildFT3FromFile(std::string); GeometryTGeo* mGeometryTGeo; //! access to geometry details diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx index aab8ae070d936..9303979ada930 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx @@ -346,6 +346,65 @@ void Detector::buildFT3NewVacuumVessel() } } +void Detector::buildFT3ScopingV3() +{ + // Build the FT3 detector according to v3 layout + // https://indico.cern.ch/event/1596309/contributions/6728167/attachments/3190117/5677220/2025-12-10-AW-ALICE3planning.pdf + // Middle disks inner radius 10 cm + // Outer disks inner radius 20 cm + + LOG(info) << "Building FT3 Detector: v3 scoping version"; + + mNumberOfLayers = 6; + float sensorThickness = 30.e-4; + float layersx2X0 = 1.e-2; + std::vector> layersConfigCSide{ + {77., 10.0, 35., layersx2X0}, // {z_layer, r_in, r_out, Layerx2X0} + {100., 10.0, 35., layersx2X0}, + {122., 10.0, 35., layersx2X0}, + {150., 20.0, 68.f, layersx2X0}, + {180., 20.0, 68.f, layersx2X0}, + {220., 20.0, 68.f, layersx2X0}}; + + std::vector> layersConfigASide{ + {77., 10.0, 35., layersx2X0}, // {z_layer, r_in, r_out, Layerx2X0} + {100., 10.0, 35., layersx2X0}, + {122., 10.0, 35., layersx2X0}, + {150., 20.0, 68.f, layersx2X0}, + {180., 20.0, 68.f, layersx2X0}, + {220., 20.0, 68.f, layersx2X0}}; + + mLayerName.resize(2); + mLayerName[0].resize(mNumberOfLayers); + mLayerName[1].resize(mNumberOfLayers); + mLayerID.clear(); + mLayers.resize(2); + + for (auto direction : {0, 1}) { + for (int layerNumber = 0; layerNumber < mNumberOfLayers; layerNumber++) { + std::string directionName = std::to_string(direction); + std::string layerName = GeometryTGeo::getFT3LayerPattern() + directionName + std::string("_") + std::to_string(layerNumber); + mLayerName[direction][layerNumber] = layerName; + float z, rIn, rOut, x0; + if (direction == 0) { // C-Side + z = layersConfigCSide[layerNumber][0]; + rIn = layersConfigCSide[layerNumber][1]; + rOut = layersConfigCSide[layerNumber][2]; + x0 = layersConfigCSide[layerNumber][3]; + } else if (direction == 1) { // A-Side + z = layersConfigASide[layerNumber][0]; + rIn = layersConfigASide[layerNumber][1]; + rOut = layersConfigASide[layerNumber][2]; + x0 = layersConfigASide[layerNumber][3]; + } + + LOG(info) << "Adding Layer " << layerName << " at z = " << z; + // Add layers + auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, z, rIn, rOut, x0); + } + } +} + //_________________________________________________________________________________________________ void Detector::buildFT3Scoping() { @@ -411,7 +470,7 @@ Detector::Detector(bool active) } else { switch (ft3BaseParam.geoModel) { case Default: - buildFT3NewVacuumVessel(); // FT3 after Upgrade days March 2024 + buildFT3ScopingV3(); // v3 Dec 25 break; case Telescope: buildBasicFT3(ft3BaseParam); // BasicFT3 = Parametrized telescopic detector (equidistant layers) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h index 95f9f9b00d7f3..c3c7de9dbe910 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h @@ -34,11 +34,11 @@ namespace VD // TODO: add a primitive segmentation with more granularity wrt 1/4 { namespace silicon { -constexpr double thickness{30 * mu}; // thickness of the silicon (should be 10 um epitaxial layer + 20 um substrate)? +constexpr double thickness{20 * mu}; // thickness of the silicon (should be 10 um epitaxial layer + 20 um substrate)? } // namespace silicon namespace metalstack { -constexpr double thickness{0 * mu}; // thickness of the copper metal stack - for the moment it is not implemented +constexpr double thickness{80 * mu}; // thickness of the copper metal stack - for the moment it is not implemented. PL: set to 80 um considering silicon as material } // namespace metalstack namespace petal { diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h index 0a2cb68f2233a..c337ddb102147 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h @@ -24,9 +24,11 @@ namespace o2::trk // Each function builds one local petal assembly (walls + layers + disks) // and then places/rotates the petal once into the mother volume. -void createIRIS4Geometry(TGeoVolume* motherVolume); // 4 petals, cylindrical L0 -void createIRIS4aGeometry(TGeoVolume* motherVolume); // 3 petals, cylindrical L0 -void createIRIS5Geometry(TGeoVolume* motherVolume); // 4 petals, rectangular L0 +void createIRISGeometryFullCyl(TGeoVolume* motherVolume); // Full-cylinder IRIS geometry (no petals, no gaps, no side walls) +void createIRISGeometryFullCylwithDisks(TGeoVolume* motherVolume); // Full-cylinder IRIS geometry (no petals, no gaps, no side walls) incl. disks +void createIRIS4Geometry(TGeoVolume* motherVolume); // 4 petals, cylindrical L0 +void createIRIS4aGeometry(TGeoVolume* motherVolume); // 3 petals, cylindrical L0 +void createIRIS5Geometry(TGeoVolume* motherVolume); // 4 petals, rectangular L0 void createSinglePetalDebug(TGeoVolume* motherVolume, int petalID = 0, int nPetals = 4, bool rectangularL0 = false); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDLayer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDLayer.h index 9e9ca2971bc3b..acf9b19342e4b 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDLayer.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDLayer.h @@ -39,9 +39,10 @@ class VDLayer protected: int mLayerNumber{0}; std::string mLayerName; - double mX2X0{0.f}; // Radiation length in units of X0 - double mChipThickness{0.f}; // thickness derived from X/X0 - double mModuleWidth{4.54f}; // cm + double mX2X0{0.f}; // Radiation length in units of X0 + double mChipThickness{0.f}; // thickness derived from X/X0 + double mSensorThickness{0.f}; // + double mModuleWidth{4.54f}; // cm // ClassDef(VDLayer, 1) }; @@ -54,6 +55,8 @@ class VDCylindricalLayer : public VDLayer double radius, double phiSpanDeg, double lengthZ, double lengthSensZ); TGeoVolume* createSensor() const; // builds the sensor volume + TGeoVolume* createChip() const; + TGeoVolume* createMetalStack() const; void createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans = nullptr) const override; private: @@ -73,6 +76,8 @@ class VDRectangularLayer : public VDLayer double width, double lengthZ, double lengthSensZ); TGeoVolume* createSensor() const; + TGeoVolume* createChip() const; + TGeoVolume* createMetalStack() const; void createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans = nullptr) const override; private: @@ -91,6 +96,8 @@ class VDDiskLayer : public VDLayer double rMin, double rMax, double phiSpanDeg, double zPos); TGeoVolume* createSensor() const; + TGeoVolume* createChip() const; + TGeoVolume* createMetalStack() const; void createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans = nullptr) const override; double getZPosition() const { return mZPos; } diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index 0924be5fb6764..e0fc6ef1ed35b 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -266,7 +266,7 @@ void Detector::createGeometry() // Alternatives: createIRIS5Geometry(vTRK); createIRIS4aGeometry(vTRK); o2::trk::clearVDSensorRegistry(); - o2::trk::createIRIS4Geometry(vTRK); + o2::trk::createIRISGeometryFullCyl(vTRK); // Fill sensor names from registry right after geometry creation const auto& regs = o2::trk::vdSensorRegistry(); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx index 5df875713262c..6ce04bb8443ef 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx @@ -67,6 +67,12 @@ inline bool isSolidToCut(const TGeoVolume* v) if (TString(nm).BeginsWith("VD_SideWall")) { return true; } + if (TString(nm).BeginsWith("VD_InnerWallCyl")) { + return true; + } + if (TString(nm).BeginsWith("VD_OuterWallCyl")) { + return true; + } if (TString(nm).Contains("_Coldplate")) { return true; } @@ -166,7 +172,34 @@ inline void buildPetalSolidsComposite(TGeoVolume* petalAsm) // Build the global cutout by rotating the petal-local composite n times with (p+0.5) phase inline void buildIrisCutoutFromPetalSolid(int nPetals) { - // Create n rotation transforms + auto* shps = gGeoManager->GetListOfShapes(); + auto* base = shps ? dynamic_cast(shps->FindObject("IRIS_PETAL_SOLIDSsh")) : nullptr; + if (!base) { + LOGP(error, "IRIS cutout: shape 'IRIS_PETAL_SOLIDSsh' not found."); + return; + } + + // IMPORTANT: for nPetals==1, a composite expression like "A:tr" is invalid. + // Just clone the petal solids shape as the global cutout. + if (nPetals == 1) { + // Remove any previous shape with same name if it exists (optional but keeps things clean) + if (shps->FindObject("IRIS_CUTOUTsh")) { + // ROOT shape lists are owned by gGeoManager; removing is not always necessary. + // Keeping it simple: just create a unique name if it already exists. + LOGP(warning, "IRIS cutout: 'IRIS_CUTOUTsh' already exists; overwriting by clone name reuse may be unsafe."); + } + + auto* cut = dynamic_cast(base->Clone("IRIS_CUTOUTsh")); + if (!cut) { + LOGP(error, "IRIS cutout: failed to clone 'IRIS_PETAL_SOLIDSsh' to 'IRIS_CUTOUTsh'."); + return; + } + + LOGP(info, "IRIS_CUTOUTsh created as clone of IRIS_PETAL_SOLIDSsh (nPetals=1)."); + return; + } + + // nPetals > 1: build union of rotated copies TString cutFormula; for (int p = 0; p < nPetals; ++p) { const double phi = (360.0 / nPetals) * (p + 0.5); @@ -175,47 +208,23 @@ inline void buildIrisCutoutFromPetalSolid(int nPetals) auto* RT = new TGeoCombiTrans(0, 0, 0, R); RT->SetName(Form("IRIS_PETAL_ROT_%d", p)); RT->RegisterYourself(); + if (p) { cutFormula += "+"; } cutFormula += Form("IRIS_PETAL_SOLIDSsh:%s", RT->GetName()); } - LOGP(info, "IRIS_CUTOUTsh formula: {}", cutFormula.Data()); - new TGeoCompositeShape("IRIS_CUTOUTsh", cutFormula.Data()); - // --- Sanity check: required matrices & shapes exist - auto* mats = gGeoManager ? gGeoManager->GetListOfMatrices() : nullptr; - auto* shps = gGeoManager ? gGeoManager->GetListOfShapes() : nullptr; + LOGP(info, "IRIS_CUTOUTsh formula: {}", cutFormula.Data()); + auto* cut = new TGeoCompositeShape("IRIS_CUTOUTsh", cutFormula.Data()); + (void)cut; - if (!mats || !shps) { - LOGP(error, "IRIS cutout sanity: gGeoManager not initialized properly (mats/shapes missing)."); + // Stronger sanity: ensure it parsed into a boolean node + auto* cutCheck = dynamic_cast(shps->FindObject("IRIS_CUTOUTsh")); + if (!cutCheck || !cutCheck->GetBoolNode()) { + LOGP(error, "IRIS cutout sanity: IRIS_CUTOUTsh exists but parsing failed (no BoolNode)."); } else { - bool ok = true; - - // Check the petal rotations were registered and referenced - for (int p = 0; p < nPetals; ++p) { - const TString name = Form("IRIS_PETAL_ROT_%d", p); - if (!mats->FindObject(name)) { - LOGP(error, "IRIS cutout sanity: missing matrix {}", name.Data()); - ok = false; - } - } - - // Check that the local petal composite exists - if (!shps->FindObject("IRIS_PETAL_SOLIDSsh")) { - LOGP(error, "IRIS cutout sanity: shape 'IRIS_PETAL_SOLIDSsh' not found."); - ok = false; - } - - // Check that the global cutout shape was created - if (!shps->FindObject("IRIS_CUTOUTsh")) { - LOGP(error, "IRIS cutout sanity: shape 'IRIS_CUTOUTsh' not found."); - ok = false; - } - - if (ok) { - LOGP(info, "IRIS cutout sanity: OK ({} petals).", nPetals); - } + LOGP(info, "IRIS cutout sanity: OK ({} petals).", nPetals); } } @@ -299,7 +308,11 @@ inline TGeoCombiTrans rotZ(double phiDeg) // ============ Petal sub-builders (LOCAL coords only, no rotation) ========= // Walls: inner cylindrical arc at r=4.8 mm (always), outer arc wall, and two side plates. -static void addPetalWalls(TGeoVolume* petalAsm, int nPetals, double outerRadius_cm = kOuterWallRadius_cm) +static void addPetalWalls(TGeoVolume* petalAsm, + int nPetals, + double outerRadius_cm = kOuterWallRadius_cm, + bool withSideWalls = true, + bool fullCylindricalRadialWalls = false) { if (!petalAsm) { LOGP(error, "addPetalWalls: petalAsm is null"); @@ -314,11 +327,21 @@ static void addPetalWalls(TGeoVolume* petalAsm, int nPetals, double outerRadius_ return; } - const double halfPhi = 0.5f * (360.f / static_cast(nPetals)); - const double halfZ = 0.5f * kPetalZ_cm; + const double halfZ = 0.5 * kPetalZ_cm; - // ---- Inner cylindrical wall (always at r=4.8 mm) ---- - { + // In full-cylinder radial-wall mode we ignore nPetals for the radial walls. + const double halfPhi = fullCylindricalRadialWalls ? 180.0 : 0.5 * (360.0 / static_cast(nPetals)); + + // ---- Inner radial wall ---- + if (fullCylindricalRadialWalls) { + auto* s = new TGeoTube(static_cast(kInnerWallRadius_cm), + static_cast(kInnerWallRadius_cm + kWallThick_cm), + static_cast(halfZ)); + auto* v = new TGeoVolume("VD_InnerWallCyl", s, med); + v->SetLineColor(kGray + 2); + v->SetTransparency(70); + petalAsm->AddNode(v, 1); + } else { auto* s = new TGeoTubeSeg(static_cast(kInnerWallRadius_cm), static_cast(kInnerWallRadius_cm + kWallThick_cm), static_cast(halfZ), @@ -330,8 +353,16 @@ static void addPetalWalls(TGeoVolume* petalAsm, int nPetals, double outerRadius_ petalAsm->AddNode(v, 1); } - // ---- Outer arc wall ---- - { + // ---- Outer radial wall ---- + if (fullCylindricalRadialWalls) { + auto* s = new TGeoTube(static_cast(outerRadius_cm), + static_cast(outerRadius_cm + kWallThick_cm), + static_cast(halfZ)); + auto* v = new TGeoVolume("VD_OuterWallCyl", s, med); + v->SetLineColor(kGray + 2); + v->SetTransparency(70); + petalAsm->AddNode(v, 1); + } else { auto* s = new TGeoTubeSeg(static_cast(outerRadius_cm), static_cast(outerRadius_cm + kWallThick_cm), static_cast(halfZ), @@ -343,6 +374,11 @@ static void addPetalWalls(TGeoVolume* petalAsm, int nPetals, double outerRadius_ petalAsm->AddNode(v, 1); } + // ---- Side plates (skip in "single petal full cylinders" mode) ---- + if (!withSideWalls) { + return; + } + // ---- Side walls (boxes) at ±halfPhi ---- const double radialLen = (outerRadius_cm - (kInnerWallRadius_cm + kWallThick_cm)); auto* sideS = new TGeoBBox(static_cast(0.5f * radialLen), @@ -369,7 +405,7 @@ static void addPetalWalls(TGeoVolume* petalAsm, int nPetals, double outerRadius_ // Build inner layers (L0..L2). L0 may be rectangular (IRIS5) or cylindrical. // φ-spans derive from spec gaps/arc; all local placement (no rotation). -static void addBarrelLayers(TGeoVolume* petalAsm, int nPetals, int petalID, bool rectangularL0) +static void addBarrelLayers(TGeoVolume* petalAsm, int nPetals, int petalID, bool rectangularL0, bool fullCylinders) { if (!petalAsm) { LOGP(error, "addBarrelLayers: petalAsm is null"); @@ -382,15 +418,15 @@ static void addBarrelLayers(TGeoVolume* petalAsm, int nPetals, int petalID, bool constexpr double arcL0_cm = 0.6247f; // 6.247 mm // φ spans - const double phiL0_deg = phiSpanFromGap(nPetals, gapL0_cm, rL0_cm); // L0 gap-defined - const double phiL1_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL1_cm); // L1 gap-defined - const double phiL2_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm); // L2 gap-defined + const double phiL0_deg = fullCylinders ? 360.0 : phiSpanFromGap(nPetals, gapL0_cm, rL0_cm); + const double phiL1_deg = fullCylinders ? 360.0 : phiSpanFromGap(nPetals, gapL1L2_cm, rL1_cm); + const double phiL2_deg = fullCylinders ? 360.0 : phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm); const std::string nameL0 = std::string(o2::trk::GeometryTGeo::getTRKPetalPattern()) + std::to_string(petalID) + "_" + std::string(o2::trk::GeometryTGeo::getTRKPetalLayerPattern()) + "0"; - if (rectangularL0) { + if (!fullCylinders && rectangularL0) { VDRectangularLayer L0(0, nameL0, kX2X0, kL0RectWidth_cm, kLenZ_cm, kLenZ_cm); @@ -438,7 +474,7 @@ static void addBarrelLayers(TGeoVolume* petalAsm, int nPetals, int petalID, bool } // Build cold plate (cylindrical) in local coordinates, and add it to the petal assembly. -static void addColdPlate(TGeoVolume* petalAsm, int nPetals, int petalId) +static void addColdPlate(TGeoVolume* petalAsm, int nPetals, int petalId, bool fullCylinders = false) { if (!petalAsm) { LOGP(error, "addColdPlate: petalAsm is null"); @@ -455,8 +491,9 @@ static void addColdPlate(TGeoVolume* petalAsm, int nPetals, int petalId) constexpr double gapL1L2_cm = 0.12f; // 1.2 mm // φ spans - const double phiSpanColdplate_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm); // L2 gap-defined - const double halfPhiDeg = 0.5f * phiSpanColdplate_deg; + const double phiSpanColdplate_deg = + fullCylinders ? 360.0 : phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm); // L2 gap-defined in normal mode + const double halfPhiDeg = 0.5 * phiSpanColdplate_deg; const double startPhi = -halfPhiDeg; const double endPhi = +halfPhiDeg; @@ -625,7 +662,7 @@ static void addIRISServiceModulesSegmented(TGeoVolume* petalAsm, int nPetals) // Build disks in local coords: each disk gets only a local Z translation. // φ span from gap at rOut. -static void addDisks(TGeoVolume* petalAsm, int nPetals, int petalID) +static void addDisks(TGeoVolume* petalAsm, int nPetals, int petalID, bool fullCylinders) { if (!petalAsm) { @@ -633,7 +670,7 @@ static void addDisks(TGeoVolume* petalAsm, int nPetals, int petalID) return; } - const double phiDisk_deg = phiSpanFromGap(nPetals, 2 * kWallThick_cm, diskRin_cm); + const double phiDisk_deg = fullCylinders ? 360.0 : phiSpanFromGap(nPetals, 2 * kWallThick_cm, diskRin_cm); for (int i = 0; i < 6; ++i) { const std::string nameD = @@ -651,21 +688,124 @@ static void addDisks(TGeoVolume* petalAsm, int nPetals, int petalID) } } +// Add Z end-cap walls to "close" the petal/cylinder volume at zMin and zMax. +// Implemented as thin rings (TGeoTube) with thickness 'capThick_cm' in Z, +// spanning radii [rIn_cm, rOut_cm]. +static void addPetalEndCaps(TGeoVolume* petalAsm, + int petalId, + double rIn_cm, + double rOut_cm, + double zMin_cm, + double zMax_cm, + double capThick_cm) +{ + if (!petalAsm) { + LOGP(error, "addPetalEndCaps: petalAsm is null"); + return; + } + + auto& matmgr = o2::base::MaterialManager::Instance(); + const TGeoMedium* med = + matmgr.getTGeoMedium("ALICE3_TRKSERVICES_ALUMINIUM5083"); + + if (!med) { + LOGP(warning, + "addPetalEndCaps: ALICE3_TRKSERVICES_ALUMINIUM5083 not found, caps not created."); + return; + } + + const double halfT = 0.5 * capThick_cm; + + auto* sh = new TGeoTube(static_cast(rIn_cm), + static_cast(rOut_cm), + static_cast(halfT)); + + TString vname = Form("Petal%d_ZCap", petalId); + auto* v = new TGeoVolume(vname, sh, med); + v->SetLineColor(kGray + 2); + v->SetTransparency(70); + + auto* trMin = new TGeoTranslation(0.0, 0.0, + static_cast(zMin_cm + halfT)); + auto* trMax = new TGeoTranslation(0.0, 0.0, + static_cast(zMax_cm - halfT)); + + petalAsm->AddNode(v, 1, trMin); + petalAsm->AddNode(v, 2, trMax); +} + // Build one complete petal assembly (walls + L0..L2 + disks) in LOCAL coords. -static TGeoVolume* buildPetalAssembly(int nPetals, int petalID, bool rectangularL0) +static TGeoVolume* buildPetalAssembly(int nPetals, + int petalID, + bool rectangularL0, + bool fullCylinders, + bool withSideWalls) { auto* petalAsm = new TGeoVolumeAssembly(Form("PETAL_%d", petalID)); - addPetalWalls(petalAsm, nPetals, kOuterWallRadius_cm); - // Pass petalID to layers/disks for naming - addBarrelLayers(petalAsm, nPetals, petalID, rectangularL0); - addColdPlate(petalAsm, nPetals, petalID); - addDisks(petalAsm, nPetals, petalID); + // In the special mode: no side walls, but keep radial walls as FULL cylinders. + addPetalWalls(petalAsm, nPetals, kOuterWallRadius_cm, + /*withSideWalls=*/withSideWalls, + /*fullCylindricalRadialWalls=*/fullCylinders); + + addBarrelLayers(petalAsm, nPetals, petalID, rectangularL0, fullCylinders); + addDisks(petalAsm, nPetals, petalID, fullCylinders); + + addColdPlate(petalAsm, nPetals, petalID, /*fullCylinders=*/false); addIRISServiceModulesSegmented(petalAsm, nPetals); return petalAsm; } +static TGeoVolume* buildFullCylAssembly(int petalID, bool withDisks) +{ + // IMPORTANT: keep naming consistent with createIRIS4/5 (PETAL_%d) + auto* petalAsm = new TGeoVolumeAssembly(Form("PETAL_%d", petalID)); + + // Radial walls only: full 360° cylinders, no side plates + addPetalWalls(petalAsm, + /*nPetals=*/1, + /*outerRadius_cm=*/kOuterWallRadius_cm, + /*withSideWalls=*/false, + /*fullCylindricalRadialWalls=*/true); + + // --- Z end-cap walls to close the petal in Z --- + { + const double zMin = -0.5 * kLenZ_cm; + const double zMax = +0.5 * kLenZ_cm; + const double rIn = kInnerWallRadius_cm; + const double rOut = kOuterWallRadius_cm + kWallThick_cm; + + addPetalEndCaps(petalAsm, + petalID, + rIn, + rOut, + zMin, + zMax, + kWallThick_cm); + } + + // Full 360° barrel cylinders + addBarrelLayers(petalAsm, + /*nPetals=*/1, + /*petalID=*/petalID, + /*rectangularL0=*/false, + /*fullCylinders=*/true); + + addColdPlate(petalAsm, 1, petalID, /*fullCylinders=*/true); + addIRISServiceModulesSegmented(petalAsm, /*nPetals=*/1); + + // Optionally add full 360° disks + if (withDisks) { + addDisks(petalAsm, + /*nPetals=*/1, + /*petalID=*/petalID, + /*fullCylinders=*/true); + } + + return petalAsm; +} + // =================== Public entry points =================== void createIRIS4Geometry(TGeoVolume* motherVolume) @@ -679,7 +819,9 @@ void createIRIS4Geometry(TGeoVolume* motherVolume) constexpr int nPetals = 4; for (int p = 0; p < nPetals; ++p) { - auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ false); + auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ false, + /*fullCylinders=*/false, + /*withSideWalls=*/true); // Build the petal-local solids composite once from the FIRST petal if (p == 0) { buildPetalSolidsComposite(petal); // <-- captures only SOLIDS in local coords @@ -704,7 +846,9 @@ void createIRIS5Geometry(TGeoVolume* motherVolume) constexpr int nPetals = 4; for (int p = 0; p < nPetals; ++p) { - auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ true); + auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ true, + /*fullCylinders=*/false, + /*withSideWalls=*/true); // Build the petal-local solids composite once from the FIRST petal if (p == 0) { buildPetalSolidsComposite(petal); // <-- captures only SOLIDS in local coords @@ -729,7 +873,9 @@ void createIRIS4aGeometry(TGeoVolume* motherVolume) constexpr int nPetals = 3; for (int p = 0; p < nPetals; ++p) { - auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ false); + auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ false, + /*fullCylinders=*/false, + /*withSideWalls=*/true); // Build the petal-local solids composite once from the FIRST petal if (p == 0) { buildPetalSolidsComposite(petal); // <-- captures only SOLIDS in local coords @@ -743,9 +889,48 @@ void createIRIS4aGeometry(TGeoVolume* motherVolume) buildIrisCutoutFromPetalSolid(nPetals); } +void createIRISGeometryFullCyl(TGeoVolume* motherVolume) +{ + if (!motherVolume) { + LOGP(error, "createIRISGeometryFullCyl: motherVolume is null"); + return; + } + + clearVDSensorRegistry(); + + constexpr int nPetals = 1; + constexpr int petalID = 0; + + auto* petal = buildFullCylAssembly(petalID, /*withDisks=*/false); + motherVolume->AddNode(petal, 1, nullptr); + + buildPetalSolidsComposite(petal); + buildIrisCutoutFromPetalSolid(nPetals); +} + +void createIRISGeometryFullCylwithDisks(TGeoVolume* motherVolume) +{ + if (!motherVolume) { + LOGP(error, "createIRISGeometryFullCylDisks: motherVolume is null"); + return; + } + + clearVDSensorRegistry(); + + constexpr int nPetals = 1; + constexpr int petalID = 0; + + auto* petal = buildFullCylAssembly(petalID, /*withDisks=*/true); + motherVolume->AddNode(petal, 1, nullptr); + + // Same cutout pipeline as createIRIS4/5: + buildPetalSolidsComposite(petal); + buildIrisCutoutFromPetalSolid(nPetals); +} + void createSinglePetalDebug(TGeoVolume* motherVolume, int petalID, int nPetals, bool rectangularL0) { - auto* petal = buildPetalAssembly(nPetals, petalID, rectangularL0); + auto* petal = buildPetalAssembly(nPetals, petalID, rectangularL0, false, true); // Optionally rotate the petal for display const double phiDeg = (360.f / static_cast(nPetals)) * (static_cast(petalID) + 0.5f); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx index 20f36f1f6f4e7..411dd485684b9 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx @@ -11,6 +11,7 @@ #include "TRKSimulation/VDLayer.h" #include "TRKBase/GeometryTGeo.h" +#include "TRKBase/Specs.h" #include "Framework/Logger.h" @@ -32,6 +33,8 @@ VDLayer::VDLayer(int layerNumber, const std::string& layerName, double layerX2X0 { constexpr double kSiX0_cm = 9.5; // Radiation length of Silicon in cm mChipThickness = mX2X0 * kSiX0_cm; + + mSensorThickness = o2::trk::constants::VD::silicon::thickness; // cm } // VDCylindricalLayer constructor @@ -83,7 +86,7 @@ TGeoVolume* VDCylindricalLayer::createSensor() const } std::string sensName = Form("%s_%s%d", this->mLayerName.c_str(), GeometryTGeo::getTRKSensorPattern(), this->mLayerNumber); const double rIn = mRadius; - const double rOut = mRadius + mChipThickness; + const double rOut = mRadius + mSensorThickness; const double halfZ = 0.5 * mLengthSensZ; const double halfPhi = 0.5 * mPhiSpanDeg; // degrees auto* shape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); @@ -106,8 +109,8 @@ TGeoVolume* VDRectangularLayer::createSensor() const } std::string sensName = Form("%s_%s%d", this->mLayerName.c_str(), GeometryTGeo::getTRKSensorPattern(), this->mLayerNumber); const double hx = 0.5 * mWidth; - const double hy = 0.5 * mChipThickness; // thickness in Y - const double hz = 0.5 * mLengthSensZ; // <-- use sensor Z length, not full layer + const double hy = 0.5 * mSensorThickness; + const double hz = 0.5 * mLengthSensZ; // <-- use sensor Z length, not full layer auto* shape = new TGeoBBox(hx, hy, hz); auto* vol = new TGeoVolume(sensName.c_str(), shape, medSi); @@ -134,8 +137,8 @@ TGeoVolume* VDDiskLayer::createSensor() const return nullptr; } std::string sensName = Form("%s_%s%d", this->mLayerName.c_str(), GeometryTGeo::getTRKSensorPattern(), this->mLayerNumber); - const double halfThickness = 0.5 * mChipThickness; // disk thickness is along Z - const double halfPhi = 0.5 * mPhiSpanDeg; // degrees + const double halfThickness = 0.5 * mSensorThickness; // active sensor thickness along Z + const double halfPhi = 0.5 * mPhiSpanDeg; // degrees // Same geometry as the layer (identical radii + phi span + thickness) auto* shape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); @@ -147,6 +150,243 @@ TGeoVolume* VDDiskLayer::createSensor() const return sensVol; } +/* +** Create metal stack +*/ + +TGeoVolume* VDCylindricalLayer::createMetalStack() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + auto* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + + const double metalT = mChipThickness - mSensorThickness; + if (metalT <= 0) { + return nullptr; // nothing to add + } + + std::string name = Form("%s_%s%d", mLayerName.c_str(), + GeometryTGeo::getTRKMetalStackPattern(), mLayerNumber); + + const double rIn = mRadius + mSensorThickness; + const double rOut = mRadius + mChipThickness; + const double halfZ = 0.5 * mLengthSensZ; + const double halfPhi = 0.5 * mPhiSpanDeg; + + auto* shape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); + auto* vol = new TGeoVolume(name.c_str(), shape, medSi); + vol->SetLineColor(kGray); + vol->SetTransparency(30); + return vol; +} + +TGeoVolume* VDRectangularLayer::createMetalStack() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + auto* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + + const double metalT = mChipThickness - mSensorThickness; + if (metalT <= 0) { + return nullptr; + } + + std::string name = Form("%s_%s%d", mLayerName.c_str(), + GeometryTGeo::getTRKMetalStackPattern(), mLayerNumber); + + const double hx = 0.5 * mWidth; + const double hy = 0.5 * metalT; + const double hz = 0.5 * mLengthSensZ; + + auto* shape = new TGeoBBox(hx, hy, hz); + auto* vol = new TGeoVolume(name.c_str(), shape, medSi); + vol->SetLineColor(kGray); + vol->SetTransparency(30); + return vol; +} + +TGeoVolume* VDDiskLayer::createMetalStack() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + + const double metalT = mChipThickness - mSensorThickness; + if (metalT <= 0) { + return nullptr; + } + + if (mRMin < 0 || mRMax <= mRMin || mPhiSpanDeg <= 0 || mPhiSpanDeg > 360.0) { + LOGP(error, "Invalid disk metal dims: rMin={}, rMax={}, metalT={}, phiSpanDeg={}", + mRMin, mRMax, metalT, mPhiSpanDeg); + return nullptr; + } + + std::string name = Form("%s_%s%d", mLayerName.c_str(), + GeometryTGeo::getTRKMetalStackPattern(), mLayerNumber); + + const double halfThickness = 0.5 * metalT; + const double halfPhi = 0.5 * mPhiSpanDeg; + + auto* shape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + auto* vol = new TGeoVolume(name.c_str(), shape, medSi); + vol->SetLineColor(kGray); + vol->SetTransparency(30); + return vol; +} + +/* +** Create chip +*/ + +TGeoVolume* VDCylindricalLayer::createChip() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + auto* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + + std::string chipName = Form("%s_%s%d", mLayerName.c_str(), + GeometryTGeo::getTRKChipPattern(), mLayerNumber); + + const double rIn = mRadius; + const double rOut = mRadius + mChipThickness; + const double halfZ = 0.5 * mLengthSensZ; + const double halfPhi = 0.5 * mPhiSpanDeg; + + auto* chipShape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); + auto* chipVol = new TGeoVolume(chipName.c_str(), chipShape, medSi); + + // sensor + if (auto* sensVol = createSensor()) { + LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); + chipVol->AddNode(sensVol, 1, nullptr); + } + + // metal stack + if (auto* metalVol = createMetalStack()) { + LOGP(debug, "Inserting {} in {} ", metalVol->GetName(), chipVol->GetName()); + chipVol->AddNode(metalVol, 1, nullptr); // concentric, no translation needed + } + + chipVol->SetLineColor(kYellow); + chipVol->SetTransparency(30); + return chipVol; +} + +TGeoVolume* VDRectangularLayer::createChip() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + auto* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + + std::string chipName = Form("%s_%s%d", mLayerName.c_str(), + GeometryTGeo::getTRKChipPattern(), mLayerNumber); + + const double hx = 0.5 * mWidth; + const double hy = 0.5 * mChipThickness; + const double hz = 0.5 * mLengthSensZ; + + auto* chipShape = new TGeoBBox(hx, hy, hz); + auto* chipVol = new TGeoVolume(chipName.c_str(), chipShape, medSi); + + // sensor (place it on the "bottom" side, like TRK) + if (auto* sensVol = createSensor()) { + auto* transSens = new TGeoTranslation(0.0, -(mChipThickness - mSensorThickness) / 2, 0.0); + LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); + chipVol->AddNode(sensVol, 1, transSens); + } + + // metal stack (remaining thickness on top) + if (auto* metalVol = createMetalStack()) { + auto* transMetal = new TGeoTranslation(0.0, +mSensorThickness / 2, 0.0); + LOGP(debug, "Inserting {} in {} ", metalVol->GetName(), chipVol->GetName()); + chipVol->AddNode(metalVol, 1, transMetal); + } + + chipVol->SetLineColor(kYellow); + chipVol->SetTransparency(30); + return chipVol; +} + +TGeoVolume* VDDiskLayer::createChip() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + + if (mRMin < 0 || mRMax <= mRMin || mChipThickness <= 0 || + mPhiSpanDeg <= 0 || mPhiSpanDeg > 360.0) { + LOGP(error, "Invalid disk chip dims: rMin={}, rMax={}, t={}, phi={}", + mRMin, mRMax, mChipThickness, mPhiSpanDeg); + return nullptr; + } + + std::string chipName = Form("%s_%s%d", mLayerName.c_str(), + GeometryTGeo::getTRKChipPattern(), mLayerNumber); + + const double halfThickness = 0.5 * mChipThickness; + const double halfPhi = 0.5 * mPhiSpanDeg; + + auto* chipShape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + auto* chipVol = new TGeoVolume(chipName.c_str(), chipShape, medSi); + chipVol->SetLineColor(kYellow); + chipVol->SetTransparency(30); + + // Sensor slab (sensitive) placed on one side in Z (TRK-like stacking convention) + if (auto* sensVol = createSensor()) { + const double zSens = -(mChipThickness - mSensorThickness) / 2.0; + auto* tSens = new TGeoTranslation(0.0, 0.0, zSens); + LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); + chipVol->AddNode(sensVol, 1, tSens); + } + + // Metal stack slab (non-sensitive), remaining thickness, also silicon + if (auto* metalVol = createMetalStack()) { + const double zMetal = +mSensorThickness / 2.0; + auto* tMetal = new TGeoTranslation(0.0, 0.0, zMetal); + LOGP(debug, "Inserting {} in {} ", metalVol->GetName(), chipVol->GetName()); + chipVol->AddNode(metalVol, 1, tMetal); + } + + return chipVol; +} + /* ** Create layer */ @@ -184,14 +424,14 @@ void VDCylindricalLayer::createLayer(TGeoVolume* motherVolume, TGeoMatrix* combi layerVol->SetLineColor(kYellow); layerVol->SetTransparency(30); - // Sensor volume (must use mLengthSensZ internally) - TGeoVolume* sensorVol = VDCylindricalLayer::createSensor(); - if (!sensorVol) { - LOGP(error, "VDCylindricalLayer::createSensor() returned null"); + // Chip volume (must use mLengthSensZ internally) + TGeoVolume* chipVol = VDCylindricalLayer::createChip(); + if (!chipVol) { + LOGP(error, "VDCylindricalLayer::createChip() returned null"); return; } - LOGP(debug, "Inserting {} in {} ", sensorVol->GetName(), layerVol->GetName()); - layerVol->AddNode(sensorVol, 1, nullptr); + LOGP(debug, "Inserting {} in {} ", chipVol->GetName(), layerVol->GetName()); + layerVol->AddNode(chipVol, 1, nullptr); // Tiling: edge-to-edge if sensor shorter than layer; else single centered // const auto zCenters = (mLengthSensZ < mLengthZ) @@ -238,14 +478,14 @@ void VDRectangularLayer::createLayer(TGeoVolume* motherVolume, TGeoMatrix* combi layerVol->SetTransparency(30); // Sensor volume (uses mLengthSensZ internally) - TGeoVolume* sensorVol = VDRectangularLayer::createSensor(); - if (!sensorVol) { - LOGP(error, "VDRectangularLayer::createSensor() returned null"); + TGeoVolume* chipVol = VDRectangularLayer::createChip(); + if (!chipVol) { + LOGP(error, "VDRectangularLayer::chipVol() returned null"); return; } - LOGP(debug, "Inserting {} in {} ", sensorVol->GetName(), layerVol->GetName()); - layerVol->AddNode(sensorVol, 1, nullptr); + LOGP(debug, "Inserting {} in {} ", chipVol->GetName(), layerVol->GetName()); + layerVol->AddNode(chipVol, 1, nullptr); // Tiling along Z, edge - to - edge if needed // const auto zCenters = (mLengthSensZ < mLengthZ) @@ -292,14 +532,14 @@ void VDDiskLayer::createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans) layerVol->SetTransparency(30); // Sensor (same size & shape as the layer for disks) - TGeoVolume* sensorVol = VDDiskLayer::createSensor(); - if (!sensorVol) { - LOGP(error, "VDDiskLayer::createSensor() returned null"); + TGeoVolume* chipVol = VDDiskLayer::createChip(); + if (!chipVol) { + LOGP(error, "VDDiskLayer::createChip() returned null"); return; } // Insert single sensor (no Z-segmentation for disks) - layerVol->AddNode(sensorVol, 1, nullptr); + layerVol->AddNode(chipVol, 1, nullptr); TGeoTranslation tz(0.0, 0.0, mZPos); motherVolume->AddNode(layerVol, 1, combiTrans ? combiTrans : &tz); From 02a0aebb5718b3faba48c0aad82916e45e93621d Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sat, 31 Jan 2026 18:59:49 +0100 Subject: [PATCH 037/133] DPL: improve type_to_task_name function (#15006) - Out of line and avoid usage of stringstream. - Remove non-sense abbreviations --- Framework/Core/CMakeLists.txt | 2 + .../Core/include/Framework/AnalysisTask.h | 3 + Framework/Core/src/AnalysisTask.cxx | 77 +++++++++++++++++++ Framework/Core/test/test_TypeToTaskName.cxx | 65 ++++++++++++++++ .../include/Framework/TypeIdHelpers.h | 17 ---- 5 files changed, 147 insertions(+), 17 deletions(-) create mode 100644 Framework/Core/src/AnalysisTask.cxx create mode 100644 Framework/Core/test/test_TypeToTaskName.cxx diff --git a/Framework/Core/CMakeLists.txt b/Framework/Core/CMakeLists.txt index 1daba5dbc9798..7357167a3fcd8 100644 --- a/Framework/Core/CMakeLists.txt +++ b/Framework/Core/CMakeLists.txt @@ -16,6 +16,7 @@ o2_add_library(Framework src/ArrowTableSlicingCache.cxx src/AnalysisDataModel.cxx src/AnalysisSupportHelpers.cxx + src/AnalysisTask.cxx src/ASoA.cxx src/ASoAHelpers.cxx src/AsyncQueue.cxx @@ -254,6 +255,7 @@ add_executable(o2-test-framework-core test/test_TimeParallelPipelining.cxx test/test_TimesliceIndex.cxx test/test_TypeTraits.cxx + test/test_TypeToTaskName.cxx test/test_TopologyPolicies.cxx test/test_Variants.cxx test/test_WorkflowHelpers.cxx diff --git a/Framework/Core/include/Framework/AnalysisTask.h b/Framework/Core/include/Framework/AnalysisTask.h index 4f8a9e719e4b9..eb98d55cc24b2 100644 --- a/Framework/Core/include/Framework/AnalysisTask.h +++ b/Framework/Core/include/Framework/AnalysisTask.h @@ -37,6 +37,9 @@ namespace o2::framework { +/// Convert a CamelCase task struct name to snake-case task name +std::string type_to_task_name(std::string_view const& camelCase); + /// A more familiar task API for the DPL analysis framework. /// This allows you to define your own tasks as subclasses /// of o2::framework::AnalysisTask and to pass them in the specification diff --git a/Framework/Core/src/AnalysisTask.cxx b/Framework/Core/src/AnalysisTask.cxx new file mode 100644 index 0000000000000..e88e6fbc6f041 --- /dev/null +++ b/Framework/Core/src/AnalysisTask.cxx @@ -0,0 +1,77 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#include + +namespace o2::framework +{ +/// Convert a CamelCase task struct name to snake-case task name +std::string type_to_task_name(std::string_view const& camelCase) +{ + std::string result; + result.reserve(camelCase.size() * 2 + 2); + + // The first character is always -. + result += "-"; + result += static_cast(std::tolower(camelCase[0])); + + for (auto it = camelCase.begin() + 1; it != camelCase.end(); ++it) { + if (std::isupper(*it) && *(it - 1) != '-') { + result += '-'; + } + result += static_cast(std::tolower(*it)); + } + // Post-process to consolidate common ALICE abbreviations + // Process backwards to handle patterns correctly + static const struct { + std::string_view pattern; + std::string_view replacement; + } abbreviations[] = { + {"-h-m-p-i-d", "-hmpid"}, + {"-e-m-c-a-l", "-emcal"}, + {"-e-m-c", "-emc"}, + {"-i-t-s", "-its"}, + {"-t-p-c", "-tpc"}, + {"-q-c-d", "-qcd"}, + {"-t-o-f", "-tof"}, + {"-t-r-d", "-trd"}, + {"-f-v0", "-fv0"}, + {"-q-a", "-qa"}, + {"-b-c", "-bc"}, + {"-q-c", "-qc"}}; + + std::string consolidated; + consolidated.reserve(result.size()); + + for (int i = result.size() - 1; i >= 0;) { + bool matched = false; + + for (const auto& abbr : abbreviations) { + int startPos = i - abbr.pattern.size() + 1; + if (startPos >= 0 && result.compare(startPos, abbr.pattern.size(), abbr.pattern.data()) == 0) { + consolidated.insert(0, abbr.replacement); + i = startPos - 1; + matched = true; + break; + } + } + + if (!matched) { + consolidated.insert(0, 1, result[i]); + --i; + } + } + if (consolidated[0] == '-') { + return std::string(consolidated.data() + 1); + } + + return consolidated; +} +} // namespace o2::framework diff --git a/Framework/Core/test/test_TypeToTaskName.cxx b/Framework/Core/test/test_TypeToTaskName.cxx new file mode 100644 index 0000000000000..b7b440b13ecfd --- /dev/null +++ b/Framework/Core/test/test_TypeToTaskName.cxx @@ -0,0 +1,65 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include "Framework/AnalysisTask.h" +#include + +using namespace o2::framework; + +TEST_CASE("TypeIdHelpers_BasicConversion") +{ + // Basic CamelCase to snake-case conversion + REQUIRE((type_to_task_name(std::string_view("SimpleTask")) == "simple-task")); + REQUIRE((type_to_task_name(std::string_view("MyTask")) == "my-task")); + REQUIRE((type_to_task_name(std::string_view("Task")) == "task")); +} + +TEST_CASE("TypeIdHelpers_AbbreviationConsolidation") +{ + // Test ALICE detector abbreviations + REQUIRE(type_to_task_name(std::string_view("ITSQA")) == "its-qa"); + REQUIRE(type_to_task_name(std::string_view("TPCQCTask")) == "tpc-qc-task"); + REQUIRE(type_to_task_name(std::string_view("EMCALQATask")) == "emcal-qa-task"); + REQUIRE(type_to_task_name(std::string_view("HMPIDTask")) == "hmpid-task"); + REQUIRE(type_to_task_name(std::string_view("ITSTPCTask")) == "its-tpc-task"); + REQUIRE(type_to_task_name(std::string_view("QCFV0Task")) == "qc-fv0-task"); +} + +TEST_CASE("TypeIdHelpers_QualityControlAbbreviations") +{ + // Test quality control abbreviations + REQUIRE(type_to_task_name(std::string_view("QATask")) == "qa-task"); + REQUIRE(type_to_task_name(std::string_view("QCTask")) == "qc-task"); + REQUIRE(type_to_task_name(std::string_view("QCDAnalysis")) == "qcd-analysis"); +} + +TEST_CASE("TypeIdHelpers_ComplexNames") +{ + // Test complex combinations + REQUIRE(type_to_task_name(std::string_view("ITSQAAnalysisTask")) == "its-qa-analysis-task"); + REQUIRE(type_to_task_name(std::string_view("TPCEMCQCTask")) == "tpc-emc-qc-task"); + REQUIRE(type_to_task_name(std::string_view("MyITSTask")) == "my-its-task"); +} + +TEST_CASE("TypeIdHelpers_EdgeCases") +{ + // Single character + REQUIRE(type_to_task_name(std::string_view("A")) == "a"); + + // All uppercase. BC is Bunch Crossing! + // + REQUIRE(type_to_task_name(std::string_view("ABC")) == "a-bc"); + REQUIRE(type_to_task_name(std::string_view("BC")) == "bc"); + + // Mixed with numbers (numbers are not uppercase, so no hyphens before them) + REQUIRE(type_to_task_name(std::string_view("Task123")) == "task123"); +} diff --git a/Framework/Foundation/include/Framework/TypeIdHelpers.h b/Framework/Foundation/include/Framework/TypeIdHelpers.h index 5eaac2151b909..1dc2464b40ec8 100644 --- a/Framework/Foundation/include/Framework/TypeIdHelpers.h +++ b/Framework/Foundation/include/Framework/TypeIdHelpers.h @@ -13,7 +13,6 @@ #define O2_FRAMEWORK_TYPEIDHELPERS_H_ #include -#include #if __cplusplus >= 202002L #include #endif @@ -82,22 +81,6 @@ struct TypeIdHelpers { } }; -/// Convert a CamelCase task struct name to snake-case task name -inline static std::string type_to_task_name(std::string_view& camelCase) -{ - std::ostringstream str; - str << static_cast(std::tolower(camelCase[0])); - - for (auto it = camelCase.begin() + 1; it != camelCase.end(); ++it) { - if (std::isupper(*it) && *(it - 1) != '-') { - str << "-"; - } - str << static_cast(std::tolower(*it)); - } - - return str.str(); -} - } // namespace o2::framework #endif // O2_FRAMEWORK_TYPEIDHELPERS_H_ From dee4e246302c2a78b02fe2d4b7a29d0f48d2b05f Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Mon, 2 Feb 2026 13:16:21 +0100 Subject: [PATCH 038/133] Revert abbreviations until we get green light from the affected people (#15009) --- Framework/Core/src/AnalysisTask.cxx | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/Framework/Core/src/AnalysisTask.cxx b/Framework/Core/src/AnalysisTask.cxx index e88e6fbc6f041..993c597a95f49 100644 --- a/Framework/Core/src/AnalysisTask.cxx +++ b/Framework/Core/src/AnalysisTask.cxx @@ -34,18 +34,9 @@ std::string type_to_task_name(std::string_view const& camelCase) std::string_view pattern; std::string_view replacement; } abbreviations[] = { - {"-h-m-p-i-d", "-hmpid"}, {"-e-m-c-a-l", "-emcal"}, - {"-e-m-c", "-emc"}, - {"-i-t-s", "-its"}, - {"-t-p-c", "-tpc"}, - {"-q-c-d", "-qcd"}, - {"-t-o-f", "-tof"}, - {"-t-r-d", "-trd"}, - {"-f-v0", "-fv0"}, - {"-q-a", "-qa"}, - {"-b-c", "-bc"}, - {"-q-c", "-qc"}}; + {"-e-m-c", "-emc"} + }; std::string consolidated; consolidated.reserve(result.size()); From 597fc9ee8673ea2bb916c6caf5bbbec8f631a224 Mon Sep 17 00:00:00 2001 From: shahoian Date: Wed, 28 Jan 2026 20:42:39 +0100 Subject: [PATCH 039/133] Leave single implementation of TRD RecoParam, init from GPUSettingsRecTRD --- .../include/Align/AlignableDetectorTRD.h | 4 +- Detectors/Align/src/AlignableDetectorTRD.cxx | 11 +- .../include/SpacePoints/TrackInterpolation.h | 4 +- .../SpacePoints/src/TrackInterpolation.cxx | 4 +- Detectors/TRD/base/CMakeLists.txt | 2 - .../TRD/base/include/TRDBase/RecoParam.h | 64 ----------- Detectors/TRD/base/src/RecoParam.cxx | 64 ----------- Detectors/TRD/base/src/TRDBaseLinkDef.h | 1 - Detectors/TRD/calibration/CMakeLists.txt | 1 + .../include/TRDCalibration/TrackBasedCalib.h | 4 +- .../TRD/calibration/src/TrackBasedCalib.cxx | 5 +- Detectors/TRD/qc/CMakeLists.txt | 1 + Detectors/TRD/qc/include/TRDQC/Tracking.h | 4 +- Detectors/TRD/qc/src/Tracking.cxx | 5 +- .../TRDWorkflow/TRDGlobalTrackingSpec.h | 4 +- .../workflow/src/TRDGlobalTrackingSpec.cxx | 6 +- GPU/GPUTracking/Base/GPUConstantMem.h | 1 - GPU/GPUTracking/CMakeLists.txt | 3 +- GPU/GPUTracking/DataTypes/GPUDataTypesIO.h | 3 +- GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx | 100 ++++++++++++++++++ GPU/GPUTracking/DataTypes/GPUTRDRecoParam.h | 84 +++++++++++++++ GPU/GPUTracking/GPUTrackingLinkDef_O2.h | 1 + GPU/GPUTracking/Global/GPUChainTracking.cxx | 13 +++ GPU/GPUTracking/Global/GPUChainTracking.h | 5 + .../Global/GPUChainTrackingGetters.inc | 2 + GPU/GPUTracking/Global/GPUChainTrackingIO.cxx | 10 ++ GPU/GPUTracking/TRDTracking/GPUTRDTracker.cxx | 48 ++------- GPU/GPUTracking/TRDTracking/GPUTRDTracker.h | 15 +-- .../TRDTracking/macros/run_trd_tracker.C | 4 +- .../include/GPUWorkflow/GPUWorkflowSpec.h | 3 + GPU/Workflow/src/GPUWorkflowSpec.cxx | 27 +++-- 31 files changed, 286 insertions(+), 217 deletions(-) delete mode 100644 Detectors/TRD/base/include/TRDBase/RecoParam.h delete mode 100644 Detectors/TRD/base/src/RecoParam.cxx create mode 100644 GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx create mode 100644 GPU/GPUTracking/DataTypes/GPUTRDRecoParam.h diff --git a/Detectors/Align/include/Align/AlignableDetectorTRD.h b/Detectors/Align/include/Align/AlignableDetectorTRD.h index a73b0f76902d2..4e7577b11055c 100644 --- a/Detectors/Align/include/Align/AlignableDetectorTRD.h +++ b/Detectors/Align/include/Align/AlignableDetectorTRD.h @@ -18,7 +18,7 @@ #define ALIGNABLEDETECTORTRD_H #include "Align/AlignableDetector.h" -#include "TRDBase/RecoParam.h" +#include "GPUTRDRecoParam.h" namespace o2 { @@ -64,7 +64,7 @@ class AlignableDetectorTRD final : public AlignableDetector int processPoints(GIndex gid, int npntCut, bool inv) final; protected: - o2::trd::RecoParam mRecoParam; // parameters required for TRD reconstruction + o2::gpu::GPUTRDRecoParam mRecoParam; // parameters required for TRD reconstruction double mNonRCCorrDzDtgl = 0.; // correction in Z for non-crossing tracklets double mCorrDVT = 0.; // correction to Vdrift*t double mExtraErrRC[2] = {0., 0.}; // extra errors for RC tracklets diff --git a/Detectors/Align/src/AlignableDetectorTRD.cxx b/Detectors/Align/src/AlignableDetectorTRD.cxx index d752553bf6ead..080d0f72b2516 100644 --- a/Detectors/Align/src/AlignableDetectorTRD.cxx +++ b/Detectors/Align/src/AlignableDetectorTRD.cxx @@ -26,6 +26,7 @@ #include "DataFormatsTRD/TrackTRD.h" #include "DataFormatsTRD/Tracklet64.h" #include "DataFormatsTRD/CalibratedTracklet.h" +#include "GPUO2InterfaceConfiguration.h" #include #include @@ -175,10 +176,12 @@ int AlignableDetectorTRD::processPoints(GIndex gid, int npntCut, bool inv) return -1; } auto propagator = o2::base::Propagator::Instance(); // float version! - static float prevBz = -99999.; - if (prevBz != propagator->getNominalBz()) { - prevBz = propagator->getNominalBz(); - mRecoParam.setBfield(prevBz); + static bool firstCall = true; + if (firstCall) { + o2::gpu::GPUO2InterfaceConfiguration config; + config.ReadConfigurableParam(config); + mRecoParam.init(propagator->getNominalBz(), &config.configReconstruction); + firstCall = false; } const auto* transformer = mController->getTRDTransformer(); auto algTrack = mController->getAlgTrack(); diff --git a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackInterpolation.h b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackInterpolation.h index e7d0fb197ea42..58627250d815e 100644 --- a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackInterpolation.h +++ b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackInterpolation.h @@ -39,7 +39,7 @@ #include "TPCReconstruction/TPCFastTransformHelperO2.h" #include "DetectorsBase/Propagator.h" #include "DataFormatsGlobalTracking/RecoContainer.h" -#include "TRDBase/RecoParam.h" +#include "GPUTRDRecoParam.h" #include "TRDBase/Geometry.h" class TTree; @@ -441,7 +441,7 @@ class TrackInterpolation std::vector mGIDsSuccess; ///< keep track of the GIDs which could be processed successfully // helpers - o2::trd::RecoParam mRecoParam; ///< parameters required for TRD refit + o2::gpu::GPUTRDRecoParam mRecoParam; ///< parameters required for TRD refit o2::trd::Geometry* mGeoTRD; ///< TRD geometry instance (needed for tilted pad correction) std::unique_ptr mFastTransform{}; ///< TPC cluster transformation float mBz; ///< required for helix approximation diff --git a/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx index 6c37be9ddc1b1..cd5e3960160a6 100644 --- a/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx +++ b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx @@ -126,7 +126,9 @@ void TrackInterpolation::init(o2::dataformats::GlobalTrackID::mask_t src, o2::da mFastTransform = std::move(TPCFastTransformHelperO2::instance()->create(0)); mBz = o2::base::Propagator::Instance()->getNominalBz(); - mRecoParam.setBfield(mBz); + o2::gpu::GPUO2InterfaceConfiguration config; + config.ReadConfigurableParam(config); + mRecoParam.init(mBz, &config.configReconstruction); mGeoTRD = o2::trd::Geometry::instance(); mParams = &SpacePointsCalibConfParam::Instance(); diff --git a/Detectors/TRD/base/CMakeLists.txt b/Detectors/TRD/base/CMakeLists.txt index 030fb6cea1e50..e0563a85a3f42 100644 --- a/Detectors/TRD/base/CMakeLists.txt +++ b/Detectors/TRD/base/CMakeLists.txt @@ -16,7 +16,6 @@ o2_add_library(TRDBase src/GeometryFlat.cxx src/PadResponse.cxx src/FeeParam.cxx - src/RecoParam.cxx src/ChamberStatus.cxx src/Calibrations.cxx src/CalOnlineGainTables.cxx @@ -38,7 +37,6 @@ o2_target_root_dictionary(TRDBase include/TRDBase/GeometryFlat.h include/TRDBase/PadResponse.h include/TRDBase/FeeParam.h - include/TRDBase/RecoParam.h include/TRDBase/Calibrations.h include/TRDBase/PadParameters.h include/TRDBase/PadCalibrations.h diff --git a/Detectors/TRD/base/include/TRDBase/RecoParam.h b/Detectors/TRD/base/include/TRDBase/RecoParam.h deleted file mode 100644 index 1828a0b1724e9..0000000000000 --- a/Detectors/TRD/base/include/TRDBase/RecoParam.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file RecoParam.h -/// \brief Error parameterizations and helper functions for TRD reconstruction -/// \author Ole Schmidt - -#ifndef O2_TRD_RECOPARAM_H -#define O2_TRD_RECOPARAM_H - -#include -#include "Rtypes.h" - -namespace o2 -{ -namespace trd -{ - -class RecoParam -{ - public: - RecoParam() = default; - RecoParam(const RecoParam&) = default; - ~RecoParam() = default; - - /// Load parameterization for given magnetic field - void setBfield(float bz); - - /// Recalculate tracklet covariance based on phi angle of related track - void recalcTrkltCov(const float tilt, const float snp, const float rowSize, std::array& cov) const; - - /// Get tracklet r-phi resolution for given phi angle - /// Resolution depends on the track angle sin(phi) = snp and is approximated by the formula - /// sigma_y(snp) = sqrt(a^2 + c^2 * (snp - b^2)^2) - /// more details are given in http://cds.cern.ch/record/2724259 in section 5.3.3 - /// \param phi angle of related track - /// \return sigma_y^2 of tracklet - float getRPhiRes(float snp) const { return (mA2 + mC2 * (snp - mB) * (snp - mB)); } - - /// Get tracklet z correction coefficient for track-eta based corraction - float getZCorrCoeffNRC() const { return mZCorrCoefNRC; } - - private: - // tracklet error parameterization depends on the magnetic field - float mA2{1.f}; ///< parameterization for tracklet position resolution - float mB{0.f}; ///< parameterization for tracklet position resolution - float mC2{0.f}; ///< parameterization for tracklet position resolution - float mZCorrCoefNRC{1.4f}; ///< tracklet z-position depends linearly on track dip angle - - ClassDefNV(RecoParam, 1); -}; - -} // namespace trd -} // namespace o2 - -#endif // O2_TRD_RECOPARAM_H diff --git a/Detectors/TRD/base/src/RecoParam.cxx b/Detectors/TRD/base/src/RecoParam.cxx deleted file mode 100644 index 34921777bdb72..0000000000000 --- a/Detectors/TRD/base/src/RecoParam.cxx +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file RecoParam.cxx -/// \brief Error parameterizations and helper functions for TRD reconstruction -/// \author Ole Schmidt - -#include "TRDBase/RecoParam.h" -#include -#include - -using namespace o2::trd; - -// error parameterizations taken from http://cds.cern.ch/record/2724259 Appendix A -void RecoParam::setBfield(float bz) -{ - if (std::fabs(std::fabs(bz) - 2) < 0.1) { - if (bz > 0) { - // magnetic field +0.2 T - mA2 = 1.6e-3f; - mB = -1.43e-2f; - mC2 = 4.55e-2f; - } else { - // magnetic field -0.2 T - mA2 = 1.6e-3f; - mB = 1.43e-2f; - mC2 = 4.55e-2f; - } - } else if (std::fabs(std::fabs(bz) - 5) < 0.1) { - if (bz > 0) { - // magnetic field +0.5 T - mA2 = 1.6e-3f; - mB = 0.125f; - mC2 = 0.0961f; - } else { - // magnetic field -0.5 T - mA2 = 1.6e-3f; - mB = -0.14f; - mC2 = 0.1156f; - } - } else { - LOG(warning) << "No error parameterization available for Bz= " << bz << ". Keeping default value (sigma_y = const. = 1cm)"; - } - LOG(info) << "Loaded error parameterization for Bz = " << bz; -} - -void RecoParam::recalcTrkltCov(const float tilt, const float snp, const float rowSize, std::array& cov) const -{ - float t2 = tilt * tilt; // tan^2 (tilt) - float c2 = 1.f / (1.f + t2); // cos^2 (tilt) - float sy2 = getRPhiRes(snp); - float sz2 = rowSize * rowSize / 12.f; - cov[0] = c2 * (sy2 + t2 * sz2); - cov[1] = c2 * tilt * (sz2 - sy2); - cov[2] = c2 * (t2 * sy2 + sz2); -} diff --git a/Detectors/TRD/base/src/TRDBaseLinkDef.h b/Detectors/TRD/base/src/TRDBaseLinkDef.h index 2d3de311a1dc0..a835def5628b2 100644 --- a/Detectors/TRD/base/src/TRDBaseLinkDef.h +++ b/Detectors/TRD/base/src/TRDBaseLinkDef.h @@ -19,7 +19,6 @@ #pragma link C++ class o2::trd::Geometry + ; #pragma link C++ class o2::trd::GeometryBase + ; #pragma link C++ class o2::trd::FeeParam + ; -#pragma link C++ class o2::trd::RecoParam + ; #pragma link C++ class o2::trd::PadResponse + ; #pragma link C++ class o2::trd::PadParameters < float > +; #pragma link C++ class o2::trd::PadParameters < char> + ; diff --git a/Detectors/TRD/calibration/CMakeLists.txt b/Detectors/TRD/calibration/CMakeLists.txt index 36d00e92bbc16..52444d2855b1f 100644 --- a/Detectors/TRD/calibration/CMakeLists.txt +++ b/Detectors/TRD/calibration/CMakeLists.txt @@ -28,6 +28,7 @@ o2_add_library(TRDCalibration O2::DetectorsBase O2::DetectorsCalibration O2::MathUtils + O2::GPUTracking O2::DetectorsDCS) o2_target_root_dictionary(TRDCalibration diff --git a/Detectors/TRD/calibration/include/TRDCalibration/TrackBasedCalib.h b/Detectors/TRD/calibration/include/TRDCalibration/TrackBasedCalib.h index 49ba9fdf3d161..7249016d9675e 100644 --- a/Detectors/TRD/calibration/include/TRDCalibration/TrackBasedCalib.h +++ b/Detectors/TRD/calibration/include/TRDCalibration/TrackBasedCalib.h @@ -24,7 +24,7 @@ #include "DataFormatsTRD/NoiseCalibration.h" #include "TRDBase/PadCalibrationsAliases.h" #include "DetectorsBase/Propagator.h" -#include "TRDBase/RecoParam.h" +#include "GPUTRDRecoParam.h" #include "Rtypes.h" @@ -90,7 +90,7 @@ class TrackBasedCalib float mMaxSnp{o2::base::Propagator::MAX_SIN_PHI}; ///< max snp when propagating tracks float mMaxStep{o2::base::Propagator::MAX_STEP}; ///< maximum step for propagation MatCorrType mMatCorr{MatCorrType::USEMatCorrNONE}; ///< if material correction should be done - RecoParam mRecoParam; ///< parameters required for TRD reconstruction + o2::gpu::GPUTRDRecoParam mRecoParam; ///< parameters required for TRD reconstruction AngularResidHistos mAngResHistos; ///< aggregated data for the track based calibration std::vector mGainCalibHistos; ///< aggregated input data for gain calibration float bz; ///< magnetic field diff --git a/Detectors/TRD/calibration/src/TrackBasedCalib.cxx b/Detectors/TRD/calibration/src/TrackBasedCalib.cxx index 011a888a47618..8fe195f861389 100644 --- a/Detectors/TRD/calibration/src/TrackBasedCalib.cxx +++ b/Detectors/TRD/calibration/src/TrackBasedCalib.cxx @@ -13,6 +13,7 @@ /// \brief Provides information required for TRD calibration which is based on the global tracking /// \author Ole Schmidt +#include "GPUO2InterfaceConfiguration.h" #include "TRDCalibration/TrackBasedCalib.h" #include "TRDCalibration/CalibrationParams.h" #include "DataFormatsTRD/Constants.h" @@ -35,7 +36,9 @@ void TrackBasedCalib::reset() void TrackBasedCalib::init() { bz = o2::base::Propagator::Instance()->getNominalBz(); - mRecoParam.setBfield(bz); + o2::gpu::GPUO2InterfaceConfiguration config; + config.ReadConfigurableParam(config); + mRecoParam.init(bz, &config.configReconstruction); } void TrackBasedCalib::setInput(const o2::globaltracking::RecoContainer& input) diff --git a/Detectors/TRD/qc/CMakeLists.txt b/Detectors/TRD/qc/CMakeLists.txt index d631de1f54246..daba4928957f9 100644 --- a/Detectors/TRD/qc/CMakeLists.txt +++ b/Detectors/TRD/qc/CMakeLists.txt @@ -21,6 +21,7 @@ o2_add_library(TRDQC O2::DataFormatsTRD O2::DataFormatsGlobalTracking O2::DetectorsBase + O2::GPUTracking O2::MathUtils) o2_target_root_dictionary(TRDQC diff --git a/Detectors/TRD/qc/include/TRDQC/Tracking.h b/Detectors/TRD/qc/include/TRDQC/Tracking.h index 880b1727ab367..f39c64286d0cc 100644 --- a/Detectors/TRD/qc/include/TRDQC/Tracking.h +++ b/Detectors/TRD/qc/include/TRDQC/Tracking.h @@ -25,7 +25,7 @@ #include "ReconstructionDataFormats/GlobalTrackID.h" #include "DataFormatsTPC/TrackTPC.h" #include "DetectorsBase/Propagator.h" -#include "TRDBase/RecoParam.h" +#include "GPUTRDRecoParam.h" #include "Rtypes.h" #include "TH1.h" @@ -107,7 +107,7 @@ class Tracking float mMaxSnp{o2::base::Propagator::MAX_SIN_PHI}; ///< max snp when propagating tracks float mMaxStep{o2::base::Propagator::MAX_STEP}; ///< maximum step for propagation MatCorrType mMatCorr{MatCorrType::USEMatCorrNONE}; ///< if material correction should be done - RecoParam mRecoParam; ///< parameters required for TRD reconstruction + o2::gpu::GPUTRDRecoParam mRecoParam; ///< parameters required for TRD reconstruction bool mPID{true}; ///< if TPC only tracks are not available we don't fill PID info bool mApplyShift{true}; diff --git a/Detectors/TRD/qc/src/Tracking.cxx b/Detectors/TRD/qc/src/Tracking.cxx index 278ebe5391ff9..9a0df7efa323b 100644 --- a/Detectors/TRD/qc/src/Tracking.cxx +++ b/Detectors/TRD/qc/src/Tracking.cxx @@ -13,6 +13,7 @@ /// \brief Check the performance of the TRD in global tracking /// \author Ole Schmidt +#include "GPUO2InterfaceConfiguration.h" #include "TRDQC/Tracking.h" #include "DataFormatsGlobalTracking/RecoContainer.h" #include "DetectorsBase/GeometryManager.h" @@ -25,7 +26,9 @@ using namespace o2::trd::constants; void Tracking::init() { - mRecoParam.setBfield(o2::base::Propagator::Instance()->getNominalBz()); + o2::gpu::GPUO2InterfaceConfiguration config; + config.ReadConfigurableParam(config); + mRecoParam.init(o2::base::Propagator::Instance()->getNominalBz(), &config.configReconstruction); } void Tracking::setInput(const o2::globaltracking::RecoContainer& input) diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/TRDGlobalTrackingSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/TRDGlobalTrackingSpec.h index 9f3b9b27d37b5..93f07dd58445e 100644 --- a/Detectors/TRD/workflow/include/TRDWorkflow/TRDGlobalTrackingSpec.h +++ b/Detectors/TRD/workflow/include/TRDWorkflow/TRDGlobalTrackingSpec.h @@ -20,6 +20,7 @@ #include "TRDBase/GeometryFlat.h" #include "GPUO2ExternalUser.h" #include "GPUTRDTracker.h" +#include "GPUTRDRecoParam.h" #include "ReconstructionDataFormats/GlobalTrackID.h" #include "DataFormatsGlobalTracking/RecoContainer.h" #include "DataFormatsTRD/TrackTRD.h" @@ -34,7 +35,6 @@ #include "TPCCalibration/CorrectionMapsLoader.h" #include "GPUO2InterfaceRefit.h" #include "TPCFastTransform.h" -#include "TRDBase/RecoParam.h" #include "DataFormatsTPC/TrackTPC.h" #include "DataFormatsITS/TrackITS.h" #include "DataFormatsITSMFT/TrkClusRef.h" @@ -94,7 +94,7 @@ class TRDGlobalTracking : public o2::framework::Task // temporary members -> should go into processor (GPUTRDTracker or additional refit processor?) std::unique_ptr mTPCRefitter; ///< TPC refitter used for TPC tracks refit during the reconstruction const o2::tpc::ClusterNativeAccess* mTPCClusterIdxStruct = nullptr; ///< struct holding the TPC cluster indices - RecoParam mRecoParam; ///< parameters required for TRD reconstruction + o2::gpu::GPUTRDRecoParam mRecoParam; ///< parameters required for TRD reconstruction gsl::span mTrackletsRaw; ///< array of raw tracklets needed for TRD refit gsl::span mTrackletsCalib; ///< array of calibrated tracklets needed for TRD refit gsl::span mTPCTracksArray; ///< input TPC tracks used for refit diff --git a/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx index 9588888df5fc6..f2d4aad829fe5 100644 --- a/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx +++ b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx @@ -43,6 +43,7 @@ // GPU header #include "GPUReconstruction.h" #include "GPUChainTracking.h" +#include "GPUChainTrackingGetters.inc" #include "GPUO2InterfaceConfiguration.h" #include "GPUO2InterfaceUtils.h" #include "GPUSettings.h" @@ -112,6 +113,8 @@ void TRDGlobalTracking::updateTimeDependentParams(ProcessingContext& pc) config.ReadConfigurableParam(config); config.configGRP.solenoidBzNominalGPU = GPUO2InterfaceUtils::getNominalGPUBz(*o2::base::GRPGeomHelper::instance().getGRPMagField()); config.configProcessing.o2PropagatorUseGPUField = false; + mRecoParam.init(o2::base::Propagator::Instance()->getNominalBz(), &config.configReconstruction); + mRec->SetSettings(&config.configGRP, &config.configReconstruction, &config.configProcessing, &cfgRecoStep); mChainTracking = mRec->AddChain(); @@ -127,12 +130,11 @@ void TRDGlobalTracking::updateTimeDependentParams(ProcessingContext& pc) mRec->RegisterGPUProcessor(mTracker, false); mChainTracking->SetTRDGeometry(std::move(mFlatGeo)); + mChainTracking->SetTRDRecoParam(&mRecoParam); if (mRec->Init()) { LOG(fatal) << "GPUReconstruction could not be initialized"; } - mRecoParam.setBfield(o2::base::Propagator::Instance()->getNominalBz()); - mTracker->PrintSettings(); LOG(info) << "Strict matching mode is " << ((mStrict) ? "ON" : "OFF"); LOGF(info, "The search road in time for ITS-TPC tracks is set to %.1f sigma and %.2f us are added to it on top", diff --git a/GPU/GPUTracking/Base/GPUConstantMem.h b/GPU/GPUTracking/Base/GPUConstantMem.h index efb83a7e874c8..94ccfa7fa6db9 100644 --- a/GPU/GPUTracking/Base/GPUConstantMem.h +++ b/GPU/GPUTracking/Base/GPUConstantMem.h @@ -57,7 +57,6 @@ struct GPUConstantMem { #ifdef GPUCA_HAS_ONNX GPUTPCNNClusterizer tpcNNClusterer[GPUCA_NSECTORS]; #endif - template GPUd() auto& getTRDTracker(); }; diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index 6a60eb9edd6d0..9e9344108ccfb 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -59,6 +59,7 @@ set(SRCS Merger/GPUTPCGMPhysicalTrackModel.cxx Merger/GPUTPCGMPolynomialFieldManager.cxx DataTypes/GPUTRDTrack.cxx + DataTypes/GPUTRDRecoParam.cxx TRDTracking/GPUTRDTracker.cxx TRDTracking/GPUTRDTrackletWord.cxx TRDTracking/GPUTRDTrackerKernels.cxx @@ -68,7 +69,7 @@ set(SRCS_DATATYPES DataTypes/GPUDataTypesConfig.cxx DataTypes/GPUConfigDump.cxx set(HDRS_CINT_O2 Merger/GPUTPCGMTrackParam.h Merger/GPUTPCGMMergedTrack.h Merger/GPUTPCGMSectorTrack.h Merger/GPUTPCGMBorderTrack.h TRDTracking/GPUTRDInterfaces.h) set(HDRS_CINT_DATATYPES DataTypes/GPUTPCGMMergedTrackHit.h) -set(HDRS_CINT_O2_ADDITIONAL DataTypes/GPUSettings.h Definitions/GPUSettingsList.h DataTypes/GPUDataTypesIO.h DataTypes/GPUDataTypesConfig.h DataTypes/GPUDataTypesQA.h DataTypes/GPUTRDTrack.h DataTypes/CalibdEdxTrackTopologyPol.h DataTypes/CalibdEdxTrackTopologySpline.h) # Manual dependencies for ROOT dictionary generation +set(HDRS_CINT_O2_ADDITIONAL DataTypes/GPUSettings.h Definitions/GPUSettingsList.h DataTypes/GPUDataTypesIO.h DataTypes/GPUDataTypesConfig.h DataTypes/GPUDataTypesQA.h DataTypes/GPUTRDTrack.h DataTypes/GPUTRDRecoParam.h DataTypes/CalibdEdxTrackTopologyPol.h DataTypes/CalibdEdxTrackTopologySpline.h) # Manual dependencies for ROOT dictionary generation set(SRCS_NO_CINT DataTypes/GPUMemorySizeScalers.cxx diff --git a/GPU/GPUTracking/DataTypes/GPUDataTypesIO.h b/GPU/GPUTracking/DataTypes/GPUDataTypesIO.h index fd98cba1dadaa..76fa569a16824 100644 --- a/GPU/GPUTracking/DataTypes/GPUDataTypesIO.h +++ b/GPU/GPUTracking/DataTypes/GPUDataTypesIO.h @@ -95,6 +95,7 @@ class TPCFastTransform; struct TPCPadGainCalib; struct TPCZSLinkMapping; +class GPUTRDRecoParam; class GPUTPCTrack; class GPUTPCHitId; class GPUTPCGMMergedTrack; @@ -135,7 +136,7 @@ struct GPUCalibObjectsTemplate { // use only pointers on PODs or flat objects he typename S::type* dEdxCalibContainer = nullptr; typename S>::type* o2Propagator = nullptr; typename S::type* itsPatternDict = nullptr; - + typename S::type* trdRecoParam = nullptr; // NN clusterizer objects typename S::type* nnClusterizerNetworks[3] = {nullptr, nullptr, nullptr}; }; diff --git a/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx b/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx new file mode 100644 index 0000000000000..70b445f7befc0 --- /dev/null +++ b/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx @@ -0,0 +1,100 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file GPUTRDRecoParam.cxx +/// \brief Error parameterizations and helper functions for TRD reconstruction +/// \author Ole Schmidt + +#include "GPUSettings.h" +#include "GPUTRDRecoParam.h" +#include "GPUCommonLogger.h" +#include "GPUCommonMath.h" + +using namespace o2::gpu; + +// error parameterizations taken from http://cds.cern.ch/record/2724259 Appendix A +void GPUTRDRecoParam::init(float bz, const GPUSettingsRec* rec) +{ + float resRPhiIdeal2 = rec ? rec->trd.trkltResRPhiIdeal * rec->trd.trkltResRPhiIdeal : 1.6e-3f; + + if (CAMath::Abs(CAMath::Abs(bz) - 2) < 0.1) { + if (bz > 0) { + // magnetic field +0.2 T + mRPhiA2 = resRPhiIdeal2; + mRPhiB = -1.43e-2f; + mRPhiC2 = 4.55e-2f; + + mDyA2 = 1.225e-3f; + mDyB = -9.8e-3f; + mDyC2 = 3.88e-2f; + + mAngleToDyA = -0.1f; + mAngleToDyB = 1.89f; + mAngleToDyC = -0.4f; + } else { + // magnetic field -0.2 T + mRPhiA2 = resRPhiIdeal2; + mRPhiB = 1.43e-2f; + mRPhiC2 = 4.55e-2f; + + mDyA2 = 1.225e-3f; + mDyB = 9.8e-3f; + mDyC2 = 3.88e-2f; + + mAngleToDyA = 0.1f; + mAngleToDyB = 1.89f; + mAngleToDyC = 0.4f; + } + } else if (CAMath::Abs(CAMath::Abs(bz) - 5) < 0.1) { + if (bz > 0) { + // magnetic field +0.5 T + mRPhiA2 = resRPhiIdeal2; + mRPhiB = 0.125f; + mRPhiC2 = 0.0961f; + + mDyA2 = 1.681e-3f; + mDyB = 0.15f; + mDyC2 = 0.1849f; + + mAngleToDyA = 0.13f; + mAngleToDyB = 2.43f; + mAngleToDyC = -0.58f; + } else { + // magnetic field -0.5 T + mRPhiA2 = resRPhiIdeal2; + mRPhiB = -0.14f; + mRPhiC2 = 0.1156f; + + mDyA2 = 2.209e-3f; + mDyB = -0.15f; + mDyC2 = 0.2025f; + + mAngleToDyA = -0.15f; + mAngleToDyB = 2.34f; + mAngleToDyC = 0.56f; + } + } else { + LOGP(warning, "No error parameterization available for Bz= {}. Keeping default value (sigma_y = const. = 1cm)", bz); + } + LOGP(info, "Loaded parameterizations for Bz={}: PhiRes:[{},{},{}] DyRes:[{},{},{}] Angle2Dy:[{},{},{}]", + bz, mRPhiA2, mRPhiB, mRPhiC2, mDyA2, mDyB, mDyC2, mAngleToDyA, mAngleToDyB, mAngleToDyC); +} + +void GPUTRDRecoParam::recalcTrkltCov(const float tilt, const float snp, const float rowSize, float* cov) const +{ + float t2 = tilt * tilt; // tan^2 (tilt) + float c2 = 1.f / (1.f + t2); // cos^2 (tilt) + float sy2 = getRPhiRes(snp); + float sz2 = rowSize * rowSize / 12.f; + cov[0] = c2 * (sy2 + t2 * sz2); + cov[1] = c2 * tilt * (sz2 - sy2); + cov[2] = c2 * (t2 * sy2 + sz2); +} diff --git a/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.h b/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.h new file mode 100644 index 0000000000000..ad0285487d3c3 --- /dev/null +++ b/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.h @@ -0,0 +1,84 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file RecoParam.h +/// \brief Error parameterizations and helper functions for TRD reconstruction +/// \author Ole Schmidt + +#ifndef O2_GPU_TRD_RECOPARAM_H +#define O2_GPU_TRD_RECOPARAM_H + +#include "GPUCommonDef.h" +#include "GPUCommonRtypes.h" +#include "GPUCommonArray.h" + +namespace o2 +{ +namespace gpu +{ +struct GPUSettingsRec; + +class GPUTRDRecoParam +{ + public: + GPUTRDRecoParam() = default; + GPUTRDRecoParam(const GPUTRDRecoParam&) = default; + ~GPUTRDRecoParam() = default; + + /// Load parameterization for given magnetic field + void init(float bz, const GPUSettingsRec* rec = nullptr); + +#if !defined(GPUCA_GPUCODE_DEVICE) + /// Recalculate tracklet covariance based on phi angle of related track + GPUd() void recalcTrkltCov(const float tilt, const float snp, const float rowSize, std::array& cov) const + { + recalcTrkltCov(tilt, snp, rowSize, cov.data()); + } +#endif + GPUd() void recalcTrkltCov(const float tilt, const float snp, const float rowSize, float* cov) const; + + /// Get tracklet r-phi resolution for given phi angle + /// Resolution depends on the track angle sin(phi) = snp and is approximated by the formula + /// sigma_y(snp) = sqrt(a^2 + c^2 * (snp - b)^2) + /// more details are given in http://cds.cern.ch/record/2724259 in section 5.3.3 + /// \param phi angle of related track + /// \return sigma_y^2 of tracklet + GPUd() float getRPhiRes(float snp) const { return (mRPhiA2 + mRPhiC2 * (snp - mRPhiB) * (snp - mRPhiB)); } + GPUd() float getDyRes(float snp) const { return mDyA2 + mDyC2 * (snp - mDyB) * (snp - mDyB); } // // a^2 + c^2 * (snp - b)^2 + GPUd() float convertAngleToDy(float snp) const { return mAngleToDyA + mAngleToDyB * snp + mAngleToDyC * snp * snp; } // a + b*snp + c*snp^2 is more accurate than sin(phi) = (dy / xDrift) / sqrt(1+(dy/xDrift)^2) + + /// Get tracklet z correction coefficient for track-eta based corraction + GPUd() float getZCorrCoeffNRC() const { return mZCorrCoefNRC; } + + private: + // tracklet error parameterization depends on the magnetic field + // rphi + float mRPhiA2{1.f}; ///< parameterization for tracklet position resolution + float mRPhiB{0.f}; ///< parameterization for tracklet position resolution + float mRPhiC2{0.f}; ///< parameterization for tracklet position resolution + // angle + float mDyA2{1.225e-3f}; ///< parameterization for tracklet angular resolution + float mDyB{0.f}; ///< parameterization for tracklet angular resolution + float mDyC2{0.f}; ///< parameterization for tracklet angular resolution + // angle to Dy + float mAngleToDyA; // parameterization for conversion track angle -> tracklet deflection + float mAngleToDyB; // parameterization for conversion track angle -> tracklet deflection + float mAngleToDyC; // parameterization for conversion track angle -> tracklet deflection + + float mZCorrCoefNRC{1.4f}; ///< tracklet z-position depends linearly on track dip angle + + ClassDefNV(GPUTRDRecoParam, 2); +}; + +} // namespace gpu +} // namespace o2 + +#endif // O2_GPU_TRD_RECOPARAM_H diff --git a/GPU/GPUTracking/GPUTrackingLinkDef_O2.h b/GPU/GPUTracking/GPUTrackingLinkDef_O2.h index 8e99514a817c5..46ced1e0481f9 100644 --- a/GPU/GPUTracking/GPUTrackingLinkDef_O2.h +++ b/GPU/GPUTracking/GPUTrackingLinkDef_O2.h @@ -27,6 +27,7 @@ #pragma link C++ struct o2::gpu::GPUTPCGMSectorTrack::sectorTrackParam + ; #pragma link C++ class o2::gpu::trackInterface < o2::gpu::GPUTPCGMTrackParam> + ; #pragma link C++ class o2::gpu::GPUTRDTrack_t < o2::gpu::trackInterface < o2::gpu::GPUTPCGMTrackParam>> + ; +#pragma link C++ class o2::gpu::GPUTRDRecoParam + ; #pragma link C++ class o2::gpu::gputpcgmmergertypes::GPUTPCOuterParam + ; #pragma link C++ class o2::gpu::gputpcgmmergertypes::InterpolationErrorHit + ; diff --git a/GPU/GPUTracking/Global/GPUChainTracking.cxx b/GPU/GPUTracking/Global/GPUChainTracking.cxx index 5c951053e155b..7216de0535329 100644 --- a/GPU/GPUTracking/Global/GPUChainTracking.cxx +++ b/GPU/GPUTracking/Global/GPUChainTracking.cxx @@ -34,6 +34,7 @@ #include "GPUTRDTracker.h" #include "AliHLTTPCRawCluster.h" #include "GPUTRDTrackletLabels.h" +#include "GPUTRDRecoParam.h" #include "display/GPUDisplayInterface.h" #include "GPUQA.h" #include "GPULogging.h" @@ -435,6 +436,9 @@ void GPUChainTracking::UpdateGPUCalibObjects(int32_t stream, const GPUCalibObjec memcpy((void*)mFlatObjectsShadow.mCalibObjects.trdGeometry, (const void*)processors()->calibObjects.trdGeometry, sizeof(*processors()->calibObjects.trdGeometry)); mFlatObjectsShadow.mCalibObjects.trdGeometry->clearInternalBufferPtr(); } + if (processors()->calibObjects.trdRecoParam && (ptrMask == nullptr || ptrMask->trdRecoParam)) { + memcpy((void*)mFlatObjectsShadow.mCalibObjects.trdRecoParam, (const void*)processors()->calibObjects.trdRecoParam, sizeof(*processors()->calibObjects.trdRecoParam)); + } if (processors()->calibObjects.tpcPadGain && (ptrMask == nullptr || ptrMask->tpcPadGain)) { memcpy((void*)mFlatObjectsShadow.mCalibObjects.tpcPadGain, (const void*)processors()->calibObjects.tpcPadGain, sizeof(*processors()->calibObjects.tpcPadGain)); } @@ -536,6 +540,9 @@ void* GPUChainTracking::GPUTrackingFlatObjects::SetPointersFlatObjects(void* mem if (mChainTracking->processors()->calibObjects.trdGeometry) { computePointerWithAlignment(mem, mCalibObjects.trdGeometry, 1); } + if (mChainTracking->processors()->calibObjects.trdRecoParam) { + computePointerWithAlignment(mem, mCalibObjects.trdRecoParam, 1); + } computePointerWithAlignment(mem, mCalibObjects.o2Propagator, 1); if (!mChainTracking->processors()->calibObjects.o2Propagator) { mCalibObjects.o2Propagator = nullptr; // Always reserve memory for o2::Propagator, since it may be propagatred only during run() not during init(). @@ -602,6 +609,12 @@ void GPUChainTracking::SetTRDGeometry(std::unique_ptr&& g processors()->calibObjects.trdGeometry = mTRDGeometryU.get(); } +void GPUChainTracking::SetTRDRecoParam(std::unique_ptr&& par) +{ + mTRDRecoParamU = std::move(par); + processors()->calibObjects.trdRecoParam = mTRDRecoParamU.get(); +} + int32_t GPUChainTracking::DoQueuedUpdates(int32_t stream, bool updateSlave) { int32_t retVal = 0; diff --git a/GPU/GPUTracking/Global/GPUChainTracking.h b/GPU/GPUTracking/Global/GPUChainTracking.h index 2dd1ece856ecf..fd75136f51d76 100644 --- a/GPU/GPUTracking/Global/GPUChainTracking.h +++ b/GPU/GPUTracking/Global/GPUChainTracking.h @@ -59,6 +59,7 @@ class GPUDisplayInterface; class GPUQA; class GPUTPCClusterStatistics; class GPUTRDGeometry; +class GPUTRDRecoParam; class TPCFastTransform; class GPUTrackingInputProvider; struct GPUChainTrackingFinalContext; @@ -178,13 +179,16 @@ class GPUChainTracking : public GPUChain const o2::tpc::CalibdEdxContainer* GetdEdxCalibContainer() const; const o2::base::MatLayerCylSet* GetMatLUT() const; const GPUTRDGeometry* GetTRDGeometry() const; + const GPUTRDRecoParam* GetTRDRecoParam() const; const o2::base::Propagator* GetO2Propagator() const; const o2::base::Propagator* GetDeviceO2Propagator(); void SetTPCFastTransform(std::unique_ptr&& tpcFastTransform, std::unique_ptr&& tpcTransformHelper); void SetMatLUT(std::unique_ptr&& lut); void SetTRDGeometry(std::unique_ptr&& geo); + void SetTRDRecoParam(std::unique_ptr&& par); void SetMatLUT(const o2::base::MatLayerCylSet* lut); void SetTRDGeometry(const o2::trd::GeometryFlat* geo); + void SetTRDRecoParam(const GPUTRDRecoParam* par); void SetO2Propagator(const o2::base::Propagator* prop); void SetCalibObjects(const GPUCalibObjectsConst& obj); void SetCalibObjects(const GPUCalibObjects& obj); @@ -267,6 +271,7 @@ class GPUChainTracking : public GPUChain std::unique_ptr mdEdxCalibContainerU; // TPC dEdx calibration container std::unique_ptr mMatLUTU; // Material Lookup Table std::unique_ptr mTRDGeometryU; // TRD Geometry + std::unique_ptr mTRDRecoParamU; // TRD RecoParam // Ptrs to internal buffers std::unique_ptr mClusterNativeAccess, mClusterNativeAccessReduced; diff --git a/GPU/GPUTracking/Global/GPUChainTrackingGetters.inc b/GPU/GPUTracking/Global/GPUChainTrackingGetters.inc index 5b72a8f23c242..b3b1773ec664e 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingGetters.inc +++ b/GPU/GPUTracking/Global/GPUChainTrackingGetters.inc @@ -26,9 +26,11 @@ inline const TPCZSLinkMapping* GPUChainTracking::GetTPCZSLinkMapping() const { r inline const o2::tpc::CalibdEdxContainer* GPUChainTracking::GetdEdxCalibContainer() const { return processors()->calibObjects.dEdxCalibContainer; } inline const o2::base::MatLayerCylSet* GPUChainTracking::GetMatLUT() const { return processors()->calibObjects.matLUT; } inline const GPUTRDGeometry* GPUChainTracking::GetTRDGeometry() const { return (GPUTRDGeometry*)processors()->calibObjects.trdGeometry; } +inline const GPUTRDRecoParam* GPUChainTracking::GetTRDRecoParam() const { return processors()->calibObjects.trdRecoParam; } inline const o2::base::Propagator* GPUChainTracking::GetO2Propagator() const { return processors()->calibObjects.o2Propagator; } inline void GPUChainTracking::SetMatLUT(const o2::base::MatLayerCylSet* lut) { processors()->calibObjects.matLUT = lut; } inline void GPUChainTracking::SetTRDGeometry(const o2::trd::GeometryFlat* geo) { processors()->calibObjects.trdGeometry = geo; } +inline void GPUChainTracking::SetTRDRecoParam(const GPUTRDRecoParam* par) { processors()->calibObjects.trdRecoParam = par; } inline void GPUChainTracking::SetCalibObjects(const GPUCalibObjectsConst& obj) { processors()->calibObjects = obj; } inline void GPUChainTracking::SetCalibObjects(const GPUCalibObjects& obj) { memcpy((void*)&processors()->calibObjects, (const void*)&obj, sizeof(obj)); } } // namespace o2::gpu diff --git a/GPU/GPUTracking/Global/GPUChainTrackingIO.cxx b/GPU/GPUTracking/Global/GPUChainTrackingIO.cxx index dd11e9989f684..6f24415564a8c 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingIO.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingIO.cxx @@ -20,6 +20,7 @@ #include "GPUTPCTrack.h" #include "GPUTPCHitId.h" #include "GPUTRDTrackletWord.h" +#include "GPUTRDRecoParam.h" #include "AliHLTTPCClusterMCData.h" #include "GPUTPCMCInfo.h" #include "GPUTRDTrack.h" @@ -337,6 +338,11 @@ void GPUChainTracking::DumpSettings(const char* dir) f += "trdgeometry.dump"; DumpStructToFile(processors()->calibObjects.trdGeometry, f.c_str()); } + if (processors()->calibObjects.trdRecoParam != nullptr) { + f = dir; + f += "trdrecoparam.dump"; + DumpStructToFile(processors()->calibObjects.trdRecoParam, f.c_str()); + } } void GPUChainTracking::ReadSettings(const char* dir) @@ -382,4 +388,8 @@ void GPUChainTracking::ReadSettings(const char* dir) f += "trdgeometry.dump"; mTRDGeometryU = ReadStructFromFile(f.c_str()); processors()->calibObjects.trdGeometry = mTRDGeometryU.get(); + f = dir; + f += "trdrecoparam.dump"; + mTRDRecoParamU = ReadStructFromFile(f.c_str()); + processors()->calibObjects.trdRecoParam = mTRDRecoParamU.get(); } diff --git a/GPU/GPUTracking/TRDTracking/GPUTRDTracker.cxx b/GPU/GPUTracking/TRDTracking/GPUTRDTracker.cxx index 2f754d2416bc1..d5d400e30df53 100644 --- a/GPU/GPUTracking/TRDTracking/GPUTRDTracker.cxx +++ b/GPU/GPUTracking/TRDTracking/GPUTRDTracker.cxx @@ -23,6 +23,7 @@ #include "GPUCommonMath.h" #include "GPUCommonAlgorithm.h" #include "GPUConstantMem.h" +#include "GPUTRDRecoParam.h" using namespace o2::gpu; @@ -92,7 +93,7 @@ void* GPUTRDTracker_t::SetPointersTracks(void* base) } template -GPUTRDTracker_t::GPUTRDTracker_t() : mR(nullptr), mIsInitialized(false), mGenerateSpacePoints(false), mProcessPerTimeFrame(false), mNAngleHistogramBins(25), mAngleHistogramRange(50), mMemoryPermanent(-1), mMemoryTracklets(-1), mMemoryTracks(-1), mNMaxCollisions(0), mNMaxTracks(0), mNMaxSpacePoints(0), mTracks(nullptr), mTrackAttribs(nullptr), mNCandidates(1), mNTracks(0), mNEvents(0), mMaxBackendThreads(100), mTrackletIndexArray(nullptr), mHypothesis(nullptr), mCandidates(nullptr), mSpacePoints(nullptr), mGeo(nullptr), mRPhiA2(0), mRPhiB(0), mRPhiC2(0), mDyA2(0), mDyB(0), mDyC2(0), mAngleToDyA(0), mAngleToDyB(0), mAngleToDyC(0), mDebugOutput(false), mMaxEta(0.84f), mRoadZ(18.f), mZCorrCoefNRC(1.4f), mTPCVdrift(2.58f), mTPCTDriftOffset(0.f), mDebug(new GPUTRDTrackerDebug()) +GPUTRDTracker_t::GPUTRDTracker_t() : mR(nullptr), mIsInitialized(false), mGenerateSpacePoints(false), mProcessPerTimeFrame(false), mNAngleHistogramBins(25), mAngleHistogramRange(50), mMemoryPermanent(-1), mMemoryTracklets(-1), mMemoryTracks(-1), mNMaxCollisions(0), mNMaxTracks(0), mNMaxSpacePoints(0), mTracks(nullptr), mTrackAttribs(nullptr), mNCandidates(1), mNTracks(0), mNEvents(0), mMaxBackendThreads(100), mTrackletIndexArray(nullptr), mHypothesis(nullptr), mCandidates(nullptr), mSpacePoints(nullptr), mGeo(nullptr), mRecoParam(nullptr), mDebugOutput(false), mMaxEta(0.84f), mRoadZ(18.f), mZCorrCoefNRC(1.4f), mTPCVdrift(2.58f), mTPCTDriftOffset(0.f), mDebug(new GPUTRDTrackerDebug()) { //-------------------------------------------------------------------- // Default constructor @@ -114,9 +115,8 @@ void GPUTRDTracker_t::InitializeProcessor() //-------------------------------------------------------------------- // Initialise tracker //-------------------------------------------------------------------- - + mRecoParam = GetConstantMem()->calibObjects.trdRecoParam; UpdateGeometry(); - mDebug->ExpandVectors(); mIsInitialized = true; } @@ -131,42 +131,6 @@ void GPUTRDTracker_t::UpdateGeometry() if (!mGeo) { GPUFatal("TRD geometry must be provided externally"); } - float Bz = Param().bzkG; - float resRPhiIdeal2 = Param().rec.trd.trkltResRPhiIdeal * Param().rec.trd.trkltResRPhiIdeal; - GPUInfo("Initializing with B-field: %f kG", Bz); - if (CAMath::Abs(CAMath::Abs(Bz) - 2) < 0.1f) { - // magnetic field +-0.2 T - if (Bz > 0) { - GPUInfo("Loading error parameterization for Bz = +2 kG"); - mRPhiA2 = resRPhiIdeal2, mRPhiB = -1.43e-2f, mRPhiC2 = 4.55e-2f; - mDyA2 = 1.225e-3f, mDyB = -9.8e-3f, mDyC2 = 3.88e-2f; - mAngleToDyA = -0.1f, mAngleToDyB = 1.89f, mAngleToDyC = -0.4f; - } else { - GPUInfo("Loading error parameterization for Bz = -2 kG"); - mRPhiA2 = resRPhiIdeal2, mRPhiB = 1.43e-2f, mRPhiC2 = 4.55e-2f; - mDyA2 = 1.225e-3f, mDyB = 9.8e-3f, mDyC2 = 3.88e-2f; - mAngleToDyA = 0.1f, mAngleToDyB = 1.89f, mAngleToDyC = 0.4f; - } - } else if (CAMath::Abs(CAMath::Abs(Bz) - 5) < 0.1f) { - // magnetic field +-0.5 T - if (Bz > 0) { - GPUInfo("Loading error parameterization for Bz = +5 kG"); - mRPhiA2 = resRPhiIdeal2, mRPhiB = 0.125f, mRPhiC2 = 0.0961f; - mDyA2 = 1.681e-3f, mDyB = 0.15f, mDyC2 = 0.1849f; - mAngleToDyA = 0.13f, mAngleToDyB = 2.43f, mAngleToDyC = -0.58f; - } else { - GPUInfo("Loading error parameterization for Bz = -5 kG"); - mRPhiA2 = resRPhiIdeal2, mRPhiB = -0.14f, mRPhiC2 = 0.1156f; - mDyA2 = 2.209e-3f, mDyB = -0.15f, mDyC2 = 0.2025f; - mAngleToDyA = -0.15f, mAngleToDyB = 2.34f, mAngleToDyC = 0.56f; - } - } else { - // magnetic field 0 T or another value which is not covered by the error parameterizations - // using default values instead - GPUWarning("No error parameterization available for Bz = %.2f kG. Keeping default value (sigma_y = const. = 1cm)", Bz); - mRPhiA2 = 1.f; - } - // obtain average radius of TRD chambers float x0[kNLayers] = {300.2f, 312.8f, 325.4f, 338.0f, 350.6f, 363.2f}; // used as default value in case no transformation matrix can be obtained auto* matrix = mGeo->GetClusterMatrix(0); @@ -967,7 +931,7 @@ GPUd() void GPUTRDTracker_t::RecalcTrkltCov(const float tilt, cons //-------------------------------------------------------------------- float t2 = tilt * tilt; // tan^2 (tilt) float c2 = 1.f / (1.f + t2); // cos^2 (tilt) - float sy2 = GetRPhiRes(snp); + float sy2 = mRecoParam->getRPhiRes(snp); float sz2 = rowSize * rowSize / 12.f; cov[0] = c2 * (sy2 + t2 * sz2); cov[1] = c2 * tilt * (sz2 - sy2); @@ -977,8 +941,8 @@ GPUd() void GPUTRDTracker_t::RecalcTrkltCov(const float tilt, cons template GPUd() float GPUTRDTracker_t::GetAngularPull(float dYtracklet, float snp) const { - float dYtrack = ConvertAngleToDy(snp); - float dYresolution = GetAngularResolution(snp); + float dYtrack = mRecoParam->convertAngleToDy(snp); + float dYresolution = mRecoParam->getDyRes(snp); if (dYresolution < 1e-6f) { return 999.f; } diff --git a/GPU/GPUTracking/TRDTracking/GPUTRDTracker.h b/GPU/GPUTracking/TRDTracking/GPUTRDTracker.h index f8fa0342ee62d..5d7530ccecc11 100644 --- a/GPU/GPUTracking/TRDTracking/GPUTRDTracker.h +++ b/GPU/GPUTracking/TRDTracking/GPUTRDTracker.h @@ -38,6 +38,7 @@ class GPUTRDGeometry; class GPUChainTracking; template class GPUTRDTrackerDebug; +class GPUTRDRecoParam; //------------------------------------------------------------------------- template @@ -114,9 +115,6 @@ class GPUTRDTracker_t : public GPUProcessor GPUd() bool AdjustSector(PROP* prop, TRDTRK* t) const; GPUd() int32_t GetSector(float alpha) const; GPUd() float GetAlphaOfSector(const int32_t sec) const; - GPUd() float GetRPhiRes(float snp) const { return (mRPhiA2 + mRPhiC2 * (snp - mRPhiB) * (snp - mRPhiB)); } // parametrization obtained from track-tracklet residuals: - GPUd() float GetAngularResolution(float snp) const { return mDyA2 + mDyC2 * (snp - mDyB) * (snp - mDyB); } // a^2 + c^2 * (snp - b)^2 - GPUd() float ConvertAngleToDy(float snp) const { return mAngleToDyA + mAngleToDyB * snp + mAngleToDyC * snp * snp; } // a + b*snp + c*snp^2 is more accurate than sin(phi) = (dy / xDrift) / sqrt(1+(dy/xDrift)^2) GPUd() float GetAngularPull(float dYtracklet, float snp) const; GPUd() void RecalcTrkltCov(const float tilt, const float snp, const float rowSize, float (&cov)[3]); GPUd() void FindChambersInRoad(const TRDTRK* t, const float roadY, const float roadZ, const int32_t iLayer, int32_t* det, const float zMax, const float alpha, const float zShiftTrk) const; @@ -174,16 +172,7 @@ class GPUTRDTracker_t : public GPUProcessor TRDTRK* mCandidates; // array of tracks for multiple hypothesis tracking GPUTRDSpacePoint* mSpacePoints; // array with tracklet coordinates in global tracking frame const GPUTRDGeometry* mGeo; // TRD geometry - /// ---- error parametrization depending on magnetic field ---- - float mRPhiA2; // parameterization for tracklet position resolution - float mRPhiB; // parameterization for tracklet position resolution - float mRPhiC2; // parameterization for tracklet position resolution - float mDyA2; // parameterization for tracklet angular resolution - float mDyB; // parameterization for tracklet angular resolution - float mDyC2; // parameterization for tracklet angular resolution - float mAngleToDyA; // parameterization for conversion track angle -> tracklet deflection - float mAngleToDyB; // parameterization for conversion track angle -> tracklet deflection - float mAngleToDyC; // parameterization for conversion track angle -> tracklet deflection + const GPUTRDRecoParam* mRecoParam; // TRD RecoParam /// ---- end error parametrization ---- bool mDebugOutput; // store debug output static constexpr const float sRadialOffset = -0.1f; // due to (possible) mis-calibration of t0 -> will become obsolete when tracklet conversion is done outside of the tracker diff --git a/GPU/GPUTracking/TRDTracking/macros/run_trd_tracker.C b/GPU/GPUTracking/TRDTracking/macros/run_trd_tracker.C index acfcf92370b00..e4b37500e1a60 100644 --- a/GPU/GPUTracking/TRDTracking/macros/run_trd_tracker.C +++ b/GPU/GPUTracking/TRDTracking/macros/run_trd_tracker.C @@ -24,6 +24,7 @@ #include "GPUTRDTrackletWord.h" #include "GPUTRDInterfaces.h" #include "GPUTRDGeometry.h" +#include "GPUTRDRecoParam.h" // O2 header #include "CommonUtils/NameConf.h" @@ -58,7 +59,7 @@ void run_trd_tracker(std::string path = "./", geo->createPadPlaneArray(); geo->createClusterMatrixArray(); const o2::trd::GeometryFlat geoFlat(*geo); - + o2::gpu::GPUTRDRecoParam trdRecoParam; //-------- init GPU reconstruction --------// // different settings are defined in GPUSettingsList.h GPUSettingsGRP cfgGRP; // defaults should be ok @@ -85,6 +86,7 @@ void run_trd_tracker(std::string path = "./", rec->RegisterGPUProcessor(tracker, false); chainTracking->SetTRDGeometry(&geoFlat); + chainTracking->SetTRDRecoParam(&trdRecoParam); if (rec->Init()) { printf("ERROR: GPUReconstruction not initialized\n"); } diff --git a/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h b/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h index d610269abca81..8dfbdaff7272f 100644 --- a/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h +++ b/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h @@ -85,6 +85,7 @@ struct TPCPadGainCalib; struct TPCZSLinkMapping; struct GPUSettingsO2; struct GPUSettingsProcessingNNclusterizer; +class GPUTRDRecoParam; class GPUO2InterfaceQA; struct GPUTrackingInOutPointers; struct GPUTrackingInOutZS; @@ -212,6 +213,7 @@ class GPURecoWorkflowSpec : public o2::framework::Task std::unique_ptr mTPCZSLinkMapping; std::unique_ptr mTPCVDriftHelper; std::unique_ptr mTRDGeometry; + std::unique_ptr mTRDRecoParam; std::unique_ptr mConfig; std::unique_ptr mConfParam; std::unique_ptr mTimer; @@ -245,6 +247,7 @@ class GPURecoWorkflowSpec : public o2::framework::Task bool mMatLUTCreated = false; bool mITSGeometryCreated = false; bool mTRDGeometryCreated = false; + bool mTRDRecoParamCreated = false; bool mPropagatorInstanceCreated = false; int32_t mTPCCutAtTimeBin = -1; }; diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index 7b1db436dbf7e..a8f95841a4dc9 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -73,6 +73,7 @@ #include "DataFormatsTRD/RecoInputContainer.h" #include "TRDBase/Geometry.h" #include "TRDBase/GeometryFlat.h" +#include "GPUTRDRecoParam.h" #include "ITSBase/GeometryTGeo.h" #include "CommonUtils/DebugStreamer.h" #include "GPUReconstructionConvert.h" @@ -273,6 +274,9 @@ void GPURecoWorkflowSpec::init(InitContext& ic) if (mSpecConfig.readTRDtracklets) { mTRDGeometry = std::make_unique(); mConfig->configCalib.trdGeometry = mTRDGeometry.get(); + + mTRDRecoParam = std::make_unique(); + mConfig->configCalib.trdRecoParam = mTRDRecoParam.get(); } mConfig->configProcessing.willProvideO2PropagatorLate = true; @@ -1059,14 +1063,21 @@ void GPURecoWorkflowSpec::doCalibUpdates(o2::framework::ProcessingContext& pc, c } mMatLUTCreated = true; } - if (mSpecConfig.readTRDtracklets && !mTRDGeometryCreated) { - auto gm = o2::trd::Geometry::instance(); - gm->createPadPlaneArray(); - gm->createClusterMatrixArray(); - mTRDGeometry = std::make_unique(*gm); - newCalibObjects.trdGeometry = mConfig->configCalib.trdGeometry = mTRDGeometry.get(); - LOG(info) << "Loaded TRD geometry"; - mTRDGeometryCreated = true; + if (mSpecConfig.readTRDtracklets) { + if (!mTRDGeometryCreated) { + auto gm = o2::trd::Geometry::instance(); + gm->createPadPlaneArray(); + gm->createClusterMatrixArray(); + mTRDGeometry = std::make_unique(*gm); + newCalibObjects.trdGeometry = mConfig->configCalib.trdGeometry = mTRDGeometry.get(); + LOG(info) << "Loaded TRD geometry"; + mTRDGeometryCreated = true; + } + if (!mTRDRecoParamCreated) { + mTRDRecoParam = std::make_unique(); + newCalibObjects.trdRecoParam = mConfig->configCalib.trdRecoParam = mTRDRecoParam.get(); + mTRDRecoParamCreated = true; + } } } needCalibUpdate = fetchCalibsCCDBTPC(pc, newCalibObjects, oldCalibObjects) || needCalibUpdate; From 8e1f22798d5e2ccfd03ace4ea07c45a18845e3e2 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 3 Feb 2026 10:48:58 +0100 Subject: [PATCH 040/133] Disable tests for reverted exceptions (#15011) --- Framework/Core/test/test_TypeToTaskName.cxx | 60 ++++++++++----------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/Framework/Core/test/test_TypeToTaskName.cxx b/Framework/Core/test/test_TypeToTaskName.cxx index b7b440b13ecfd..af51bc04613f5 100644 --- a/Framework/Core/test/test_TypeToTaskName.cxx +++ b/Framework/Core/test/test_TypeToTaskName.cxx @@ -18,48 +18,48 @@ using namespace o2::framework; TEST_CASE("TypeIdHelpers_BasicConversion") { // Basic CamelCase to snake-case conversion - REQUIRE((type_to_task_name(std::string_view("SimpleTask")) == "simple-task")); - REQUIRE((type_to_task_name(std::string_view("MyTask")) == "my-task")); - REQUIRE((type_to_task_name(std::string_view("Task")) == "task")); +// REQUIRE((type_to_task_name(std::string_view("SimpleTask")) == "simple-task")); +// REQUIRE((type_to_task_name(std::string_view("MyTask")) == "my-task")); +// REQUIRE((type_to_task_name(std::string_view("Task")) == "task")); } TEST_CASE("TypeIdHelpers_AbbreviationConsolidation") { // Test ALICE detector abbreviations - REQUIRE(type_to_task_name(std::string_view("ITSQA")) == "its-qa"); - REQUIRE(type_to_task_name(std::string_view("TPCQCTask")) == "tpc-qc-task"); +// REQUIRE(type_to_task_name(std::string_view("ITSQA")) == "its-qa"); +// REQUIRE(type_to_task_name(std::string_view("TPCQCTask")) == "tpc-qc-task"); REQUIRE(type_to_task_name(std::string_view("EMCALQATask")) == "emcal-qa-task"); - REQUIRE(type_to_task_name(std::string_view("HMPIDTask")) == "hmpid-task"); - REQUIRE(type_to_task_name(std::string_view("ITSTPCTask")) == "its-tpc-task"); - REQUIRE(type_to_task_name(std::string_view("QCFV0Task")) == "qc-fv0-task"); +// REQUIRE(type_to_task_name(std::string_view("HMPIDTask")) == "hmpid-task"); +// REQUIRE(type_to_task_name(std::string_view("ITSTPCTask")) == "its-tpc-task"); +// REQUIRE(type_to_task_name(std::string_view("QCFV0Task")) == "qc-fv0-task"); } -TEST_CASE("TypeIdHelpers_QualityControlAbbreviations") -{ - // Test quality control abbreviations - REQUIRE(type_to_task_name(std::string_view("QATask")) == "qa-task"); - REQUIRE(type_to_task_name(std::string_view("QCTask")) == "qc-task"); - REQUIRE(type_to_task_name(std::string_view("QCDAnalysis")) == "qcd-analysis"); -} +//TEST_CASE("TypeIdHelpers_QualityControlAbbreviations") +//{ +// // Test quality control abbreviations +// REQUIRE(type_to_task_name(std::string_view("QATask")) == "qa-task"); +// REQUIRE(type_to_task_name(std::string_view("QCTask")) == "qc-task"); +// REQUIRE(type_to_task_name(std::string_view("QCDAnalysis")) == "qcd-analysis"); +//} TEST_CASE("TypeIdHelpers_ComplexNames") { // Test complex combinations - REQUIRE(type_to_task_name(std::string_view("ITSQAAnalysisTask")) == "its-qa-analysis-task"); +// REQUIRE(type_to_task_name(std::string_view("ITSQAAnalysisTask")) == "its-qa-analysis-task"); REQUIRE(type_to_task_name(std::string_view("TPCEMCQCTask")) == "tpc-emc-qc-task"); - REQUIRE(type_to_task_name(std::string_view("MyITSTask")) == "my-its-task"); +// REQUIRE(type_to_task_name(std::string_view("MyITSTask")) == "my-its-task"); } -TEST_CASE("TypeIdHelpers_EdgeCases") -{ - // Single character - REQUIRE(type_to_task_name(std::string_view("A")) == "a"); - - // All uppercase. BC is Bunch Crossing! - // - REQUIRE(type_to_task_name(std::string_view("ABC")) == "a-bc"); - REQUIRE(type_to_task_name(std::string_view("BC")) == "bc"); - - // Mixed with numbers (numbers are not uppercase, so no hyphens before them) - REQUIRE(type_to_task_name(std::string_view("Task123")) == "task123"); -} +//TEST_CASE("TypeIdHelpers_EdgeCases") +//{ +// // Single character +// REQUIRE(type_to_task_name(std::string_view("A")) == "a"); +// +// // All uppercase. BC is Bunch Crossing! +// // +// REQUIRE(type_to_task_name(std::string_view("ABC")) == "a-bc"); +// REQUIRE(type_to_task_name(std::string_view("BC")) == "bc"); +// +// // Mixed with numbers (numbers are not uppercase, so no hyphens before them) +// REQUIRE(type_to_task_name(std::string_view("Task123")) == "task123"); +//} From ee2b995e2450fb5b6a5314b5eca18969e5d260c6 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 3 Feb 2026 14:01:01 +0100 Subject: [PATCH 041/133] Brown paperbag issue with reverted feature. (#15012) --- Framework/Core/test/test_TypeToTaskName.cxx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Framework/Core/test/test_TypeToTaskName.cxx b/Framework/Core/test/test_TypeToTaskName.cxx index af51bc04613f5..cd5a359db0446 100644 --- a/Framework/Core/test/test_TypeToTaskName.cxx +++ b/Framework/Core/test/test_TypeToTaskName.cxx @@ -25,10 +25,10 @@ TEST_CASE("TypeIdHelpers_BasicConversion") TEST_CASE("TypeIdHelpers_AbbreviationConsolidation") { - // Test ALICE detector abbreviations +// Test ALICE detector abbreviations // REQUIRE(type_to_task_name(std::string_view("ITSQA")) == "its-qa"); // REQUIRE(type_to_task_name(std::string_view("TPCQCTask")) == "tpc-qc-task"); - REQUIRE(type_to_task_name(std::string_view("EMCALQATask")) == "emcal-qa-task"); +// REQUIRE(type_to_task_name(std::string_view("EMCALQATask")) == "emcal-qa-task"); // REQUIRE(type_to_task_name(std::string_view("HMPIDTask")) == "hmpid-task"); // REQUIRE(type_to_task_name(std::string_view("ITSTPCTask")) == "its-tpc-task"); // REQUIRE(type_to_task_name(std::string_view("QCFV0Task")) == "qc-fv0-task"); @@ -42,13 +42,13 @@ TEST_CASE("TypeIdHelpers_AbbreviationConsolidation") // REQUIRE(type_to_task_name(std::string_view("QCDAnalysis")) == "qcd-analysis"); //} -TEST_CASE("TypeIdHelpers_ComplexNames") -{ - // Test complex combinations +//TEST_CASE("TypeIdHelpers_ComplexNames") +//{ +// Test complex combinations // REQUIRE(type_to_task_name(std::string_view("ITSQAAnalysisTask")) == "its-qa-analysis-task"); - REQUIRE(type_to_task_name(std::string_view("TPCEMCQCTask")) == "tpc-emc-qc-task"); +// REQUIRE(type_to_task_name(std::string_view("TPCEMCQCTask")) == "tpc-emc-qc-task"); // REQUIRE(type_to_task_name(std::string_view("MyITSTask")) == "my-its-task"); -} +//} //TEST_CASE("TypeIdHelpers_EdgeCases") //{ From ff39f95db1067234eac4ab9dd3681ecba15949bd Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Tue, 3 Feb 2026 15:33:50 +0100 Subject: [PATCH 042/133] Fix and improve TPC Loopers implementation --- Generators/include/Generators/Generator.h | 12 +++++------ Generators/include/Generators/TPCLoopers.h | 2 -- Generators/src/Generator.cxx | 23 ++++++++++++++++++++-- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/Generators/include/Generators/Generator.h b/Generators/include/Generators/Generator.h index 3484601aa42bb..f413aeccfa3ab 100644 --- a/Generators/include/Generators/Generator.h +++ b/Generators/include/Generators/Generator.h @@ -17,10 +17,6 @@ #include "FairGenerator.h" #include "TParticle.h" #include "Generators/Trigger.h" -#ifdef GENERATORS_WITH_TPCLOOPERS -#include "Generators/TPCLoopers.h" -#include "Generators/TPCLoopersParam.h" -#endif #include #include #include @@ -38,6 +34,8 @@ namespace o2 namespace eventgen { +class GenTPCLoopers; // Forward declaration + /*****************************************************************/ /*****************************************************************/ @@ -60,7 +58,7 @@ class Generator : public FairGenerator /** constructor **/ Generator(const Char_t* name, const Char_t* title = "ALICEo2 Generator"); /** destructor **/ - ~Generator() override = default; + ~Generator() override; /** Initialize the generator if needed **/ Bool_t Init() override; @@ -169,9 +167,9 @@ class Generator : public FairGenerator // global static information about (upper limit of) number of events to be generated static unsigned int gTotalNEvents; -#ifdef GENERATORS_WITH_TPCLOOPERS // Loopers generator instance - std::unique_ptr mTPCLoopersGen = nullptr; + o2::eventgen::GenTPCLoopers* mTPCLoopersGen = nullptr; +#ifdef GENERATORS_WITH_TPCLOOPERS bool initTPCLoopersGen(); #endif diff --git a/Generators/include/Generators/TPCLoopers.h b/Generators/include/Generators/TPCLoopers.h index 6a1d3ef262e22..a144a947fc11b 100644 --- a/Generators/include/Generators/TPCLoopers.h +++ b/Generators/include/Generators/TPCLoopers.h @@ -16,14 +16,12 @@ #ifdef GENERATORS_WITH_TPCLOOPERS #include -#endif #include #include #include "TRandom3.h" #include #include "TParticle.h" -#ifdef GENERATORS_WITH_TPCLOOPERS // Static Ort::Env instance for multiple onnx model loading extern Ort::Env global_env; diff --git a/Generators/src/Generator.cxx b/Generators/src/Generator.cxx index 465a8ffb7ee22..ecea311c94de7 100644 --- a/Generators/src/Generator.cxx +++ b/Generators/src/Generator.cxx @@ -27,6 +27,10 @@ #include "TGrid.h" #include "CCDB/BasicCCDBManager.h" #include +#ifdef GENERATORS_WITH_TPCLOOPERS +#include "Generators/TPCLoopers.h" +#include "Generators/TPCLoopersParam.h" +#endif namespace o2 { @@ -94,6 +98,19 @@ Generator::Generator(const Char_t* name, const Char_t* title) : FairGenerator(na #endif } +/*****************************************************************/ + +Generator::~Generator() +{ + /** destructor **/ +#ifdef GENERATORS_WITH_TPCLOOPERS + if (mTPCLoopersGen) { + delete mTPCLoopersGen; + mTPCLoopersGen = nullptr; + } +#endif +} + /*****************************************************************/ #ifdef GENERATORS_WITH_TPCLOOPERS bool Generator::initTPCLoopersGen() @@ -171,7 +188,7 @@ bool Generator::initTPCLoopersGen() nclxrate = isAlien[2] || isCCDB[2] ? local_names[2] : nclxrate; try { // Create the TPC loopers generator with the provided parameters - mTPCLoopersGen = std::make_unique(model_pairs, model_compton, poisson, gauss, scaler_pair, scaler_compton); + mTPCLoopersGen = new o2::eventgen::GenTPCLoopers(model_pairs, model_compton, poisson, gauss, scaler_pair, scaler_compton); const auto& intrate = loopersParam.intrate; // Configure the generator with flat gas loopers defined per orbit with clusters/track info // If intrate is negative (default), automatic IR from collisioncontext.root will be used @@ -188,7 +205,9 @@ bool Generator::initTPCLoopersGen() LOG(info) << "TPC Loopers generator initialized successfully"; } catch (const std::exception& e) { LOG(error) << "Failed to initialize TPC Loopers generator: " << e.what(); - mTPCLoopersGen.reset(); + delete mTPCLoopersGen; + mTPCLoopersGen = nullptr; + return kFALSE; } return kTRUE; } From 71634e3b6983e1e54596784828b6fcb145261c9f Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 5 Feb 2026 09:20:34 +0100 Subject: [PATCH 043/133] DPL: Improve message when we do not have enough resources to process. (#15016) --- Framework/Core/src/DataProcessingDevice.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 8a306c7b96001..ccfb58db7559a 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -1399,12 +1399,12 @@ void DataProcessingDevice::Run() if (schedulingStats.numberOfUnscheduledSinceLastScheduled > 100 || (uv_now(state.loop) - schedulingStats.lastScheduled) > 30000) { O2_SIGNPOST_EVENT_EMIT_WARN(scheduling, sid, "Run", - "Not enough resources to schedule computation. %zu skipped so far. Last scheduled at %zu.", + "Not enough resources to schedule computation. %zu skipped so far. Last scheduled at %zu. Data is not lost and it will be scheduled again.", schedulingStats.numberOfUnscheduledSinceLastScheduled.load(), schedulingStats.lastScheduled.load()); } else { O2_SIGNPOST_EVENT_EMIT(scheduling, sid, "Run", - "Not enough resources to schedule computation. %zu skipped so far. Last scheduled at %zu.", + "Not enough resources to schedule computation. %zu skipped so far. Last scheduled at %zu. Data is not lost and it will be scheduled again.", schedulingStats.numberOfUnscheduledSinceLastScheduled.load(), schedulingStats.lastScheduled.load()); } From bfa44ca0e7a6b8d42cca3ac93f0b7a423869c5a0 Mon Sep 17 00:00:00 2001 From: Gabriele Cimador Date: Fri, 19 Dec 2025 10:59:27 +0100 Subject: [PATCH 044/133] GPU Framework: remove GPUDefParametersDefaults.h and automatically generate GPU parameters using json file and CMake --- .../ITS/tracking/GPU/cuda/CMakeLists.txt | 2 +- GPU/GPUTracking/Base/cuda/CMakeLists.txt | 4 +- GPU/GPUTracking/Base/hip/CMakeLists.txt | 4 +- GPU/GPUTracking/CMakeLists.txt | 21 +- .../Definitions/.clang-format-ignore | 1 + .../Definitions/GPUDefParametersDefaults.h | 589 ------------------ .../Definitions/GPUParameters.json | 582 +++++++++++++++++ GPU/GPUTracking/Definitions/GPUSettingsList.h | 2 +- .../cmake/generateGPUParamHeader.cmake | 37 ++ .../cmake/gpu_param_header_generator.cmake | 105 ++++ GPU/documentation/build-O2.md | 2 +- dependencies/FindO2GPU.cmake | 43 +- log.txt | 0 13 files changed, 777 insertions(+), 615 deletions(-) create mode 100644 GPU/GPUTracking/Definitions/.clang-format-ignore delete mode 100644 GPU/GPUTracking/Definitions/GPUDefParametersDefaults.h create mode 100644 GPU/GPUTracking/Definitions/GPUParameters.json create mode 100644 GPU/GPUTracking/cmake/generateGPUParamHeader.cmake create mode 100644 GPU/GPUTracking/cmake/gpu_param_header_generator.cmake create mode 100644 log.txt diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt index 1f6a046a81350..e38dbb1ef20e8 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt @@ -35,5 +35,5 @@ if(CUDA_ENABLED) set_property(TARGET ${targetName} PROPERTY CUDA_SEPARABLE_COMPILATION ON) target_compile_definitions(${targetName} PRIVATE $) - set_target_cuda_arch(${targetName}) + set_target_gpu_arch("CUDA" ${targetName}) endif() diff --git a/GPU/GPUTracking/Base/cuda/CMakeLists.txt b/GPU/GPUTracking/Base/cuda/CMakeLists.txt index 05ed091eb83ea..226bacbf88157 100644 --- a/GPU/GPUTracking/Base/cuda/CMakeLists.txt +++ b/GPU/GPUTracking/Base/cuda/CMakeLists.txt @@ -74,7 +74,7 @@ add_custom_command( COMMAND cat ${GPUDIR}/Base/GPUStdSystemHeaders.h >> ${GPU_RTC_BIN}.src COMMAND ${CMAKE_CUDA_COMPILER} ${GPU_RTC_DEFINES} ${GPU_RTC_INCLUDES} -std=c++${CMAKE_CUDA_STANDARD} -D__CUDA_ARCH__=${RTC_CUDA_ARCH} -Wno-deprecated-gpu-targets -D__CUDACC__ -x c++ -M -MD -MT ${GPU_RTC_BIN}.src -MF ${GPU_RTC_BIN}.src.d ${GPU_RTC_SRC} COMMAND ${CMAKE_CUDA_COMPILER} ${GPU_RTC_DEFINES} ${GPU_RTC_INCLUDES} -std=c++${CMAKE_CUDA_STANDARD} -D__CUDA_ARCH__=${RTC_CUDA_ARCH} -Wno-deprecated-gpu-targets -D__CUDACC__ -x c++ -E -Xcompiler "-nostdinc -P" ${GPU_RTC_SRC} >> ${GPU_RTC_BIN}.src - DEPENDS ${GPU_RTC_SRC} ${GPUDIR}/Base/GPUStdSystemHeaders.h ${GPUDIR}/Base/cuda/GPUReconstructionCUDAIncludesSystem.h ${GPUDIR}/Base/GPUStdSystemHeaders.h + DEPENDS ${GPU_RTC_SRC} ${GPUDIR}/Base/GPUStdSystemHeaders.h ${GPUDIR}/Base/cuda/GPUReconstructionCUDAIncludesSystem.h ${GPUDIR}/Base/GPUStdSystemHeaders.h GPU_PARAM_HEADER_AUTO_ALL DEPFILE ${GPU_RTC_BIN}.src.d COMMAND_EXPAND_LISTS COMMENT "Preparing CUDA RTC source file ${GPU_RTC_BIN}.src" @@ -149,7 +149,7 @@ endif() # Setting target architecture and adding GPU libraries target_link_libraries(${targetName} PRIVATE cuda cudart) -set_target_cuda_arch(${targetName}) +set_target_gpu_arch("CUDA" ${targetName}) #target_link_options(${targetName} PRIVATE "LINKER:--version-script=${CMAKE_CURRENT_SOURCE_DIR}/version_script.ld") #set_target_properties(${targetName} PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/version_script.ld) diff --git a/GPU/GPUTracking/Base/hip/CMakeLists.txt b/GPU/GPUTracking/Base/hip/CMakeLists.txt index 501509d8dfcf6..d148e376abca9 100644 --- a/GPU/GPUTracking/Base/hip/CMakeLists.txt +++ b/GPU/GPUTracking/Base/hip/CMakeLists.txt @@ -125,7 +125,7 @@ add_custom_command( COMMAND cat ${GPUDIR}/Base/hip/GPUReconstructionHIPIncludesSystem.h | grep -v GPUStdSystemHeaders.h >> ${GPU_RTC_BIN}.src COMMAND cat ${GPUDIR}/Base/GPUStdSystemHeaders.h >> ${GPU_RTC_BIN}.src COMMAND ${CMAKE_HIP_COMPILER} ${GPU_RTC_DEFINES} ${GPU_RTC_INCLUDES} -std=c++${CMAKE_HIP_STANDARD} -D__HIPCC__ -D__HIP_DEVICE_COMPILE__ -x c++ -nostdinc -E -P ${GPU_RTC_SRC} -MD -MT ${GPU_RTC_BIN}.src -MF ${GPU_RTC_BIN}.src.d >> ${GPU_RTC_BIN}.src - DEPENDS ${GPU_RTC_SRC} ${GPUDIR}/Base/GPUStdSystemHeaders.h ${GPUDIR}/Base/hip/GPUReconstructionHIPIncludesSystem.h ${GPUDIR}/Base/GPUStdSystemHeaders.h ${MODULE}_HIPIFIED + DEPENDS ${GPU_RTC_SRC} ${GPUDIR}/Base/GPUStdSystemHeaders.h ${GPUDIR}/Base/hip/GPUReconstructionHIPIncludesSystem.h ${GPUDIR}/Base/GPUStdSystemHeaders.h ${MODULE}_HIPIFIED GPU_PARAM_HEADER_AUTO_ALL DEPFILE ${GPU_RTC_BIN}.src.d COMMAND_EXPAND_LISTS COMMENT "Preparing HIP RTC source file ${GPU_RTC_BIN}.src" @@ -219,7 +219,7 @@ endif() # Setting target architecture and adding GPU libraries target_link_libraries(${targetName} PRIVATE hip::host hip::device hip::hipcub roc::rocthrust) -set_target_hip_arch(${targetName}) +set_target_gpu_arch("HIP" ${targetName}) target_link_libraries(${MODULE}_CXX PRIVATE TBB::tbb) diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index 9e9344108ccfb..a2d91b6ed4c5e 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -107,6 +107,12 @@ set(SRCS_NO_H SectorTracker/GPUTPCTrackerDump.cxx Global/GPUChainTrackingDebugAndProfiling.cxx Global/GPUChainTrackingIO.cxx) +set(ON_THE_FLY_DIR ${CMAKE_CURRENT_BINARY_DIR}/include_gpu_onthefly) +file(MAKE_DIRECTORY ${ON_THE_FLY_DIR}) +include(cmake/generateGPUParamHeader.cmake) +set(GPU_DEFAULT_PARAMS_HEADER ${ON_THE_FLY_DIR}/GPUDefParametersDefaults.h) +generate_gpu_param_header("AUTO" ${GPU_DEFAULT_PARAMS_HEADER}) # generate header with default GPU parameters, arch selected by CMake variables + set(HDRS_INSTALL ${HDRS_CINT_O2} ${HDRS_CINT_DATATYPES} @@ -135,9 +141,9 @@ set(HDRS_INSTALL DataTypes/GPUO2ExternalUser.h Debug/GPUROOTDump.h Definitions/GPUDefConstantsAndSettings.h + ${GPU_DEFAULT_PARAMS_HEADER} Definitions/GPUDefParametersWrapper.h Definitions/GPUDefParametersConstants.h - Definitions/GPUDefParametersDefaults.h Definitions/GPUDef.h Definitions/GPUDefMacros.h Definitions/GPULogging.h @@ -239,8 +245,6 @@ set(TEMPLATE_HEADER_LIST Base/GPUReconstructionKernelList.template.h Definitions/GPUDefParametersLoad.template.inc) set(GENERATED_HEADERS_LIST "") -set(ON_THE_FLY_DIR ${CMAKE_CURRENT_BINARY_DIR}/include_gpu_onthefly) -file(MAKE_DIRECTORY ${ON_THE_FLY_DIR}) foreach(TEMPLATE_FILE ${TEMPLATE_HEADER_LIST}) get_filename_component(OUTPUT_FILE_NAME ${TEMPLATE_FILE} NAME) string(REPLACE ".template" "" OUTPUT_FILE_NAME ${OUTPUT_FILE_NAME}) @@ -286,6 +290,7 @@ set(HDRS_CINT_DATATYPES ${HDRS_CINT_DATATYPES} ${HDRS_TMP}) unset(HDRS_TMP) set(INCDIRS + ${ON_THE_FLY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/Definitions ${CMAKE_CURRENT_SOURCE_DIR}/DataTypes @@ -302,14 +307,14 @@ set(INCDIRS ${CMAKE_CURRENT_SOURCE_DIR}/Refit ${CMAKE_CURRENT_SOURCE_DIR}/Debug ${CMAKE_CURRENT_SOURCE_DIR}/DataCompression - ${CMAKE_CURRENT_SOURCE_DIR}/TPCClusterFinder - ${ON_THE_FLY_DIR}) + ${CMAKE_CURRENT_SOURCE_DIR}/TPCClusterFinder) # Main CMake part for O2 if(ALIGPU_BUILD_TYPE STREQUAL "O2") o2_add_library(GPUDataTypes TARGETVARNAME targetName PUBLIC_INCLUDE_DIRECTORIES . + ${ON_THE_FLY_DIR} Definitions DataTypes PUBLIC_LINK_LIBRARIES O2::GPUUtils @@ -409,15 +414,17 @@ set(GPU_CONST_PARAM_ARCHITECTUES AMPERE TURING VEGA MI100) set(GPU_CONST_PARAM_FILES "") foreach(GPU_ARCH ${GPU_CONST_PARAM_ARCHITECTUES}) set(PARAMFILE ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch/gpu_const_param_${GPU_ARCH}.par) + set(GPU_ARCH_PARAMS_HEADER ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch/GPUDefParametersDefaults_${GPU_ARCH}.h) + generate_gpu_param_header(${GPU_ARCH} ${GPU_ARCH_PARAMS_HEADER}) add_custom_command( OUTPUT ${PARAMFILE} COMMAND bash -c - "echo -e '#define GPUCA_GPUTYPE_${GPU_ARCH}\\n#define PARAMETER_FILE \"GPUDefParametersDefaults.h\"\\ngInterpreter->AddIncludePath(\"${CMAKE_CURRENT_SOURCE_DIR}/Definitions\");\\ngInterpreter->AddIncludePath(\"${ON_THE_FLY_DIR}\");\\n.x ${CMAKE_CURRENT_SOURCE_DIR}/Standalone/tools/dumpGPUDefParam.C(\"${PARAMFILE}\")\\n.q\\n'" + "echo -e '#define GPUCA_GPUTYPE_${GPU_ARCH}\\n#define PARAMETER_FILE \"${GPU_ARCH_PARAMS_HEADER}\"\\ngInterpreter->AddIncludePath(\"${CMAKE_CURRENT_SOURCE_DIR}/Definitions\");\\ngInterpreter->AddIncludePath(\"${ON_THE_FLY_DIR}\");\\n.x ${CMAKE_CURRENT_SOURCE_DIR}/Standalone/tools/dumpGPUDefParam.C(\"${PARAMFILE}\")\\n.q\\n'" | root -l -b > /dev/null VERBATIM WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch MAIN_DEPENDENCY Standalone/tools/dumpGPUDefParam.C - DEPENDS Definitions/GPUDefParametersDefaults.h + DEPENDS ${GPU_ARCH_PARAMS_HEADER} ${ON_THE_FLY_DIR}/GPUDefParametersLoadPrepare.h ${ON_THE_FLY_DIR}/GPUDefParametersLoad.inc COMMENT "Generating GPU parameter set for architecture ${GPU_ARCH}") diff --git a/GPU/GPUTracking/Definitions/.clang-format-ignore b/GPU/GPUTracking/Definitions/.clang-format-ignore new file mode 100644 index 0000000000000..5ffee2498bd7e --- /dev/null +++ b/GPU/GPUTracking/Definitions/.clang-format-ignore @@ -0,0 +1 @@ +GPUParameters.json diff --git a/GPU/GPUTracking/Definitions/GPUDefParametersDefaults.h b/GPU/GPUTracking/Definitions/GPUDefParametersDefaults.h deleted file mode 100644 index 1be881ee6323e..0000000000000 --- a/GPU/GPUTracking/Definitions/GPUDefParametersDefaults.h +++ /dev/null @@ -1,589 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file GPUDefParametersDefaults.h -/// \author David Rohr - -// This file contains compile-time constants affecting the GPU performance. - -#if !defined(GPUDEFPARAMETERSDEFAULTS_H) -#define GPUDEFPARAMETERSDEFAULTS_H -// clang-format off - -// Launch bound definition, 3 optional parameters: maxThreads per block, minBlocks per multiprocessor, force number of blocks (not passed to compiler as launch bounds) - -// GPU Run Configuration -#if defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS) // Avoid including for RTC generation besides normal include protection. - // GPU-architecture-dependent default settings - #if defined(GPUCA_GPUTYPE_MI100) - #define GPUCA_WARP_SIZE 64 - #define GPUCA_PAR_AMD_EUS_PER_CU 4 - #define GPUCA_THREAD_COUNT_DEFAULT 256 - #define GPUCA_LB_GPUTPCCreateTrackingData 256, 7 - #define GPUCA_LB_GPUTPCStartHitsSorter 1024, 5 - #define GPUCA_LB_GPUTPCStartHitsFinder 1024, 2 - #define GPUCA_LB_GPUTPCTrackletConstructor 768, 8 - #define GPUCA_LB_GPUTPCTrackletSelector 384, 5 - #define GPUCA_LB_GPUTPCNeighboursFinder 192, 8 - #define GPUCA_LB_GPUTPCNeighboursCleaner 128, 5 - #define GPUCA_LB_GPUTPCExtrapolationTracking 256, 7 - #define GPUCA_LB_GPUTPCCFDecodeZS 64, 4 - #define GPUCA_LB_GPUTPCCFDecodeZSLink GPUCA_WARP_SIZE - #define GPUCA_LB_GPUTPCCFDecodeZSDenseLink GPUCA_WARP_SIZE, 4 - #define GPUCA_LB_GPUTPCCFGather 1024, 5 - #define GPUCA_LB_GPUTPCGMMergerTrackFit 192, 2 - #define GPUCA_LB_GPUTPCGMMergerFollowLoopers 256, 5 - #define GPUCA_LB_GPUTPCGMMergerSectorRefit 64, 4 - #define GPUCA_LB_GPUTPCGMMergerUnpackResetIds 256 - #define GPUCA_LB_GPUTPCGMMergerUnpackGlobal 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step0 512 - #define GPUCA_LB_GPUTPCGMMergerResolve_step1 512 - #define GPUCA_LB_GPUTPCGMMergerResolve_step2 512 - #define GPUCA_LB_GPUTPCGMMergerResolve_step3 512 - #define GPUCA_LB_GPUTPCGMMergerResolve_step4 512 - #define GPUCA_LB_GPUTPCGMMergerClearLinks 256 - #define GPUCA_LB_GPUTPCGMMergerMergeWithinPrepare 256 - #define GPUCA_LB_GPUTPCGMMergerMergeSectorsPrepare 256 - #define GPUCA_LB_GPUTPCGMMergerMergeBorders_step0 512 - #define GPUCA_LB_GPUTPCGMMergerMergeBorders_step2 512 - #define GPUCA_LB_GPUTPCGMMergerMergeCE 512 - #define GPUCA_LB_GPUTPCGMMergerLinkExtrapolatedTracks 256 - #define GPUCA_LB_GPUTPCGMMergerCollect 768, 1 - #define GPUCA_LB_GPUTPCGMMergerSortTracksPrepare 256 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step0 256 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step1 256 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step2 256 - #define GPUCA_LB_GPUTPCGMMergerFinalize_0 256 - #define GPUCA_LB_GPUTPCGMMergerFinalize_1 256 - #define GPUCA_LB_GPUTPCGMMergerFinalize_2 256 - #define GPUCA_LB_GPUTPCCompressionKernels_step0attached 128, 1 - #define GPUCA_LB_GPUTPCCompressionKernels_step1unattached 512, 2 - #define GPUCA_LB_GPUTPCDecompressionKernels_step0attached 128, 2 - #define GPUCA_LB_GPUTPCDecompressionKernels_step1unattached 64, 2 - #define GPUCA_LB_GPUTPCCFCheckPadBaseline 576, 2 - #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillIndexMap 512 - #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillFromDigits 512 - #define GPUCA_LB_GPUTPCCFChargeMapFiller_findFragmentStart 512 - #define GPUCA_LB_GPUTPCCFPeakFinder 512, 9 - #define GPUCA_LB_GPUTPCCFNoiseSuppression 512 - #define GPUCA_LB_GPUTPCCFDeconvolution 512, 5 - #define GPUCA_LB_GPUTPCCFClusterizer 448, 3 - #define GPUCA_LB_COMPRESSION_GATHER 1024 - #define GPUCA_PAR_NEIGHBOURS_FINDER_MAX_NNEIGHUP 10 - #define PAR_NEIGHBOURS_FINDER_UNROLL_GLOBAL 4 - #define GPUCA_PAR_NEIGHBOURS_FINDER_UNROLL_SHARED 0 - #define GPUCA_PAR_TRACKLET_SELECTOR_HITS_REG_SIZE 9 - #define GPUCA_PAR_ALTERNATE_BORDER_SORT 1 - #define GPUCA_PAR_SORT_BEFORE_FIT 1 - #define GPUCA_PAR_NO_ATOMIC_PRECHECK 1 - #define GPUCA_PAR_DEDX_STORAGE_TYPE uint16_t - #define GPUCA_PAR_MERGER_INTERPOLATION_ERROR_TYPE half - #define GPUCA_PAR_COMP_GATHER_KERNEL 4 - #define GPUCA_PAR_COMP_GATHER_MODE 3 - #elif defined(GPUCA_GPUTYPE_VEGA) - #define GPUCA_WARP_SIZE 64 - #define GPUCA_PAR_AMD_EUS_PER_CU 4 - #define GPUCA_THREAD_COUNT_DEFAULT 256 - #define GPUCA_LB_GPUTPCCreateTrackingData 192, 2 - #define GPUCA_LB_GPUTPCStartHitsSorter 512, 7 - #define GPUCA_LB_GPUTPCStartHitsFinder 1024, 7 - #define GPUCA_LB_GPUTPCTrackletConstructor 512, 10 - #define GPUCA_LB_GPUTPCTrackletSelector 192, 10 - #define GPUCA_LB_GPUTPCNeighboursFinder 960, 8 - #define GPUCA_LB_GPUTPCNeighboursCleaner 384, 9 - #define GPUCA_LB_GPUTPCExtrapolationTracking 256, 2 - #define GPUCA_LB_GPUTPCCFDecodeZS 64, 1 - #define GPUCA_LB_GPUTPCCFDecodeZSLink GPUCA_WARP_SIZE - #define GPUCA_LB_GPUTPCCFDecodeZSDenseLink GPUCA_WARP_SIZE, 14 - #define GPUCA_LB_GPUTPCCFGather 1024, 1 - #define GPUCA_LB_GPUTPCGMMergerTrackFit 64, 7 - #define GPUCA_LB_GPUTPCGMMergerFollowLoopers 256, 4 - #define GPUCA_LB_GPUTPCGMMergerSectorRefit 256, 2 - #define GPUCA_LB_GPUTPCGMMergerUnpackResetIds 256 - #define GPUCA_LB_GPUTPCGMMergerUnpackGlobal 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step0 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step1 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step2 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step3 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step4 256 - #define GPUCA_LB_GPUTPCGMMergerClearLinks 256 - #define GPUCA_LB_GPUTPCGMMergerMergeWithinPrepare 256 - #define GPUCA_LB_GPUTPCGMMergerMergeSectorsPrepare 256 - #define GPUCA_LB_GPUTPCGMMergerMergeBorders_step0 256 - #define GPUCA_LB_GPUTPCGMMergerMergeBorders_step2 256 - #define GPUCA_LB_GPUTPCGMMergerMergeCE 256 - #define GPUCA_LB_GPUTPCGMMergerLinkExtrapolatedTracks 256 - #define GPUCA_LB_GPUTPCGMMergerCollect 1024, 1 - #define GPUCA_LB_GPUTPCGMMergerSortTracksPrepare 256 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step0 256 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step1 256 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step2 256 - #define GPUCA_LB_GPUTPCGMMergerFinalize_0 256 - #define GPUCA_LB_GPUTPCGMMergerFinalize_1 256 - #define GPUCA_LB_GPUTPCGMMergerFinalize_2 256 - #define GPUCA_LB_GPUTPCCompressionKernels_step0attached 64, 2 - #define GPUCA_LB_GPUTPCCompressionKernels_step1unattached 512, 2 - #define GPUCA_LB_GPUTPCDecompressionKernels_step0attached 128, 2 - #define GPUCA_LB_GPUTPCDecompressionKernels_step1unattached 64, 2 - #define GPUCA_LB_GPUTPCCFCheckPadBaseline 576, 2 - #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillIndexMap 512 - #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillFromDigits 512 - #define GPUCA_LB_GPUTPCCFChargeMapFiller_findFragmentStart 512 - #define GPUCA_LB_GPUTPCCFPeakFinder 512, 4 - #define GPUCA_LB_GPUTPCCFNoiseSuppression 512 - #define GPUCA_LB_GPUTPCCFDeconvolution 512, 5 - #define GPUCA_LB_GPUTPCCFClusterizer 512, 2 - #define GPUCA_LB_COMPRESSION_GATHER 1024 - #define GPUCA_PAR_NEIGHBOURS_FINDER_MAX_NNEIGHUP 4 - #define GPUCA_PAR_NEIGHBOURS_FINDER_UNROLL_GLOBAL 2 - #define GPUCA_PAR_NEIGHBOURS_FINDER_UNROLL_SHARED 0 - #define GPUCA_PAR_TRACKLET_SELECTOR_HITS_REG_SIZE 27 - #define GPUCA_PAR_ALTERNATE_BORDER_SORT 1 - #define GPUCA_PAR_SORT_BEFORE_FIT 1 - #define GPUCA_PAR_NO_ATOMIC_PRECHECK 1 - #define GPUCA_PAR_DEDX_STORAGE_TYPE uint16_t - #define GPUCA_PAR_MERGER_INTERPOLATION_ERROR_TYPE half - #define GPUCA_PAR_COMP_GATHER_KERNEL 4 - #define GPUCA_PAR_COMP_GATHER_MODE 3 - #elif defined(GPUCA_GPUTYPE_AMPERE) - #define GPUCA_WARP_SIZE 32 - #define GPUCA_THREAD_COUNT_DEFAULT 512 - #define GPUCA_LB_GPUTPCCreateTrackingData 384 - #define GPUCA_LB_GPUTPCStartHitsSorter 512, 1 - #define GPUCA_LB_GPUTPCStartHitsFinder 512 - #define GPUCA_LB_GPUTPCTrackletConstructor 256, 2 // best single-kernel: 128, 4 - #define GPUCA_LB_GPUTPCTrackletSelector 192, 3 // best single-kernel: 128, 4 - #define GPUCA_LB_GPUTPCNeighboursFinder 640, 1 // best single-kernel: 768, 1 - #define GPUCA_LB_GPUTPCNeighboursCleaner 512 - #define GPUCA_LB_GPUTPCExtrapolationTracking 128, 4 - #define GPUCA_LB_GPUTPCCFDecodeZS 64, 10 - #define GPUCA_LB_GPUTPCCFDecodeZSLink GPUCA_WARP_SIZE - #define GPUCA_LB_GPUTPCCFDecodeZSDenseLink GPUCA_WARP_SIZE - #define GPUCA_LB_GPUTPCCFGather 1024, 1 - #define GPUCA_LB_GPUTPCGMMergerTrackFit 64, 4 - #define GPUCA_LB_GPUTPCGMMergerFollowLoopers 64, 12 - #define GPUCA_LB_GPUTPCGMMergerSectorRefit 32, 6 - #define GPUCA_LB_GPUTPCGMMergerUnpackResetIds 256 - #define GPUCA_LB_GPUTPCGMMergerUnpackGlobal 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step0 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step1 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step2 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step3 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step4 256, 4 - #define GPUCA_LB_GPUTPCGMMergerClearLinks 256 - #define GPUCA_LB_GPUTPCGMMergerMergeWithinPrepare 256 - #define GPUCA_LB_GPUTPCGMMergerMergeSectorsPrepare 256, 2 - #define GPUCA_LB_GPUTPCGMMergerMergeBorders_step0 192 - #define GPUCA_LB_GPUTPCGMMergerMergeBorders_step2 64, 2 - #define GPUCA_LB_GPUTPCGMMergerMergeCE 256 - #define GPUCA_LB_GPUTPCGMMergerLinkExtrapolatedTracks 256 - #define GPUCA_LB_GPUTPCGMMergerCollect 256, 2 - #define GPUCA_LB_GPUTPCGMMergerSortTracksPrepare 256 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step0 256 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step1 256 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step2 256 - #define GPUCA_LB_GPUTPCGMMergerFinalize_0 256 - #define GPUCA_LB_GPUTPCGMMergerFinalize_1 256 - #define GPUCA_LB_GPUTPCGMMergerFinalize_2 256 - #define GPUCA_LB_GPUTPCCompressionKernels_step0attached 64, 2 - #define GPUCA_LB_GPUTPCCompressionKernels_step1unattached 512, 3 - #define GPUCA_LB_GPUTPCDecompressionKernels_step0attached 32, 1 - #define GPUCA_LB_GPUTPCDecompressionKernels_step1unattached 32, 1 - #define GPUCA_LB_GPUTPCCFCheckPadBaseline 576,2 - #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillIndexMap 448 - #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillFromDigits 448 - #define GPUCA_LB_GPUTPCCFChargeMapFiller_findFragmentStart 448 - #define GPUCA_LB_GPUTPCCFPeakFinder 128 - #define GPUCA_LB_GPUTPCCFNoiseSuppression 448 - #define GPUCA_LB_GPUTPCCFDeconvolution 384 - #define GPUCA_LB_GPUTPCCFClusterizer 448 - #define GPUCA_LB_COMPRESSION_GATHER 1024 - #define GPUCA_PAR_NEIGHBOURS_FINDER_MAX_NNEIGHUP 4 - #define GPUCA_PAR_TRACKLET_SELECTOR_HITS_REG_SIZE 20 - #define GPUCA_PAR_ALTERNATE_BORDER_SORT 1 - #define GPUCA_PAR_SORT_BEFORE_FIT 1 - #define GPUCA_PAR_NO_ATOMIC_PRECHECK 1 - #define GPUCA_PAR_DEDX_STORAGE_TYPE uint16_t - #define GPUCA_PAR_MERGER_INTERPOLATION_ERROR_TYPE half - #define GPUCA_PAR_COMP_GATHER_KERNEL 4 - #define GPUCA_PAR_COMP_GATHER_MODE 3 - #elif defined(GPUCA_GPUTYPE_TURING) - #define GPUCA_WARP_SIZE 32 - #define GPUCA_THREAD_COUNT_DEFAULT 512 - #define GPUCA_LB_GPUTPCCreateTrackingData 256 - #define GPUCA_LB_GPUTPCStartHitsSorter 512, 1 - #define GPUCA_LB_GPUTPCStartHitsFinder 512 - #define GPUCA_LB_GPUTPCTrackletConstructor 256, 2 - #define GPUCA_LB_GPUTPCTrackletSelector 192, 3 - #define GPUCA_LB_GPUTPCNeighboursFinder 640, 1 - #define GPUCA_LB_GPUTPCNeighboursCleaner 512 - #define GPUCA_LB_GPUTPCExtrapolationTracking 192, 2 - #define GPUCA_LB_GPUTPCCFDecodeZS 64, 8 - #define GPUCA_LB_GPUTPCCFDecodeZSLink GPUCA_WARP_SIZE - #define GPUCA_LB_GPUTPCCFDecodeZSDenseLink GPUCA_WARP_SIZE - #define GPUCA_LB_GPUTPCCFGather 1024, 1 - #define GPUCA_LB_GPUTPCGMMergerTrackFit 32, 8 - #define GPUCA_LB_GPUTPCGMMergerFollowLoopers 128, 4 - #define GPUCA_LB_GPUTPCGMMergerSectorRefit 64, 5 - #define GPUCA_LB_GPUTPCGMMergerUnpackResetIds 256 - #define GPUCA_LB_GPUTPCGMMergerUnpackGlobal 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step0 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step1 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step2 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step3 256 - #define GPUCA_LB_GPUTPCGMMergerResolve_step4 256, 4 - #define GPUCA_LB_GPUTPCGMMergerClearLinks 256 - #define GPUCA_LB_GPUTPCGMMergerMergeWithinPrepare 256 - #define GPUCA_LB_GPUTPCGMMergerMergeSectorsPrepare 256, 2 - #define GPUCA_LB_GPUTPCGMMergerMergeBorders_step0 192 - #define GPUCA_LB_GPUTPCGMMergerMergeBorders_step2 256 - #define GPUCA_LB_GPUTPCGMMergerMergeCE 256 - #define GPUCA_LB_GPUTPCGMMergerLinkExtrapolatedTracks 256 - #define GPUCA_LB_GPUTPCGMMergerCollect 128, 2 - #define GPUCA_LB_GPUTPCGMMergerSortTracksPrepare 256 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step0 256 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step1 256 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step2 256 - #define GPUCA_LB_GPUTPCGMMergerFinalize_0 256 - #define GPUCA_LB_GPUTPCGMMergerFinalize_1 256 - #define GPUCA_LB_GPUTPCGMMergerFinalize_2 256 - #define GPUCA_LB_GPUTPCCompressionKernels_step0attached 128 - #define GPUCA_LB_GPUTPCCompressionKernels_step1unattached 512, 2 - #define GPUCA_LB_GPUTPCDecompressionKernels_step0attached 32, 1 - #define GPUCA_LB_GPUTPCDecompressionKernels_step1unattached 32, 1 - #define GPUCA_LB_COMPRESSION_GATHER 1024 - #define GPUCA_PAR_NEIGHBOURS_FINDER_MAX_NNEIGHUP 4 - #define GPUCA_PAR_TRACKLET_SELECTOR_HITS_REG_SIZE 20 - #define GPUCA_PAR_ALTERNATE_BORDER_SORT 1 - #define GPUCA_PAR_SORT_BEFORE_FIT 1 - #define GPUCA_PAR_NO_ATOMIC_PRECHECK 1 - #define GPUCA_PAR_COMP_GATHER_KERNEL 4 - #define GPUCA_PAR_COMP_GATHER_MODE 3 - #define GPUCA_PAR_DEDX_STORAGE_TYPE uint16_t - #define GPUCA_PAR_MERGER_INTERPOLATION_ERROR_TYPE half - #elif defined(GPUCA_GPUTYPE_OPENCL) - #else - #error GPU TYPE NOT SET - #endif - - // Default settings for GPU, if not already set for selected GPU type - #ifndef GPUCA_WARP_SIZE - #define GPUCA_WARP_SIZE 32 - #endif - #ifndef GPUCA_PAR_AMD_EUS_PER_CU - #define GPUCA_PAR_AMD_EUS_PER_CU 0 - #endif - #ifndef GPUCA_THREAD_COUNT_DEFAULT - #define GPUCA_THREAD_COUNT_DEFAULT 256 - #endif - #ifndef GPUCA_LB_GPUTPCCreateTrackingData - #define GPUCA_LB_GPUTPCCreateTrackingData 256 - #endif - #ifndef GPUCA_LB_GPUTPCTrackletConstructor - #define GPUCA_LB_GPUTPCTrackletConstructor 256 - #endif - #ifndef GPUCA_LB_GPUTPCTrackletSelector - #define GPUCA_LB_GPUTPCTrackletSelector 256 - #endif - #ifndef GPUCA_LB_GPUTPCNeighboursFinder - #define GPUCA_LB_GPUTPCNeighboursFinder 256 - #endif - #ifndef GPUCA_LB_GPUTPCNeighboursCleaner - #define GPUCA_LB_GPUTPCNeighboursCleaner 256 - #endif - #ifndef GPUCA_LB_GPUTPCExtrapolationTracking - #define GPUCA_LB_GPUTPCExtrapolationTracking 256 - #endif - #ifndef GPUCA_LB_GPUTRDTrackerKernels_gpuVersion - #define GPUCA_LB_GPUTRDTrackerKernels_gpuVersion 512 - #endif - #ifndef GPUCA_LB_GPUTPCCreateOccupancyMap_fill - #define GPUCA_LB_GPUTPCCreateOccupancyMap_fill 256 - #endif - #ifndef GPUCA_LB_GPUTPCCreateOccupancyMap_fold - #define GPUCA_LB_GPUTPCCreateOccupancyMap_fold 256 - #endif - #ifndef GPUCA_LB_GPUTRDTrackerKernels_o2Version - #define GPUCA_LB_GPUTRDTrackerKernels_o2Version 512 - #endif - #ifndef GPUCA_LB_GPUTPCCompressionKernels_step0attached - #define GPUCA_LB_GPUTPCCompressionKernels_step0attached 256 - #endif - #ifndef GPUCA_LB_GPUTPCCompressionKernels_step1unattached - #define GPUCA_LB_GPUTPCCompressionKernels_step1unattached 256 - #endif - #ifndef GPUCA_LB_GPUTPCDecompressionKernels_step0attached - #define GPUCA_LB_GPUTPCDecompressionKernels_step0attached 256 - #endif - #ifndef GPUCA_LB_GPUTPCDecompressionKernels_step1unattached - #define GPUCA_LB_GPUTPCDecompressionKernels_step1unattached 256 - #endif - #ifndef GPUCA_LB_GPUTPCDecompressionUtilKernels_sortPerSectorRow - #define GPUCA_LB_GPUTPCDecompressionUtilKernels_sortPerSectorRow 256 - #endif - #ifndef GPUCA_LB_GPUTPCDecompressionUtilKernels_countFilteredClusters - #define GPUCA_LB_GPUTPCDecompressionUtilKernels_countFilteredClusters 256 - #endif - #ifndef GPUCA_LB_GPUTPCDecompressionUtilKernels_storeFilteredClusters - #define GPUCA_LB_GPUTPCDecompressionUtilKernels_storeFilteredClusters 256 - #endif - #ifndef GPUCA_LB_GPUTPCCFDecodeZS - #define GPUCA_LB_GPUTPCCFDecodeZS 128, 4 - #endif - #ifndef GPUCA_LB_GPUTPCCFDecodeZSLink - #define GPUCA_LB_GPUTPCCFDecodeZSLink GPUCA_WARP_SIZE - #endif - #ifndef GPUCA_LB_GPUTPCCFDecodeZSDenseLink - #define GPUCA_LB_GPUTPCCFDecodeZSDenseLink GPUCA_WARP_SIZE - #endif - #ifndef GPUCA_LB_GPUTPCCFGather - #define GPUCA_LB_GPUTPCCFGather 1024, 1 - #endif - #ifndef GPUCA_LB_COMPRESSION_GATHER - #define GPUCA_LB_COMPRESSION_GATHER 1024 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerTrackFit - #define GPUCA_LB_GPUTPCGMMergerTrackFit 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerFollowLoopers - #define GPUCA_LB_GPUTPCGMMergerFollowLoopers 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerSectorRefit - #define GPUCA_LB_GPUTPCGMMergerSectorRefit 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerUnpackResetIds - #define GPUCA_LB_GPUTPCGMMergerUnpackResetIds 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerUnpackGlobal - #define GPUCA_LB_GPUTPCGMMergerUnpackGlobal 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerResolve_step0 - #define GPUCA_LB_GPUTPCGMMergerResolve_step0 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerResolve_step1 - #define GPUCA_LB_GPUTPCGMMergerResolve_step1 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerResolve_step2 - #define GPUCA_LB_GPUTPCGMMergerResolve_step2 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerResolve_step3 - #define GPUCA_LB_GPUTPCGMMergerResolve_step3 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerResolve_step4 - #define GPUCA_LB_GPUTPCGMMergerResolve_step4 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerClearLinks - #define GPUCA_LB_GPUTPCGMMergerClearLinks 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerMergeWithinPrepare - #define GPUCA_LB_GPUTPCGMMergerMergeWithinPrepare 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerMergeSectorsPrepare - #define GPUCA_LB_GPUTPCGMMergerMergeSectorsPrepare 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerMergeBorders_step0 - #define GPUCA_LB_GPUTPCGMMergerMergeBorders_step0 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerMergeBorders_step2 - #define GPUCA_LB_GPUTPCGMMergerMergeBorders_step2 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerMergeCE - #define GPUCA_LB_GPUTPCGMMergerMergeCE 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerLinkExtrapolatedTracks - #define GPUCA_LB_GPUTPCGMMergerLinkExtrapolatedTracks 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerCollect - #define GPUCA_LB_GPUTPCGMMergerCollect 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerSortTracksPrepare - #define GPUCA_LB_GPUTPCGMMergerSortTracksPrepare 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerPrepareForFit_step0 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step0 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerPrepareForFit_step1 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step1 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerPrepareForFit_step2 - #define GPUCA_LB_GPUTPCGMMergerPrepareForFit_step2 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerFinalize_step0 - #define GPUCA_LB_GPUTPCGMMergerFinalize_step0 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerFinalize_step1 - #define GPUCA_LB_GPUTPCGMMergerFinalize_step1 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerFinalize_step2 - #define GPUCA_LB_GPUTPCGMMergerFinalize_step2 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerMergeLoopers_step0 - #define GPUCA_LB_GPUTPCGMMergerMergeLoopers_step0 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerMergeLoopers_step1 - #define GPUCA_LB_GPUTPCGMMergerMergeLoopers_step1 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMMergerMergeLoopers_step2 - #define GPUCA_LB_GPUTPCGMMergerMergeLoopers_step2 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMO2Output_prepare - #define GPUCA_LB_GPUTPCGMO2Output_prepare 256 - #endif - #ifndef GPUCA_LB_GPUTPCGMO2Output_output - #define GPUCA_LB_GPUTPCGMO2Output_output 256 - #endif - #ifndef GPUCA_LB_GPUTPCStartHitsFinder - #define GPUCA_LB_GPUTPCStartHitsFinder 256 - #endif - #ifndef GPUCA_LB_GPUTPCStartHitsSorter - #define GPUCA_LB_GPUTPCStartHitsSorter 256 - #endif - #ifndef GPUCA_LB_GPUTPCCFCheckPadBaseline - #define GPUCA_LB_GPUTPCCFCheckPadBaseline 576 - #endif - #ifndef GPUCA_LB_GPUTPCCFChargeMapFiller_fillIndexMap - #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillIndexMap 512 - #endif - #ifndef GPUCA_LB_GPUTPCCFChargeMapFiller_fillFromDigits - #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillFromDigits 512 - #endif - #ifndef GPUCA_LB_GPUTPCCFChargeMapFiller_findFragmentStart - #define GPUCA_LB_GPUTPCCFChargeMapFiller_findFragmentStart 512 - #endif - #ifndef GPUCA_LB_GPUTPCCFPeakFinder - #define GPUCA_LB_GPUTPCCFPeakFinder 512 - #endif - #ifndef GPUCA_LB_GPUTPCCFNoiseSuppression - #define GPUCA_LB_GPUTPCCFNoiseSuppression 512 - #endif - #ifndef GPUCA_LB_GPUTPCCFDeconvolution - #define GPUCA_LB_GPUTPCCFDeconvolution 512 - #endif - #ifndef GPUCA_LB_GPUTPCCFClusterizer - #define GPUCA_LB_GPUTPCCFClusterizer 512 - #endif - #ifndef GPUCA_LB_GPUTPCNNClusterizerKernels - #define GPUCA_LB_GPUTPCNNClusterizerKernels 512 - #endif - #ifndef GPUCA_LB_GPUTrackingRefitKernel_mode0asGPU - #define GPUCA_LB_GPUTrackingRefitKernel_mode0asGPU 256 - #endif - #ifndef GPUCA_LB_GPUTrackingRefitKernel_mode1asTrackParCov - #define GPUCA_LB_GPUTrackingRefitKernel_mode1asTrackParCov 256 - #endif - #ifndef GPUCA_LB_GPUMemClean16 - #define GPUCA_LB_GPUMemClean16 GPUCA_THREAD_COUNT_DEFAULT, 1 - #endif - #ifndef GPUCA_LB_GPUitoa - #define GPUCA_LB_GPUitoa GPUCA_THREAD_COUNT_DEFAULT, 1 - #endif - // These kernel launch-bounds are derrived from one of the constants set above - #define GPUCA_LB_GPUTPCCFNoiseSuppression_noiseSuppression GPUCA_LB_GPUTPCCFNoiseSuppression - #define GPUCA_LB_GPUTPCCFNoiseSuppression_updatePeaks GPUCA_LB_GPUTPCCFNoiseSuppression - - #define GPUCA_LB_GPUTPCNNClusterizerKernels_runCfClusterizer GPUCA_LB_GPUTPCNNClusterizerKernels - #define GPUCA_LB_GPUTPCNNClusterizerKernels_fillInputNNCPU GPUCA_LB_GPUTPCNNClusterizerKernels - #define GPUCA_LB_GPUTPCNNClusterizerKernels_fillInputNNGPU 1024 - #define GPUCA_LB_GPUTPCNNClusterizerKernels_determineClass1Labels GPUCA_LB_GPUTPCNNClusterizerKernels - #define GPUCA_LB_GPUTPCNNClusterizerKernels_determineClass2Labels GPUCA_LB_GPUTPCNNClusterizerKernels - #define GPUCA_LB_GPUTPCNNClusterizerKernels_publishClass1Regression GPUCA_LB_GPUTPCNNClusterizerKernels - #define GPUCA_LB_GPUTPCNNClusterizerKernels_publishClass2Regression GPUCA_LB_GPUTPCNNClusterizerKernels - #define GPUCA_LB_GPUTPCNNClusterizerKernels_publishDeconvolutionFlags GPUCA_LB_GPUTPCNNClusterizerKernels - - #define GPUCA_LB_GPUTPCCFStreamCompaction_scanStart GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE - #define GPUCA_LB_GPUTPCCFStreamCompaction_scanUp GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE - #define GPUCA_LB_GPUTPCCFStreamCompaction_scanTop GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE - #define GPUCA_LB_GPUTPCCFStreamCompaction_scanDown GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE - #define GPUCA_LB_GPUTPCCFStreamCompaction_compactDigits GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE - #define GPUCA_LB_GPUTPCCompressionGatherKernels_unbuffered GPUCA_LB_COMPRESSION_GATHER - #define GPUCA_LB_GPUTPCCompressionGatherKernels_buffered32 GPUCA_LB_COMPRESSION_GATHER - #define GPUCA_LB_GPUTPCCompressionGatherKernels_buffered64 GPUCA_LB_COMPRESSION_GATHER - #define GPUCA_LB_GPUTPCCompressionGatherKernels_buffered128 GPUCA_LB_COMPRESSION_GATHER - #define GPUCA_LB_GPUTPCCompressionGatherKernels_multiBlock GPUCA_LB_COMPRESSION_GATHER - - // Defaults for non-LB parameters - #ifndef GPUCA_PAR_SORT_STARTHITS - #define GPUCA_PAR_SORT_STARTHITS 1 - #endif - #ifndef GPUCA_PAR_NEIGHBOURS_FINDER_MAX_NNEIGHUP - #define GPUCA_PAR_NEIGHBOURS_FINDER_MAX_NNEIGHUP 6 - #endif - #ifndef GPUCA_PAR_NEIGHBOURS_FINDER_UNROLL_GLOBAL - #define GPUCA_PAR_NEIGHBOURS_FINDER_UNROLL_GLOBAL 4 - #endif - #ifndef GPUCA_PAR_NEIGHBOURS_FINDER_UNROLL_SHARED - #define GPUCA_PAR_NEIGHBOURS_FINDER_UNROLL_SHARED 1 - #endif - #ifndef GPUCA_PAR_TRACKLET_SELECTOR_HITS_REG_SIZE - #define GPUCA_PAR_TRACKLET_SELECTOR_HITS_REG_SIZE 12 - #endif - #ifndef GPUCA_PAR_ALTERNATE_BORDER_SORT - #define GPUCA_PAR_ALTERNATE_BORDER_SORT 0 - #endif - #ifndef GPUCA_PAR_SORT_BEFORE_FIT - #define GPUCA_PAR_SORT_BEFORE_FIT 0 - #endif - #ifndef GPUCA_PAR_COMP_GATHER_KERNEL - #define GPUCA_PAR_COMP_GATHER_KERNEL 0 - #endif - #ifndef GPUCA_PAR_COMP_GATHER_MODE - #define GPUCA_PAR_COMP_GATHER_MODE 2 - #endif - #ifndef GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE - #define GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE 512 - #endif -#endif // defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS) - -#ifndef GPUCA_GPUCODE_GENRTC - // Defaults (also for CPU) for non-LB parameters - #ifndef GPUCA_PAR_SORT_STARTHITS - #define GPUCA_PAR_SORT_STARTHITS 0 - #endif - #ifndef GPUCA_PAR_NEIGHBOURS_FINDER_MAX_NNEIGHUP - #define GPUCA_PAR_NEIGHBOURS_FINDER_MAX_NNEIGHUP 0 - #endif - #ifndef GPUCA_PAR_NEIGHBOURS_FINDER_UNROLL_GLOBAL - #define GPUCA_PAR_NEIGHBOURS_FINDER_UNROLL_GLOBAL 0 - #endif - #ifndef GPUCA_PAR_NEIGHBOURS_FINDER_UNROLL_SHARED - #define GPUCA_PAR_NEIGHBOURS_FINDER_UNROLL_SHARED 0 - #endif - #ifndef GPUCA_PAR_TRACKLET_SELECTOR_HITS_REG_SIZE - #define GPUCA_PAR_TRACKLET_SELECTOR_HITS_REG_SIZE 0 - #endif - #ifndef GPUCA_PAR_ALTERNATE_BORDER_SORT - #define GPUCA_PAR_ALTERNATE_BORDER_SORT 0 - #endif - #ifndef GPUCA_PAR_SORT_BEFORE_FIT - #define GPUCA_PAR_SORT_BEFORE_FIT 0 - #endif - #ifndef GPUCA_PAR_COMP_GATHER_KERNEL - #define GPUCA_PAR_COMP_GATHER_KERNEL 0 - #endif - #ifndef GPUCA_PAR_COMP_GATHER_MODE - #define GPUCA_PAR_COMP_GATHER_MODE 0 - #endif - #ifndef GPUCA_PAR_NO_ATOMIC_PRECHECK - #define GPUCA_PAR_NO_ATOMIC_PRECHECK 0 - #endif - #ifndef GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE - #define GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE 0 - #endif - #ifndef GPUCA_PAR_DEDX_STORAGE_TYPE - #define GPUCA_PAR_DEDX_STORAGE_TYPE float - #endif - #ifndef GPUCA_PAR_MERGER_INTERPOLATION_ERROR_TYPE - #define GPUCA_PAR_MERGER_INTERPOLATION_ERROR_TYPE float - #endif -#endif // GPUCA_GPUCODE_GENRTC - -// clang-format on -#endif // GPUDEFPARAMETERSDEFAULTS_H diff --git a/GPU/GPUTracking/Definitions/GPUParameters.json b/GPU/GPUTracking/Definitions/GPUParameters.json new file mode 100644 index 0000000000000..e8f1c24520813 --- /dev/null +++ b/GPU/GPUTracking/Definitions/GPUParameters.json @@ -0,0 +1,582 @@ +{ + "CORE": { + "WARP_SIZE": { + "default": 32, + "MI100": 64, + "VEGA": 64, + "AMPERE": 32, + "TURING": 32 + }, + "THREAD_COUNT_DEFAULT": { + "default": 256, + "MI100": 256, + "VEGA": 256, + "AMPERE": 512, + "TURING": 512 + } + }, + "LB": { + "GPUTPCCreateTrackingData": { + "default": 256, + "MI100": [256, 7], + "VEGA": [192, 2], + "AMPERE": 384, + "TURING": 256 + }, + "GPUTPCTrackletConstructor": { + "default": 256, + "MI100": [768, 8], + "VEGA": [512, 10], + "AMPERE": [256, 2], + "TURING": [256, 2] + }, + "GPUTPCTrackletSelector": { + "default": 256, + "MI100": [384, 5], + "VEGA": [192, 10], + "AMPERE": [192, 3], + "TURING": [192, 3] + }, + "GPUTPCNeighboursFinder": { + "default": 256, + "MI100": [192, 8], + "VEGA": [960, 8], + "AMPERE": [640, 1], + "TURING": [640, 1] + }, + "GPUTPCNeighboursCleaner": { + "default": 256, + "MI100": [128, 5], + "VEGA": [384, 9], + "AMPERE": 512, + "TURING": 512 + }, + "GPUTPCExtrapolationTracking": { + "default": 256, + "MI100": [256, 7], + "VEGA": [256, 2], + "AMPERE": [128, 4], + "TURING": [192, 2] + }, + "GPUTRDTrackerKernels_gpuVersion": { + "default": 512 + }, + "GPUTPCCreateOccupancyMap_fill": { + "default": 256 + }, + "GPUTPCCreateOccupancyMap_fold": { + "default": 256 + }, + "GPUTRDTrackerKernels_o2Version": { + "default": 512 + }, + "GPUTPCCompressionKernels_step0attached": { + "default": 256, + "MI100": [128, 1], + "VEGA": [64, 2], + "AMPERE": [64, 2], + "TURING": 128 + }, + "GPUTPCCompressionKernels_step1unattached": { + "default": 256, + "MI100": [512, 2], + "VEGA": [512, 2], + "AMPERE": [512, 3], + "TURING": [512, 2] + }, + "GPUTPCDecompressionKernels_step0attached": { + "default": 256, + "MI100": [128, 2], + "VEGA": [128, 2], + "AMPERE": [32, 1], + "TURING": [32, 1] + }, + "GPUTPCDecompressionKernels_step1unattached": { + "default": 256, + "MI100": [64, 2], + "VEGA": [64, 2], + "AMPERE": [32, 1], + "TURING": [32, 1] + }, + "GPUTPCDecompressionUtilKernels_sortPerSectorRow": { + "default": 256 + }, + "GPUTPCDecompressionUtilKernels_countFilteredClusters": { + "default": 256 + }, + "GPUTPCDecompressionUtilKernels_storeFilteredClusters": { + "default": 256 + }, + "GPUTPCCFDecodeZS": { + "default": [128, 4], + "MI100": [64, 4], + "VEGA": [64, 1], + "AMPERE": [64, 10], + "TURING": [64, 8] + }, + "GPUTPCCFDecodeZSLink": { + "default": "GPUCA_WARP_SIZE", + "MI100": "GPUCA_WARP_SIZE", + "VEGA": "GPUCA_WARP_SIZE", + "AMPERE": "GPUCA_WARP_SIZE", + "TURING": "GPUCA_WARP_SIZE" + }, + "GPUTPCCFDecodeZSDenseLink": { + "default": "GPUCA_WARP_SIZE", + "MI100": ["GPUCA_WARP_SIZE", 4], + "VEGA": ["GPUCA_WARP_SIZE", 14], + "AMPERE": "GPUCA_WARP_SIZE", + "TURING": "GPUCA_WARP_SIZE" + }, + "GPUTPCCFGather": { + "default": [1024, 1], + "MI100": [1024, 5], + "VEGA": [1024, 1], + "AMPERE": [1024, 1], + "TURING": [1024, 1] + }, + "COMPRESSION_GATHER": { + "default": 1024, + "MI100": 1024, + "VEGA": 1024, + "AMPERE": 1024, + "TURING": 1024 + }, + "GPUTPCGMMergerTrackFit": { + "default": 256, + "MI100": [192, 2], + "VEGA": [64, 7], + "AMPERE": [64, 4], + "TURING": [32, 8] + }, + "GPUTPCGMMergerFollowLoopers": { + "default": 256, + "MI100": [256, 5], + "VEGA": [256, 4], + "AMPERE": [64, 12], + "TURING": [128, 4] + }, + "GPUTPCGMMergerSectorRefit": { + "default": 256, + "MI100": [64, 4], + "VEGA": [256, 2], + "AMPERE": [32, 6], + "TURING": [64, 5] + }, + "GPUTPCGMMergerUnpackResetIds": { + "default": 256, + "MI100": 256, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerUnpackGlobal": { + "default": 256, + "MI100": 256, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerResolve_step0": { + "default": 256, + "MI100": 512, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerResolve_step1": { + "default": 256, + "MI100": 512, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerResolve_step2": { + "default": 256, + "MI100": 512, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerResolve_step3": { + "default": 256, + "MI100": 512, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerResolve_step4": { + "default": 256, + "MI100": 512, + "VEGA": 256, + "AMPERE": [256, 4], + "TURING": [256, 4] + }, + "GPUTPCGMMergerClearLinks": { + "default": 256, + "MI100": 256, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerMergeWithinPrepare": { + "default": 256, + "MI100": 256, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerMergeSectorsPrepare": { + "default": 256, + "MI100": 256, + "VEGA": 256, + "AMPERE": [256, 2], + "TURING": [256, 2] + }, + "GPUTPCGMMergerMergeBorders_step0": { + "default": 256, + "MI100": 512, + "VEGA": 256, + "AMPERE": 192, + "TURING": 192 + }, + "GPUTPCGMMergerMergeBorders_step2": { + "default": 256, + "MI100": 512, + "VEGA": 256, + "AMPERE": [64, 2], + "TURING": 256 + }, + "GPUTPCGMMergerMergeCE": { + "default": 256, + "MI100": 512, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerLinkExtrapolatedTracks": { + "default": 256, + "MI100": 256, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerCollect": { + "default": 256, + "MI100": [768, 1], + "VEGA": [1024, 1], + "AMPERE": [256, 2], + "TURING": [128, 2] + }, + "GPUTPCGMMergerSortTracksPrepare": { + "default": 256, + "MI100": 256, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerPrepareForFit_step0": { + "default": 256, + "MI100": 256, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerPrepareForFit_step1": { + "default": 256, + "MI100": 256, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerPrepareForFit_step2": { + "default": 256, + "MI100": 256, + "VEGA": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerFinalize_step0": { + "default": 256, + "VEGA": 256 + }, + "GPUTPCGMMergerFinalize_step1": { + "default": 256, + "VEGA": 256 + }, + "GPUTPCGMMergerFinalize_step2": { + "default": 256, + "VEGA": 256 + }, + "GPUTPCGMMergerMergeLoopers_step0": { + "default": 256 + }, + "GPUTPCGMMergerMergeLoopers_step1": { + "default": 256 + }, + "GPUTPCGMMergerMergeLoopers_step2": { + "default": 256 + }, + "GPUTPCGMO2Output_prepare": { + "default": 256 + }, + "GPUTPCGMO2Output_output": { + "default": 256 + }, + "GPUTPCStartHitsFinder": { + "default": 256, + "MI100": [1024, 2], + "VEGA": [1024, 7], + "AMPERE": 512, + "TURING": 512 + }, + "GPUTPCStartHitsSorter": { + "default": 256, + "MI100": [1024, 5], + "VEGA": [512, 7], + "AMPERE": [512, 1], + "TURING": [512, 1] + }, + "GPUTPCCFCheckPadBaseline": { + "default": 64, + "MI100": [64, 10], + "VEGA": [64, 2], + "AMPERE": [64, 8] + }, + "GPUTPCCFChargeMapFiller_fillIndexMap": { + "default": 512, + "MI100": 512, + "VEGA": 512, + "AMPERE": 448 + }, + "GPUTPCCFChargeMapFiller_fillFromDigits": { + "default": 512, + "MI100": 512, + "VEGA": 512, + "AMPERE": 448 + }, + "GPUTPCCFChargeMapFiller_findFragmentStart": { + "default": 512, + "MI100": 512, + "VEGA": 512, + "AMPERE": 448 + }, + "GPUTPCCFPeakFinder": { + "default": 512, + "MI100": [512, 9], + "VEGA": [512, 4], + "AMPERE": 128 + }, + "GPUTPCCFNoiseSuppression": { + "default": 512, + "MI100": 512, + "VEGA": 512, + "AMPERE": 448 + }, + "GPUTPCCFDeconvolution": { + "default": 512, + "MI100": [512, 5], + "VEGA": [512, 5], + "AMPERE": 384 + }, + "GPUTPCCFClusterizer": { + "default": 512, + "MI100": [448, 3], + "VEGA": [512, 2], + "AMPERE": 448 + }, + "GPUTPCNNClusterizerKernels": { + "default": 512 + }, + "GPUTrackingRefitKernel_mode0asGPU": { + "default": 256 + }, + "GPUTrackingRefitKernel_mode1asTrackParCov": { + "default": 256 + }, + "GPUMemClean16": { + "default": ["GPUCA_THREAD_COUNT_DEFAULT", 1] + }, + "GPUitoa": { + "default": ["GPUCA_THREAD_COUNT_DEFAULT", 1] + }, + "GPUTPCCFNoiseSuppression_noiseSuppression": { + "default": "GPUCA_LB_GPUTPCCFNoiseSuppression" + }, + "GPUTPCCFNoiseSuppression_updatePeaks": { + "default": "GPUCA_LB_GPUTPCCFNoiseSuppression" + }, + "GPUTPCNNClusterizerKernels_runCfClusterizer": { + "default": "GPUCA_LB_GPUTPCNNClusterizerKernels" + }, + "GPUTPCNNClusterizerKernels_fillInputNNCPU": { + "default": "GPUCA_LB_GPUTPCNNClusterizerKernels" + }, + "GPUTPCNNClusterizerKernels_fillInputNNGPU": { + "default": 1024 + }, + "GPUTPCNNClusterizerKernels_determineClass1Labels": { + "default": "GPUCA_LB_GPUTPCNNClusterizerKernels" + }, + "GPUTPCNNClusterizerKernels_determineClass2Labels": { + "default": "GPUCA_LB_GPUTPCNNClusterizerKernels" + }, + "GPUTPCNNClusterizerKernels_publishClass1Regression": { + "default": "GPUCA_LB_GPUTPCNNClusterizerKernels" + }, + "GPUTPCNNClusterizerKernels_publishClass2Regression": { + "default": "GPUCA_LB_GPUTPCNNClusterizerKernels" + }, + "GPUTPCNNClusterizerKernels_publishDeconvolutionFlags": { + "default": "GPUCA_LB_GPUTPCNNClusterizerKernels" + }, + "GPUTPCCFStreamCompaction_scanStart": { + "default": "GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE" + }, + "GPUTPCCFStreamCompaction_scanUp": { + "default": "GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE" + }, + "GPUTPCCFStreamCompaction_scanTop": { + "default": "GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE" + }, + "GPUTPCCFStreamCompaction_scanDown": { + "default": "GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE" + }, + "GPUTPCCFStreamCompaction_compactDigits": { + "default": "GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE" + }, + "GPUTPCCompressionGatherKernels_unbuffered": { + "default": "GPUCA_LB_COMPRESSION_GATHER" + }, + "GPUTPCCompressionGatherKernels_buffered32": { + "default": "GPUCA_LB_COMPRESSION_GATHER" + }, + "GPUTPCCompressionGatherKernels_buffered64": { + "default": "GPUCA_LB_COMPRESSION_GATHER" + }, + "GPUTPCCompressionGatherKernels_buffered128": { + "default": "GPUCA_LB_COMPRESSION_GATHER" + }, + "GPUTPCCompressionGatherKernels_multiBlock": { + "default": "GPUCA_LB_COMPRESSION_GATHER" + }, + "GPUTPCGMMergerFinalize_0": { + "default": 256, + "MI100": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerFinalize_1": { + "default": 256, + "MI100": 256, + "AMPERE": 256, + "TURING": 256 + }, + "GPUTPCGMMergerFinalize_2": { + "default": 256, + "MI100": 256, + "AMPERE": 256, + "TURING": 256 + } + }, + "PAR": { + "AMD_EUS_PER_CU": { + "default": 0, + "default_cpu": 0, + "MI100": 4, + "VEGA": 4 + }, + "SORT_STARTHITS": { + "default": 1, + "default_cpu": 0 + }, + "NEIGHBOURS_FINDER_MAX_NNEIGHUP": { + "default": 6, + "default_cpu": 0, + "MI100": 10, + "VEGA": 4, + "AMPERE": 4, + "TURING": 4 + }, + "NEIGHBOURS_FINDER_UNROLL_GLOBAL": { + "default": 4, + "default_cpu": 0, + "MI100": 4, + "VEGA": 2 + }, + "NEIGHBOURS_FINDER_UNROLL_SHARED": { + "default": 1, + "default_cpu": 0, + "MI100": 0, + "VEGA": 0 + }, + "TRACKLET_SELECTOR_HITS_REG_SIZE": { + "default": 12, + "default_cpu": 0, + "MI100": 9, + "VEGA": 27, + "AMPERE": 20, + "TURING": 20 + }, + "ALTERNATE_BORDER_SORT": { + "default": 0, + "default_cpu": 0, + "MI100": 1, + "VEGA": 1, + "AMPERE": 1, + "TURING": 1 + }, + "SORT_BEFORE_FIT": { + "default": 0, + "default_cpu": 0, + "MI100": 1, + "VEGA": 1, + "AMPERE": 1, + "TURING": 1 + }, + "NO_ATOMIC_PRECHECK": { + "default": 0, + "default_cpu": 0, + "MI100": 1, + "VEGA": 1, + "AMPERE": 1, + "TURING": 1 + }, + "DEDX_STORAGE_TYPE": { + "default": "float", + "default_cpu": "float", + "MI100": "uint16_t", + "VEGA": "uint16_t", + "AMPERE": "uint16_t", + "TURING": "uint16_t" + }, + "MERGER_INTERPOLATION_ERROR_TYPE": { + "default": "float", + "default_cpu": "float", + "MI100": "half", + "VEGA": "half", + "AMPERE": "half", + "TURING": "half" + }, + "COMP_GATHER_KERNEL": { + "default": 0, + "default_cpu": 0, + "MI100": 4, + "VEGA": 4, + "AMPERE": 4, + "TURING": 4 + }, + "COMP_GATHER_MODE": { + "default": 2, + "default_cpu": 0, + "MI100": 3, + "VEGA": 3, + "AMPERE": 3, + "TURING": 3 + }, + "CF_SCAN_WORKGROUP_SIZE": { + "default": 512, + "default_cpu": 0 + } + } +} diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index 9bfe6feb14d8d..c61056466929e 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -624,7 +624,7 @@ AddSubConfig(GPUSettingsEG, EG) EndConfig() #endif // BeginConfig -//Settings for the O2 workfllow +//Settings for the O2 workflow #if !defined(QCONFIG_PARSER_CXX) && (defined(GPUCA_O2_LIB) || defined(GPUCA_O2_INTERFACE)) BeginSubConfig(GPUSettingsO2, global, configStandalone, "O2", 0, "O2 workflow settings", global) AddOption(solenoidBzNominalGPU, float, -1e6f, "", 0, "Field strength of solenoid Bz in kGaus") diff --git a/GPU/GPUTracking/cmake/generateGPUParamHeader.cmake b/GPU/GPUTracking/cmake/generateGPUParamHeader.cmake new file mode 100644 index 0000000000000..712bf4641b825 --- /dev/null +++ b/GPU/GPUTracking/cmake/generateGPUParamHeader.cmake @@ -0,0 +1,37 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +# file generateGPUParamHeader.cmake +# author Gabriele Cimador + +function(generate_gpu_param_header GPU_ARCH OUT_HEADER) + set(GPU_PARAM_JSON ${CMAKE_SOURCE_DIR}/GPU/GPUTracking/Definitions/GPUParameters.json) + set(TARGET_ARCH "UNKNOWN") + if(GPU_ARCH STREQUAL "AUTO") + detect_gpu_arch("AUTO") + else() + set(TARGET_ARCH ${GPU_ARCH}) + endif() + add_custom_command( + OUTPUT ${OUT_HEADER} + COMMAND ${CMAKE_COMMAND} + -DOUT_HEADER=${OUT_HEADER} + -DGPU_PARAM_JSON=${GPU_PARAM_JSON} + -DTARGET_ARCH_SHORT=${TARGET_ARCH} + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/gpu_param_header_generator.cmake + DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/gpu_param_header_generator.cmake + ${GPU_PARAM_JSON} + COMMENT "Generating GPU parameter header for ${TARGET_ARCH}" + VERBATIM + ) + add_custom_target(GPU_PARAM_HEADER_${GPU_ARCH}_ALL ALL DEPENDS ${OUT_HEADER}) +endfunction() \ No newline at end of file diff --git a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake new file mode 100644 index 0000000000000..3949322b5abfa --- /dev/null +++ b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake @@ -0,0 +1,105 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +# file gpu_param_header_generator.cmake +# author Gabriele Cimador + +file(READ "${GPU_PARAM_JSON}" JSON_CONTENT) +set(TMP_HEADER "${OUT_HEADER}.tmp") +file(WRITE "${TMP_HEADER}" "#ifndef GPUDEFPARAMETERSDEFAULTS_H\n#define GPUDEFPARAMETERSDEFAULTS_H\n\n") +file(APPEND "${TMP_HEADER}" "// This file is auto-generated from gpu_params.json. Do not edit directly.\n") +string(REPLACE "," ";" ARCH_LIST "${TARGET_ARCH_SHORT}") +file(APPEND "${TMP_HEADER}" "// Architectures: ${TARGET_ARCH_SHORT}\n\n") +file(APPEND "${TMP_HEADER}" "#if defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS) // Avoid including for RTC generation besides normal include protection.\n\n") + +# Types +set(TYPES CORE LB PAR) +foreach(ARCH IN LISTS ARCH_LIST) + file(APPEND "${TMP_HEADER}" "#if defined(GPUCA_GPUTYPE_${ARCH})\n\n") + foreach(TYPE IN LISTS TYPES) + # Get all keys of this TYPE as a semicolon-separated list + string(JSON n_params LENGTH "${JSON_CONTENT}" "${TYPE}") + math(EXPR last "${n_params} - 1") + foreach(i RANGE 0 ${last}) + string(JSON param_name MEMBER "${JSON_CONTENT}" "${TYPE}" "${i}") + string(JSON n_archs LENGTH "${JSON_CONTENT}" "${TYPE}" "${param_name}") + math(EXPR last_arch "${n_archs} - 1") + + foreach(iArch RANGE 0 ${last_arch}) + string(JSON arch MEMBER "${JSON_CONTENT}" "${TYPE}" "${param_name}" "${iArch}") + if(arch STREQUAL "${ARCH}") + string(JSON param_values GET "${JSON_CONTENT}" "${TYPE}" "${param_name}" "${ARCH}") + if(TYPE STREQUAL "LB") + set(MACRO_NAME "GPUCA_LB_${param_name}") + elseif(TYPE STREQUAL "PAR") + set(MACRO_NAME "GPUCA_PAR_${param_name}") + else() + set(MACRO_NAME "GPUCA_${param_name}") + endif() + set(vals "${param_values}") + string(REGEX REPLACE "^\\[ *" "" vals "${vals}") + string(REGEX REPLACE " *\\]$" "" vals "${vals}") + string(REGEX REPLACE "\"" "" vals "${vals}") + set(MACRO_DEFINITION "#define ${MACRO_NAME} ${vals}") + file(APPEND "${TMP_HEADER}" "${MACRO_DEFINITION}\n") + endif() + endforeach() + endforeach() + endforeach() + file(APPEND "${TMP_HEADER}" "\n#endif // GPUCA_GPUTYPE_${ARCH}\n\n") +endforeach() + +file(APPEND "${TMP_HEADER}" "\n// Default parameters if not defined for the target architecture\n\n") +#Default parameters +foreach(TYPE IN LISTS TYPES) + # Get all keys of this TYPE as a semicolon-separated list + string(JSON n_params LENGTH "${JSON_CONTENT}" "${TYPE}") + math(EXPR last "${n_params} - 1") + foreach(i RANGE 0 ${last}) + string(JSON param_name MEMBER "${JSON_CONTENT}" "${TYPE}" "${i}") + string(JSON param_values GET "${JSON_CONTENT}" "${TYPE}" "${param_name}" "default") + if(TYPE STREQUAL "LB") + set(MACRO_NAME "GPUCA_LB_${param_name}") + elseif(TYPE STREQUAL "PAR") + set(MACRO_NAME "GPUCA_PAR_${param_name}") + else() + set(MACRO_NAME "GPUCA_${param_name}") + endif() + set(vals "${param_values}") + string(REGEX REPLACE "^\\[ *" "" vals "${vals}") + string(REGEX REPLACE " *\\]$" "" vals "${vals}") + string(REGEX REPLACE "\"" "" vals "${vals}") + set(MACRO_DEFINITION "#define ${MACRO_NAME} ${vals}") + file(APPEND "${TMP_HEADER}" "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") + endforeach() +endforeach() +file(APPEND "${TMP_HEADER}" "#endif // defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS)\n\n") + +#Defaults for non-LB parameters also for CPU fallback +file(APPEND "${TMP_HEADER}" "#ifndef GPUCA_GPUCODE_GENRTC //Defaults for non-LB parameters also for CPU fallback\n\n") # Get all keys of this TYPE as a semicolon-separated list +string(JSON n_params LENGTH "${JSON_CONTENT}" "PAR") +math(EXPR last "${n_params} - 1") +foreach(i RANGE 0 ${last}) + string(JSON param_name MEMBER "${JSON_CONTENT}" "PAR" "${i}") + string(JSON param_values GET "${JSON_CONTENT}" "PAR" "${param_name}" "default_cpu") + set(MACRO_NAME "GPUCA_PAR_${param_name}") + set(vals "${param_values}") + string(REGEX REPLACE "^\\[ *" "" vals "${vals}") + string(REGEX REPLACE " *\\]$" "" vals "${vals}") + string(REGEX REPLACE "\"" "" vals "${vals}") + set(MACRO_DEFINITION "#define ${MACRO_NAME} ${vals}") + file(APPEND "${TMP_HEADER}" "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") +endforeach() +file(APPEND "${TMP_HEADER}" "\n#endif // GPUCA_GPUCODE_GENRTC\n") + +file(APPEND "${TMP_HEADER}" "\n#endif // GPUDEFPARAMETERSDEFAULTS_H\n") +file(RENAME "${TMP_HEADER}" "${OUT_HEADER}") +message(STATUS "Generated ${OUT_HEADER}") diff --git a/GPU/documentation/build-O2.md b/GPU/documentation/build-O2.md index dd21f7e154a63..b04fe562b8c2f 100644 --- a/GPU/documentation/build-O2.md +++ b/GPU/documentation/build-O2.md @@ -37,7 +37,7 @@ Advantages: - One can see enabled GPU features / versions / architectures in the version string of `gpu-system`. Disadvantages: -- Need system `CMake` >= `3.26` for the detsction at aliBuild level. +- Need system `CMake` >= `3.26` for the detection at aliBuild level. - `FindO2GPU.cmake` is duplicated in O2 and alidist and must be kept in sync. But at least this is checked and gives an error otherwise. - Running cmake during the system check takes around 5 sec for every aliBuild command involving O2 or ONNX. diff --git a/dependencies/FindO2GPU.cmake b/dependencies/FindO2GPU.cmake index 21e2d7cad239a..3cfcaef82fcca 100644 --- a/dependencies/FindO2GPU.cmake +++ b/dependencies/FindO2GPU.cmake @@ -10,7 +10,7 @@ # or submit itself to any jurisdiction. # NOTE!!!! - Whenever this file is changed, move it over to alidist/resources -# FindO2GPU.cmake Version 9 +# FindO2GPU.cmake Version 10 set(CUDA_COMPUTETARGET_DEFAULT_FULL 80-real 86-real 89-real 120-real 75-virtual) set(HIP_AMDGPUTARGET_DEFAULT_FULL gfx906;gfx908) @@ -44,34 +44,53 @@ if(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET STREQUAL "default") set(HIP_AMDGPUTARGET ${HIP_AMDGPUTARGET_DEFAULT_FULL}) endif() -function(set_target_cuda_arch target) - if(CUDA_COMPUTETARGET AND (CUDA_COMPUTETARGET MATCHES "86" OR CUDA_COMPUTETARGET MATCHES "89")) +function(detect_gpu_arch backend) # Detect GPU architecture, optionally filterring by backend + set(TARGET_ARCH "") + set(CUDA_TARGET "") + set(HIP_TARGET "") + + if(CUDA_COMPUTETARGET AND CUDA_COMPUTETARGET MATCHES "86|89") + set(CUDA_TARGET AMPERE) message(STATUS "Using optimized CUDA settings for Ampere GPU") - target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_AMPERE) elseif(CUDA_COMPUTETARGET AND CUDA_COMPUTETARGET MATCHES "75") + set(CUDA_TARGET TURING) message(STATUS "Using optimized CUDA settings for Turing GPU") - target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_TURING) else() + set(CUDA_TARGET AMPERE) message(STATUS "Defaulting optimized CUDA settings for Ampere GPU") - target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_AMPERE) endif() -endfunction() -function(set_target_hip_arch target) if(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET MATCHES "gfx906") + set(HIP_TARGET VEGA) message(STATUS "Using optimized HIP settings for MI50 GPU") - target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_VEGA) elseif(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET MATCHES "gfx908") + set(HIP_TARGET MI100) message(STATUS "Using optimized HIP settings for MI100 GPU") - target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_MI100) elseif(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET MATCHES "gfx90a") + set(HIP_TARGET MI100) message(STATUS "Using optimized HIP settings for MI210 GPU") - target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_MI100) else() - target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_VEGA) + set(HIP_TARGET VEGA) + message(STATUS "Defaulting optimized HIP settings for VEGA GPU") + endif() + + if(backend STREQUAL "CUDA") # CUDA filter + set(TARGET_ARCH "${CUDA_TARGET}" PARENT_SCOPE) + return() + elseif(backend STREQUAL "HIP") # HIP filter + set(TARGET_ARCH "${HIP_TARGET}" PARENT_SCOPE) + return() + else() # Return both + set(TARGET_ARCH "${CUDA_TARGET},${HIP_TARGET}" PARENT_SCOPE) endif() endfunction() +function(set_target_gpu_arch backend target) + detect_gpu_arch("${backend}") + message(STATUS "Compiling for ${TARGET_ARCH}") + target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_${TARGET_ARCH}) +endfunction() + # Need to strip c++17 imposed by alidist defaults STRING(REGEX REPLACE "\-std=[^ ]*" "" O2_GPU_CMAKE_CXX_FLAGS_NOSTD "${CMAKE_CXX_FLAGS}") diff --git a/log.txt b/log.txt new file mode 100644 index 0000000000000..e69de29bb2d1d From f683cac8423902087d678de4181d4a2089376296 Mon Sep 17 00:00:00 2001 From: Gabriele Cimador Date: Wed, 28 Jan 2026 17:53:20 +0100 Subject: [PATCH 045/133] GPU Framework: refactor generation of default GPU parameters --- GPU/GPUTracking/CMakeLists.txt | 2 +- .../Definitions/Parameters/.clang-format | 1 + .../{ => Parameters}/.clang-format-ignore | 0 .../{ => Parameters}/GPUParameters.json | 0 .../cmake/generateGPUParamHeader.cmake | 37 ------ .../cmake/gpu_param_header_generator.cmake | 117 ++++++++---------- dependencies/FindO2GPU.cmake | 8 +- 7 files changed, 58 insertions(+), 107 deletions(-) create mode 100644 GPU/GPUTracking/Definitions/Parameters/.clang-format rename GPU/GPUTracking/Definitions/{ => Parameters}/.clang-format-ignore (100%) rename GPU/GPUTracking/Definitions/{ => Parameters}/GPUParameters.json (100%) delete mode 100644 GPU/GPUTracking/cmake/generateGPUParamHeader.cmake diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index a2d91b6ed4c5e..816d578fb31a3 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -109,7 +109,7 @@ set(SRCS_NO_H SectorTracker/GPUTPCTrackerDump.cxx set(ON_THE_FLY_DIR ${CMAKE_CURRENT_BINARY_DIR}/include_gpu_onthefly) file(MAKE_DIRECTORY ${ON_THE_FLY_DIR}) -include(cmake/generateGPUParamHeader.cmake) +include(cmake/gpu_param_header_generator.cmake) set(GPU_DEFAULT_PARAMS_HEADER ${ON_THE_FLY_DIR}/GPUDefParametersDefaults.h) generate_gpu_param_header("AUTO" ${GPU_DEFAULT_PARAMS_HEADER}) # generate header with default GPU parameters, arch selected by CMake variables diff --git a/GPU/GPUTracking/Definitions/Parameters/.clang-format b/GPU/GPUTracking/Definitions/Parameters/.clang-format new file mode 100644 index 0000000000000..e3845288a2aec --- /dev/null +++ b/GPU/GPUTracking/Definitions/Parameters/.clang-format @@ -0,0 +1 @@ +DisableFormat: true diff --git a/GPU/GPUTracking/Definitions/.clang-format-ignore b/GPU/GPUTracking/Definitions/Parameters/.clang-format-ignore similarity index 100% rename from GPU/GPUTracking/Definitions/.clang-format-ignore rename to GPU/GPUTracking/Definitions/Parameters/.clang-format-ignore diff --git a/GPU/GPUTracking/Definitions/GPUParameters.json b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json similarity index 100% rename from GPU/GPUTracking/Definitions/GPUParameters.json rename to GPU/GPUTracking/Definitions/Parameters/GPUParameters.json diff --git a/GPU/GPUTracking/cmake/generateGPUParamHeader.cmake b/GPU/GPUTracking/cmake/generateGPUParamHeader.cmake deleted file mode 100644 index 712bf4641b825..0000000000000 --- a/GPU/GPUTracking/cmake/generateGPUParamHeader.cmake +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright 2019-2020 CERN and copyright holders of ALICE O2. -# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -# All rights not expressly granted are reserved. -# -# This software is distributed under the terms of the GNU General Public -# License v3 (GPL Version 3), copied verbatim in the file "COPYING". -# -# In applying this license CERN does not waive the privileges and immunities -# granted to it by virtue of its status as an Intergovernmental Organization -# or submit itself to any jurisdiction. - -# file generateGPUParamHeader.cmake -# author Gabriele Cimador - -function(generate_gpu_param_header GPU_ARCH OUT_HEADER) - set(GPU_PARAM_JSON ${CMAKE_SOURCE_DIR}/GPU/GPUTracking/Definitions/GPUParameters.json) - set(TARGET_ARCH "UNKNOWN") - if(GPU_ARCH STREQUAL "AUTO") - detect_gpu_arch("AUTO") - else() - set(TARGET_ARCH ${GPU_ARCH}) - endif() - add_custom_command( - OUTPUT ${OUT_HEADER} - COMMAND ${CMAKE_COMMAND} - -DOUT_HEADER=${OUT_HEADER} - -DGPU_PARAM_JSON=${GPU_PARAM_JSON} - -DTARGET_ARCH_SHORT=${TARGET_ARCH} - -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/gpu_param_header_generator.cmake - DEPENDS - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/gpu_param_header_generator.cmake - ${GPU_PARAM_JSON} - COMMENT "Generating GPU parameter header for ${TARGET_ARCH}" - VERBATIM - ) - add_custom_target(GPU_PARAM_HEADER_${GPU_ARCH}_ALL ALL DEPENDS ${OUT_HEADER}) -endfunction() \ No newline at end of file diff --git a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake index 3949322b5abfa..38b92421616f2 100644 --- a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake +++ b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake @@ -12,31 +12,18 @@ # file gpu_param_header_generator.cmake # author Gabriele Cimador -file(READ "${GPU_PARAM_JSON}" JSON_CONTENT) -set(TMP_HEADER "${OUT_HEADER}.tmp") -file(WRITE "${TMP_HEADER}" "#ifndef GPUDEFPARAMETERSDEFAULTS_H\n#define GPUDEFPARAMETERSDEFAULTS_H\n\n") -file(APPEND "${TMP_HEADER}" "// This file is auto-generated from gpu_params.json. Do not edit directly.\n") -string(REPLACE "," ";" ARCH_LIST "${TARGET_ARCH_SHORT}") -file(APPEND "${TMP_HEADER}" "// Architectures: ${TARGET_ARCH_SHORT}\n\n") -file(APPEND "${TMP_HEADER}" "#if defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS) // Avoid including for RTC generation besides normal include protection.\n\n") - -# Types -set(TYPES CORE LB PAR) -foreach(ARCH IN LISTS ARCH_LIST) - file(APPEND "${TMP_HEADER}" "#if defined(GPUCA_GPUTYPE_${ARCH})\n\n") - foreach(TYPE IN LISTS TYPES) - # Get all keys of this TYPE as a semicolon-separated list - string(JSON n_params LENGTH "${JSON_CONTENT}" "${TYPE}") +function(generate_macros json_content header types arch_key use_ifndef_guard) + foreach(TYPE IN LISTS types) + string(JSON n_params LENGTH "${json_content}" "${TYPE}") math(EXPR last "${n_params} - 1") foreach(i RANGE 0 ${last}) - string(JSON param_name MEMBER "${JSON_CONTENT}" "${TYPE}" "${i}") + string(JSON param_name MEMBER "${json_content}" "${TYPE}" "${i}") string(JSON n_archs LENGTH "${JSON_CONTENT}" "${TYPE}" "${param_name}") math(EXPR last_arch "${n_archs} - 1") - foreach(iArch RANGE 0 ${last_arch}) string(JSON arch MEMBER "${JSON_CONTENT}" "${TYPE}" "${param_name}" "${iArch}") - if(arch STREQUAL "${ARCH}") - string(JSON param_values GET "${JSON_CONTENT}" "${TYPE}" "${param_name}" "${ARCH}") + if(arch STREQUAL "${arch_key}") + string(JSON param_values GET "${JSON_CONTENT}" "${TYPE}" "${param_name}" "${arch_key}") if(TYPE STREQUAL "LB") set(MACRO_NAME "GPUCA_LB_${param_name}") elseif(TYPE STREQUAL "PAR") @@ -49,57 +36,57 @@ foreach(ARCH IN LISTS ARCH_LIST) string(REGEX REPLACE " *\\]$" "" vals "${vals}") string(REGEX REPLACE "\"" "" vals "${vals}") set(MACRO_DEFINITION "#define ${MACRO_NAME} ${vals}") - file(APPEND "${TMP_HEADER}" "${MACRO_DEFINITION}\n") + if(use_ifndef_guard) + # fallback defaults are wrapped in #ifndef + file(APPEND "${header}" "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") + else() + file(APPEND "${header}" "${MACRO_DEFINITION}\n") + endif() endif() endforeach() endforeach() endforeach() - file(APPEND "${TMP_HEADER}" "\n#endif // GPUCA_GPUTYPE_${ARCH}\n\n") -endforeach() +endfunction() + +function(generate_gpu_param_header GPU_ARCH OUT_HEADER) + set(GPU_PARAM_JSON ${CMAKE_SOURCE_DIR}/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json) + set(TARGET_ARCH "UNKNOWN") + if(GPU_ARCH STREQUAL "AUTO") + detect_gpu_arch("ALL") + else() + set(TARGET_ARCH ${GPU_ARCH}) + endif() + file(READ "${GPU_PARAM_JSON}" JSON_CONTENT) + set(TMP_HEADER "${OUT_HEADER}.tmp") + message(STATUS "OUT_HEADER = '${OUT_HEADER}'") + message(STATUS "TMP_HEADER = '${TMP_HEADER}'") + file(WRITE "${TMP_HEADER}" "#ifndef GPUDEFPARAMETERSDEFAULTS_H\n#define GPUDEFPARAMETERSDEFAULTS_H\n\n") + file(APPEND "${TMP_HEADER}" "// This file is auto-generated from gpu_params.json. Do not edit directly.\n") + string(REPLACE "," ";" ARCH_LIST "${TARGET_ARCH}") + file(APPEND "${TMP_HEADER}" "// Architectures: ${TARGET_ARCH}\n\n") + file(APPEND "${TMP_HEADER}" "#if defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS) // Avoid including for RTC generation besides normal include protection.\n\n") -file(APPEND "${TMP_HEADER}" "\n// Default parameters if not defined for the target architecture\n\n") -#Default parameters -foreach(TYPE IN LISTS TYPES) - # Get all keys of this TYPE as a semicolon-separated list - string(JSON n_params LENGTH "${JSON_CONTENT}" "${TYPE}") - math(EXPR last "${n_params} - 1") - foreach(i RANGE 0 ${last}) - string(JSON param_name MEMBER "${JSON_CONTENT}" "${TYPE}" "${i}") - string(JSON param_values GET "${JSON_CONTENT}" "${TYPE}" "${param_name}" "default") - if(TYPE STREQUAL "LB") - set(MACRO_NAME "GPUCA_LB_${param_name}") - elseif(TYPE STREQUAL "PAR") - set(MACRO_NAME "GPUCA_PAR_${param_name}") - else() - set(MACRO_NAME "GPUCA_${param_name}") - endif() - set(vals "${param_values}") - string(REGEX REPLACE "^\\[ *" "" vals "${vals}") - string(REGEX REPLACE " *\\]$" "" vals "${vals}") - string(REGEX REPLACE "\"" "" vals "${vals}") - set(MACRO_DEFINITION "#define ${MACRO_NAME} ${vals}") - file(APPEND "${TMP_HEADER}" "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") + # Types + set(TYPES CORE LB PAR) + # Per architecture definitions + foreach(ARCH IN LISTS ARCH_LIST) + file(APPEND "${TMP_HEADER}" "#if defined(GPUCA_GPUTYPE_${ARCH})\n\n") + generate_macros("${JSON_CONTENT}" "${TMP_HEADER}" "${TYPES}" "${ARCH}" "") + file(APPEND "${TMP_HEADER}" "\n#endif // GPUCA_GPUTYPE_${ARCH}\n\n") endforeach() -endforeach() -file(APPEND "${TMP_HEADER}" "#endif // defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS)\n\n") -#Defaults for non-LB parameters also for CPU fallback -file(APPEND "${TMP_HEADER}" "#ifndef GPUCA_GPUCODE_GENRTC //Defaults for non-LB parameters also for CPU fallback\n\n") # Get all keys of this TYPE as a semicolon-separated list -string(JSON n_params LENGTH "${JSON_CONTENT}" "PAR") -math(EXPR last "${n_params} - 1") -foreach(i RANGE 0 ${last}) - string(JSON param_name MEMBER "${JSON_CONTENT}" "PAR" "${i}") - string(JSON param_values GET "${JSON_CONTENT}" "PAR" "${param_name}" "default_cpu") - set(MACRO_NAME "GPUCA_PAR_${param_name}") - set(vals "${param_values}") - string(REGEX REPLACE "^\\[ *" "" vals "${vals}") - string(REGEX REPLACE " *\\]$" "" vals "${vals}") - string(REGEX REPLACE "\"" "" vals "${vals}") - set(MACRO_DEFINITION "#define ${MACRO_NAME} ${vals}") - file(APPEND "${TMP_HEADER}" "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") -endforeach() -file(APPEND "${TMP_HEADER}" "\n#endif // GPUCA_GPUCODE_GENRTC\n") + # Default parameters + file(APPEND "${TMP_HEADER}" "\n// Default parameters if not defined for the target architecture\n\n") + generate_macros("${JSON_CONTENT}" "${TMP_HEADER}" "${TYPES}" "default" "use_ifndef_guard") + file(APPEND "${TMP_HEADER}" "#endif // defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS)\n\n") + + # CPU fallback + file(APPEND "${TMP_HEADER}" "#ifndef GPUCA_GPUCODE_GENRTC //Defaults for non-LB parameters also for CPU fallback\n\n") # Get all keys of this TYPE as a semicolon-separated list + generate_macros("${JSON_CONTENT}" "${TMP_HEADER}" "PAR" "default_cpu" "use_ifndef_guard") + file(APPEND "${TMP_HEADER}" "\n#endif // GPUCA_GPUCODE_GENRTC\n") -file(APPEND "${TMP_HEADER}" "\n#endif // GPUDEFPARAMETERSDEFAULTS_H\n") -file(RENAME "${TMP_HEADER}" "${OUT_HEADER}") -message(STATUS "Generated ${OUT_HEADER}") + file(APPEND "${TMP_HEADER}" "\n#endif // GPUDEFPARAMETERSDEFAULTS_H\n") + file(RENAME "${TMP_HEADER}" "${OUT_HEADER}") + message(STATUS "Generated ${OUT_HEADER}") + add_custom_target(GPU_PARAM_HEADER_${GPU_ARCH}_ALL ALL DEPENDS ${OUT_HEADER} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/gpu_param_header_generator.cmake ${GPU_PARAM_JSON}) +endfunction() \ No newline at end of file diff --git a/dependencies/FindO2GPU.cmake b/dependencies/FindO2GPU.cmake index 3cfcaef82fcca..4ed29ec61f3e1 100644 --- a/dependencies/FindO2GPU.cmake +++ b/dependencies/FindO2GPU.cmake @@ -45,9 +45,6 @@ if(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET STREQUAL "default") endif() function(detect_gpu_arch backend) # Detect GPU architecture, optionally filterring by backend - set(TARGET_ARCH "") - set(CUDA_TARGET "") - set(HIP_TARGET "") if(CUDA_COMPUTETARGET AND CUDA_COMPUTETARGET MATCHES "86|89") set(CUDA_TARGET AMPERE) @@ -80,8 +77,11 @@ function(detect_gpu_arch backend) # Detect GPU architecture, optionally filterri elseif(backend STREQUAL "HIP") # HIP filter set(TARGET_ARCH "${HIP_TARGET}" PARENT_SCOPE) return() - else() # Return both + elseif(backend STREQUAL "ALL") # Return both set(TARGET_ARCH "${CUDA_TARGET},${HIP_TARGET}" PARENT_SCOPE) + return() + else() + message(FATAL_ERROR "Unknown backend provided: ${backend}") endif() endfunction() From fe431a1ae55bf5a05e617210f7fab1f4f35aa378 Mon Sep 17 00:00:00 2001 From: Gabriele Cimador Date: Thu, 29 Jan 2026 13:16:58 +0100 Subject: [PATCH 046/133] GPU Framework: Add OpenCL support to GPU param header generation + update CFCheckPadBaseline parameters --- .../Definitions/Parameters/GPUParameters.json | 8 ++++---- .../cmake/gpu_param_header_generator.cmake | 20 ++++++++++++------- dependencies/FindO2GPU.cmake | 19 ++++++++++++------ 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json index e8f1c24520813..674efc9ea0912 100644 --- a/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json +++ b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json @@ -338,10 +338,10 @@ "TURING": [512, 1] }, "GPUTPCCFCheckPadBaseline": { - "default": 64, - "MI100": [64, 10], - "VEGA": [64, 2], - "AMPERE": [64, 8] + "default": 576, + "MI100": [576, 2], + "VEGA": [576, 2], + "AMPERE": [576, 2] }, "GPUTPCCFChargeMapFiller_fillIndexMap": { "default": 512, diff --git a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake index 38b92421616f2..3770e30f2583c 100644 --- a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake +++ b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake @@ -49,7 +49,7 @@ function(generate_macros json_content header types arch_key use_ifndef_guard) endfunction() function(generate_gpu_param_header GPU_ARCH OUT_HEADER) - set(GPU_PARAM_JSON ${CMAKE_SOURCE_DIR}/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json) + set(GPU_PARAM_JSON ${CMAKE_CURRENT_SOURCE_DIR}/Definitions/Parameters/GPUParameters.json) set(TARGET_ARCH "UNKNOWN") if(GPU_ARCH STREQUAL "AUTO") detect_gpu_arch("ALL") @@ -58,8 +58,6 @@ function(generate_gpu_param_header GPU_ARCH OUT_HEADER) endif() file(READ "${GPU_PARAM_JSON}" JSON_CONTENT) set(TMP_HEADER "${OUT_HEADER}.tmp") - message(STATUS "OUT_HEADER = '${OUT_HEADER}'") - message(STATUS "TMP_HEADER = '${TMP_HEADER}'") file(WRITE "${TMP_HEADER}" "#ifndef GPUDEFPARAMETERSDEFAULTS_H\n#define GPUDEFPARAMETERSDEFAULTS_H\n\n") file(APPEND "${TMP_HEADER}" "// This file is auto-generated from gpu_params.json. Do not edit directly.\n") string(REPLACE "," ";" ARCH_LIST "${TARGET_ARCH}") @@ -69,11 +67,19 @@ function(generate_gpu_param_header GPU_ARCH OUT_HEADER) # Types set(TYPES CORE LB PAR) # Per architecture definitions + set(_first TRUE) foreach(ARCH IN LISTS ARCH_LIST) - file(APPEND "${TMP_HEADER}" "#if defined(GPUCA_GPUTYPE_${ARCH})\n\n") - generate_macros("${JSON_CONTENT}" "${TMP_HEADER}" "${TYPES}" "${ARCH}" "") - file(APPEND "${TMP_HEADER}" "\n#endif // GPUCA_GPUTYPE_${ARCH}\n\n") + if(_first) + file(APPEND "${TMP_HEADER}" "#if defined(GPUCA_GPUTYPE_${ARCH})\n\n") + set(_first FALSE) + else() + file(APPEND "${TMP_HEADER}" "#elif defined(GPUCA_GPUTYPE_${ARCH})\n\n") + endif() + generate_macros("${JSON_CONTENT}" "${TMP_HEADER}" "${TYPES}" "${ARCH}" "") endforeach() + if(NOT _first) + file(APPEND "${TMP_HEADER}" "#else\n#error GPU TYPE NOT SET\n#endif\n") + endif() # Default parameters file(APPEND "${TMP_HEADER}" "\n// Default parameters if not defined for the target architecture\n\n") @@ -81,7 +87,7 @@ function(generate_gpu_param_header GPU_ARCH OUT_HEADER) file(APPEND "${TMP_HEADER}" "#endif // defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS)\n\n") # CPU fallback - file(APPEND "${TMP_HEADER}" "#ifndef GPUCA_GPUCODE_GENRTC //Defaults for non-LB parameters also for CPU fallback\n\n") # Get all keys of this TYPE as a semicolon-separated list + file(APPEND "${TMP_HEADER}" "#ifndef GPUCA_GPUCODE_GENRTC //Defaults for non-LB parameters also for CPU fallback\n\n") generate_macros("${JSON_CONTENT}" "${TMP_HEADER}" "PAR" "default_cpu" "use_ifndef_guard") file(APPEND "${TMP_HEADER}" "\n#endif // GPUCA_GPUCODE_GENRTC\n") diff --git a/dependencies/FindO2GPU.cmake b/dependencies/FindO2GPU.cmake index 4ed29ec61f3e1..e02bf932ab784 100644 --- a/dependencies/FindO2GPU.cmake +++ b/dependencies/FindO2GPU.cmake @@ -73,13 +73,21 @@ function(detect_gpu_arch backend) # Detect GPU architecture, optionally filterri if(backend STREQUAL "CUDA") # CUDA filter set(TARGET_ARCH "${CUDA_TARGET}" PARENT_SCOPE) - return() elseif(backend STREQUAL "HIP") # HIP filter set(TARGET_ARCH "${HIP_TARGET}" PARENT_SCOPE) - return() - elseif(backend STREQUAL "ALL") # Return both - set(TARGET_ARCH "${CUDA_TARGET},${HIP_TARGET}" PARENT_SCOPE) - return() + elseif(backend STREQUAL "ALL") # Return enabled backends + set(_archs "") + if(CUDA_ENABLED) + list(APPEND _archs "${CUDA_TARGET}") + endif() + if(HIP_ENABLED) + list(APPEND _archs "${HIP_TARGET}") + endif() + if(OPENCL_ENABLED) + list(APPEND _archs "OPENCL") + endif() + list(JOIN _archs "," TARGET_ARCH) + set(TARGET_ARCH "${TARGET_ARCH}" PARENT_SCOPE) else() message(FATAL_ERROR "Unknown backend provided: ${backend}") endif() @@ -87,7 +95,6 @@ endfunction() function(set_target_gpu_arch backend target) detect_gpu_arch("${backend}") - message(STATUS "Compiling for ${TARGET_ARCH}") target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_${TARGET_ARCH}) endfunction() From 5a15ab5c1b12b1010a439bcd16751e375059795f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Thu, 5 Feb 2026 11:47:27 +0100 Subject: [PATCH 047/133] GPU: Delete unused files (#15015) * GPU: Remove unused files * Add missing suffix * Revert "Add missing suffix" This reverts commit 81879979abeac8839164d57d33d65a392c4b3aea. * Remove cxx suffix --- ...tGPUsortHIP.hip.cxx => testGPUsortHIP.hip} | 0 .../SectorTracker/GPUTPCDefinitions.h | 25 -- .../utils/makefile_opencl_compiler.cxx | 258 ------------------ .../utils/opencl_compiler_structs.h | 28 -- GPU/GPUTracking/utils/opencl_obtain_program.h | 91 ------ 5 files changed, 402 deletions(-) rename GPU/GPUTracking/Base/hip/test/{testGPUsortHIP.hip.cxx => testGPUsortHIP.hip} (100%) delete mode 100644 GPU/GPUTracking/SectorTracker/GPUTPCDefinitions.h delete mode 100644 GPU/GPUTracking/utils/makefile_opencl_compiler.cxx delete mode 100644 GPU/GPUTracking/utils/opencl_compiler_structs.h delete mode 100644 GPU/GPUTracking/utils/opencl_obtain_program.h diff --git a/GPU/GPUTracking/Base/hip/test/testGPUsortHIP.hip.cxx b/GPU/GPUTracking/Base/hip/test/testGPUsortHIP.hip similarity index 100% rename from GPU/GPUTracking/Base/hip/test/testGPUsortHIP.hip.cxx rename to GPU/GPUTracking/Base/hip/test/testGPUsortHIP.hip diff --git a/GPU/GPUTracking/SectorTracker/GPUTPCDefinitions.h b/GPU/GPUTracking/SectorTracker/GPUTPCDefinitions.h deleted file mode 100644 index 7d9d607b9b88d..0000000000000 --- a/GPU/GPUTracking/SectorTracker/GPUTPCDefinitions.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file GPUTPCDefinitions.h -/// \author Sergey Gorbunov, David Rohr - -#ifndef GPUTPCDEFINITIONS_H -#define GPUTPCDEFINITIONS_H - -#include "AliHLTDataTypes.h" - -namespace GPUTPCDefinitions -{ -extern const AliHLTComponentDataType fgkTrackletsDataType; -} - -#endif // GPUTPCDEFINITIONS_H diff --git a/GPU/GPUTracking/utils/makefile_opencl_compiler.cxx b/GPU/GPUTracking/utils/makefile_opencl_compiler.cxx deleted file mode 100644 index f6400cc3369e0..0000000000000 --- a/GPU/GPUTracking/utils/makefile_opencl_compiler.cxx +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file makefile_opencl_compiler.cxx -/// \author David Rohr - -#define CL_TARGET_OPENCL_VERSION 220 -#define _CRT_SECURE_NO_WARNINGS -#include "CL/opencl.h" -#include -#include -#include -#include -#include - -#include "opencl_compiler_structs.h" - -#define quit(arg) \ - { \ - fprintf(stderr, arg "\n"); \ - return (1); \ - } -#define DEFAULT_OPENCL_COMPILER_OPTIONS "" -#define DEFAULT_OUTPUT_FILE "opencl.out" - -int32_t main(int argc, char** argv) -{ - const char* output_file = DEFAULT_OUTPUT_FILE; - std::string compiler_options = DEFAULT_OPENCL_COMPILER_OPTIONS; - std::vector files; - - printf("Passing command line options:\n"); - bool add_option = false; - for (int32_t i = 1; i < argc; i++) { - if (add_option) { - compiler_options += " "; - compiler_options += argv[i]; - } else if (strcmp(argv[i], "--") == 0) { - add_option = true; - } else if (strcmp(argv[i], "-output-file") == 0) { - if (++i >= argc) { - quit("Output file name missing"); - } - output_file = argv[i]; - } else { - fprintf(stderr, "%s\n", argv[i]); - files.push_back(argv[i]); - } - } - - cl_int ocl_error; - cl_uint num_platforms; - if (clGetPlatformIDs(0, nullptr, &num_platforms) != CL_SUCCESS) { - quit("Error getting OpenCL Platform Count"); - } - if (num_platforms == 0) { - quit("No OpenCL Platform found"); - } - printf("%d OpenCL Platforms found\n", num_platforms); - - // Query platforms - cl_platform_id* platforms = new cl_platform_id[num_platforms]; - if (platforms == nullptr) { - quit("Memory allocation error"); - } - if (clGetPlatformIDs(num_platforms, platforms, nullptr) != CL_SUCCESS) { - quit("Error getting OpenCL Platforms"); - } - - cl_platform_id platform; - bool found = false; - - _makefiles_opencl_platform_info pinfo; - for (uint32_t i_platform = 0; i_platform < num_platforms; i_platform++) { - clGetPlatformInfo(platforms[i_platform], CL_PLATFORM_PROFILE, 64, pinfo.platform_profile, nullptr); - clGetPlatformInfo(platforms[i_platform], CL_PLATFORM_VERSION, 64, pinfo.platform_version, nullptr); - clGetPlatformInfo(platforms[i_platform], CL_PLATFORM_NAME, 64, pinfo.platform_name, nullptr); - clGetPlatformInfo(platforms[i_platform], CL_PLATFORM_VENDOR, 64, pinfo.platform_vendor, nullptr); - printf("Available Platform %u: (%s %s) %s %s\n", i_platform, pinfo.platform_profile, pinfo.platform_version, pinfo.platform_vendor, pinfo.platform_name); - if (strcmp(pinfo.platform_vendor, "Advanced Micro Devices, Inc.") == 0 && strcmp(pinfo.platform_version, "OpenCL 2.0 AMD-APP (1800.8)") == 0) { - found = true; - printf("AMD OpenCL Platform found (%u)\n", i_platform); - platform = platforms[i_platform]; - break; - } - } - if (found == false) { - quit("Did not find AMD OpenCL Platform"); - } - - if (clGetDeviceIDs(platform, CL_DEVICE_TYPE_ALL, 0, nullptr, &pinfo.count) != CL_SUCCESS) { - quit("Error getting OPENCL Device Count"); - } - - // Query devices - cl_device_id* devices = new cl_device_id[pinfo.count]; - if (devices == nullptr) { - quit("Memory allocation error"); - } - if (clGetDeviceIDs(platform, CL_DEVICE_TYPE_ALL, pinfo.count, devices, nullptr) != CL_SUCCESS) { - quit("Error getting OpenCL devices"); - } - - _makefiles_opencl_device_info dinfo; - cl_device_type device_type; - cl_uint freq, shaders; - - printf("Available OPENCL devices:\n"); - for (uint32_t i = 0; i < pinfo.count; i++) { - printf("Examining device %u\n", i); - - clGetDeviceInfo(devices[i], CL_DEVICE_NAME, 64, dinfo.device_name, nullptr); - clGetDeviceInfo(devices[i], CL_DEVICE_VENDOR, 64, dinfo.device_vendor, nullptr); - clGetDeviceInfo(devices[i], CL_DEVICE_TYPE, sizeof(cl_device_type), &device_type, nullptr); - clGetDeviceInfo(devices[i], CL_DEVICE_MAX_CLOCK_FREQUENCY, sizeof(freq), &freq, nullptr); - clGetDeviceInfo(devices[i], CL_DEVICE_MAX_COMPUTE_UNITS, sizeof(shaders), &shaders, nullptr); - clGetDeviceInfo(devices[i], CL_DEVICE_ADDRESS_BITS, sizeof(dinfo.nbits), &dinfo.nbits, nullptr); - printf("Found Device %u : %s %s (Frequency %d, Shaders %d, %d bit)\n", i, dinfo.device_vendor, dinfo.device_name, (int32_t)freq, (int32_t)shaders, (int32_t)dinfo.nbits); - } - - if (files.size() == 0) { - quit("Syntax: opencl [-output-file OUTPUT_FILE] FILE1 [FILE2] ... [FILEn] [-- COMPILER_OPTION_1] [COMPILER_OPTION_2] ... [COMPILER_OPTION_N]"); - } - - char** buffers = (char**)malloc(files.size() * sizeof(char*)); - if (buffers == nullptr) { - quit("Memory allocation error\n"); - } - for (uint32_t i = 0; i < files.size(); i++) { - printf("Reading source file %s\n", files[i]); - FILE* fp = fopen(files[i], "rb"); - if (fp == nullptr) { - printf("Cannot open %s\n", files[i]); - free(buffers); - return (1); - } - fseek(fp, 0, SEEK_END); - size_t file_size = ftell(fp); - fseek(fp, 0, SEEK_SET); - - buffers[i] = (char*)malloc(file_size + 1); - if (buffers[i] == nullptr) { - quit("Memory allocation error"); - } - if (fread(buffers[i], 1, file_size, fp) != file_size) { - quit("Error reading file"); - } - buffers[i][file_size] = 0; - fclose(fp); - } - - printf("Creating OpenCL Context\n"); - // Create OpenCL context - cl_context context = clCreateContext(nullptr, pinfo.count, devices, nullptr, nullptr, &ocl_error); - if (ocl_error != CL_SUCCESS) { - quit("Error creating OpenCL context"); - } - - printf("Creating OpenCL Program Object\n"); - // Create OpenCL program object - cl_program program = clCreateProgramWithSource(context, (cl_uint)files.size(), (const char**)buffers, nullptr, &ocl_error); - if (ocl_error != CL_SUCCESS) { - quit("Error creating program object"); - } - - printf("Compiling OpenCL Program\n"); - // Compile program - ocl_error = clBuildProgram(program, pinfo.count, devices, compiler_options.c_str(), nullptr, nullptr); - if (ocl_error != CL_SUCCESS) { - fprintf(stderr, "OpenCL Error while building program: %d (Compiler options: %s)\n", ocl_error, compiler_options.c_str()); - fprintf(stderr, "OpenCL Kernel:\n\n"); - for (uint32_t i = 0; i < files.size(); i++) { - printf("%s\n\n", buffers[i]); - } - - for (uint32_t i = 0; i < pinfo.count; i++) { - cl_build_status status; - clGetProgramBuildInfo(program, devices[i], CL_PROGRAM_BUILD_STATUS, sizeof(status), &status, nullptr); - if (status == CL_BUILD_ERROR) { - size_t log_size; - clGetProgramBuildInfo(program, devices[i], CL_PROGRAM_BUILD_LOG, 0, nullptr, &log_size); - char* build_log = (char*)malloc(log_size + 1); - if (build_log == nullptr) { - quit("Memory allocation error"); - } - clGetProgramBuildInfo(program, devices[i], CL_PROGRAM_BUILD_LOG, log_size, build_log, nullptr); - fprintf(stderr, "Build Log (device %d):\n\n%s\n\n", i, build_log); - free(build_log); - } - } - } - for (uint32_t i = 0; i < files.size(); i++) { - free(buffers[i]); - } - free(buffers); - if (ocl_error != CL_SUCCESS) { - return (1); - } - - printf("Obtaining program binaries\n"); - size_t* binary_sizes = (size_t*)malloc(pinfo.count * sizeof(size_t)); - if (binary_sizes == nullptr) { - quit("Memory allocation error"); - } - clGetProgramInfo(program, CL_PROGRAM_BINARY_SIZES, pinfo.count * sizeof(size_t), binary_sizes, nullptr); - char** binary_buffers = (char**)malloc(pinfo.count * sizeof(char*)); - if (binary_buffers == nullptr) { - quit("Memory allocation error"); - } - for (uint32_t i = 0; i < pinfo.count; i++) { - printf("Binary size for device %d: %d\n", i, (int32_t)binary_sizes[i]); - binary_buffers[i] = (char*)malloc(binary_sizes[i]); - memset(binary_buffers[i], 0, binary_sizes[i]); - if (binary_buffers[i] == nullptr) { - quit("Memory allocation error"); - } - } - clGetProgramInfo(program, CL_PROGRAM_BINARIES, pinfo.count * sizeof(char*), binary_buffers, nullptr); - - printf("Programs obtained successfully, cleaning up opencl\n"); - clReleaseProgram(program); - clReleaseContext(context); - - printf("Writing binaries to file (%s)\n", output_file); - FILE* fp; - fp = fopen(output_file, "w+b"); - if (fp == nullptr) { - quit("Error opening output file\n"); - } - const char* magic_bytes = "QOCLPB"; - fwrite(magic_bytes, 1, strlen(magic_bytes) + 1, fp); - fwrite(&pinfo, 1, sizeof(pinfo), fp); - for (uint32_t i = 0; i < pinfo.count; i++) { - clGetDeviceInfo(devices[i], CL_DEVICE_NAME, 64, dinfo.device_name, nullptr); - clGetDeviceInfo(devices[i], CL_DEVICE_VENDOR, 64, dinfo.device_vendor, nullptr); - dinfo.binary_size = binary_sizes[i]; - fwrite(&dinfo, 1, sizeof(dinfo), fp); - fwrite(binary_buffers[i], 1, binary_sizes[i], fp); - } - fclose(fp); - - printf("All done, cleaning up remaining buffers\n"); - for (uint32_t i = 0; i < pinfo.count; i++) { - free(binary_buffers[i]); - } - free(binary_sizes); - free(binary_buffers); - - return (0); -} diff --git a/GPU/GPUTracking/utils/opencl_compiler_structs.h b/GPU/GPUTracking/utils/opencl_compiler_structs.h deleted file mode 100644 index 68e0a4f184480..0000000000000 --- a/GPU/GPUTracking/utils/opencl_compiler_structs.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file opencl_compiler_structs.h -/// \author David Rohr - -struct _makefiles_opencl_platform_info { - char platform_profile[64]; - char platform_version[64]; - char platform_name[64]; - char platform_vendor[64]; - cl_uint count; -}; - -struct _makefiles_opencl_device_info { - char device_name[64]; - char device_vendor[64]; - cl_uint nbits; - size_t binary_size; -}; diff --git a/GPU/GPUTracking/utils/opencl_obtain_program.h b/GPU/GPUTracking/utils/opencl_obtain_program.h deleted file mode 100644 index 6c10ca9d47de1..0000000000000 --- a/GPU/GPUTracking/utils/opencl_obtain_program.h +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file opencl_obtain_program.h -/// \author David Rohr - -#ifndef MAKEFILES_OPENCL_OBTAIN_PROGRAMH -#define MAKEFILES_OPENCL_OBTAIN_PROGRAMH - -#include -#include -#include "opencl_compiler_structs.h" - -static int32_t _makefiles_opencl_obtain_program_helper(cl_context context, cl_uint num_devices, cl_device_id* devices, cl_program* program, char* binaries) -{ - const char* magic_bytes = "QOCLPB"; - if (strncmp(magic_bytes, binaries, strlen(magic_bytes)) != 0) { - printf("Internal error accessing opencl program\n"); - return (1); - } - char* current_ptr = binaries + strlen(magic_bytes) + 1; - _makefiles_opencl_platform_info* pinfo = (_makefiles_opencl_platform_info*)current_ptr; - current_ptr += sizeof(_makefiles_opencl_platform_info); - - if (num_devices != pinfo->count) { - printf("Number of devices differs from number of devices in opencl program\n"); - return (1); - } - // printf("Obtaining program for OpenCL Platform: (%s %s) %s %s\n", pinfo->platform_profile, pinfo->platform_version, pinfo->platform_vendor, pinfo->platform_name); - - std::vector program_sizes(pinfo->count); - std::vector program_binaries(pinfo->count); - - for (uint32_t i = 0; i < pinfo->count; i++) { - char device_name[64], device_vendor[64]; - cl_uint nbits; - clGetDeviceInfo(devices[i], CL_DEVICE_NAME, 64, device_name, nullptr); - clGetDeviceInfo(devices[i], CL_DEVICE_VENDOR, 64, device_vendor, nullptr); - clGetDeviceInfo(devices[i], CL_DEVICE_ADDRESS_BITS, sizeof(nbits), &nbits, nullptr); - _makefiles_opencl_device_info* dinfo = (_makefiles_opencl_device_info*)current_ptr; - if (strcmp(device_name, dinfo->device_name) != 0 || strcmp(device_vendor, dinfo->device_vendor) != 0) { - printf("Device list is different to device list from opencl program (Device %d: '%s - %s' != '%s - %s')\n", i, device_vendor, device_name, dinfo->device_vendor, dinfo->device_name); - return (1); - } - if (nbits != dinfo->nbits) { - printf("Pointer size of device and stored device binary differs\n"); - return (1); - } - current_ptr += sizeof(_makefiles_opencl_device_info); - // printf("Device %d: %s %s (size %ld)\n", i, dinfo->device_vendor, dinfo->device_name, (int64_t) dinfo->binary_size); - program_sizes[i] = dinfo->binary_size; - program_binaries[i] = current_ptr; - current_ptr += dinfo->binary_size; - } - - cl_int return_status[pinfo->count]; - cl_int ocl_error; - *program = clCreateProgramWithBinary(context, num_devices, devices, program_sizes.data(), (const uint8_t**)program_binaries.data(), return_status, &ocl_error); - - if (ocl_error != CL_SUCCESS) { - printf("Error loading program\n"); - return (1); - } - - for (uint32_t i = 0; i < pinfo->count; i++) { - if (return_status[i] != CL_SUCCESS) { - printf("Error loading program for device %d\n", i); - clReleaseProgram(*program); - return (1); - } - } - - ocl_error = clBuildProgram(*program, num_devices, devices, "", nullptr, nullptr); - if (ocl_error != CL_SUCCESS) { - printf("Error building program\n"); - clReleaseProgram(*program); - return (1); - } - - return (0); -} - -#endif From d64bc8603cc75d8de8e6523d75ac49f83f265364 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 5 Feb 2026 12:07:23 +0100 Subject: [PATCH 048/133] Add the .clang-format-ignore (#15019) --- .clang-format-ignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .clang-format-ignore diff --git a/.clang-format-ignore b/.clang-format-ignore new file mode 100644 index 0000000000000..a6c57f5fb2ffb --- /dev/null +++ b/.clang-format-ignore @@ -0,0 +1 @@ +*.json From 414ba09ac65b6bfe4202cab6327246817dee2646 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 5 Feb 2026 10:24:37 +0100 Subject: [PATCH 049/133] Add back tuned parameters for old architectures TAHITI TESLA FERMI PASCAL KEPLER --- .../Definitions/Parameters/.clang-format | 1 - .../Parameters/.clang-format-ignore | 1 - .../Definitions/Parameters/GPUParameters.json | 46 +++++++++++++++++-- 3 files changed, 41 insertions(+), 7 deletions(-) delete mode 100644 GPU/GPUTracking/Definitions/Parameters/.clang-format delete mode 100644 GPU/GPUTracking/Definitions/Parameters/.clang-format-ignore diff --git a/GPU/GPUTracking/Definitions/Parameters/.clang-format b/GPU/GPUTracking/Definitions/Parameters/.clang-format deleted file mode 100644 index e3845288a2aec..0000000000000 --- a/GPU/GPUTracking/Definitions/Parameters/.clang-format +++ /dev/null @@ -1 +0,0 @@ -DisableFormat: true diff --git a/GPU/GPUTracking/Definitions/Parameters/.clang-format-ignore b/GPU/GPUTracking/Definitions/Parameters/.clang-format-ignore deleted file mode 100644 index 5ffee2498bd7e..0000000000000 --- a/GPU/GPUTracking/Definitions/Parameters/.clang-format-ignore +++ /dev/null @@ -1 +0,0 @@ -GPUParameters.json diff --git a/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json index 674efc9ea0912..285919559c04c 100644 --- a/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json +++ b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json @@ -1,11 +1,17 @@ { "CORE": { "WARP_SIZE": { - "default": 32, - "MI100": 64, - "VEGA": 64, - "AMPERE": 32, - "TURING": 32 + "default": 32, + "default_cpu": 1, + "MI100": 64, + "VEGA": 64, + "TAHITI": 32, + "TESLA": 32, + "FERMI": 32, + "PASCAL": 32, + "KEPLER": 32, + "AMPERE": 32, + "TURING": 32 }, "THREAD_COUNT_DEFAULT": { "default": 256, @@ -27,6 +33,11 @@ "default": 256, "MI100": [768, 8], "VEGA": [512, 10], + "TAHITI": [256, 2], + "TESLA": [256, 1], + "FERMI": [256, 2], + "PASCAL": [1024, 2], + "KEPLER": [512, 4], "AMPERE": [256, 2], "TURING": [256, 2] }, @@ -34,6 +45,11 @@ "default": 256, "MI100": [384, 5], "VEGA": [192, 10], + "TAHITI": [256, 3], + "TESLA": [256, 1], + "FERMI": [256, 3], + "PASCAL": [512, 4], + "KEPLER": [256, 3], "AMPERE": [192, 3], "TURING": [192, 3] }, @@ -41,6 +57,11 @@ "default": 256, "MI100": [192, 8], "VEGA": [960, 8], + "TAHITI": 256, + "TESLA": 256, + "FERMI": 256, + "PASCAL": 512, + "KEPLER": 256, "AMPERE": [640, 1], "TURING": [640, 1] }, @@ -48,6 +69,11 @@ "default": 256, "MI100": [128, 5], "VEGA": [384, 9], + "TAHITI": 256, + "TESLA": 256, + "FERMI": 256, + "PASCAL": 256, + "KEPLER": 256, "AMPERE": 512, "TURING": 512 }, @@ -327,6 +353,11 @@ "default": 256, "MI100": [1024, 2], "VEGA": [1024, 7], + "TAHITI": 256, + "TESLA": 256, + "PASCAL": 256, + "FERMI": 256, + "KEPLER": 256, "AMPERE": 512, "TURING": 512 }, @@ -334,6 +365,11 @@ "default": 256, "MI100": [1024, 5], "VEGA": [512, 7], + "TAHITI": 256, + "TESLA": 256, + "PASCAL": 256, + "FERMI": 256, + "KEPLER": 256, "AMPERE": [512, 1], "TURING": [512, 1] }, From 024cbcab8238cc0a7c4a6259d518fabbfc498b91 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 5 Feb 2026 10:29:50 +0100 Subject: [PATCH 050/133] Don't use 'No CUDA devices found' as CUDA architecture --- dependencies/FindO2GPU.cmake | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/dependencies/FindO2GPU.cmake b/dependencies/FindO2GPU.cmake index e02bf932ab784..ec6b7323ad5d1 100644 --- a/dependencies/FindO2GPU.cmake +++ b/dependencies/FindO2GPU.cmake @@ -10,7 +10,7 @@ # or submit itself to any jurisdiction. # NOTE!!!! - Whenever this file is changed, move it over to alidist/resources -# FindO2GPU.cmake Version 10 +# FindO2GPU.cmake Version 11 set(CUDA_COMPUTETARGET_DEFAULT_FULL 80-real 86-real 89-real 120-real 75-virtual) set(HIP_AMDGPUTARGET_DEFAULT_FULL gfx906;gfx908) @@ -173,9 +173,7 @@ if(ENABLE_CUDA) message(${FAILURE_SEVERITY} "CUDA was found but cannot be enabled") set(CMAKE_CUDA_COMPILER OFF) endif() - find_path(THRUST_INCLUDE_DIR thrust/version.h PATHS ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES} - PATH_SUFFIXES "" cccl - NO_DEFAULT_PATH) + find_path(THRUST_INCLUDE_DIR thrust/version.h PATHS ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES} PATH_SUFFIXES "" cccl NO_DEFAULT_PATH) if(THRUST_INCLUDE_DIR STREQUAL "THRUST_INCLUDE_DIR-NOTFOUND") message(${FAILURE_SEVERITY} "CUDA found but thrust not available, looked under: ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES}") set(CMAKE_CUDA_COMPILER OFF) @@ -188,7 +186,7 @@ if(ENABLE_CUDA) endif() endif() if(NOT CMAKE_CUDA_ARCHITECTURES OR O2_GPU_CUDA_UPDATE_NATIVE_ARCHITECTURE) - if(NOT CMAKE_CUDA_ARCHITECTURES_NATIVE STREQUAL "") + if(NOT CMAKE_CUDA_ARCHITECTURES_NATIVE STREQUAL "" AND NOT CMAKE_CUDA_ARCHITECTURES_NATIVE MATCHES "No CUDA devices found") set(CMAKE_CUDA_ARCHITECTURES ${CMAKE_CUDA_ARCHITECTURES_NATIVE}) else() set(CMAKE_CUDA_ARCHITECTURES ${CUDA_COMPUTETARGET_DEFAULT_MINIMAL}) From 9e910d691cb379f93c7c1dc7a09ee9193cddaa06 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 5 Feb 2026 15:59:08 +0100 Subject: [PATCH 051/133] GPU: Add converter scripts for CSV parameter file to JSON and vice versa --- .../Base/cuda/GPUReconstructionCUDA.cu | 2 +- .../Definitions/Parameters/csv_to_json.sh | 46 +++++++++++++++++ .../Definitions/Parameters/json_to_csv.python | 50 +++++++++++++++++++ 3 files changed, 97 insertions(+), 1 deletion(-) create mode 100755 GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh create mode 100755 GPU/GPUTracking/Definitions/Parameters/json_to_csv.python diff --git a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu index 8e896ca513f53..c919581eefdde 100644 --- a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu +++ b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu @@ -189,7 +189,7 @@ int32_t GPUReconstructionCUDA::InitDevice_Runtime() bestDeviceSpeed = deviceSpeed; } else { if (GetProcessingSettings().debugLevel >= 2 && GetProcessingSettings().deviceNum < 0) { - GPUInfo("Skipping: Speed %f < %f\n", deviceSpeed, bestDeviceSpeed); + GPUInfo("Skipping: Speed %f <= %f\n", deviceSpeed, bestDeviceSpeed); } } } diff --git a/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh b/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh new file mode 100755 index 0000000000000..ae9d3b7704284 --- /dev/null +++ b/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +[[ -z $1 ]] && { echo "Usage: csv_to_json.sh CSV_FILE"; exit 1; } + +awk -vFPAT='([^,]*)|(\"([^\"]|\"\")*\")' \ + 'BEGIN { + print "{" + } { + if (count == 0) { + for (i = 1; i <= NF; i++) { + names[i] = $i + } + } else if ($1 == "CORE:" || $1 == "LB:" || $1 == "PAR:") { + if (paramprinted) print "\n }" + else if (lineprinted) print "" + if (catprinted) print " }," + lineprinted = 0 + paramprinted = 0 + catprinted = 1 + gsub(/:$/, "", $1) + print " \""$1"\": {"; + } else if ($1 != "") { + if (lineprinted) print "" + if (paramprinted) print " }," + lineprinted = 0 + paramprinted = 1 + print " \""$1"\": {"; + lineprinted = 0 + for (i=2; i<=NF; i++) { + if ($i != "") { + gsub(/^"/, "", $i) + gsub(/"$/, "", $i) + gsub(/""/, "\"", $i) + if (lineprinted) print "," + lineprinted = 1 + printf(" \"%s\": %s", names[i], $i) + } + } + } + count++; + } END { + if (paramprinted) print "\n }" + if (catprinted) print " }" + print "}" + }' \ + $1 diff --git a/GPU/GPUTracking/Definitions/Parameters/json_to_csv.python b/GPU/GPUTracking/Definitions/Parameters/json_to_csv.python new file mode 100755 index 0000000000000..a6640239604e0 --- /dev/null +++ b/GPU/GPUTracking/Definitions/Parameters/json_to_csv.python @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +import sys, json, csv, string + +if len(sys.argv) != 3: + sys.exit("usage: json2csv.py input.json output.csv") + +try: + data = json.load(open(sys.argv[1])) +except Exception: + sys.exit("invalid json") + +if set(data) != {"CORE", "LB", "PAR"}: + sys.exit("invalid categories") + +arches = [] +seen = set() +for cat in data.values(): + if not isinstance(cat, dict): + sys.exit("data not 2-dimensional") + for param in cat.values(): + if not isinstance(param, dict): + sys.exit("data not 2-dimensional") + for a in param.keys(): + if a not in seen: + seen.add(a) + arches.append(a) + +cols = 1 + len(arches) +empty = [""] * cols + +with open(sys.argv[2], "w", newline="") as f: + w = csv.writer(f, lineterminator="\n") + w.writerow(["Architecture", *arches]) + w.writerow(empty) + cats = list(data.items()) + for ci, (cname, cat) in enumerate(cats): + w.writerow([f"{cname}:"] + [""] * (cols - 1)) + for pname, param in cat.items(): + row = [pname] + for a in arches: + v = param.get(a, "") + if isinstance(v, list): + row.append(json.dumps(v)) + elif isinstance(v, str) and not v == "": + row.append('"' + v + '"') + else: + row.append(v) + w.writerow(row) + if ci != len(cats) - 1: + w.writerow(empty) From c8834dee0b39f86f394dfb07f5a42ab09659dffa Mon Sep 17 00:00:00 2001 From: shahoian Date: Thu, 5 Feb 2026 11:27:52 +0100 Subject: [PATCH 052/133] Promote --ctf-dict from process to workflow level option --- .../Base/include/DetectorsBase/CTFCoderBase.h | 14 ++++---- .../include/CPVReconstruction/CTFCoder.h | 2 +- .../include/CPVWorkflow/EntropyDecoderSpec.h | 4 +-- .../include/CPVWorkflow/EntropyEncoderSpec.h | 4 +-- .../CPV/workflow/src/EntropyDecoderSpec.cxx | 13 +++---- .../CPV/workflow/src/EntropyEncoderSpec.cxx | 13 +++---- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- .../include/CTFWorkflow/CTFReaderSpec.h | 1 + Detectors/CTF/workflow/src/CTFReaderSpec.cxx | 1 - .../CTF/workflow/src/ctf-reader-workflow.cxx | 34 ++++++++++--------- .../include/CTPReconstruction/CTFCoder.h | 2 +- .../include/CTPWorkflow/EntropyDecoderSpec.h | 4 +-- .../include/CTPWorkflow/EntropyEncoderSpec.h | 4 +-- .../CTP/workflow/src/EntropyDecoderSpec.cxx | 16 ++++----- .../CTP/workflow/src/EntropyEncoderSpec.cxx | 16 ++++----- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- .../include/EMCALReconstruction/CTFCoder.h | 2 +- .../EMCALWorkflow/EntropyDecoderSpec.h | 4 +-- .../EMCALWorkflow/EntropyEncoderSpec.h | 4 +-- .../EMCAL/workflow/src/EntropyDecoderSpec.cxx | 16 ++++----- .../EMCAL/workflow/src/EntropyEncoderSpec.cxx | 23 ++++++------- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- .../include/FDDReconstruction/CTFCoder.h | 2 +- .../include/FDDWorkflow/EntropyDecoderSpec.h | 4 +-- .../include/FDDWorkflow/EntropyEncoderSpec.h | 4 +-- .../FDD/workflow/src/EntropyDecoderSpec.cxx | 16 ++++----- .../FDD/workflow/src/EntropyEncoderSpec.cxx | 16 ++++----- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- .../include/FT0Reconstruction/CTFCoder.h | 2 +- .../include/FT0Workflow/EntropyDecoderSpec.h | 4 +-- .../include/FT0Workflow/EntropyEncoderSpec.h | 4 +-- .../FT0/workflow/src/EntropyDecoderSpec.cxx | 15 ++++---- .../FT0/workflow/src/EntropyEncoderSpec.cxx | 16 ++++----- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- .../include/FV0Reconstruction/CTFCoder.h | 2 +- .../include/FV0Workflow/EntropyDecoderSpec.h | 4 +-- .../include/FV0Workflow/EntropyEncoderSpec.h | 4 +-- .../FV0/workflow/src/EntropyDecoderSpec.cxx | 16 ++++----- .../FV0/workflow/src/EntropyEncoderSpec.cxx | 16 ++++----- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- .../include/HMPIDReconstruction/CTFCoder.h | 2 +- .../HMPIDWorkflow/EntropyDecoderSpec.h | 2 +- .../HMPIDWorkflow/EntropyEncoderSpec.h | 2 +- .../HMPID/workflow/src/EntropyDecoderSpec.cxx | 18 +++++----- .../HMPID/workflow/src/EntropyEncoderSpec.cxx | 18 +++++----- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- .../include/ITSMFTReconstruction/CTFCoder.h | 2 +- .../ITSMFTWorkflow/EntropyDecoderSpec.h | 4 +-- .../ITSMFTWorkflow/EntropyEncoderSpec.h | 4 +-- .../workflow/src/EntropyDecoderSpec.cxx | 22 ++++++------ .../workflow/src/EntropyEncoderSpec.cxx | 18 +++++----- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- .../MUON/MCH/CTF/include/MCHCTF/CTFCoder.h | 2 +- .../CTF/include/MCHCTF/EntropyDecoderSpec.h | 2 +- .../MUON/MCH/CTF/src/EntropyDecoderSpec.cxx | 18 +++++----- .../Workflow/src/entropy-encoder-workflow.cxx | 21 ++++++------ .../MUON/MID/CTF/include/MIDCTF/CTFCoder.h | 2 +- .../include/MIDWorkflow/EntropyDecoderSpec.h | 4 +-- .../include/MIDWorkflow/EntropyEncoderSpec.h | 4 +-- .../MID/Workflow/src/EntropyDecoderSpec.cxx | 16 ++++----- .../MID/Workflow/src/EntropyEncoderSpec.cxx | 16 ++++----- .../Workflow/src/entropy-encoder-workflow.cxx | 3 +- .../include/PHOSReconstruction/CTFCoder.h | 2 +- .../include/PHOSWorkflow/EntropyDecoderSpec.h | 4 +-- .../include/PHOSWorkflow/EntropyEncoderSpec.h | 4 +-- .../PHOS/workflow/src/EntropyDecoderSpec.cxx | 16 ++++----- .../PHOS/workflow/src/EntropyEncoderSpec.cxx | 16 ++++----- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- .../include/TOFReconstruction/CTFCoder.h | 2 +- .../TOFWorkflowUtils/EntropyDecoderSpec.h | 4 +-- .../TOFWorkflowUtils/EntropyEncoderSpec.h | 4 +-- .../TOF/workflow/src/EntropyDecoderSpec.cxx | 16 ++++----- .../TOF/workflow/src/EntropyEncoderSpec.cxx | 16 ++++----- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- .../include/TPCReconstruction/CTFCoder.h | 2 +- .../include/TPCWorkflow/EntropyDecoderSpec.h | 4 +-- .../include/TPCWorkflow/EntropyEncoderSpec.h | 4 +-- .../include/TPCWorkflow/RecoWorkflow.h | 1 + .../TPC/workflow/src/EntropyDecoderSpec.cxx | 14 ++++---- .../TPC/workflow/src/EntropyEncoderSpec.cxx | 16 ++++----- Detectors/TPC/workflow/src/RecoWorkflow.cxx | 4 +-- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- .../TPC/workflow/src/tpc-reco-workflow.cxx | 2 ++ .../include/TRDReconstruction/CTFCoder.h | 2 +- .../include/TRDWorkflow/EntropyDecoderSpec.h | 2 +- .../include/TRDWorkflow/EntropyEncoderSpec.h | 2 +- .../TRD/workflow/src/EntropyDecoderSpec.cxx | 18 +++++----- .../TRD/workflow/src/EntropyEncoderSpec.cxx | 18 +++++----- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- .../include/ZDCReconstruction/CTFCoder.h | 2 +- .../include/ZDCWorkflow/EntropyDecoderSpec.h | 4 +-- .../include/ZDCWorkflow/EntropyEncoderSpec.h | 4 +-- .../ZDC/workflow/src/EntropyDecoderSpec.cxx | 16 ++++----- .../ZDC/workflow/src/EntropyEncoderSpec.cxx | 16 ++++----- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- 95 files changed, 378 insertions(+), 358 deletions(-) diff --git a/Detectors/Base/include/DetectorsBase/CTFCoderBase.h b/Detectors/Base/include/DetectorsBase/CTFCoderBase.h index bf4f37ecbeff5..593bf37df5879 100644 --- a/Detectors/Base/include/DetectorsBase/CTFCoderBase.h +++ b/Detectors/Base/include/DetectorsBase/CTFCoderBase.h @@ -58,8 +58,8 @@ class CTFCoderBase Decoder }; CTFCoderBase() = delete; - CTFCoderBase(int n, DetID det, float memFactor = 1.f) : mCoders(n), mDet(det), mMemMarginFactor(memFactor > 1.f ? memFactor : 1.f) {} - CTFCoderBase(OpType op, int n, DetID det, float memFactor = 1.f) : mOpType(op), mCoders(n), mDet(det), mMemMarginFactor(memFactor > 1.f ? memFactor : 1.f) {} + CTFCoderBase(int n, DetID det, float memFactor = 1.f, const std::string& ctfdictOpt = "none") : mCoders(n), mDet(det), mMemMarginFactor(memFactor > 1.f ? memFactor : 1.f), mDictOpt{ctfdictOpt} {} + CTFCoderBase(OpType op, int n, DetID det, float memFactor = 1.f, const std::string& ctfdictOpt = "none") : mOpType(op), mCoders(n), mDet(det), mMemMarginFactor(memFactor > 1.f ? memFactor : 1.f), mDictOpt{ctfdictOpt} {} virtual ~CTFCoderBase() = default; virtual void createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBase::OpType op) = 0; @@ -189,6 +189,7 @@ class CTFCoderBase std::vector loadDictionaryFromTree(TTree* tree); std::vector mCoders; // encoders/decoders DetID mDet; + std::string mDictOpt{}; std::string mDictBinding{"ctfdict"}; std::string mTrigOffsBinding{"trigoffset"}; CTFDictHeader mExtHeader; // external dictionary header @@ -325,13 +326,12 @@ void CTFCoderBase::init(o2::framework::InitContext& ic) } } } - auto dict = ic.options().get("ctf-dict"); - if (dict.empty() || dict == "ccdb") { // load from CCDB + if (mDictOpt.empty() || mDictOpt == "ccdb") { // load from CCDB mLoadDictFromCCDB = true; } else { - if (dict != "none") { // none means per-CTF dictionary will created on the fly - createCodersFromFile(dict, mOpType); - LOGP(info, "Loaded {} from {}", mExtHeader.asString(), dict); + if (mDictOpt != "none") { // none means per-CTF dictionary will created on the fly + createCodersFromFile(mDictOpt, mOpType); + LOGP(info, "Loaded {} from {}", mExtHeader.asString(), mDictOpt); } else { LOGP(info, "Internal per-TF CTF Dict will be created"); } diff --git a/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h b/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h index ab5082b5c748c..a5f9d0eac90e8 100644 --- a/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h +++ b/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h @@ -35,7 +35,7 @@ namespace cpv class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::CPV) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::CPV, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/CPV/workflow/include/CPVWorkflow/EntropyDecoderSpec.h b/Detectors/CPV/workflow/include/CPVWorkflow/EntropyDecoderSpec.h index 09de778360d74..7192b1b2f6353 100644 --- a/Detectors/CPV/workflow/include/CPVWorkflow/EntropyDecoderSpec.h +++ b/Detectors/CPV/workflow/include/CPVWorkflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ namespace cpv class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -41,7 +41,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt); } // namespace cpv } // namespace o2 diff --git a/Detectors/CPV/workflow/include/CPVWorkflow/EntropyEncoderSpec.h b/Detectors/CPV/workflow/include/CPVWorkflow/EntropyEncoderSpec.h index 24c229179fe1d..a1851ebb97377 100644 --- a/Detectors/CPV/workflow/include/CPVWorkflow/EntropyEncoderSpec.h +++ b/Detectors/CPV/workflow/include/CPVWorkflow/EntropyEncoderSpec.h @@ -28,7 +28,7 @@ namespace cpv class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR = false); + EntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -42,7 +42,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace cpv } // namespace o2 diff --git a/Detectors/CPV/workflow/src/EntropyDecoderSpec.cxx b/Detectors/CPV/workflow/src/EntropyDecoderSpec.cxx index 7c14dc70dd430..518a646e23cb9 100644 --- a/Detectors/CPV/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/CPV/workflow/src/EntropyDecoderSpec.cxx @@ -25,7 +25,7 @@ namespace o2 namespace cpv { -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); @@ -74,7 +74,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"triggers"}, "CPV", "CLUSTERTRIGRECS", 0, Lifetime::Timeframe}, @@ -83,16 +83,17 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) std::vector inputs; inputs.emplace_back("ctf_CPV", "CPV", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_CPV", "CPV", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CPV/Calib/CTFDictionaryTree")); + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_CPV", "CPV", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CPV/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "cpv-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } } // namespace cpv diff --git a/Detectors/CPV/workflow/src/EntropyEncoderSpec.cxx b/Detectors/CPV/workflow/src/EntropyEncoderSpec.cxx index 31ed720e66335..54fb1354ad60c 100644 --- a/Detectors/CPV/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/CPV/workflow/src/EntropyEncoderSpec.cxx @@ -26,7 +26,7 @@ namespace o2 namespace cpv { -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -70,12 +70,14 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("triggers", "CPV", "CLUSTERTRIGRECS", 0, Lifetime::Timeframe); inputs.emplace_back("clusters", "CPV", "CLUSTERS", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "CPV", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CPV/Calib/CTFDictionaryTree")); + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "CPV", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CPV/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -84,9 +86,8 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) inputs, Outputs{{"CPV", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "CPV", "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; diff --git a/Detectors/CPV/workflow/src/entropy-encoder-workflow.cxx b/Detectors/CPV/workflow/src/entropy-encoder-workflow.cxx index d7e79c4cea430..6f9445d9ddd16 100644 --- a/Detectors/CPV/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/CPV/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -37,6 +38,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::cpv::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::cpv::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h b/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h index ab03649c0646b..081e6cf4d968a 100644 --- a/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h +++ b/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h @@ -32,6 +32,7 @@ struct CTFReaderInp { std::string metricChannel{}; std::string fileIRFrames{}; std::string fileRunTimeSpans{}; + std::string dictOpt{}; std::vector ctfIDs{}; bool reverseCTFIDs{false}; bool skipSkimmedOutTF = false; diff --git a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx index 3810230637e5f..4100ebb37c61d 100644 --- a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx +++ b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx @@ -645,7 +645,6 @@ DataProcessorSpec getCTFReaderSpec(const CTFReaderInp& inp) if (!inp.sup0xccdb) { outputs.emplace_back(OutputSpec{{"TFDist"}, o2::header::gDataOriginFLP, o2::header::gDataDescriptionDISTSTF, 0xccdb}); } - options.emplace_back(ConfigParamSpec{"select-ctf-ids", VariantType::String, "", {"comma-separated list CTF IDs to inject (from cumulative counter of CTFs seen)"}}); options.emplace_back(ConfigParamSpec{"reverse-select-ctf-ids", VariantType::Bool, false, {"reverse order of to inject CTF IDs"}}); options.emplace_back(ConfigParamSpec{"impose-run-start-timstamp", VariantType::Int64, 0L, {"impose run start time stamp (ms), ignored if 0"}}); diff --git a/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx b/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx index cddf694251a01..fc50c971c5d20 100644 --- a/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx +++ b/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx @@ -52,6 +52,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options; options.push_back(ConfigParamSpec{"ctf-input", VariantType::String, "none", {"comma-separated list CTF input files"}}); + options.push_back(ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}); options.push_back(ConfigParamSpec{"onlyDet", VariantType::String, std::string{DetID::ALL}, {"comma-separated list of detectors to accept. Overrides skipDet"}}); options.push_back(ConfigParamSpec{"skipDet", VariantType::String, std::string{DetID::NONE}, {"comma-separate list of detectors to skip"}}); options.push_back(ConfigParamSpec{"loop", VariantType::Int, 0, {"loop N times (infinite for N<0)"}}); @@ -132,6 +133,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) ctfInput.fileRunTimeSpans = configcontext.options().get("run-time-span-file"); ctfInput.skipSkimmedOutTF = configcontext.options().get("skip-skimmed-out-tf"); ctfInput.invertIRFramesSelection = configcontext.options().get("invert-irframe-selection"); + ctfInput.dictOpt = configcontext.options().get("ctf-dict"); int verbosity = configcontext.options().get("ctf-reader-verbosity"); int rateLimitingIPCID = std::stoi(configcontext.options().get("timeframes-rate-limit-ipcid")); @@ -181,52 +183,52 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // add decoders for all allowed detectors. if (ctfInput.detMask[DetID::ITS]) { - addSpecs(o2::itsmft::getEntropyDecoderSpec(DetID::getDataOrigin(DetID::ITS), verbosity, configcontext.options().get("its-digits"), ctfInput.subspec)); + addSpecs(o2::itsmft::getEntropyDecoderSpec(DetID::getDataOrigin(DetID::ITS), verbosity, configcontext.options().get("its-digits"), ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::MFT]) { - addSpecs(o2::itsmft::getEntropyDecoderSpec(DetID::getDataOrigin(DetID::MFT), verbosity, configcontext.options().get("mft-digits"), ctfInput.subspec)); + addSpecs(o2::itsmft::getEntropyDecoderSpec(DetID::getDataOrigin(DetID::MFT), verbosity, configcontext.options().get("mft-digits"), ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::TPC]) { - addSpecs(o2::tpc::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::tpc::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::TRD]) { - addSpecs(o2::trd::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::trd::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::TOF]) { - addSpecs(o2::tof::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::tof::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::FT0]) { - addSpecs(o2::ft0::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::ft0::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::FV0]) { - addSpecs(o2::fv0::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::fv0::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::FDD]) { - addSpecs(o2::fdd::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::fdd::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::MID]) { - addSpecs(o2::mid::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::mid::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::MCH]) { - addSpecs(o2::mch::getEntropyDecoderSpec(verbosity, "mch-entropy-decoder", ctfInput.subspec)); + addSpecs(o2::mch::getEntropyDecoderSpec(verbosity, "mch-entropy-decoder", ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::EMC]) { - addSpecs(o2::emcal::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.decSSpecEMC)); + addSpecs(o2::emcal::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.decSSpecEMC, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::PHS]) { - addSpecs(o2::phos::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::phos::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::CPV]) { - addSpecs(o2::cpv::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::cpv::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::ZDC]) { - addSpecs(o2::zdc::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::zdc::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::HMP]) { - addSpecs(o2::hmpid::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::hmpid::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::CTP]) { - addSpecs(o2::ctp::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::ctp::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } bool combine = configcontext.options().get("combine-devices"); diff --git a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h index 87657f6a6f8c6..b17db0e77be28 100644 --- a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h +++ b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h @@ -37,7 +37,7 @@ namespace ctp class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::CTP) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::CTP, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/CTP/workflow/include/CTPWorkflow/EntropyDecoderSpec.h b/Detectors/CTP/workflow/include/CTPWorkflow/EntropyDecoderSpec.h index eee7abb08d16c..dda45c9f11a34 100644 --- a/Detectors/CTP/workflow/include/CTPWorkflow/EntropyDecoderSpec.h +++ b/Detectors/CTP/workflow/include/CTPWorkflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ namespace ctp class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -42,7 +42,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt); } // namespace ctp } // namespace o2 diff --git a/Detectors/CTP/workflow/include/CTPWorkflow/EntropyEncoderSpec.h b/Detectors/CTP/workflow/include/CTPWorkflow/EntropyEncoderSpec.h index 3a023ce2022dc..a63119264e071 100644 --- a/Detectors/CTP/workflow/include/CTPWorkflow/EntropyEncoderSpec.h +++ b/Detectors/CTP/workflow/include/CTPWorkflow/EntropyEncoderSpec.h @@ -28,7 +28,7 @@ namespace ctp class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR, bool noLumi); + EntropyEncoderSpec(bool selIR, bool noLumi, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -43,7 +43,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, bool noLumiInput = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, bool noLumiInput = false, const std::string& ctfdictOpt = "none"); } // namespace ctp } // namespace o2 diff --git a/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx b/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx index 8c2f5d05aa031..0fa8fb0004e4c 100644 --- a/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx @@ -24,8 +24,7 @@ namespace o2 { namespace ctp { - -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); @@ -90,7 +89,7 @@ void EntropyDecoderSpec::updateTimeDependentParams(framework::ProcessingContext& } } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"digits"}, "CTP", "DIGITS", 0, Lifetime::Timeframe}, @@ -99,18 +98,19 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) std::vector inputs; inputs.emplace_back("ctf_CTP", "CTP", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_CTP", "CTP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CTP/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_CTP", "CTP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CTP/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); inputs.emplace_back("ctpconfig", "CTP", "CTPCONFIG", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/Config", 1)); return DataProcessorSpec{ "ctp-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"ignore-ctpinputs-decoding-ctf", VariantType::Bool, false, {"Inputs alignment: false - CTF decoder - has to be compatible with reco: allowed options: 10,01,00"}}, + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"ignore-ctpinputs-decoding-ctf", VariantType::Bool, false, {"Inputs alignment: false - CTF decoder - has to be compatible with reco: allowed options: 10,01,00"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace ctp } // namespace o2 diff --git a/Detectors/CTP/workflow/src/EntropyEncoderSpec.cxx b/Detectors/CTP/workflow/src/EntropyEncoderSpec.cxx index 44e64d7505977..902fe22dadcc9 100644 --- a/Detectors/CTP/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/CTP/workflow/src/EntropyEncoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace ctp { - -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, bool nolumi) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR), mNoLumi(nolumi) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, bool nolumi, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR), mNoLumi(nolumi) { mTimer.Stop(); mTimer.Reset(); @@ -77,14 +76,17 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR, bool nolumi) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, bool nolumi, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("digits", "CTP", "DIGITS", 0, Lifetime::Timeframe); if (!nolumi) { inputs.emplace_back("CTPLumi", "CTP", "LUMI", 0, Lifetime::Timeframe); } - inputs.emplace_back("ctfdict", "CTP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CTP/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "CTP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CTP/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -92,13 +94,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR, bool nolumi) "ctp-entropy-encoder", inputs, Outputs{{"CTP", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "CTP", "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR, nolumi)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, nolumi, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace ctp } // namespace o2 diff --git a/Detectors/CTP/workflow/src/entropy-encoder-workflow.cxx b/Detectors/CTP/workflow/src/entropy-encoder-workflow.cxx index 1fcaa89be9888..9057d16df4384 100644 --- a/Detectors/CTP/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/CTP/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"no-lumi-input", VariantType::Bool, false, {"Lumi info not available"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; @@ -38,6 +39,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::ctp::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("no-lumi-input"))); + wf.emplace_back(o2::ctp::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("no-lumi-input"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h index 23deb75ffb049..6584775057d9f 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h @@ -35,7 +35,7 @@ namespace emcal class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::EMC) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::EMC, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyDecoderSpec.h b/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyDecoderSpec.h index 0215e0ae65e43..9cc5ba7887473 100644 --- a/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyDecoderSpec.h +++ b/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ namespace emcal class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity, unsigned int sspecOut); + EntropyDecoderSpec(int verbosity, unsigned int sspecOut, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -42,7 +42,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspecInp, unsigned int sspecOut = 0); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspecInp, unsigned int sspecOut = 0, const std::string& ctfdictOpt = "none"); } // namespace emcal } // namespace o2 diff --git a/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyEncoderSpec.h b/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyEncoderSpec.h index cdfb342e7ff11..df502beef30df 100644 --- a/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyEncoderSpec.h +++ b/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyEncoderSpec.h @@ -28,7 +28,7 @@ namespace emcal class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR); + EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -42,7 +42,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace emcal } // namespace o2 diff --git a/Detectors/EMCAL/workflow/src/EntropyDecoderSpec.cxx b/Detectors/EMCAL/workflow/src/EntropyDecoderSpec.cxx index 700f468e9e73d..ecc0e45492bea 100644 --- a/Detectors/EMCAL/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/EMCAL/workflow/src/EntropyDecoderSpec.cxx @@ -24,8 +24,7 @@ namespace o2 { namespace emcal { - -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, unsigned int sspecOut) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder), mSSpecOut(sspecOut) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, unsigned int sspecOut, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt), mSSpecOut(sspecOut) { mTimer.Stop(); mTimer.Reset(); @@ -74,7 +73,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspecInp, unsigned int sspecOut) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspecInp, unsigned int sspecOut, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"triggers"}, "EMC", "CELLSTRGR", sspecOut, Lifetime::Timeframe}, @@ -83,17 +82,18 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspecInp, un std::vector inputs; inputs.emplace_back("ctf_EMC", "EMC", "CTFDATA", sspecInp, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_EMC", "EMC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("EMC/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_EMC", "EMC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("EMC/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "emcal-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity, sspecOut)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, sspecOut, ctfdictOpt)}, + Options{{"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace emcal } // namespace o2 diff --git a/Detectors/EMCAL/workflow/src/EntropyEncoderSpec.cxx b/Detectors/EMCAL/workflow/src/EntropyEncoderSpec.cxx index 773c4c65fc9fe..2928a71a167bc 100644 --- a/Detectors/EMCAL/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/EMCAL/workflow/src/EntropyEncoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace emcal { - -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -71,12 +70,15 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("triggers", "EMC", "CELLSTRGR", 0, Lifetime::Timeframe); inputs.emplace_back("cells", "EMC", "CELLS", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "EMC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("EMC/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "EMC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("EMC/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -85,14 +87,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) inputs, Outputs{{"EMC", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "EMC", "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{ - {"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, - {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, - {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, - {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, + {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, + {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace emcal } // namespace o2 diff --git a/Detectors/EMCAL/workflow/src/entropy-encoder-workflow.cxx b/Detectors/EMCAL/workflow/src/entropy-encoder-workflow.cxx index e6af02fa10d49..953b726fcb971 100644 --- a/Detectors/EMCAL/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/EMCAL/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -37,6 +38,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::emcal::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::emcal::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h index 94a0c6f64659d..cb3b13aa9b8e4 100644 --- a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h +++ b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h @@ -36,7 +36,7 @@ namespace fdd class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::FDD) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::FDD, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode digits to buffer with CTF diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyDecoderSpec.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyDecoderSpec.h index a6ee132ee0c34..1fd3cd7835cd9 100644 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyDecoderSpec.h +++ b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ namespace fdd class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -41,7 +41,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt); } // namespace fdd } // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyEncoderSpec.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyEncoderSpec.h index 87dcca02e869f..37d43f477e836 100644 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyEncoderSpec.h +++ b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyEncoderSpec.h @@ -28,7 +28,7 @@ namespace fdd class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR); + EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -42,7 +42,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace fdd } // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx b/Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx index fb5b173fb7a94..33c140b5bc198 100644 --- a/Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx @@ -24,8 +24,7 @@ namespace o2 { namespace fdd { - -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); @@ -73,7 +72,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"digits"}, "FDD", "DIGITSBC", 0, Lifetime::Timeframe}, @@ -82,17 +81,18 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) std::vector inputs; inputs.emplace_back("ctf_FDD", "FDD", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_FDD", "FDD", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FDD/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_FDD", "FDD", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FDD/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "fdd-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace fdd } // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/EntropyEncoderSpec.cxx b/Detectors/FIT/FDD/workflow/src/EntropyEncoderSpec.cxx index abb2518e5ae0b..be81f7ca7d3d4 100644 --- a/Detectors/FIT/FDD/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/FIT/FDD/workflow/src/EntropyEncoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace fdd { - -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -69,12 +68,15 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("digits", "FDD", "DIGITSBC", 0, Lifetime::Timeframe); inputs.emplace_back("channels", "FDD", "DIGITSCH", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "FDD", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FDD/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "FDD", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FDD/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -82,13 +84,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) "fdd-entropy-encoder", inputs, Outputs{{"FDD", "CTFDATA", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace fdd } // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/entropy-encoder-workflow.cxx b/Detectors/FIT/FDD/workflow/src/entropy-encoder-workflow.cxx index bcc42ebc2e086..0e43c6e3c4ba0 100644 --- a/Detectors/FIT/FDD/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/FIT/FDD/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -37,6 +38,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::fdd::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::fdd::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h index 65646c161dde5..5c2e0f0627ef1 100644 --- a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h +++ b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h @@ -37,7 +37,7 @@ namespace ft0 class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::FT0) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::FT0, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode digits to buffer with CTF diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyDecoderSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyDecoderSpec.h index 4f8e8b5e9be63..d6009accfa45b 100644 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyDecoderSpec.h +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ namespace ft0 class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -41,7 +41,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt); } // namespace ft0 } // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyEncoderSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyEncoderSpec.h index 8fd597af8629d..a1b3714fdbb26 100644 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyEncoderSpec.h +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyEncoderSpec.h @@ -28,7 +28,7 @@ namespace ft0 class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR); + EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -42,7 +42,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace ft0 } // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx b/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx index 65d3585350888..066c5cc547c2e 100644 --- a/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx @@ -24,8 +24,7 @@ namespace o2 { namespace ft0 { - -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); @@ -73,7 +72,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"digits"}, "FT0", "DIGITSBC", 0, Lifetime::Timeframe}, @@ -82,16 +81,18 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) std::vector inputs; inputs.emplace_back("ctf_FT0", "FT0", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_FT0", "FT0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FT0/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_FT0", "FT0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FT0/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "ft0-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace ft0 } // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/EntropyEncoderSpec.cxx b/Detectors/FIT/FT0/workflow/src/EntropyEncoderSpec.cxx index 81bdc2e729bb4..7be6618a61103 100644 --- a/Detectors/FIT/FT0/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/FIT/FT0/workflow/src/EntropyEncoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace ft0 { - -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -70,12 +69,15 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("digits", "FT0", "DIGITSBC", 0, Lifetime::Timeframe); inputs.emplace_back("channels", "FT0", "DIGITSCH", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "FT0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FT0/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "FT0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FT0/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -83,13 +85,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) "ft0-entropy-encoder", inputs, Outputs{{"FT0", "CTFDATA", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace ft0 } // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/entropy-encoder-workflow.cxx b/Detectors/FIT/FT0/workflow/src/entropy-encoder-workflow.cxx index 6a98bbdafd53b..2b4a86df0a614 100644 --- a/Detectors/FIT/FT0/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/FIT/FT0/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -37,6 +38,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::ft0::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::ft0::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h index 4398e19c0a5ed..fdff035b934ef 100644 --- a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h +++ b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h @@ -33,7 +33,7 @@ namespace fv0 class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::FV0) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::FV0, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode digits to buffer with CTF diff --git a/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyDecoderSpec.h b/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyDecoderSpec.h index 67b74f45e42bf..76f1aae5e728d 100644 --- a/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyDecoderSpec.h +++ b/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ namespace fv0 class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -41,7 +41,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt); } // namespace fv0 } // namespace o2 diff --git a/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyEncoderSpec.h b/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyEncoderSpec.h index db4f154a302c7..0df9403a88a12 100644 --- a/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyEncoderSpec.h +++ b/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyEncoderSpec.h @@ -28,7 +28,7 @@ namespace fv0 class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR); + EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -42,7 +42,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace fv0 } // namespace o2 diff --git a/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx b/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx index 9310905ad41b9..7babe9fdea6ed 100644 --- a/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx @@ -24,8 +24,7 @@ namespace o2 { namespace fv0 { - -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); @@ -73,7 +72,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"digits"}, "FV0", "DIGITSBC", 0, Lifetime::Timeframe}, @@ -82,17 +81,18 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) std::vector inputs; inputs.emplace_back("ctf_FV0", "FV0", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_FV0", "FV0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FV0/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_FV0", "FV0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FV0/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "fv0-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace fv0 } // namespace o2 diff --git a/Detectors/FIT/FV0/workflow/src/EntropyEncoderSpec.cxx b/Detectors/FIT/FV0/workflow/src/EntropyEncoderSpec.cxx index a25c16a5d697c..2448af09fac4e 100644 --- a/Detectors/FIT/FV0/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/FIT/FV0/workflow/src/EntropyEncoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace fv0 { - -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -71,12 +70,15 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("digits", "FV0", "DIGITSBC", 0, Lifetime::Timeframe); inputs.emplace_back("channels", "FV0", "DIGITSCH", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "FV0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FV0/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "FV0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FV0/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -85,13 +87,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) inputs, Outputs{{"FV0", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "FV0", "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace fv0 } // namespace o2 diff --git a/Detectors/FIT/FV0/workflow/src/entropy-encoder-workflow.cxx b/Detectors/FIT/FV0/workflow/src/entropy-encoder-workflow.cxx index 90f37996b55b7..f1b1bfa456316 100644 --- a/Detectors/FIT/FV0/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/FIT/FV0/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -37,6 +38,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::fv0::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::fv0::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h index 39242355a3de9..0e6694d2353ac 100644 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h +++ b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h @@ -35,7 +35,7 @@ namespace hmpid class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::HMP) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::HMP, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/HMPID/workflow/include/HMPIDWorkflow/EntropyDecoderSpec.h b/Detectors/HMPID/workflow/include/HMPIDWorkflow/EntropyDecoderSpec.h index 8c64f326a6878..d03a30ab905e5 100644 --- a/Detectors/HMPID/workflow/include/HMPIDWorkflow/EntropyDecoderSpec.h +++ b/Detectors/HMPID/workflow/include/HMPIDWorkflow/EntropyDecoderSpec.h @@ -24,7 +24,7 @@ namespace hmpid { /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt = "none"); } // namespace hmpid } // namespace o2 diff --git a/Detectors/HMPID/workflow/include/HMPIDWorkflow/EntropyEncoderSpec.h b/Detectors/HMPID/workflow/include/HMPIDWorkflow/EntropyEncoderSpec.h index 2fb9fd301f13b..9c2c4eb5b4fb0 100644 --- a/Detectors/HMPID/workflow/include/HMPIDWorkflow/EntropyEncoderSpec.h +++ b/Detectors/HMPID/workflow/include/HMPIDWorkflow/EntropyEncoderSpec.h @@ -24,7 +24,7 @@ namespace hmpid { /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace hmpid } // namespace o2 diff --git a/Detectors/HMPID/workflow/src/EntropyDecoderSpec.cxx b/Detectors/HMPID/workflow/src/EntropyDecoderSpec.cxx index aa22979bc305f..9ec05efc846fb 100644 --- a/Detectors/HMPID/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/HMPID/workflow/src/EntropyDecoderSpec.cxx @@ -26,11 +26,10 @@ namespace o2 { namespace hmpid { - class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -42,7 +41,7 @@ class EntropyDecoderSpec : public o2::framework::Task TStopwatch mTimer; }; -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); @@ -91,7 +90,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"triggers"}, "HMP", "INTRECORDS", 0, Lifetime::Timeframe}, @@ -100,17 +99,18 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) std::vector inputs; inputs.emplace_back("ctf_HMP", "HMP", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_HMP", "HMP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("HMP/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_HMP", "HMP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("HMP/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "hmpid-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace hmpid } // namespace o2 diff --git a/Detectors/HMPID/workflow/src/EntropyEncoderSpec.cxx b/Detectors/HMPID/workflow/src/EntropyEncoderSpec.cxx index 95723f42d0fd6..c29c1cee459bc 100644 --- a/Detectors/HMPID/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/HMPID/workflow/src/EntropyEncoderSpec.cxx @@ -27,11 +27,10 @@ namespace o2 { namespace hmpid { - class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR = false); + EntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -44,7 +43,7 @@ class EntropyEncoderSpec : public o2::framework::Task TStopwatch mTimer; }; -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -89,12 +88,15 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("triggers", "HMP", "INTRECORDS", 0, Lifetime::Timeframe); inputs.emplace_back("digits", "HMP", "DIGITS", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "HMP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("HMP/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "HMP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("HMP/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -103,13 +105,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) inputs, Outputs{{"HMP", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "HMP", "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace hmpid } // namespace o2 diff --git a/Detectors/HMPID/workflow/src/entropy-encoder-workflow.cxx b/Detectors/HMPID/workflow/src/entropy-encoder-workflow.cxx index fde5e0183abd6..76e7eae10508e 100644 --- a/Detectors/HMPID/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/HMPID/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -37,6 +38,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::hmpid::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::hmpid::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h index 94c14424f6ce3..57d989038342a 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h @@ -45,7 +45,7 @@ class CTFCoder final : public o2::ctf::CTFCoderBase using PMatrix = std::array, ClusterPattern::MaxColSpan + 2>; using RowColBuff = std::vector; - CTFCoder(o2::ctf::CTFCoderBase::OpType op, o2::detectors::DetID det) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), det) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, o2::detectors::DetID det, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, det, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode clusters to buffer with CTF diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyDecoderSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyDecoderSpec.h index 4ed4e99f4b6f8..a64f2bf8c063c 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyDecoderSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyDecoderSpec.h @@ -32,7 +32,7 @@ namespace itsmft class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(o2::header::DataOrigin orig, int verbosity, bool getDigits = false); + EntropyDecoderSpec(o2::header::DataOrigin orig, int verbosity, bool getDigits = false, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void init(o2::framework::InitContext& ic) final; void run(o2::framework::ProcessingContext& pc) final; @@ -60,7 +60,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(o2::header::DataOrigin orig, int verbosity, bool getDigits, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(o2::header::DataOrigin orig, int verbosity, bool getDigits, unsigned int sspec, const std::string& ctfdictOpt); } // namespace itsmft } // namespace o2 diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyEncoderSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyEncoderSpec.h index c10ae16c95a3e..588cae6339489 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyEncoderSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyEncoderSpec.h @@ -30,7 +30,7 @@ namespace itsmft class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR); + EntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -48,7 +48,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace itsmft } // namespace o2 diff --git a/Detectors/ITSMFT/common/workflow/src/EntropyDecoderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/EntropyDecoderSpec.cxx index 4edbc10d5bfbd..f90b708af1996 100644 --- a/Detectors/ITSMFT/common/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/EntropyDecoderSpec.cxx @@ -28,9 +28,8 @@ namespace o2 { namespace itsmft { - -EntropyDecoderSpec::EntropyDecoderSpec(o2::header::DataOrigin orig, int verbosity, bool getDigits) - : mOrigin(orig), mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, orig == o2::header::gDataOriginITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT), mGetDigits(getDigits) +EntropyDecoderSpec::EntropyDecoderSpec(o2::header::DataOrigin orig, int verbosity, bool getDigits, const std::string& ctfdictOpt) + : mOrigin(orig), mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, orig == o2::header::gDataOriginITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT, ctfdictOpt), mGetDigits(getDigits) { assert(orig == o2::header::gDataOriginITS || orig == o2::header::gDataOriginMFT); mDetPrefix = orig == o2::header::gDataOriginITS ? "_ITS" : "_MFT"; @@ -119,7 +118,7 @@ void EntropyDecoderSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matche } } -DataProcessorSpec getEntropyDecoderSpec(o2::header::DataOrigin orig, int verbosity, bool getDigits, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(o2::header::DataOrigin orig, int verbosity, bool getDigits, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs; // this is a special dummy input which makes sense only in sync workflows @@ -141,20 +140,19 @@ DataProcessorSpec getEntropyDecoderSpec(o2::header::DataOrigin orig, int verbosi inputs.emplace_back(std::string("ctf") + nm, orig, "CTFDATA", sspec, Lifetime::Timeframe); inputs.emplace_back(std::string("noise") + nm, orig, "NOISEMAP", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/NoiseMap", orig.as()))); inputs.emplace_back(std::string("cldict") + nm, orig, "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/ClusterDictionary", orig.as()))); - inputs.emplace_back(std::string("ctfdict") + nm, orig, "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/CTFDictionaryTree", orig.as()))); + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back(std::string("ctfdict") + nm, orig, "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/CTFDictionaryTree", orig.as()))); + } inputs.emplace_back(std::string("trigoffset"), "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ EntropyDecoderSpec::getName(orig), inputs, outputs, - AlgorithmSpec{adaptFromTask(orig, verbosity, getDigits)}, - Options{ - {"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"mask-noise", VariantType::Bool, false, {"apply noise mask to digits or clusters (involves reclusterization)"}}, - {"ignore-cluster-dictionary", VariantType::Bool, false, {"do not use cluster dictionary, always store explicit patterns"}}, - {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; + AlgorithmSpec{adaptFromTask(orig, verbosity, getDigits, ctfdictOpt)}, + Options{{"mask-noise", VariantType::Bool, false, {"apply noise mask to digits or clusters (involves reclusterization)"}}, + {"ignore-cluster-dictionary", VariantType::Bool, false, {"do not use cluster dictionary, always store explicit patterns"}}, + {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace itsmft } // namespace o2 diff --git a/Detectors/ITSMFT/common/workflow/src/EntropyEncoderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/EntropyEncoderSpec.cxx index 4b35f6cc44e39..a824184330547 100644 --- a/Detectors/ITSMFT/common/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/EntropyEncoderSpec.cxx @@ -27,9 +27,8 @@ namespace o2 { namespace itsmft { - -EntropyEncoderSpec::EntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR) - : mOrigin(orig), mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, orig == o2::header::gDataOriginITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR, const std::string& ctfdictOpt) + : mOrigin(orig), mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, orig == o2::header::gDataOriginITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT, ctfdictOpt), mSelIR(selIR) { assert(orig == o2::header::gDataOriginITS || orig == o2::header::gDataOriginMFT); mTimer.Stop(); @@ -112,7 +111,7 @@ void EntropyEncoderSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) } } -DataProcessorSpec getEntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR) +DataProcessorSpec getEntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("compClusters", orig, "COMPCLUSTERS", 0, Lifetime::Timeframe); @@ -123,19 +122,20 @@ DataProcessorSpec getEntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR) inputs.emplace_back("cldict", orig, "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/ClusterDictionary", orig.as()))); inputs.emplace_back("alppar", orig, "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Config/AlpideParam", orig.as()))); } - inputs.emplace_back("ctfdict", orig, "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/CTFDictionaryTree", orig.as()))); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", orig, "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/CTFDictionaryTree", orig.as()))); + } return DataProcessorSpec{ orig == o2::header::gDataOriginITS ? "its-entropy-encoder" : "mft-entropy-encoder", inputs, Outputs{{orig, "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, orig, "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(orig, selIR)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(orig, selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace itsmft } // namespace o2 diff --git a/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx b/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx index 6b0585e293db6..5f09fd6c69a97 100644 --- a/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx @@ -24,6 +24,7 @@ void customize(std::vector& workflowOptions) std::vector options{ ConfigParamSpec{"runmft", VariantType::Bool, false, {"source detector is MFT (default ITS)"}}, ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -40,7 +41,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); bool selIR = cfgc.options().get("select-ir-frames"); if (cfgc.options().get("runmft")) { - wf.emplace_back(o2::itsmft::getEntropyEncoderSpec("MFT", selIR)); + wf.emplace_back(o2::itsmft::getEntropyEncoderSpec("MFT", selIR, cfgc.options().get("ctf-dict"))); } else { wf.emplace_back(o2::itsmft::getEntropyEncoderSpec("ITS", selIR)); } diff --git a/Detectors/MUON/MCH/CTF/include/MCHCTF/CTFCoder.h b/Detectors/MUON/MCH/CTF/include/MCHCTF/CTFCoder.h index 2d65cbbaea614..5c9da95a98354 100644 --- a/Detectors/MUON/MCH/CTF/include/MCHCTF/CTFCoder.h +++ b/Detectors/MUON/MCH/CTF/include/MCHCTF/CTFCoder.h @@ -37,7 +37,7 @@ namespace mch class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::MCH) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::MCH, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/MUON/MCH/CTF/include/MCHCTF/EntropyDecoderSpec.h b/Detectors/MUON/MCH/CTF/include/MCHCTF/EntropyDecoderSpec.h index f28ca90e9a339..0c3534ff5cdd1 100644 --- a/Detectors/MUON/MCH/CTF/include/MCHCTF/EntropyDecoderSpec.h +++ b/Detectors/MUON/MCH/CTF/include/MCHCTF/EntropyDecoderSpec.h @@ -22,7 +22,7 @@ namespace o2 namespace mch { /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, const char* specName, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, const char* specName, unsigned int sspec, const std::string& ctfdictOpt = "none"); } // namespace mch } // namespace o2 diff --git a/Detectors/MUON/MCH/CTF/src/EntropyDecoderSpec.cxx b/Detectors/MUON/MCH/CTF/src/EntropyDecoderSpec.cxx index 9ec13fed85690..653120bd9b630 100644 --- a/Detectors/MUON/MCH/CTF/src/EntropyDecoderSpec.cxx +++ b/Detectors/MUON/MCH/CTF/src/EntropyDecoderSpec.cxx @@ -27,11 +27,10 @@ namespace o2 { namespace mch { - class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -43,7 +42,7 @@ class EntropyDecoderSpec : public o2::framework::Task TStopwatch mTimer; }; -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); @@ -91,7 +90,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, const char* specName, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, const char* specName, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"rofs"}, "MCH", "DIGITROFS", 0, Lifetime::Timeframe}, @@ -101,17 +100,18 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, const char* specName, uns std::vector inputs; inputs.emplace_back("ctf_MCH", "MCH", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_MCH", "MCH", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("MCH/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_MCH", "MCH", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("MCH/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ specName, inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace mch } // namespace o2 diff --git a/Detectors/MUON/MCH/Workflow/src/entropy-encoder-workflow.cxx b/Detectors/MUON/MCH/Workflow/src/entropy-encoder-workflow.cxx index 058202dfb802b..b5f371edfc759 100644 --- a/Detectors/MUON/MCH/Workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/MUON/MCH/Workflow/src/entropy-encoder-workflow.cxx @@ -27,11 +27,10 @@ namespace o2 { namespace mch { - class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR); + EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -44,7 +43,7 @@ class EntropyEncoderSpec : public o2::framework::Task TStopwatch mTimer; }; -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -85,12 +84,15 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(const char* specName, bool selIR) +DataProcessorSpec getEntropyEncoderSpec(const char* specName, bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("rofs", "MCH", "DIGITROFS", 0, Lifetime::Timeframe); inputs.emplace_back("digits", "MCH", "DIGITS", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "MCH", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("MCH/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "MCH", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("MCH/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -99,14 +101,12 @@ DataProcessorSpec getEntropyEncoderSpec(const char* specName, bool selIR) inputs, Outputs{{"MCH", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "MCH", "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace mch } // namespace o2 @@ -118,6 +118,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -133,6 +134,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); bool selIR = cfgc.options().get("select-ir-frames"); - wf.emplace_back(o2::mch::getEntropyEncoderSpec("mch-entropy-encoder", selIR)); + wf.emplace_back(o2::mch::getEntropyEncoderSpec("mch-entropy-encoder", selIR, cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h b/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h index 5afc42550ae3e..defec7207f808 100644 --- a/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h +++ b/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h @@ -37,7 +37,7 @@ namespace mid class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::MID) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::MID, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/EntropyDecoderSpec.h b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/EntropyDecoderSpec.h index 301db519b9a5f..8f466ac8b7a54 100644 --- a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/EntropyDecoderSpec.h +++ b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ namespace mid class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -41,7 +41,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt); } // namespace mid } // namespace o2 diff --git a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/EntropyEncoderSpec.h b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/EntropyEncoderSpec.h index e90c96e6ac8fe..20858ca6dfc07 100644 --- a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/EntropyEncoderSpec.h +++ b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/EntropyEncoderSpec.h @@ -29,7 +29,7 @@ namespace mid class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR); + EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -43,7 +43,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace mid } // namespace o2 diff --git a/Detectors/MUON/MID/Workflow/src/EntropyDecoderSpec.cxx b/Detectors/MUON/MID/Workflow/src/EntropyDecoderSpec.cxx index 5a8df6f8e81cb..0f6dc8bbaa995 100644 --- a/Detectors/MUON/MID/Workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/MUON/MID/Workflow/src/EntropyDecoderSpec.cxx @@ -26,8 +26,7 @@ namespace o2 { namespace mid { - -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); @@ -84,7 +83,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs; for (o2::header::DataHeader::SubSpecificationType subSpec = 0; subSpec < NEvTypes; ++subSpec) { @@ -94,17 +93,18 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) outputs.emplace_back(OutputSpec{{"ctfrep"}, "MID", "CTFDECREP", 0, Lifetime::Timeframe}); std::vector inputs; inputs.emplace_back("ctf_MID", "MID", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_MID", "MID", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("MID/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_MID", "MID", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("MID/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "mid-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace mid } // namespace o2 diff --git a/Detectors/MUON/MID/Workflow/src/EntropyEncoderSpec.cxx b/Detectors/MUON/MID/Workflow/src/EntropyEncoderSpec.cxx index a472d6e28ff16..d75fe3fa6fbf2 100644 --- a/Detectors/MUON/MID/Workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/MUON/MID/Workflow/src/EntropyEncoderSpec.cxx @@ -32,8 +32,7 @@ namespace o2 { namespace mid { - -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -100,12 +99,15 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("rofs", ConcreteDataTypeMatcher(header::gDataOriginMID, "DATAROF"), Lifetime::Timeframe); inputs.emplace_back("cols", ConcreteDataTypeMatcher(header::gDataOriginMID, "DATA"), Lifetime::Timeframe); - inputs.emplace_back("ctfdict", header::gDataOriginMID, "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("MID/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", header::gDataOriginMID, "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("MID/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -114,13 +116,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) inputs, Outputs{{header::gDataOriginMID, "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, header::gDataOriginMID, "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace mid } // namespace o2 diff --git a/Detectors/MUON/MID/Workflow/src/entropy-encoder-workflow.cxx b/Detectors/MUON/MID/Workflow/src/entropy-encoder-workflow.cxx index 56c482c514e38..25b038190281a 100644 --- a/Detectors/MUON/MID/Workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/MUON/MID/Workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -37,6 +38,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::mid::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::mid::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h index 8a7172f634a33..e222328a351c0 100644 --- a/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h +++ b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h @@ -35,7 +35,7 @@ namespace phos class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::PHS) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::PHS, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/PHOS/workflow/include/PHOSWorkflow/EntropyDecoderSpec.h b/Detectors/PHOS/workflow/include/PHOSWorkflow/EntropyDecoderSpec.h index 1890864af77ea..a6045cf36f7b2 100644 --- a/Detectors/PHOS/workflow/include/PHOSWorkflow/EntropyDecoderSpec.h +++ b/Detectors/PHOS/workflow/include/PHOSWorkflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ namespace phos class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -41,7 +41,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt); } // namespace phos } // namespace o2 diff --git a/Detectors/PHOS/workflow/include/PHOSWorkflow/EntropyEncoderSpec.h b/Detectors/PHOS/workflow/include/PHOSWorkflow/EntropyEncoderSpec.h index 4ac8240f4c234..c88bddedc7e20 100644 --- a/Detectors/PHOS/workflow/include/PHOSWorkflow/EntropyEncoderSpec.h +++ b/Detectors/PHOS/workflow/include/PHOSWorkflow/EntropyEncoderSpec.h @@ -28,7 +28,7 @@ namespace phos class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR); + EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -42,7 +42,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace phos } // namespace o2 diff --git a/Detectors/PHOS/workflow/src/EntropyDecoderSpec.cxx b/Detectors/PHOS/workflow/src/EntropyDecoderSpec.cxx index a3d15862a2057..20b161b2d2325 100644 --- a/Detectors/PHOS/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/PHOS/workflow/src/EntropyDecoderSpec.cxx @@ -24,8 +24,7 @@ namespace o2 { namespace phos { - -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); @@ -74,7 +73,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"triggers"}, "PHS", "CELLTRIGREC", 0, Lifetime::Timeframe}, @@ -83,17 +82,18 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) std::vector inputs; inputs.emplace_back("ctf_PHS", "PHS", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_PHS", "PHS", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("PHS/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_PHS", "PHS", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("PHS/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "phos-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace phos } // namespace o2 diff --git a/Detectors/PHOS/workflow/src/EntropyEncoderSpec.cxx b/Detectors/PHOS/workflow/src/EntropyEncoderSpec.cxx index a932a45f1bb53..66a0e04ed3895 100644 --- a/Detectors/PHOS/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/PHOS/workflow/src/EntropyEncoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace phos { - -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -70,12 +69,15 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("triggers", "PHS", "CELLTRIGREC", 0, Lifetime::Timeframe); inputs.emplace_back("cells", "PHS", "CELLS", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "PHS", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("PHS/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "PHS", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("PHS/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -84,13 +86,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) inputs, Outputs{{"PHS", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "PHS", "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace phos } // namespace o2 diff --git a/Detectors/PHOS/workflow/src/entropy-encoder-workflow.cxx b/Detectors/PHOS/workflow/src/entropy-encoder-workflow.cxx index c7266925060c2..41642cd026089 100644 --- a/Detectors/PHOS/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/PHOS/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -37,6 +38,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::phos::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::phos::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h b/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h index e7a203cfcb25e..53cdf59d08572 100644 --- a/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h +++ b/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h @@ -34,7 +34,7 @@ namespace tof class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::TOF) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::TOF, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode clusters to buffer with CTF diff --git a/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyDecoderSpec.h b/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyDecoderSpec.h index c09aa6abc9f7b..714b23d955a78 100644 --- a/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyDecoderSpec.h +++ b/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyDecoderSpec.h @@ -29,7 +29,7 @@ namespace tof class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -43,7 +43,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt); } // namespace tof } // namespace o2 diff --git a/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyEncoderSpec.h b/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyEncoderSpec.h index ee0739c076597..27377b6447d1c 100644 --- a/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyEncoderSpec.h +++ b/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyEncoderSpec.h @@ -29,7 +29,7 @@ namespace tof class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR); + EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -43,7 +43,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace tof } // namespace o2 diff --git a/Detectors/TOF/workflow/src/EntropyDecoderSpec.cxx b/Detectors/TOF/workflow/src/EntropyDecoderSpec.cxx index 400914c64021f..8c0445e3ee3cb 100644 --- a/Detectors/TOF/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/TOF/workflow/src/EntropyDecoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace tof { - -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); @@ -93,7 +92,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"digitheader"}, o2::header::gDataOriginTOF, "DIGITHEADER", 0, Lifetime::Timeframe}, @@ -105,17 +104,18 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) std::vector inputs; inputs.emplace_back("ctf_TOF", "TOF", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_TOF", "TOF", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TOF/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_TOF", "TOF", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TOF/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "tof-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace tof } // namespace o2 diff --git a/Detectors/TOF/workflow/src/EntropyEncoderSpec.cxx b/Detectors/TOF/workflow/src/EntropyEncoderSpec.cxx index 3fc47955f53c0..27d7c162cf670 100644 --- a/Detectors/TOF/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/TOF/workflow/src/EntropyEncoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace tof { - -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -71,13 +70,16 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("compDigits", o2::header::gDataOriginTOF, "DIGITS", 0, Lifetime::Timeframe); inputs.emplace_back("patterns", o2::header::gDataOriginTOF, "PATTERNS", 0, Lifetime::Timeframe); inputs.emplace_back("ROframes", o2::header::gDataOriginTOF, "READOUTWINDOW", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "TOF", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TOF/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "TOF", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TOF/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -86,14 +88,12 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) inputs, Outputs{{o2::header::gDataOriginTOF, "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "TOF", "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, {"irframe-shift", VariantType::Int, o2::tof::Geo::LATENCYWINDOW_IN_BC, {"IRFrame shift to account for latency"}}, {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace tof } // namespace o2 diff --git a/Detectors/TOF/workflow/src/entropy-encoder-workflow.cxx b/Detectors/TOF/workflow/src/entropy-encoder-workflow.cxx index 1969672ad3fa3..5cf882e2723d6 100644 --- a/Detectors/TOF/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/TOF/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -37,6 +38,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::tof::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::tof::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h index 12d66ef6a6e7c..2c6fac7dcde2a 100644 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h +++ b/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h @@ -122,7 +122,7 @@ struct MergedColumnsDecoder { class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::TPC) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::TPC, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode compressed clusters to flat buffer diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/EntropyDecoderSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/EntropyDecoderSpec.h index d36391adfab51..767b68644d698 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/EntropyDecoderSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ namespace tpc class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none") : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); @@ -47,7 +47,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt = "none"); } // namespace tpc } // namespace o2 diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/EntropyEncoderSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/EntropyEncoderSpec.h index ac6fafec0a554..1b8483953a8ab 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/EntropyEncoderSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/EntropyEncoderSpec.h @@ -45,7 +45,7 @@ class VDriftHelper; class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool fromFile, bool selIR = false, std::shared_ptr pgg = std::shared_ptr()); + EntropyEncoderSpec(bool fromFile, bool selIR = false, std::shared_ptr pgg = std::shared_ptr(), const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -71,7 +71,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool inputFromFile, bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool inputFromFile, bool selIR = false, const std::string& ctfdictOpt = "none"); } // end namespace tpc } // end namespace o2 diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/RecoWorkflow.h b/Detectors/TPC/workflow/include/TPCWorkflow/RecoWorkflow.h index a5368f451a820..8e8a6a96eed63 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/RecoWorkflow.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/RecoWorkflow.h @@ -85,6 +85,7 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, int caClusterer = 0, // int zsOnTheFly = 0, bool askDISTSTF = true, + const std::string& ctfdictOpt = "none", bool selIR = false, bool filteredInp = false, int deadMapSources = -1, diff --git a/Detectors/TPC/workflow/src/EntropyDecoderSpec.cxx b/Detectors/TPC/workflow/src/EntropyDecoderSpec.cxx index 4ff3573918722..dd73d582553e6 100644 --- a/Detectors/TPC/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/TPC/workflow/src/EntropyDecoderSpec.cxx @@ -26,7 +26,6 @@ namespace o2 { namespace tpc { - void EntropyDecoderSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) { if (mCTFCoder.finaliseCCDB(matcher, obj)) { @@ -66,11 +65,14 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("ctf_TPC", "TPC", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_TPC", "TPC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TPC/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_TPC", "TPC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TPC/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ @@ -79,10 +81,8 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) Outputs{OutputSpec{{"output"}, "TPC", "COMPCLUSTERSFLAT", 0, Lifetime::Timeframe}, OutputSpec{{"trigger"}, "TPC", "TRIGGERWORDS", 0, Lifetime::Timeframe}, OutputSpec{{"ctfrep"}, "TPC", "CTFDECREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace tpc } // namespace o2 diff --git a/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx b/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx index 2efa7077be125..73bdfa1905f3b 100644 --- a/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx @@ -38,10 +38,9 @@ namespace o2 { namespace tpc { - EntropyEncoderSpec::~EntropyEncoderSpec() = default; -EntropyEncoderSpec::EntropyEncoderSpec(bool fromFile, bool selIR, std::shared_ptr pgg) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mFromFile(fromFile), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool fromFile, bool selIR, std::shared_ptr pgg, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mFromFile(fromFile), mSelIR(selIR) { if (mSelIR) { mGRPRequest = pgg; @@ -305,13 +304,16 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool inputFromFile, bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool inputFromFile, bool selIR, const std::string& ctfdictOpt) { std::vector inputs; header::DataDescription inputType = inputFromFile ? header::DataDescription("COMPCLUSTERS") : header::DataDescription("COMPCLUSTERSFLAT"); inputs.emplace_back("input", "TPC", inputType, 0, Lifetime::Timeframe); inputs.emplace_back("trigger", "TPC", "TRIGGERWORDS", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "TPC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TPC/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "TPC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TPC/Calib/CTFDictionaryTree")); + } std::shared_ptr ggreq; if (selIR) { @@ -324,9 +326,8 @@ DataProcessorSpec getEntropyEncoderSpec(bool inputFromFile, bool selIR) inputs, Outputs{{"TPC", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "TPC", "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(inputFromFile, selIR, ggreq)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"no-ctf-columns-combining", VariantType::Bool, false, {"Do not combine correlated columns in CTF"}}, + AlgorithmSpec{adaptFromTask(inputFromFile, selIR, ggreq, ctfdictOpt)}, + Options{{"no-ctf-columns-combining", VariantType::Bool, false, {"Do not combine correlated columns in CTF"}}, {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, {"irframe-clusters-maxeta", VariantType::Float, 1.5f, {"Max eta for non-assigned clusters"}}, @@ -335,6 +336,5 @@ DataProcessorSpec getEntropyEncoderSpec(bool inputFromFile, bool selIR) {"nThreads-tpc-encoder", VariantType::UInt32, 1u, {"number of threads to use for decoding"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace tpc } // namespace o2 diff --git a/Detectors/TPC/workflow/src/RecoWorkflow.cxx b/Detectors/TPC/workflow/src/RecoWorkflow.cxx index 98a9841fac8b2..3054dd5d61519 100644 --- a/Detectors/TPC/workflow/src/RecoWorkflow.cxx +++ b/Detectors/TPC/workflow/src/RecoWorkflow.cxx @@ -101,7 +101,7 @@ const std::unordered_map OutputMap{ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vector const& tpcSectors, unsigned long tpcSectorMask, std::vector const& laneConfiguration, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, bool propagateMC, unsigned nLanes, std::string const& cfgInput, std::string const& cfgOutput, bool disableRootInput, - int caClusterer, int zsOnTheFly, bool askDISTSTF, bool selIR, bool filteredInp, int deadMapSources, bool useMCTimeGain) + int caClusterer, int zsOnTheFly, bool askDISTSTF, const std::string& ctfdictOpt, bool selIR, bool filteredInp, int deadMapSources, bool useMCTimeGain) { InputType inputType; try { @@ -507,7 +507,7 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto // // selected by output type 'encoded-clusters' if (runClusterEncoder) { - specs.emplace_back(o2::tpc::getEntropyEncoderSpec(!runGPUReco && inputType != InputType::CompClustersFlatForEncode, selIR)); + specs.emplace_back(o2::tpc::getEntropyEncoderSpec(!runGPUReco && inputType != InputType::CompClustersFlatForEncode, selIR, ctfdictOpt)); } ////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Detectors/TPC/workflow/src/entropy-encoder-workflow.cxx b/Detectors/TPC/workflow/src/entropy-encoder-workflow.cxx index 3f9029cf384a9..c09eb193e0fbf 100644 --- a/Detectors/TPC/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/TPC/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}, ConfigParamSpec{"inputFromFile", VariantType::Bool, false, {"Expect COMPCLUSTERS from file"}}}; @@ -38,6 +39,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::tpc::getEntropyEncoderSpec(cfgc.options().get("inputFromFile"), cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::tpc::getEntropyEncoderSpec(cfgc.options().get("inputFromFile"), cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx b/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx index 3c8804de8b536..07b1c293bff98 100644 --- a/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx +++ b/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx @@ -71,6 +71,7 @@ void customize(std::vector& workflowOptions) {"configFile", VariantType::String, "", {"configuration file for configurable parameters"}}, {"filtered-input", VariantType::Bool, false, {"Filtered tracks, clusters input, prefix dataDescriptors with F"}}, {"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}, + {"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, {"tpc-deadMap-sources", VariantType::Int, -1, {"Sources to consider for TPC dead channel map creation; -1=all, 0=deactivated"}}, {"tpc-mc-time-gain", VariantType::Bool, false, {"use time gain calibration for MC (true) or for data (false)"}}, }; @@ -182,6 +183,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) !cfgc.options().get("no-ca-clusterer"), // !cfgc.options().get("no-tpc-zs-on-the-fly"), // !cfgc.options().get("ignore-dist-stf"), // + cfgc.options().get("ctf-dict"), cfgc.options().get("select-ir-frames"), cfgc.options().get("filtered-input"), cfgc.options().get("tpc-deadMap-sources"), diff --git a/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h b/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h index 9eeaf19db5025..adb584ef15ec4 100644 --- a/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h +++ b/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h @@ -36,7 +36,7 @@ namespace trd class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::TRD) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::TRD, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/EntropyDecoderSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/EntropyDecoderSpec.h index 53c591e343134..9521d6262afbf 100644 --- a/Detectors/TRD/workflow/include/TRDWorkflow/EntropyDecoderSpec.h +++ b/Detectors/TRD/workflow/include/TRDWorkflow/EntropyDecoderSpec.h @@ -24,7 +24,7 @@ namespace trd { /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt = "none"); } // namespace trd } // namespace o2 diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/EntropyEncoderSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/EntropyEncoderSpec.h index 673b600bee051..e31a629225f2c 100644 --- a/Detectors/TRD/workflow/include/TRDWorkflow/EntropyEncoderSpec.h +++ b/Detectors/TRD/workflow/include/TRDWorkflow/EntropyEncoderSpec.h @@ -24,7 +24,7 @@ namespace trd { /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace trd } // namespace o2 diff --git a/Detectors/TRD/workflow/src/EntropyDecoderSpec.cxx b/Detectors/TRD/workflow/src/EntropyDecoderSpec.cxx index b30732927c182..2caa4c370a021 100644 --- a/Detectors/TRD/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/TRD/workflow/src/EntropyDecoderSpec.cxx @@ -27,11 +27,10 @@ namespace o2 { namespace trd { - class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -44,7 +43,7 @@ class EntropyDecoderSpec : public o2::framework::Task TStopwatch mTimer; }; -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); @@ -109,7 +108,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"triggers"}, "TRD", "TRKTRGRD", 0, Lifetime::Timeframe}, @@ -119,19 +118,20 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) std::vector inputs; inputs.emplace_back("ctf_TRD", "TRD", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_TRD", "TRD", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TRD/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_TRD", "TRD", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TRD/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "trd-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"correct-trd-trigger-offset", VariantType::Bool, false, {"Correct decoded IR by TriggerOffsetsParam::LM_L0"}}, + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"correct-trd-trigger-offset", VariantType::Bool, false, {"Correct decoded IR by TriggerOffsetsParam::LM_L0"}}, {"bogus-trigger-rejection", VariantType::Int, 10, {">0 : discard, warn N times, <0 : warn only, =0: no check for triggers with no tracklets or bogus IR"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace trd } // namespace o2 diff --git a/Detectors/TRD/workflow/src/EntropyEncoderSpec.cxx b/Detectors/TRD/workflow/src/EntropyEncoderSpec.cxx index d345dd74141ed..18b9a012db2f1 100644 --- a/Detectors/TRD/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/TRD/workflow/src/EntropyEncoderSpec.cxx @@ -27,11 +27,10 @@ namespace o2 { namespace trd { - class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR); + EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -44,7 +43,7 @@ class EntropyEncoderSpec : public o2::framework::Task TStopwatch mTimer; }; -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -92,13 +91,16 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("triggers", "TRD", "TRKTRGRD", 0, Lifetime::Timeframe); inputs.emplace_back("tracklets", "TRD", "TRACKLETS", 0, Lifetime::Timeframe); inputs.emplace_back("digits", "TRD", "DIGITS", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "TRD", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TRD/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "TRD", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TRD/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -107,14 +109,12 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) inputs, Outputs{{"TRD", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "TRD", "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, {"bogus-trigger-check", VariantType::Int, 10, {"max bogus triggers to report, all if < 0"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace trd } // namespace o2 diff --git a/Detectors/TRD/workflow/src/entropy-encoder-workflow.cxx b/Detectors/TRD/workflow/src/entropy-encoder-workflow.cxx index 83fff5bceedef..177f6e4913a26 100644 --- a/Detectors/TRD/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/TRD/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -37,6 +38,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::trd::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::trd::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h b/Detectors/ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h index f8823e4fc66a5..a299431ef17fc 100644 --- a/Detectors/ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h +++ b/Detectors/ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h @@ -35,7 +35,7 @@ namespace zdc class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::ZDC) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::ZDC, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/ZDC/workflow/include/ZDCWorkflow/EntropyDecoderSpec.h b/Detectors/ZDC/workflow/include/ZDCWorkflow/EntropyDecoderSpec.h index ae53ca8bdd0fb..6226b4dc99fe3 100644 --- a/Detectors/ZDC/workflow/include/ZDCWorkflow/EntropyDecoderSpec.h +++ b/Detectors/ZDC/workflow/include/ZDCWorkflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ namespace zdc class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -41,7 +41,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt); } // namespace zdc } // namespace o2 diff --git a/Detectors/ZDC/workflow/include/ZDCWorkflow/EntropyEncoderSpec.h b/Detectors/ZDC/workflow/include/ZDCWorkflow/EntropyEncoderSpec.h index 4979de5a30332..44c4585bf0c3f 100644 --- a/Detectors/ZDC/workflow/include/ZDCWorkflow/EntropyEncoderSpec.h +++ b/Detectors/ZDC/workflow/include/ZDCWorkflow/EntropyEncoderSpec.h @@ -29,7 +29,7 @@ namespace zdc class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR); + EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -43,7 +43,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace zdc } // namespace o2 diff --git a/Detectors/ZDC/workflow/src/EntropyDecoderSpec.cxx b/Detectors/ZDC/workflow/src/EntropyDecoderSpec.cxx index bf870324ce442..59c774662525a 100644 --- a/Detectors/ZDC/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/ZDC/workflow/src/EntropyDecoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace zdc { - -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); @@ -81,7 +80,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"trig"}, "ZDC", "DIGITSBC", 0, Lifetime::Timeframe}, @@ -91,17 +90,18 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) std::vector inputs; inputs.emplace_back("ctf_ZDC", "ZDC", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_ZDC", "ZDC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("ZDC/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_ZDC", "ZDC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("ZDC/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "zdc-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace zdc } // namespace o2 diff --git a/Detectors/ZDC/workflow/src/EntropyEncoderSpec.cxx b/Detectors/ZDC/workflow/src/EntropyEncoderSpec.cxx index abbd821fcb749..1a12360645ab2 100644 --- a/Detectors/ZDC/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/ZDC/workflow/src/EntropyEncoderSpec.cxx @@ -27,8 +27,7 @@ namespace o2 { namespace zdc { - -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -74,13 +73,16 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("trig", "ZDC", "DIGITSBC", 0, Lifetime::Timeframe); inputs.emplace_back("chan", "ZDC", "DIGITSCH", 0, Lifetime::Timeframe); inputs.emplace_back("peds", "ZDC", "DIGITSPD", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "ZDC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("ZDC/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "ZDC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("ZDC/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -89,13 +91,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) inputs, Outputs{{"ZDC", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "ZDC", "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace zdc } // namespace o2 diff --git a/Detectors/ZDC/workflow/src/entropy-encoder-workflow.cxx b/Detectors/ZDC/workflow/src/entropy-encoder-workflow.cxx index 070c65ac9196a..9ab0e10098f43 100644 --- a/Detectors/ZDC/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/ZDC/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -38,6 +39,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); bool selIR = cfgc.options().get("select-ir-frames"); - wf.emplace_back(o2::zdc::getEntropyEncoderSpec(selIR)); + wf.emplace_back(o2::zdc::getEntropyEncoderSpec(selIR, cfgc.options().get("ctf-dict"))); return wf; } From d00ca87143fa6617948d44aff6ea98e75b7ba7a9 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Wed, 4 Feb 2026 08:06:11 +0100 Subject: [PATCH 053/133] Revert "ITS: GPU: more memory clearing in processNeighbours" This reverts commit aa3ef3751f282ee477e0636d6bd5697c43103381. Signed-off-by: Felix Schlepper --- .../ITS/tracking/GPU/cuda/TrackingKernels.cu | 84 ++++++------------- 1 file changed, 26 insertions(+), 58 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index 50888c676df77..a12237358c8bd 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -39,7 +39,6 @@ // O2 track model #include "ReconstructionDataFormats/Track.h" #include "DetectorsBase/Propagator.h" -#include "utils/strtag.h" using namespace o2::track; namespace o2::its @@ -1107,19 +1106,11 @@ void processNeighboursHandler(const int startLayer, const int nBlocks, const int nThreads) { - constexpr uint64_t Tag = qStr2Tag("ITS_PNH1"); - - // allocators used auto allocInt = gpu::TypedAllocator(alloc); auto allocCellSeed = gpu::TypedAllocator>(alloc); - // use sync_policy, this part cannot be run async but tell thrust to use the allocator - auto sync_policy = THRUST_NAMESPACE::par(gpu::TypedAllocator(alloc)); - - // put initial computation on Tag1 - alloc->pushTagOnStack(Tag); - - // start processing of cells thrust::device_vector> foundSeedsTable(nCells[startLayer] + 1, 0, allocInt); + auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(gpu::Stream::DefaultStream); + gpu::processNeighboursKernel<<>>( startLayer, startLevel, @@ -1138,10 +1129,10 @@ void processNeighboursHandler(const int startLayer, maxChi2ClusterAttachment, propagator, matCorrType); - thrust::exclusive_scan(sync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); - auto foundSeeds{foundSeedsTable.back()}; - thrust::device_vector> updatedCellId(foundSeeds, 0, allocInt); - thrust::device_vector, gpu::TypedAllocator>> updatedCellSeed(foundSeeds, allocCellSeed); + thrust::exclusive_scan(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); + + thrust::device_vector> updatedCellId(foundSeedsTable.back(), 0, allocInt); + thrust::device_vector, gpu::TypedAllocator>> updatedCellSeed(foundSeedsTable.back(), allocCellSeed); gpu::processNeighboursKernel<<>>( startLayer, startLevel, @@ -1160,41 +1151,20 @@ void processNeighboursHandler(const int startLayer, maxChi2ClusterAttachment, propagator, matCorrType); + GPUChkErrS(cudaStreamSynchronize(gpu::Stream::DefaultStream)); - // now do inward steps until stop is reached int level = startLevel; - - // Host buffers to break dependency - // FIXME: these should be on our memory resource! - std::vector hostCellId; - std::vector> hostCellSeed; - - // inward loop + thrust::device_vector> lastCellId(allocInt); + thrust::device_vector, gpu::TypedAllocator>> lastCellSeed(allocCellSeed); for (int iLayer{startLayer - 1}; iLayer > 0 && level > 2; --iLayer) { - // copy current results to host - hostCellId.resize(updatedCellId.size()); - hostCellSeed.resize(updatedCellSeed.size()); - thrust::copy(updatedCellId.begin(), updatedCellId.end(), hostCellId.begin()); - thrust::copy(updatedCellSeed.begin(), updatedCellSeed.end(), hostCellSeed.begin()); - - auto lastCellSeedSize{hostCellSeed.size()}; - // but before we clear the memory, and immediately start a new block - alloc->popTagOffStack(Tag); - alloc->pushTagOnStack(Tag); - - // based on the previous step's result create new LUT and zero it - thrust::device_vector>(allocInt).swap(foundSeedsTable); - foundSeedsTable.resize(lastCellSeedSize + 1); - thrust::fill(sync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), 0); - - // recreate lastCell vectors from host - thrust::device_vector> lastCellId(hostCellId.begin(), hostCellId.end(), allocInt); - thrust::device_vector, gpu::TypedAllocator>> lastCellSeed(hostCellSeed.begin(), hostCellSeed.end(), allocCellSeed); - // also create new vectors on new block + lastCellSeed.swap(updatedCellSeed); + lastCellId.swap(updatedCellId); thrust::device_vector, gpu::TypedAllocator>>(allocCellSeed).swap(updatedCellSeed); thrust::device_vector>(allocInt).swap(updatedCellId); + auto lastCellSeedSize{lastCellSeed.size()}; + foundSeedsTable.resize(lastCellSeedSize + 1); + thrust::fill(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), 0); - // start step gpu::processNeighboursKernel<<>>( iLayer, --level, @@ -1213,13 +1183,14 @@ void processNeighboursHandler(const int startLayer, maxChi2ClusterAttachment, propagator, matCorrType); - // how many new seeds where found - thrust::exclusive_scan(sync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); - foundSeeds = foundSeedsTable.back(); - // do a resize, we don't need to set the memory now since we know that all of these are written to - // Note though this does not clear the memory... + thrust::exclusive_scan(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); + + auto foundSeeds{foundSeedsTable.back()}; updatedCellId.resize(foundSeeds); + thrust::fill(nosync_policy, updatedCellId.begin(), updatedCellId.end(), 0); updatedCellSeed.resize(foundSeeds); + thrust::fill(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), CellSeed()); + gpu::processNeighboursKernel<<>>( iLayer, level, @@ -1239,15 +1210,12 @@ void processNeighboursHandler(const int startLayer, propagator, matCorrType); } - - // final copy of result - const auto selector = gpu::seed_selector(1.e3, maxChi2NDF * ((startLevel + 2) * 2 - 5)); - const auto count = thrust::count_if(sync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), selector); - thrust::device_vector, gpu::TypedAllocator>> outSeeds(count, allocCellSeed); - thrust::copy_if(sync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), outSeeds.begin(), selector); - seedsHost.reserve(seedsHost.size() + count); - thrust::copy(outSeeds.begin(), outSeeds.end(), std::back_inserter(seedsHost)); - alloc->popTagOffStack(Tag); + GPUChkErrS(cudaStreamSynchronize(gpu::Stream::DefaultStream)); + thrust::device_vector, gpu::TypedAllocator>> outSeeds(updatedCellSeed.size(), allocCellSeed); + auto end = thrust::copy_if(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), outSeeds.begin(), gpu::seed_selector(1.e3, maxChi2NDF * ((startLevel + 2) * 2 - 5))); + auto s{end - outSeeds.begin()}; + seedsHost.reserve(seedsHost.size() + s); + thrust::copy(outSeeds.begin(), outSeeds.begin() + s, std::back_inserter(seedsHost)); } template From 19803f64dccc492a0bfd8d528ea481dcc972faf8 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Thu, 5 Feb 2026 17:44:10 +0100 Subject: [PATCH 054/133] ITS: GPU: add minimal version of clearing Signed-off-by: Felix Schlepper --- Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index a12237358c8bd..eacf514c7a91d 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -32,9 +32,9 @@ #include "ITStracking/Cluster.h" #include "ITStracking/Cell.h" #include "DataFormatsITS/TrackITS.h" - #include "ITStrackingGPU/TrackingKernels.h" #include "ITStrackingGPU/Utils.h" +#include "utils/strtag.h" // O2 track model #include "ReconstructionDataFormats/Track.h" @@ -1106,6 +1106,8 @@ void processNeighboursHandler(const int startLayer, const int nBlocks, const int nThreads) { + constexpr uint64_t Tag = qStr2Tag("ITS_PNH1"); + alloc->pushTagOnStack(Tag); auto allocInt = gpu::TypedAllocator(alloc); auto allocCellSeed = gpu::TypedAllocator>(alloc); thrust::device_vector> foundSeedsTable(nCells[startLayer] + 1, 0, allocInt); @@ -1216,6 +1218,7 @@ void processNeighboursHandler(const int startLayer, auto s{end - outSeeds.begin()}; seedsHost.reserve(seedsHost.size() + s); thrust::copy(outSeeds.begin(), outSeeds.begin() + s, std::back_inserter(seedsHost)); + alloc->popTagOffStack(Tag); } template From 5639312fdb8f49ef5365f32985bf9a53187add07 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 5 Feb 2026 21:27:14 +0100 Subject: [PATCH 055/133] GPU CMake: Avoid repetitive JSON parsing --- GPU/GPUTracking/CMakeLists.txt | 6 ++-- .../Definitions/Parameters/GPUParameters.json | 1 - .../cmake/gpu_param_header_generator.cmake | 31 +++++++++++++------ 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index 816d578fb31a3..adfb79a78b994 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -410,12 +410,12 @@ target_sources(${targetName} BASE_DIRS ${CMAKE_CURRENT_BINARY_DIR}) make_directory(${CMAKE_CURRENT_BINARY_DIR}/genGPUArch) -set(GPU_CONST_PARAM_ARCHITECTUES AMPERE TURING VEGA MI100) +set(GPU_CONST_PARAM_ARCHITECTUES "AMPERE;TURING;VEGA;MI100") set(GPU_CONST_PARAM_FILES "") +set(GPU_ARCH_PARAMS_HEADER ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch/GPUDefParametersDefaults_OnTheFly.h) +generate_gpu_param_header("${GPU_CONST_PARAM_ARCHITECTUES}" ${GPU_ARCH_PARAMS_HEADER}) foreach(GPU_ARCH ${GPU_CONST_PARAM_ARCHITECTUES}) set(PARAMFILE ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch/gpu_const_param_${GPU_ARCH}.par) - set(GPU_ARCH_PARAMS_HEADER ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch/GPUDefParametersDefaults_${GPU_ARCH}.h) - generate_gpu_param_header(${GPU_ARCH} ${GPU_ARCH_PARAMS_HEADER}) add_custom_command( OUTPUT ${PARAMFILE} COMMAND bash -c diff --git a/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json index 285919559c04c..3c6f1af1aab2f 100644 --- a/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json +++ b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json @@ -2,7 +2,6 @@ "CORE": { "WARP_SIZE": { "default": 32, - "default_cpu": 1, "MI100": 64, "VEGA": 64, "TAHITI": 32, diff --git a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake index 3770e30f2583c..526303a353106 100644 --- a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake +++ b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake @@ -12,7 +12,10 @@ # file gpu_param_header_generator.cmake # author Gabriele Cimador -function(generate_macros json_content header types arch_key use_ifndef_guard) +function(generate_macros json_content output types arch_list) + foreach(arch IN LISTS arch_list) + set(${output}_${arch} "") + endforeach() foreach(TYPE IN LISTS types) string(JSON n_params LENGTH "${json_content}" "${TYPE}") math(EXPR last "${n_params} - 1") @@ -22,8 +25,12 @@ function(generate_macros json_content header types arch_key use_ifndef_guard) math(EXPR last_arch "${n_archs} - 1") foreach(iArch RANGE 0 ${last_arch}) string(JSON arch MEMBER "${JSON_CONTENT}" "${TYPE}" "${param_name}" "${iArch}") - if(arch STREQUAL "${arch_key}") - string(JSON param_values GET "${JSON_CONTENT}" "${TYPE}" "${param_name}" "${arch_key}") + if(arch STREQUAL "default_cpu" AND NOT TYPE STREQUAL "PAR") + message(FATAL_ERROR "Bogus entry ${param_name} for ${arch}") + endif() + list(FIND arch_list "${arch}" list_idx) + if(list_idx GREATER -1) + string(JSON param_values GET "${JSON_CONTENT}" "${TYPE}" "${param_name}" "${arch}") if(TYPE STREQUAL "LB") set(MACRO_NAME "GPUCA_LB_${param_name}") elseif(TYPE STREQUAL "PAR") @@ -36,16 +43,19 @@ function(generate_macros json_content header types arch_key use_ifndef_guard) string(REGEX REPLACE " *\\]$" "" vals "${vals}") string(REGEX REPLACE "\"" "" vals "${vals}") set(MACRO_DEFINITION "#define ${MACRO_NAME} ${vals}") - if(use_ifndef_guard) + if(arch MATCHES ^default) # fallback defaults are wrapped in #ifndef - file(APPEND "${header}" "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") + string(APPEND ${output}_${arch} "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") else() - file(APPEND "${header}" "${MACRO_DEFINITION}\n") + string(APPEND ${output}_${arch} "${MACRO_DEFINITION}\n") endif() endif() endforeach() endforeach() endforeach() + foreach(arch IN LISTS arch_list) + set(${output}_${arch} "${${output}_${arch}}" PARENT_SCOPE) + endforeach() endfunction() function(generate_gpu_param_header GPU_ARCH OUT_HEADER) @@ -68,6 +78,7 @@ function(generate_gpu_param_header GPU_ARCH OUT_HEADER) set(TYPES CORE LB PAR) # Per architecture definitions set(_first TRUE) + generate_macros("${JSON_CONTENT}" TMP_OUTPUT "${TYPES}" "${ARCH_LIST};default;default_cpu") foreach(ARCH IN LISTS ARCH_LIST) if(_first) file(APPEND "${TMP_HEADER}" "#if defined(GPUCA_GPUTYPE_${ARCH})\n\n") @@ -75,7 +86,7 @@ function(generate_gpu_param_header GPU_ARCH OUT_HEADER) else() file(APPEND "${TMP_HEADER}" "#elif defined(GPUCA_GPUTYPE_${ARCH})\n\n") endif() - generate_macros("${JSON_CONTENT}" "${TMP_HEADER}" "${TYPES}" "${ARCH}" "") + file(APPEND "${TMP_HEADER}" ${TMP_OUTPUT_${ARCH}}) endforeach() if(NOT _first) file(APPEND "${TMP_HEADER}" "#else\n#error GPU TYPE NOT SET\n#endif\n") @@ -83,16 +94,16 @@ function(generate_gpu_param_header GPU_ARCH OUT_HEADER) # Default parameters file(APPEND "${TMP_HEADER}" "\n// Default parameters if not defined for the target architecture\n\n") - generate_macros("${JSON_CONTENT}" "${TMP_HEADER}" "${TYPES}" "default" "use_ifndef_guard") + file(APPEND "${TMP_HEADER}" ${TMP_OUTPUT_default}) file(APPEND "${TMP_HEADER}" "#endif // defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS)\n\n") # CPU fallback file(APPEND "${TMP_HEADER}" "#ifndef GPUCA_GPUCODE_GENRTC //Defaults for non-LB parameters also for CPU fallback\n\n") - generate_macros("${JSON_CONTENT}" "${TMP_HEADER}" "PAR" "default_cpu" "use_ifndef_guard") + file(APPEND "${TMP_HEADER}" ${TMP_OUTPUT_default_cpu}) file(APPEND "${TMP_HEADER}" "\n#endif // GPUCA_GPUCODE_GENRTC\n") file(APPEND "${TMP_HEADER}" "\n#endif // GPUDEFPARAMETERSDEFAULTS_H\n") file(RENAME "${TMP_HEADER}" "${OUT_HEADER}") message(STATUS "Generated ${OUT_HEADER}") add_custom_target(GPU_PARAM_HEADER_${GPU_ARCH}_ALL ALL DEPENDS ${OUT_HEADER} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/gpu_param_header_generator.cmake ${GPU_PARAM_JSON}) -endfunction() \ No newline at end of file +endfunction() From e7b5a26d2ad81644dc692c3b740d3124f49d71b0 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 5 Feb 2026 22:06:35 +0100 Subject: [PATCH 056/133] GPU CMake: Generate optimized parameter files for all available architectures, not only for hardcoded list --- GPU/GPUTracking/CMakeLists.txt | 3 +- .../cmake/gpu_param_header_generator.cmake | 37 +++++++++++++++---- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index adfb79a78b994..9f349d0e21f4f 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -410,10 +410,9 @@ target_sources(${targetName} BASE_DIRS ${CMAKE_CURRENT_BINARY_DIR}) make_directory(${CMAKE_CURRENT_BINARY_DIR}/genGPUArch) -set(GPU_CONST_PARAM_ARCHITECTUES "AMPERE;TURING;VEGA;MI100") set(GPU_CONST_PARAM_FILES "") set(GPU_ARCH_PARAMS_HEADER ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch/GPUDefParametersDefaults_OnTheFly.h) -generate_gpu_param_header("${GPU_CONST_PARAM_ARCHITECTUES}" ${GPU_ARCH_PARAMS_HEADER}) +generate_gpu_param_header("ALL" ${GPU_ARCH_PARAMS_HEADER} "GPU_CONST_PARAM_ARCHITECTUES") foreach(GPU_ARCH ${GPU_CONST_PARAM_ARCHITECTUES}) set(PARAMFILE ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch/gpu_const_param_${GPU_ARCH}.par) add_custom_command( diff --git a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake index 526303a353106..0c3e905a697c0 100644 --- a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake +++ b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake @@ -12,10 +12,12 @@ # file gpu_param_header_generator.cmake # author Gabriele Cimador -function(generate_macros json_content output types arch_list) +function(generate_macros json_content output types arch_list arch_list_output) foreach(arch IN LISTS arch_list) - set(${output}_${arch} "") + set(OUTPUT_TMP_${arch} "") endforeach() + set(arch_list_output_tmp) + list(FIND arch_list "ALL" do_all_architectures) foreach(TYPE IN LISTS types) string(JSON n_params LENGTH "${json_content}" "${TYPE}") math(EXPR last "${n_params} - 1") @@ -28,7 +30,14 @@ function(generate_macros json_content output types arch_list) if(arch STREQUAL "default_cpu" AND NOT TYPE STREQUAL "PAR") message(FATAL_ERROR "Bogus entry ${param_name} for ${arch}") endif() - list(FIND arch_list "${arch}" list_idx) + if(do_all_architectures GREATER -1) + if(arch_list_output AND NOT arch MATCHES ^default) + list(APPEND arch_list_output_tmp "${arch}") + endif() + set(list_idx 0) + else() + list(FIND arch_list "${arch}" list_idx) + endif() if(list_idx GREATER -1) string(JSON param_values GET "${JSON_CONTENT}" "${TYPE}" "${param_name}" "${arch}") if(TYPE STREQUAL "LB") @@ -45,17 +54,22 @@ function(generate_macros json_content output types arch_list) set(MACRO_DEFINITION "#define ${MACRO_NAME} ${vals}") if(arch MATCHES ^default) # fallback defaults are wrapped in #ifndef - string(APPEND ${output}_${arch} "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") + string(APPEND OUTPUT_TMP_${arch} "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") else() - string(APPEND ${output}_${arch} "${MACRO_DEFINITION}\n") + string(APPEND OUTPUT_TMP_${arch} "${MACRO_DEFINITION}\n") endif() endif() endforeach() endforeach() endforeach() foreach(arch IN LISTS arch_list) - set(${output}_${arch} "${${output}_${arch}}" PARENT_SCOPE) + set(${output}_${arch} "${OUTPUT_TMP_${arch}}" PARENT_SCOPE) endforeach() + if(arch_list_output) + list(REMOVE_DUPLICATES arch_list_output_tmp) + list(SORT arch_list_output_tmp) + set(${arch_list_output} "${arch_list_output_tmp}" PARENT_SCOPE) + endif() endfunction() function(generate_gpu_param_header GPU_ARCH OUT_HEADER) @@ -78,7 +92,14 @@ function(generate_gpu_param_header GPU_ARCH OUT_HEADER) set(TYPES CORE LB PAR) # Per architecture definitions set(_first TRUE) - generate_macros("${JSON_CONTENT}" TMP_OUTPUT "${TYPES}" "${ARCH_LIST};default;default_cpu") + generate_macros("${JSON_CONTENT}" TMP_OUTPUT "${TYPES}" "${ARCH_LIST};default;default_cpu" "JSON_ARCHITECTURES") + list(FIND ARCH_LIST "ALL" do_all_architectures) + if(ARGC GREATER 2) + set(${ARGV2} "${JSON_ARCHITECTURES}" PARENT_SCOPE) + endif() + if(do_all_architectures GREATER -1) + set(ARCH_LIST ${JSON_ARCHITECTURES}) + endif() foreach(ARCH IN LISTS ARCH_LIST) if(_first) file(APPEND "${TMP_HEADER}" "#if defined(GPUCA_GPUTYPE_${ARCH})\n\n") @@ -98,7 +119,7 @@ function(generate_gpu_param_header GPU_ARCH OUT_HEADER) file(APPEND "${TMP_HEADER}" "#endif // defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS)\n\n") # CPU fallback - file(APPEND "${TMP_HEADER}" "#ifndef GPUCA_GPUCODE_GENRTC //Defaults for non-LB parameters also for CPU fallback\n\n") + file(APPEND "${TMP_HEADER}" "#ifndef GPUCA_GPUCODE_GENRTC // Defaults for non-LB parameters also for CPU fallback\n\n") file(APPEND "${TMP_HEADER}" ${TMP_OUTPUT_default_cpu}) file(APPEND "${TMP_HEADER}" "\n#endif // GPUCA_GPUCODE_GENRTC\n") From 0f4e3152fb0fe97fd29acffbc7954937cf82e381 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 5 Feb 2026 22:36:28 +0100 Subject: [PATCH 057/133] GPU CMake: Make source for GPU parameters configurable, auto-convert CSV to JSON if necessary --- GPU/GPUTracking/CMakeLists.txt | 17 +++ .../Definitions/Parameters/GPUParameters.csv | 113 ++++++++++++++++++ ...meters.json => GPUParameters.json.example} | 0 GPU/GPUTracking/Standalone/cmake/config.cmake | 1 + .../cmake/gpu_param_header_generator.cmake | 1 - 5 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 GPU/GPUTracking/Definitions/Parameters/GPUParameters.csv rename GPU/GPUTracking/Definitions/Parameters/{GPUParameters.json => GPUParameters.json.example} (100%) diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index 9f349d0e21f4f..cd17d8f284b13 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -107,6 +107,23 @@ set(SRCS_NO_H SectorTracker/GPUTPCTrackerDump.cxx Global/GPUChainTrackingDebugAndProfiling.cxx Global/GPUChainTrackingIO.cxx) +if(GPUCA_OVERRIDE_PARAMETER_FILE) + set(GPU_PARAM_JSON ${GPUCA_OVERRIDE_PARAMETER_FILE}) +else() + set(GPU_PARAM_JSON ${CMAKE_CURRENT_SOURCE_DIR}/Definitions/Parameters/GPUParameters.csv) +endif() +get_filename_component(GPU_PARAM_JSON_EXT ${GPU_PARAM_JSON} EXT) +string(TOLOWER "${GPU_PARAM_JSON_EXT}" GPU_PARAM_JSON_EXT) +if(GPU_PARAM_JSON_EXT STREQUAL .csv) + execute_process( + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/Definitions/Parameters/csv_to_json.sh "${GPU_PARAM_JSON}" + OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/gpu_parameters.json + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + message(STATUS "Converted ${GPU_PARAM_JSON} to ${CMAKE_CURRENT_BINARY_DIR}/gpu_parameters.json") + set(GPU_PARAM_JSON ${CMAKE_CURRENT_BINARY_DIR}/gpu_parameters.json) +endif() + set(ON_THE_FLY_DIR ${CMAKE_CURRENT_BINARY_DIR}/include_gpu_onthefly) file(MAKE_DIRECTORY ${ON_THE_FLY_DIR}) include(cmake/gpu_param_header_generator.cmake) diff --git a/GPU/GPUTracking/Definitions/Parameters/GPUParameters.csv b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.csv new file mode 100644 index 0000000000000..5afa99554f5d0 --- /dev/null +++ b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.csv @@ -0,0 +1,113 @@ +Architecture,default,MI100,VEGA,TAHITI,TESLA,FERMI,PASCAL,KEPLER,AMPERE,TURING,default_cpu +,,,,,,,,,,, +CORE:,,,,,,,,,,, +WARP_SIZE,32,64,64,32,32,32,32,32,32,32, +THREAD_COUNT_DEFAULT,256,256,256,,,,,,512,512, +,,,,,,,,,,, +LB:,,,,,,,,,,, +GPUTPCCreateTrackingData,256,"[256, 7]","[192, 2]",,,,,,384,256, +GPUTPCTrackletConstructor,256,"[768, 8]","[512, 10]","[256, 2]","[256, 1]","[256, 2]","[1024, 2]","[512, 4]","[256, 2]","[256, 2]", +GPUTPCTrackletSelector,256,"[384, 5]","[192, 10]","[256, 3]","[256, 1]","[256, 3]","[512, 4]","[256, 3]","[192, 3]","[192, 3]", +GPUTPCNeighboursFinder,256,"[192, 8]","[960, 8]",256,256,256,512,256,"[640, 1]","[640, 1]", +GPUTPCNeighboursCleaner,256,"[128, 5]","[384, 9]",256,256,256,256,256,512,512, +GPUTPCExtrapolationTracking,256,"[256, 7]","[256, 2]",,,,,,"[128, 4]","[192, 2]", +GPUTRDTrackerKernels_gpuVersion,512,,,,,,,,,, +GPUTPCCreateOccupancyMap_fill,256,,,,,,,,,, +GPUTPCCreateOccupancyMap_fold,256,,,,,,,,,, +GPUTRDTrackerKernels_o2Version,512,,,,,,,,,, +GPUTPCCompressionKernels_step0attached,256,"[128, 1]","[64, 2]",,,,,,"[64, 2]",128, +GPUTPCCompressionKernels_step1unattached,256,"[512, 2]","[512, 2]",,,,,,"[512, 3]","[512, 2]", +GPUTPCDecompressionKernels_step0attached,256,"[128, 2]","[128, 2]",,,,,,"[32, 1]","[32, 1]", +GPUTPCDecompressionKernels_step1unattached,256,"[64, 2]","[64, 2]",,,,,,"[32, 1]","[32, 1]", +GPUTPCDecompressionUtilKernels_sortPerSectorRow,256,,,,,,,,,, +GPUTPCDecompressionUtilKernels_countFilteredClusters,256,,,,,,,,,, +GPUTPCDecompressionUtilKernels_storeFilteredClusters,256,,,,,,,,,, +GPUTPCCFDecodeZS,"[128, 4]","[64, 4]","[64, 1]",,,,,,"[64, 10]","[64, 8]", +GPUTPCCFDecodeZSLink,"""GPUCA_WARP_SIZE""","""GPUCA_WARP_SIZE""","""GPUCA_WARP_SIZE""",,,,,,"""GPUCA_WARP_SIZE""","""GPUCA_WARP_SIZE""", +GPUTPCCFDecodeZSDenseLink,"""GPUCA_WARP_SIZE""","[""GPUCA_WARP_SIZE"", 4]","[""GPUCA_WARP_SIZE"", 14]",,,,,,"""GPUCA_WARP_SIZE""","""GPUCA_WARP_SIZE""", +GPUTPCCFGather,"[1024, 1]","[1024, 5]","[1024, 1]",,,,,,"[1024, 1]","[1024, 1]", +COMPRESSION_GATHER,1024,1024,1024,,,,,,1024,1024, +GPUTPCGMMergerTrackFit,256,"[192, 2]","[64, 7]",,,,,,"[64, 4]","[32, 8]", +GPUTPCGMMergerFollowLoopers,256,"[256, 5]","[256, 4]",,,,,,"[64, 12]","[128, 4]", +GPUTPCGMMergerSectorRefit,256,"[64, 4]","[256, 2]",,,,,,"[32, 6]","[64, 5]", +GPUTPCGMMergerUnpackResetIds,256,256,256,,,,,,256,256, +GPUTPCGMMergerUnpackGlobal,256,256,256,,,,,,256,256, +GPUTPCGMMergerResolve_step0,256,512,256,,,,,,256,256, +GPUTPCGMMergerResolve_step1,256,512,256,,,,,,256,256, +GPUTPCGMMergerResolve_step2,256,512,256,,,,,,256,256, +GPUTPCGMMergerResolve_step3,256,512,256,,,,,,256,256, +GPUTPCGMMergerResolve_step4,256,512,256,,,,,,"[256, 4]","[256, 4]", +GPUTPCGMMergerClearLinks,256,256,256,,,,,,256,256, +GPUTPCGMMergerMergeWithinPrepare,256,256,256,,,,,,256,256, +GPUTPCGMMergerMergeSectorsPrepare,256,256,256,,,,,,"[256, 2]","[256, 2]", +GPUTPCGMMergerMergeBorders_step0,256,512,256,,,,,,192,192, +GPUTPCGMMergerMergeBorders_step2,256,512,256,,,,,,"[64, 2]",256, +GPUTPCGMMergerMergeCE,256,512,256,,,,,,256,256, +GPUTPCGMMergerLinkExtrapolatedTracks,256,256,256,,,,,,256,256, +GPUTPCGMMergerCollect,256,"[768, 1]","[1024, 1]",,,,,,"[256, 2]","[128, 2]", +GPUTPCGMMergerSortTracksPrepare,256,256,256,,,,,,256,256, +GPUTPCGMMergerPrepareForFit_step0,256,256,256,,,,,,256,256, +GPUTPCGMMergerPrepareForFit_step1,256,256,256,,,,,,256,256, +GPUTPCGMMergerPrepareForFit_step2,256,256,256,,,,,,256,256, +GPUTPCGMMergerFinalize_step0,256,,256,,,,,,,, +GPUTPCGMMergerFinalize_step1,256,,256,,,,,,,, +GPUTPCGMMergerFinalize_step2,256,,256,,,,,,,, +GPUTPCGMMergerMergeLoopers_step0,256,,,,,,,,,, +GPUTPCGMMergerMergeLoopers_step1,256,,,,,,,,,, +GPUTPCGMMergerMergeLoopers_step2,256,,,,,,,,,, +GPUTPCGMO2Output_prepare,256,,,,,,,,,, +GPUTPCGMO2Output_output,256,,,,,,,,,, +GPUTPCStartHitsFinder,256,"[1024, 2]","[1024, 7]",256,256,256,256,256,512,512, +GPUTPCStartHitsSorter,256,"[1024, 5]","[512, 7]",256,256,256,256,256,"[512, 1]","[512, 1]", +GPUTPCCFCheckPadBaseline,576,"[576, 2]","[576, 2]",,,,,,"[576, 2]",, +GPUTPCCFChargeMapFiller_fillIndexMap,512,512,512,,,,,,448,, +GPUTPCCFChargeMapFiller_fillFromDigits,512,512,512,,,,,,448,, +GPUTPCCFChargeMapFiller_findFragmentStart,512,512,512,,,,,,448,, +GPUTPCCFPeakFinder,512,"[512, 9]","[512, 4]",,,,,,128,, +GPUTPCCFNoiseSuppression,512,512,512,,,,,,448,, +GPUTPCCFDeconvolution,512,"[512, 5]","[512, 5]",,,,,,384,, +GPUTPCCFClusterizer,512,"[448, 3]","[512, 2]",,,,,,448,, +GPUTPCNNClusterizerKernels,512,,,,,,,,,, +GPUTrackingRefitKernel_mode0asGPU,256,,,,,,,,,, +GPUTrackingRefitKernel_mode1asTrackParCov,256,,,,,,,,,, +GPUMemClean16,"[""GPUCA_THREAD_COUNT_DEFAULT"", 1]",,,,,,,,,, +GPUitoa,"[""GPUCA_THREAD_COUNT_DEFAULT"", 1]",,,,,,,,,, +GPUTPCCFNoiseSuppression_noiseSuppression,"""GPUCA_LB_GPUTPCCFNoiseSuppression""",,,,,,,,,, +GPUTPCCFNoiseSuppression_updatePeaks,"""GPUCA_LB_GPUTPCCFNoiseSuppression""",,,,,,,,,, +GPUTPCNNClusterizerKernels_runCfClusterizer,"""GPUCA_LB_GPUTPCNNClusterizerKernels""",,,,,,,,,, +GPUTPCNNClusterizerKernels_fillInputNNCPU,"""GPUCA_LB_GPUTPCNNClusterizerKernels""",,,,,,,,,, +GPUTPCNNClusterizerKernels_fillInputNNGPU,1024,,,,,,,,,, +GPUTPCNNClusterizerKernels_determineClass1Labels,"""GPUCA_LB_GPUTPCNNClusterizerKernels""",,,,,,,,,, +GPUTPCNNClusterizerKernels_determineClass2Labels,"""GPUCA_LB_GPUTPCNNClusterizerKernels""",,,,,,,,,, +GPUTPCNNClusterizerKernels_publishClass1Regression,"""GPUCA_LB_GPUTPCNNClusterizerKernels""",,,,,,,,,, +GPUTPCNNClusterizerKernels_publishClass2Regression,"""GPUCA_LB_GPUTPCNNClusterizerKernels""",,,,,,,,,, +GPUTPCNNClusterizerKernels_publishDeconvolutionFlags,"""GPUCA_LB_GPUTPCNNClusterizerKernels""",,,,,,,,,, +GPUTPCCFStreamCompaction_scanStart,"""GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE""",,,,,,,,,, +GPUTPCCFStreamCompaction_scanUp,"""GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE""",,,,,,,,,, +GPUTPCCFStreamCompaction_scanTop,"""GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE""",,,,,,,,,, +GPUTPCCFStreamCompaction_scanDown,"""GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE""",,,,,,,,,, +GPUTPCCFStreamCompaction_compactDigits,"""GPUCA_PAR_CF_SCAN_WORKGROUP_SIZE""",,,,,,,,,, +GPUTPCCompressionGatherKernels_unbuffered,"""GPUCA_LB_COMPRESSION_GATHER""",,,,,,,,,, +GPUTPCCompressionGatherKernels_buffered32,"""GPUCA_LB_COMPRESSION_GATHER""",,,,,,,,,, +GPUTPCCompressionGatherKernels_buffered64,"""GPUCA_LB_COMPRESSION_GATHER""",,,,,,,,,, +GPUTPCCompressionGatherKernels_buffered128,"""GPUCA_LB_COMPRESSION_GATHER""",,,,,,,,,, +GPUTPCCompressionGatherKernels_multiBlock,"""GPUCA_LB_COMPRESSION_GATHER""",,,,,,,,,, +GPUTPCGMMergerFinalize_0,256,256,,,,,,,256,256, +GPUTPCGMMergerFinalize_1,256,256,,,,,,,256,256, +GPUTPCGMMergerFinalize_2,256,256,,,,,,,256,256, +,,,,,,,,,,, +PAR:,,,,,,,,,,, +AMD_EUS_PER_CU,0,4,4,,,,,,,,0 +SORT_STARTHITS,1,,,,,,,,,,0 +NEIGHBOURS_FINDER_MAX_NNEIGHUP,6,10,4,,,,,,4,4,0 +NEIGHBOURS_FINDER_UNROLL_GLOBAL,4,4,2,,,,,,,,0 +NEIGHBOURS_FINDER_UNROLL_SHARED,1,0,0,,,,,,,,0 +TRACKLET_SELECTOR_HITS_REG_SIZE,12,9,27,,,,,,20,20,0 +ALTERNATE_BORDER_SORT,0,1,1,,,,,,1,1,0 +SORT_BEFORE_FIT,0,1,1,,,,,,1,1,0 +NO_ATOMIC_PRECHECK,0,1,1,,,,,,1,1,0 +DEDX_STORAGE_TYPE,"""float""","""uint16_t""","""uint16_t""",,,,,,"""uint16_t""","""uint16_t""","""float""" +MERGER_INTERPOLATION_ERROR_TYPE,"""float""","""half""","""half""",,,,,,"""half""","""half""","""float""" +COMP_GATHER_KERNEL,0,4,4,,,,,,4,4,0 +COMP_GATHER_MODE,2,3,3,,,,,,3,3,0 +CF_SCAN_WORKGROUP_SIZE,512,,,,,,,,,,0 diff --git a/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json.example similarity index 100% rename from GPU/GPUTracking/Definitions/Parameters/GPUParameters.json rename to GPU/GPUTracking/Definitions/Parameters/GPUParameters.json.example diff --git a/GPU/GPUTracking/Standalone/cmake/config.cmake b/GPU/GPUTracking/Standalone/cmake/config.cmake index ca723063b6d3b..9355311db617c 100644 --- a/GPU/GPUTracking/Standalone/cmake/config.cmake +++ b/GPU/GPUTracking/Standalone/cmake/config.cmake @@ -41,3 +41,4 @@ set(CUDA_COMPUTETARGET "default") # 86 89 #set(GPUCA_CONFIG_COMPILER gcc) # gcc / clang #set(GPUCA_CONFIG_WERROR 1) #add_definitions(-DGPUCA_GPU_DEBUG_PRINT) +#set(GPUCA_OVERRIDE_PARAMETER_FILE "foo.csv") diff --git a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake index 0c3e905a697c0..5bf1454cb31d8 100644 --- a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake +++ b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake @@ -73,7 +73,6 @@ function(generate_macros json_content output types arch_list arch_list_output) endfunction() function(generate_gpu_param_header GPU_ARCH OUT_HEADER) - set(GPU_PARAM_JSON ${CMAKE_CURRENT_SOURCE_DIR}/Definitions/Parameters/GPUParameters.json) set(TARGET_ARCH "UNKNOWN") if(GPU_ARCH STREQUAL "AUTO") detect_gpu_arch("ALL") From fe395fea62618db410e4703f00e9f233e5a6ac44 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 5 Feb 2026 22:54:07 +0100 Subject: [PATCH 058/133] GPU CMake: Use FILE GENERATE to generate Default Parameter Headers to track changes and rerun if necessary --- GPU/GPUTracking/CMakeLists.txt | 2 + .../cmake/gpu_param_header_generator.cmake | 40 ++++++++----------- 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index cd17d8f284b13..14118d9b71e9c 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -112,6 +112,8 @@ if(GPUCA_OVERRIDE_PARAMETER_FILE) else() set(GPU_PARAM_JSON ${CMAKE_CURRENT_SOURCE_DIR}/Definitions/Parameters/GPUParameters.csv) endif() +set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${GPU_PARAM_JSON}") + get_filename_component(GPU_PARAM_JSON_EXT ${GPU_PARAM_JSON} EXT) string(TOLOWER "${GPU_PARAM_JSON_EXT}" GPU_PARAM_JSON_EXT) if(GPU_PARAM_JSON_EXT STREQUAL .csv) diff --git a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake index 5bf1454cb31d8..e79a96034103d 100644 --- a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake +++ b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake @@ -80,17 +80,15 @@ function(generate_gpu_param_header GPU_ARCH OUT_HEADER) set(TARGET_ARCH ${GPU_ARCH}) endif() file(READ "${GPU_PARAM_JSON}" JSON_CONTENT) - set(TMP_HEADER "${OUT_HEADER}.tmp") - file(WRITE "${TMP_HEADER}" "#ifndef GPUDEFPARAMETERSDEFAULTS_H\n#define GPUDEFPARAMETERSDEFAULTS_H\n\n") - file(APPEND "${TMP_HEADER}" "// This file is auto-generated from gpu_params.json. Do not edit directly.\n") + set(TMP_HEADER "#ifndef GPUDEFPARAMETERSDEFAULTS_H\n#define GPUDEFPARAMETERSDEFAULTS_H\n\n") + string(APPEND TMP_HEADER "// This file is auto-generated from gpu_params.json. Do not edit directly.\n") string(REPLACE "," ";" ARCH_LIST "${TARGET_ARCH}") - file(APPEND "${TMP_HEADER}" "// Architectures: ${TARGET_ARCH}\n\n") - file(APPEND "${TMP_HEADER}" "#if defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS) // Avoid including for RTC generation besides normal include protection.\n\n") + string(APPEND TMP_HEADER "// Architectures: ${TARGET_ARCH}\n\n") + string(APPEND TMP_HEADER "#if defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS) // Avoid including for RTC generation besides normal include protection.\n\n") # Types set(TYPES CORE LB PAR) # Per architecture definitions - set(_first TRUE) generate_macros("${JSON_CONTENT}" TMP_OUTPUT "${TYPES}" "${ARCH_LIST};default;default_cpu" "JSON_ARCHITECTURES") list(FIND ARCH_LIST "ALL" do_all_architectures) if(ARGC GREATER 2) @@ -99,31 +97,25 @@ function(generate_gpu_param_header GPU_ARCH OUT_HEADER) if(do_all_architectures GREATER -1) set(ARCH_LIST ${JSON_ARCHITECTURES}) endif() + string(APPEND TMP_HEADER "#if 0\n") foreach(ARCH IN LISTS ARCH_LIST) - if(_first) - file(APPEND "${TMP_HEADER}" "#if defined(GPUCA_GPUTYPE_${ARCH})\n\n") - set(_first FALSE) - else() - file(APPEND "${TMP_HEADER}" "#elif defined(GPUCA_GPUTYPE_${ARCH})\n\n") - endif() - file(APPEND "${TMP_HEADER}" ${TMP_OUTPUT_${ARCH}}) + string(APPEND TMP_HEADER "\n#elif defined(GPUCA_GPUTYPE_${ARCH})\n") + string(APPEND TMP_HEADER ${TMP_OUTPUT_${ARCH}}) endforeach() - if(NOT _first) - file(APPEND "${TMP_HEADER}" "#else\n#error GPU TYPE NOT SET\n#endif\n") - endif() + string(APPEND TMP_HEADER "#else\n#error GPU TYPE NOT SET\n#endif\n") # Default parameters - file(APPEND "${TMP_HEADER}" "\n// Default parameters if not defined for the target architecture\n\n") - file(APPEND "${TMP_HEADER}" ${TMP_OUTPUT_default}) - file(APPEND "${TMP_HEADER}" "#endif // defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS)\n\n") + string(APPEND TMP_HEADER "\n// Default parameters if not defined for the target architecture\n\n") + string(APPEND TMP_HEADER ${TMP_OUTPUT_default}) + string(APPEND TMP_HEADER "#endif // defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS)\n\n") # CPU fallback - file(APPEND "${TMP_HEADER}" "#ifndef GPUCA_GPUCODE_GENRTC // Defaults for non-LB parameters also for CPU fallback\n\n") - file(APPEND "${TMP_HEADER}" ${TMP_OUTPUT_default_cpu}) - file(APPEND "${TMP_HEADER}" "\n#endif // GPUCA_GPUCODE_GENRTC\n") + string(APPEND TMP_HEADER "#ifndef GPUCA_GPUCODE_GENRTC // Defaults for non-LB parameters also for CPU fallback\n\n") + string(APPEND TMP_HEADER ${TMP_OUTPUT_default_cpu}) + string(APPEND TMP_HEADER "\n#endif // GPUCA_GPUCODE_GENRTC\n") - file(APPEND "${TMP_HEADER}" "\n#endif // GPUDEFPARAMETERSDEFAULTS_H\n") - file(RENAME "${TMP_HEADER}" "${OUT_HEADER}") + string(APPEND TMP_HEADER "\n#endif // GPUDEFPARAMETERSDEFAULTS_H\n") + file(GENERATE OUTPUT "${OUT_HEADER}" CONTENT "${TMP_HEADER}") message(STATUS "Generated ${OUT_HEADER}") add_custom_target(GPU_PARAM_HEADER_${GPU_ARCH}_ALL ALL DEPENDS ${OUT_HEADER} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/gpu_param_header_generator.cmake ${GPU_PARAM_JSON}) endfunction() From 6c63d01ba5c76408a9000db3a5c45cab39ea4611 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 6 Feb 2026 09:46:34 +0100 Subject: [PATCH 059/133] GPU CSV to JSON converter: Workaround to be compatible to MacOS --- GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh b/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh index ae9d3b7704284..373bd18ba7cd4 100755 --- a/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh +++ b/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh @@ -2,7 +2,13 @@ [[ -z $1 ]] && { echo "Usage: csv_to_json.sh CSV_FILE"; exit 1; } -awk -vFPAT='([^,]*)|(\"([^\"]|\"\")*\")' \ +DELIM=$'\xFF' +sed -E \ + ':loop + s/^(([^"]*"[^"]*")*[^"]*),/\1'$DELIM'/; + t loop' \ + $1 | \ +awk -F$DELIM \ 'BEGIN { print "{" } { @@ -42,5 +48,4 @@ awk -vFPAT='([^,]*)|(\"([^\"]|\"\")*\")' \ if (paramprinted) print "\n }" if (catprinted) print " }" print "}" - }' \ - $1 + }' From c2cae5e77332edc5f876e2ff8de9d78f494fd795 Mon Sep 17 00:00:00 2001 From: ddobrigk Date: Sat, 7 Feb 2026 13:01:30 +0100 Subject: [PATCH 060/133] Add ability to retain TrackQA for all global tracks (#15010) * Add ability to retain TrackQA for all global tracks * Do check in one go for writeQAData * Change default to false * Change actual defaults to false --- .../AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h | 1 + Detectors/AOD/src/AODProducerWorkflowSpec.cxx | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h index 615a7f96de13e..2d16f343dc1eb 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h @@ -237,6 +237,7 @@ class AODProducerWorkflowDPL : public Task bool mThinTracks{false}; bool mPropTracks{false}; bool mPropMuons{false}; + float mTrackQCKeepGlobalTracks{false}; float mTrackQCFraction{0.00}; int64_t mTrackQCNTrCut{4}; float mTrackQCDCAxy{3.}; diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index b18514949114d..6dcb702791b43 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -499,7 +499,7 @@ void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, float weight = 0; static std::uniform_real_distribution<> distr(0., 1.); - bool writeQAData = o2::math_utils::Tsallis::downsampleTsallisCharged(data.getTrackParam(trackIndex).getPt(), mTrackQCFraction, mSqrtS, weight, distr(mGenerator)); + bool writeQAData = o2::math_utils::Tsallis::downsampleTsallisCharged(data.getTrackParam(trackIndex).getPt(), mTrackQCFraction, mSqrtS, weight, distr(mGenerator)) || (src != GIndex::TPC && mTrackQCKeepGlobalTracks); auto extraInfoHolder = processBarrelTrack(collisionID, collisionBC, trackIndex, data, bcsMap); if (writeQAData) { @@ -1719,6 +1719,7 @@ void AODProducerWorkflowDPL::init(InitContext& ic) LOGP(warn, "Specified non-default empty streamer mask!"); } } + mTrackQCKeepGlobalTracks = ic.options().get("trackqc-keepglobaltracks"); mTrackQCFraction = ic.options().get("trackqc-fraction"); mTrackQCNTrCut = ic.options().get("trackqc-NTrCut"); mTrackQCDCAxy = ic.options().get("trackqc-tpc-dca"); @@ -3348,6 +3349,7 @@ DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, boo ConfigParamSpec{"hepmc-update", VariantType::String, "always", {"When to update HepMC Aux tables: always - force update, never - never update, all - if all keys are present, any - when any key is present (not valid yet)"}}, ConfigParamSpec{"propagate-muons", VariantType::Bool, false, {"Propagate muons to IP"}}, ConfigParamSpec{"thin-tracks", VariantType::Bool, false, {"Produce thinned track tables"}}, + ConfigParamSpec{"trackqc-keepglobaltracks", VariantType::Bool, false, {"Always keep TrackQA for global tracks"}}, ConfigParamSpec{"trackqc-fraction", VariantType::Float, float(0.1), {"Fraction of tracks to QC"}}, ConfigParamSpec{"trackqc-NTrCut", VariantType::Int64, 4L, {"Minimal length of the track - in amount of tracklets"}}, ConfigParamSpec{"trackqc-tpc-dca", VariantType::Float, 3.f, {"Keep TPC standalone track with this DCAxy to the PV"}}, From 1dedc84cef1cc35cc858e31d47da7da51d361ecd Mon Sep 17 00:00:00 2001 From: shahor02 Date: Sat, 7 Feb 2026 13:02:06 +0100 Subject: [PATCH 061/133] Store TPC track A/C side info in the AO2D TrackExtra.fFlags unused bits (#15014) * Store TPC track A/C side info in TrackExtra.fFlags unused bits * Add dynamic columns hasTPCSideA/C, hasTPCSideAOnly/COnly, hasTPCBothSides --- Detectors/AOD/src/AODProducerWorkflowSpec.cxx | 6 ++++++ Framework/Core/include/Framework/AnalysisDataModel.h | 10 ++++++++++ Framework/Core/include/Framework/DataTypes.h | 2 ++ 3 files changed, 18 insertions(+) diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index 6dcb702791b43..be169ad4be19d 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -2646,6 +2646,12 @@ AODProducerWorkflowDPL::TrackExtraInfo AODProducerWorkflowDPL::processBarrelTrac if (tpcOrig.getdEdx().dEdxTotTPC == 0) { extraInfoHolder.flags |= o2::aod::track::TPCdEdxAlt; } + if (tpcOrig.hasASideClusters()) { + extraInfoHolder.flags |= o2::aod::track::TPCSideA; + } + if (tpcOrig.hasCSideClusters()) { + extraInfoHolder.flags |= o2::aod::track::TPCSideC; + } extraInfoHolder.tpcInnerParam = tpcOrig.getP() / tpcOrig.getAbsCharge(); extraInfoHolder.tpcChi2NCl = tpcOrig.getNClusters() ? tpcOrig.getChi2() / tpcOrig.getNClusters() : 0; extraInfoHolder.tpcSignal = dEdx.dEdxTotTPC; diff --git a/Framework/Core/include/Framework/AnalysisDataModel.h b/Framework/Core/include/Framework/AnalysisDataModel.h index b174f3858e165..e3032830beaac 100644 --- a/Framework/Core/include/Framework/AnalysisDataModel.h +++ b/Framework/Core/include/Framework/AnalysisDataModel.h @@ -404,6 +404,16 @@ DECLARE_SOA_DYNAMIC_COLUMN(HasTOF, hasTOF, //! Flag to check if track has a TOF [](uint8_t detectorMap) -> bool { return detectorMap & o2::aod::track::TOF; }); DECLARE_SOA_DYNAMIC_COLUMN(IsPVContributor, isPVContributor, //! Run 3: Has this track contributed to the collision vertex fit [](uint8_t flags) -> bool { return (flags & o2::aod::track::PVContributor) == o2::aod::track::PVContributor; }); +DECLARE_SOA_DYNAMIC_COLUMN(HasTPCSideA, hasTPCSideA, //! Run 3: Has this track TPC clusters from side A? + [](uint8_t flags) -> bool { return (flags & o2::aod::track::TPCSideA) == o2::aod::track::TPCSideA; }); +DECLARE_SOA_DYNAMIC_COLUMN(HasTPCSideAOnly, hasTPCSideAOnly, //! Run 3: Has this track TPC clusters from side A only? + [](uint8_t flags) -> bool { return (flags & (o2::aod::track::TPCSideA | o2::aod::track::TPCSideC)) == o2::aod::track::TPCSideA; }); +DECLARE_SOA_DYNAMIC_COLUMN(HasTPCSideC, hasTPCSideC, //! Run 3: Has this track TPC clusters from side C? + [](uint8_t flags) -> bool { return (flags & o2::aod::track::TPCSideC) == o2::aod::track::TPCSideC; }); +DECLARE_SOA_DYNAMIC_COLUMN(HasTPCSideCOnly, hasTPCSideCOnly, //! Run 3: Has this track TPC clusters from side C only? + [](uint8_t flags) -> bool { return (flags & (o2::aod::track::TPCSideA | o2::aod::track::TPCSideC)) == o2::aod::track::TPCSideC; }); +DECLARE_SOA_DYNAMIC_COLUMN(HasTPCBothSides, hasTPCBothSides, //! Run 3: Has this track TPC clusters from both side A and C? + [](uint8_t flags) -> bool { return (flags & (o2::aod::track::TPCSideA | o2::aod::track::TPCSideC)) == (o2::aod::track::TPCSideA || o2::aod::track::TPCSideC); }); DECLARE_SOA_DYNAMIC_COLUMN(PIDForTracking, pidForTracking, //! PID hypothesis used during tracking. See the constants in the class PID in PID.h [](uint32_t flags) -> uint32_t { return flags >> 28; }); DECLARE_SOA_DYNAMIC_COLUMN(TPCNClsFound, tpcNClsFound, //! Number of found TPC clusters diff --git a/Framework/Core/include/Framework/DataTypes.h b/Framework/Core/include/Framework/DataTypes.h index e273a78f8d0a2..3d49d6d3c03d0 100644 --- a/Framework/Core/include/Framework/DataTypes.h +++ b/Framework/Core/include/Framework/DataTypes.h @@ -51,6 +51,8 @@ enum TrackFlags : uint32_t { OrphanTrack = 0x4, // Track has no association with any collision vertex TrackTimeAsym = 0x8, // track with an asymmetric time range TPCdEdxAlt = 0x10, // TPCSignal and tpcNClsFindableMinusPID correspond for alternative dEdx since the nominal was 0 + TPCSideA = 0x20, // TPC track has A-side clusters (if any) + TPCSideC = 0x40, // TPC track has C-side clusters (if any) // NOTE Highest 4 (29..32) bits reserved for PID hypothesis }; enum TrackFlagsRun2Enum { From a9e312faaff4b436fdf839ea5bc45ed4c0049a4a Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 6 Feb 2026 10:37:15 +0100 Subject: [PATCH 062/133] GPU: Generate GPU parameter files only if GPU build is actually enabled --- GPU/GPUTracking/CMakeLists.txt | 48 +++++++++---------- .../Standalone/tools/dumpGPUDefParam.C | 2 +- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index 14118d9b71e9c..4ff1355672d7c 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -428,31 +428,6 @@ target_sources(${targetName} FILES ${GENERATED_HEADERS_LIST} BASE_DIRS ${CMAKE_CURRENT_BINARY_DIR}) -make_directory(${CMAKE_CURRENT_BINARY_DIR}/genGPUArch) -set(GPU_CONST_PARAM_FILES "") -set(GPU_ARCH_PARAMS_HEADER ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch/GPUDefParametersDefaults_OnTheFly.h) -generate_gpu_param_header("ALL" ${GPU_ARCH_PARAMS_HEADER} "GPU_CONST_PARAM_ARCHITECTUES") -foreach(GPU_ARCH ${GPU_CONST_PARAM_ARCHITECTUES}) - set(PARAMFILE ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch/gpu_const_param_${GPU_ARCH}.par) - add_custom_command( - OUTPUT ${PARAMFILE} - COMMAND bash -c - "echo -e '#define GPUCA_GPUTYPE_${GPU_ARCH}\\n#define PARAMETER_FILE \"${GPU_ARCH_PARAMS_HEADER}\"\\ngInterpreter->AddIncludePath(\"${CMAKE_CURRENT_SOURCE_DIR}/Definitions\");\\ngInterpreter->AddIncludePath(\"${ON_THE_FLY_DIR}\");\\n.x ${CMAKE_CURRENT_SOURCE_DIR}/Standalone/tools/dumpGPUDefParam.C(\"${PARAMFILE}\")\\n.q\\n'" - | root -l -b > /dev/null - VERBATIM - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch - MAIN_DEPENDENCY Standalone/tools/dumpGPUDefParam.C - DEPENDS ${GPU_ARCH_PARAMS_HEADER} - ${ON_THE_FLY_DIR}/GPUDefParametersLoadPrepare.h - ${ON_THE_FLY_DIR}/GPUDefParametersLoad.inc - COMMENT "Generating GPU parameter set for architecture ${GPU_ARCH}") - LIST(APPEND GPU_CONST_PARAM_FILES ${PARAMFILE}) -endforeach() - -add_custom_target(${MODULE}_GPU_CONST_PARAM_ARCHS ALL DEPENDS ${GPU_CONST_PARAM_FILES}) -install(FILES ${GPU_CONST_PARAM_FILES} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/GPU/arch_param) - - # Add compile definitions and libraries depending on available optional dependencies if(GPUCA_QA) message(STATUS "Building GPU QA") @@ -473,6 +448,29 @@ if(CUDA_ENABLED OR OPENCL_ENABLED OR HIP_ENABLED) if(CMAKE_SYSTEM_NAME MATCHES Darwin) message(WARNING "GPU Tracking disabled on MacOS") else() + make_directory(${CMAKE_CURRENT_BINARY_DIR}/genGPUArch) + set(GPU_CONST_PARAM_FILES "") + set(GPU_ARCH_PARAMS_HEADER ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch/GPUDefParametersDefaults_OnTheFly.h) + generate_gpu_param_header("ALL" ${GPU_ARCH_PARAMS_HEADER} "GPU_CONST_PARAM_ARCHITECTUES") + foreach(GPU_ARCH ${GPU_CONST_PARAM_ARCHITECTUES}) + set(PARAMFILE ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch/gpu_const_param_${GPU_ARCH}.par) + add_custom_command( + OUTPUT ${PARAMFILE} + COMMAND bash -c + "echo -e '#define GPUCA_GPUTYPE_${GPU_ARCH}\\n#define PARAMETER_FILE \"${GPU_ARCH_PARAMS_HEADER}\"\\ngInterpreter->AddIncludePath(\"${CMAKE_CURRENT_SOURCE_DIR}/Definitions\");\\ngInterpreter->AddIncludePath(\"${ON_THE_FLY_DIR}\");\\n.x ${CMAKE_CURRENT_SOURCE_DIR}/Standalone/tools/dumpGPUDefParam.C(\"${PARAMFILE}\")\\n.q\\n'" + | root -l -b > /dev/null + VERBATIM + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch + MAIN_DEPENDENCY Standalone/tools/dumpGPUDefParam.C + DEPENDS ${GPU_ARCH_PARAMS_HEADER} + ${ON_THE_FLY_DIR}/GPUDefParametersLoadPrepare.h + ${ON_THE_FLY_DIR}/GPUDefParametersLoad.inc + COMMENT "Generating GPU parameter set for architecture ${GPU_ARCH}") + LIST(APPEND GPU_CONST_PARAM_FILES ${PARAMFILE}) + endforeach() + add_custom_target(${MODULE}_GPU_CONST_PARAM_ARCHS ALL DEPENDS ${GPU_CONST_PARAM_FILES}) + install(FILES ${GPU_CONST_PARAM_FILES} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/GPU/arch_param) + if(CUDA_ENABLED) add_subdirectory(Base/cuda) endif() diff --git a/GPU/GPUTracking/Standalone/tools/dumpGPUDefParam.C b/GPU/GPUTracking/Standalone/tools/dumpGPUDefParam.C index f6866bb80da05..30d10bcdd2a8e 100644 --- a/GPU/GPUTracking/Standalone/tools/dumpGPUDefParam.C +++ b/GPU/GPUTracking/Standalone/tools/dumpGPUDefParam.C @@ -18,7 +18,7 @@ // echo -e '#define GPUCA_GPUTYPE_AMPERE\n#define PARAMETER_FILE "GPUDefParametersDefaults.h"\ngInterpreter->AddIncludePath("'`pwd`'/include/GPU");\n.x share/GPU/tools/dumpGPUDefParam.C("default_AMPERE.par")\n.q\n' | root -l -b #ifndef PARAMETER_FILE -#error Must provide the PARAMETER_FILE as preprocessor define, e.g. -DHEADER_TO_INCLUDE='"GPUDefParametersDefaults.h"' +#error Must provide the PARAMETER_FILE as preprocessor define, e.g. -DPARAMETER_FILE='"GPUDefParametersDefaults.h"' #endif #define GPUCA_GPUCODE From acd7f3bcd98bb944f2e0ff44b855c342161cfe2c Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 6 Feb 2026 12:24:28 +0100 Subject: [PATCH 063/133] GPU Parameter CSV: sort such that defaults are first --- .../Definitions/Parameters/GPUParameters.csv | 140 +++++++++--------- .../Definitions/Parameters/json_to_csv.python | 1 + 2 files changed, 71 insertions(+), 70 deletions(-) diff --git a/GPU/GPUTracking/Definitions/Parameters/GPUParameters.csv b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.csv index 5afa99554f5d0..fc27de72ea2f1 100644 --- a/GPU/GPUTracking/Definitions/Parameters/GPUParameters.csv +++ b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.csv @@ -1,72 +1,72 @@ -Architecture,default,MI100,VEGA,TAHITI,TESLA,FERMI,PASCAL,KEPLER,AMPERE,TURING,default_cpu +Architecture,default,default_cpu,MI100,VEGA,TAHITI,TESLA,FERMI,PASCAL,KEPLER,AMPERE,TURING ,,,,,,,,,,, CORE:,,,,,,,,,,, -WARP_SIZE,32,64,64,32,32,32,32,32,32,32, -THREAD_COUNT_DEFAULT,256,256,256,,,,,,512,512, +WARP_SIZE,32,,64,64,32,32,32,32,32,32,32 +THREAD_COUNT_DEFAULT,256,,256,256,,,,,,512,512 ,,,,,,,,,,, LB:,,,,,,,,,,, -GPUTPCCreateTrackingData,256,"[256, 7]","[192, 2]",,,,,,384,256, -GPUTPCTrackletConstructor,256,"[768, 8]","[512, 10]","[256, 2]","[256, 1]","[256, 2]","[1024, 2]","[512, 4]","[256, 2]","[256, 2]", -GPUTPCTrackletSelector,256,"[384, 5]","[192, 10]","[256, 3]","[256, 1]","[256, 3]","[512, 4]","[256, 3]","[192, 3]","[192, 3]", -GPUTPCNeighboursFinder,256,"[192, 8]","[960, 8]",256,256,256,512,256,"[640, 1]","[640, 1]", -GPUTPCNeighboursCleaner,256,"[128, 5]","[384, 9]",256,256,256,256,256,512,512, -GPUTPCExtrapolationTracking,256,"[256, 7]","[256, 2]",,,,,,"[128, 4]","[192, 2]", +GPUTPCCreateTrackingData,256,,"[256, 7]","[192, 2]",,,,,,384,256 +GPUTPCTrackletConstructor,256,,"[768, 8]","[512, 10]","[256, 2]","[256, 1]","[256, 2]","[1024, 2]","[512, 4]","[256, 2]","[256, 2]" +GPUTPCTrackletSelector,256,,"[384, 5]","[192, 10]","[256, 3]","[256, 1]","[256, 3]","[512, 4]","[256, 3]","[192, 3]","[192, 3]" +GPUTPCNeighboursFinder,256,,"[192, 8]","[960, 8]",256,256,256,512,256,"[640, 1]","[640, 1]" +GPUTPCNeighboursCleaner,256,,"[128, 5]","[384, 9]",256,256,256,256,256,512,512 +GPUTPCExtrapolationTracking,256,,"[256, 7]","[256, 2]",,,,,,"[128, 4]","[192, 2]" GPUTRDTrackerKernels_gpuVersion,512,,,,,,,,,, GPUTPCCreateOccupancyMap_fill,256,,,,,,,,,, GPUTPCCreateOccupancyMap_fold,256,,,,,,,,,, GPUTRDTrackerKernels_o2Version,512,,,,,,,,,, -GPUTPCCompressionKernels_step0attached,256,"[128, 1]","[64, 2]",,,,,,"[64, 2]",128, -GPUTPCCompressionKernels_step1unattached,256,"[512, 2]","[512, 2]",,,,,,"[512, 3]","[512, 2]", -GPUTPCDecompressionKernels_step0attached,256,"[128, 2]","[128, 2]",,,,,,"[32, 1]","[32, 1]", -GPUTPCDecompressionKernels_step1unattached,256,"[64, 2]","[64, 2]",,,,,,"[32, 1]","[32, 1]", +GPUTPCCompressionKernels_step0attached,256,,"[128, 1]","[64, 2]",,,,,,"[64, 2]",128 +GPUTPCCompressionKernels_step1unattached,256,,"[512, 2]","[512, 2]",,,,,,"[512, 3]","[512, 2]" +GPUTPCDecompressionKernels_step0attached,256,,"[128, 2]","[128, 2]",,,,,,"[32, 1]","[32, 1]" +GPUTPCDecompressionKernels_step1unattached,256,,"[64, 2]","[64, 2]",,,,,,"[32, 1]","[32, 1]" GPUTPCDecompressionUtilKernels_sortPerSectorRow,256,,,,,,,,,, GPUTPCDecompressionUtilKernels_countFilteredClusters,256,,,,,,,,,, GPUTPCDecompressionUtilKernels_storeFilteredClusters,256,,,,,,,,,, -GPUTPCCFDecodeZS,"[128, 4]","[64, 4]","[64, 1]",,,,,,"[64, 10]","[64, 8]", -GPUTPCCFDecodeZSLink,"""GPUCA_WARP_SIZE""","""GPUCA_WARP_SIZE""","""GPUCA_WARP_SIZE""",,,,,,"""GPUCA_WARP_SIZE""","""GPUCA_WARP_SIZE""", -GPUTPCCFDecodeZSDenseLink,"""GPUCA_WARP_SIZE""","[""GPUCA_WARP_SIZE"", 4]","[""GPUCA_WARP_SIZE"", 14]",,,,,,"""GPUCA_WARP_SIZE""","""GPUCA_WARP_SIZE""", -GPUTPCCFGather,"[1024, 1]","[1024, 5]","[1024, 1]",,,,,,"[1024, 1]","[1024, 1]", -COMPRESSION_GATHER,1024,1024,1024,,,,,,1024,1024, -GPUTPCGMMergerTrackFit,256,"[192, 2]","[64, 7]",,,,,,"[64, 4]","[32, 8]", -GPUTPCGMMergerFollowLoopers,256,"[256, 5]","[256, 4]",,,,,,"[64, 12]","[128, 4]", -GPUTPCGMMergerSectorRefit,256,"[64, 4]","[256, 2]",,,,,,"[32, 6]","[64, 5]", -GPUTPCGMMergerUnpackResetIds,256,256,256,,,,,,256,256, -GPUTPCGMMergerUnpackGlobal,256,256,256,,,,,,256,256, -GPUTPCGMMergerResolve_step0,256,512,256,,,,,,256,256, -GPUTPCGMMergerResolve_step1,256,512,256,,,,,,256,256, -GPUTPCGMMergerResolve_step2,256,512,256,,,,,,256,256, -GPUTPCGMMergerResolve_step3,256,512,256,,,,,,256,256, -GPUTPCGMMergerResolve_step4,256,512,256,,,,,,"[256, 4]","[256, 4]", -GPUTPCGMMergerClearLinks,256,256,256,,,,,,256,256, -GPUTPCGMMergerMergeWithinPrepare,256,256,256,,,,,,256,256, -GPUTPCGMMergerMergeSectorsPrepare,256,256,256,,,,,,"[256, 2]","[256, 2]", -GPUTPCGMMergerMergeBorders_step0,256,512,256,,,,,,192,192, -GPUTPCGMMergerMergeBorders_step2,256,512,256,,,,,,"[64, 2]",256, -GPUTPCGMMergerMergeCE,256,512,256,,,,,,256,256, -GPUTPCGMMergerLinkExtrapolatedTracks,256,256,256,,,,,,256,256, -GPUTPCGMMergerCollect,256,"[768, 1]","[1024, 1]",,,,,,"[256, 2]","[128, 2]", -GPUTPCGMMergerSortTracksPrepare,256,256,256,,,,,,256,256, -GPUTPCGMMergerPrepareForFit_step0,256,256,256,,,,,,256,256, -GPUTPCGMMergerPrepareForFit_step1,256,256,256,,,,,,256,256, -GPUTPCGMMergerPrepareForFit_step2,256,256,256,,,,,,256,256, -GPUTPCGMMergerFinalize_step0,256,,256,,,,,,,, -GPUTPCGMMergerFinalize_step1,256,,256,,,,,,,, -GPUTPCGMMergerFinalize_step2,256,,256,,,,,,,, +GPUTPCCFDecodeZS,"[128, 4]",,"[64, 4]","[64, 1]",,,,,,"[64, 10]","[64, 8]" +GPUTPCCFDecodeZSLink,"""GPUCA_WARP_SIZE""",,"""GPUCA_WARP_SIZE""","""GPUCA_WARP_SIZE""",,,,,,"""GPUCA_WARP_SIZE""","""GPUCA_WARP_SIZE""" +GPUTPCCFDecodeZSDenseLink,"""GPUCA_WARP_SIZE""",,"[""GPUCA_WARP_SIZE"", 4]","[""GPUCA_WARP_SIZE"", 14]",,,,,,"""GPUCA_WARP_SIZE""","""GPUCA_WARP_SIZE""" +GPUTPCCFGather,"[1024, 1]",,"[1024, 5]","[1024, 1]",,,,,,"[1024, 1]","[1024, 1]" +COMPRESSION_GATHER,1024,,1024,1024,,,,,,1024,1024 +GPUTPCGMMergerTrackFit,256,,"[192, 2]","[64, 7]",,,,,,"[64, 4]","[32, 8]" +GPUTPCGMMergerFollowLoopers,256,,"[256, 5]","[256, 4]",,,,,,"[64, 12]","[128, 4]" +GPUTPCGMMergerSectorRefit,256,,"[64, 4]","[256, 2]",,,,,,"[32, 6]","[64, 5]" +GPUTPCGMMergerUnpackResetIds,256,,256,256,,,,,,256,256 +GPUTPCGMMergerUnpackGlobal,256,,256,256,,,,,,256,256 +GPUTPCGMMergerResolve_step0,256,,512,256,,,,,,256,256 +GPUTPCGMMergerResolve_step1,256,,512,256,,,,,,256,256 +GPUTPCGMMergerResolve_step2,256,,512,256,,,,,,256,256 +GPUTPCGMMergerResolve_step3,256,,512,256,,,,,,256,256 +GPUTPCGMMergerResolve_step4,256,,512,256,,,,,,"[256, 4]","[256, 4]" +GPUTPCGMMergerClearLinks,256,,256,256,,,,,,256,256 +GPUTPCGMMergerMergeWithinPrepare,256,,256,256,,,,,,256,256 +GPUTPCGMMergerMergeSectorsPrepare,256,,256,256,,,,,,"[256, 2]","[256, 2]" +GPUTPCGMMergerMergeBorders_step0,256,,512,256,,,,,,192,192 +GPUTPCGMMergerMergeBorders_step2,256,,512,256,,,,,,"[64, 2]",256 +GPUTPCGMMergerMergeCE,256,,512,256,,,,,,256,256 +GPUTPCGMMergerLinkExtrapolatedTracks,256,,256,256,,,,,,256,256 +GPUTPCGMMergerCollect,256,,"[768, 1]","[1024, 1]",,,,,,"[256, 2]","[128, 2]" +GPUTPCGMMergerSortTracksPrepare,256,,256,256,,,,,,256,256 +GPUTPCGMMergerPrepareForFit_step0,256,,256,256,,,,,,256,256 +GPUTPCGMMergerPrepareForFit_step1,256,,256,256,,,,,,256,256 +GPUTPCGMMergerPrepareForFit_step2,256,,256,256,,,,,,256,256 +GPUTPCGMMergerFinalize_step0,256,,,256,,,,,,, +GPUTPCGMMergerFinalize_step1,256,,,256,,,,,,, +GPUTPCGMMergerFinalize_step2,256,,,256,,,,,,, GPUTPCGMMergerMergeLoopers_step0,256,,,,,,,,,, GPUTPCGMMergerMergeLoopers_step1,256,,,,,,,,,, GPUTPCGMMergerMergeLoopers_step2,256,,,,,,,,,, GPUTPCGMO2Output_prepare,256,,,,,,,,,, GPUTPCGMO2Output_output,256,,,,,,,,,, -GPUTPCStartHitsFinder,256,"[1024, 2]","[1024, 7]",256,256,256,256,256,512,512, -GPUTPCStartHitsSorter,256,"[1024, 5]","[512, 7]",256,256,256,256,256,"[512, 1]","[512, 1]", -GPUTPCCFCheckPadBaseline,576,"[576, 2]","[576, 2]",,,,,,"[576, 2]",, -GPUTPCCFChargeMapFiller_fillIndexMap,512,512,512,,,,,,448,, -GPUTPCCFChargeMapFiller_fillFromDigits,512,512,512,,,,,,448,, -GPUTPCCFChargeMapFiller_findFragmentStart,512,512,512,,,,,,448,, -GPUTPCCFPeakFinder,512,"[512, 9]","[512, 4]",,,,,,128,, -GPUTPCCFNoiseSuppression,512,512,512,,,,,,448,, -GPUTPCCFDeconvolution,512,"[512, 5]","[512, 5]",,,,,,384,, -GPUTPCCFClusterizer,512,"[448, 3]","[512, 2]",,,,,,448,, +GPUTPCStartHitsFinder,256,,"[1024, 2]","[1024, 7]",256,256,256,256,256,512,512 +GPUTPCStartHitsSorter,256,,"[1024, 5]","[512, 7]",256,256,256,256,256,"[512, 1]","[512, 1]" +GPUTPCCFCheckPadBaseline,576,,"[576, 2]","[576, 2]",,,,,,"[576, 2]", +GPUTPCCFChargeMapFiller_fillIndexMap,512,,512,512,,,,,,448, +GPUTPCCFChargeMapFiller_fillFromDigits,512,,512,512,,,,,,448, +GPUTPCCFChargeMapFiller_findFragmentStart,512,,512,512,,,,,,448, +GPUTPCCFPeakFinder,512,,"[512, 9]","[512, 4]",,,,,,128, +GPUTPCCFNoiseSuppression,512,,512,512,,,,,,448, +GPUTPCCFDeconvolution,512,,"[512, 5]","[512, 5]",,,,,,384, +GPUTPCCFClusterizer,512,,"[448, 3]","[512, 2]",,,,,,448, GPUTPCNNClusterizerKernels,512,,,,,,,,,, GPUTrackingRefitKernel_mode0asGPU,256,,,,,,,,,, GPUTrackingRefitKernel_mode1asTrackParCov,256,,,,,,,,,, @@ -92,22 +92,22 @@ GPUTPCCompressionGatherKernels_buffered32,"""GPUCA_LB_COMPRESSION_GATHER""",,,,, GPUTPCCompressionGatherKernels_buffered64,"""GPUCA_LB_COMPRESSION_GATHER""",,,,,,,,,, GPUTPCCompressionGatherKernels_buffered128,"""GPUCA_LB_COMPRESSION_GATHER""",,,,,,,,,, GPUTPCCompressionGatherKernels_multiBlock,"""GPUCA_LB_COMPRESSION_GATHER""",,,,,,,,,, -GPUTPCGMMergerFinalize_0,256,256,,,,,,,256,256, -GPUTPCGMMergerFinalize_1,256,256,,,,,,,256,256, -GPUTPCGMMergerFinalize_2,256,256,,,,,,,256,256, +GPUTPCGMMergerFinalize_0,256,,256,,,,,,,256,256 +GPUTPCGMMergerFinalize_1,256,,256,,,,,,,256,256 +GPUTPCGMMergerFinalize_2,256,,256,,,,,,,256,256 ,,,,,,,,,,, PAR:,,,,,,,,,,, -AMD_EUS_PER_CU,0,4,4,,,,,,,,0 -SORT_STARTHITS,1,,,,,,,,,,0 -NEIGHBOURS_FINDER_MAX_NNEIGHUP,6,10,4,,,,,,4,4,0 -NEIGHBOURS_FINDER_UNROLL_GLOBAL,4,4,2,,,,,,,,0 -NEIGHBOURS_FINDER_UNROLL_SHARED,1,0,0,,,,,,,,0 -TRACKLET_SELECTOR_HITS_REG_SIZE,12,9,27,,,,,,20,20,0 -ALTERNATE_BORDER_SORT,0,1,1,,,,,,1,1,0 -SORT_BEFORE_FIT,0,1,1,,,,,,1,1,0 -NO_ATOMIC_PRECHECK,0,1,1,,,,,,1,1,0 -DEDX_STORAGE_TYPE,"""float""","""uint16_t""","""uint16_t""",,,,,,"""uint16_t""","""uint16_t""","""float""" -MERGER_INTERPOLATION_ERROR_TYPE,"""float""","""half""","""half""",,,,,,"""half""","""half""","""float""" -COMP_GATHER_KERNEL,0,4,4,,,,,,4,4,0 -COMP_GATHER_MODE,2,3,3,,,,,,3,3,0 -CF_SCAN_WORKGROUP_SIZE,512,,,,,,,,,,0 +AMD_EUS_PER_CU,0,0,4,4,,,,,,, +SORT_STARTHITS,1,0,,,,,,,,, +NEIGHBOURS_FINDER_MAX_NNEIGHUP,6,0,10,4,,,,,,4,4 +NEIGHBOURS_FINDER_UNROLL_GLOBAL,4,0,4,2,,,,,,, +NEIGHBOURS_FINDER_UNROLL_SHARED,1,0,0,0,,,,,,, +TRACKLET_SELECTOR_HITS_REG_SIZE,12,0,9,27,,,,,,20,20 +ALTERNATE_BORDER_SORT,0,0,1,1,,,,,,1,1 +SORT_BEFORE_FIT,0,0,1,1,,,,,,1,1 +NO_ATOMIC_PRECHECK,0,0,1,1,,,,,,1,1 +DEDX_STORAGE_TYPE,"""float""","""float""","""uint16_t""","""uint16_t""",,,,,,"""uint16_t""","""uint16_t""" +MERGER_INTERPOLATION_ERROR_TYPE,"""float""","""float""","""half""","""half""",,,,,,"""half""","""half""" +COMP_GATHER_KERNEL,0,0,4,4,,,,,,4,4 +COMP_GATHER_MODE,2,0,3,3,,,,,,3,3 +CF_SCAN_WORKGROUP_SIZE,512,0,,,,,,,,, diff --git a/GPU/GPUTracking/Definitions/Parameters/json_to_csv.python b/GPU/GPUTracking/Definitions/Parameters/json_to_csv.python index a6640239604e0..1ae15662021a3 100755 --- a/GPU/GPUTracking/Definitions/Parameters/json_to_csv.python +++ b/GPU/GPUTracking/Definitions/Parameters/json_to_csv.python @@ -27,6 +27,7 @@ for cat in data.values(): cols = 1 + len(arches) empty = [""] * cols +arches = sorted(arches, key=lambda x: 0 if x.startswith("default") else 1) with open(sys.argv[2], "w", newline="") as f: w = csv.writer(f, lineterminator="\n") From f182c291126a2875decfa42eb1bcad743c18f618 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sat, 7 Feb 2026 14:03:20 +0100 Subject: [PATCH 064/133] GPU CMake: Better detection of GPU optimization setting from architecture string --- dependencies/FindO2GPU.cmake | 48 +++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/dependencies/FindO2GPU.cmake b/dependencies/FindO2GPU.cmake index ec6b7323ad5d1..928454f93b4f8 100644 --- a/dependencies/FindO2GPU.cmake +++ b/dependencies/FindO2GPU.cmake @@ -46,30 +46,48 @@ endif() function(detect_gpu_arch backend) # Detect GPU architecture, optionally filterring by backend - if(CUDA_COMPUTETARGET AND CUDA_COMPUTETARGET MATCHES "86|89") + string(REGEX MATCH "^[ \t\r\n]*[0-9]+" CUDA_FIRST_TARGET "${CUDA_COMPUTETARGET}") + string(STRIP "${CUDA_FIRST_TARGET}" CUDA_FIRST_TARGET) + if(NOT CUDA_FIRST_TARGET) + set(CUDA_FIRST_TARGET 86) + message(STATUS "CUDA_COMPUTETARGET not set, defaulting CUDA optimization for architecture ${CUDA_FIRST_TARGET}") + endif() + if(CUDA_FIRST_TARGET GREATER_EQUAL 86) set(CUDA_TARGET AMPERE) - message(STATUS "Using optimized CUDA settings for Ampere GPU") - elseif(CUDA_COMPUTETARGET AND CUDA_COMPUTETARGET MATCHES "75") + elseif(CUDA_FIRST_TARGET GREATER_EQUAL 75) set(CUDA_TARGET TURING) - message(STATUS "Using optimized CUDA settings for Turing GPU") + elseif(CUDA_FIRST_TARGET GREATER_EQUAL 60) + set(CUDA_TARGET PASCAL) + elseif(CUDA_FIRST_TARGET GREATER_EQUAL 30) + set(CUDA_TARGET KEPLER) + elseif(CUDA_FIRST_TARGET GREATER_EQUAL 20) + set(CUDA_TARGET FERMI) else() - set(CUDA_TARGET AMPERE) - message(STATUS "Defaulting optimized CUDA settings for Ampere GPU") + set(CUDA_TARGET TESLA) endif() + message(STATUS "Using optimized CUDA settings for ${CUDA_TARGET} GPU (sm_${CUDA_FIRST_TARGET})") - if(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET MATCHES "gfx906") - set(HIP_TARGET VEGA) - message(STATUS "Using optimized HIP settings for MI50 GPU") - elseif(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET MATCHES "gfx908") - set(HIP_TARGET MI100) - message(STATUS "Using optimized HIP settings for MI100 GPU") - elseif(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET MATCHES "gfx90a") + string(REGEX MATCH "^[ \t\r\n]*gfx[0-9]+" HIP_FIRST_TARGET "${HIP_AMDGPUTARGET}") + string(STRIP "${HIP_FIRST_TARGET}" HIP_FIRST_TARGET) + string(REGEX REPLACE "^gfx" "" HIP_FIRST_TARGET "${HIP_FIRST_TARGET}") + if(NOT HIP_FIRST_TARGET) + set(HIP_FIRST_TARGET 906) + message(STATUS "HIP_AMDGPUTARGET not set, defaulting HIP optimization for architecture ${HIP_FIRST_TARGET}") + endif() + string(TOLOWER "${HIP_FIRST_TARGET}" HIP_FIRST_TARGET) + string(REGEX MATCH "....$" HIP_FIRST_TARGET_PADDED "0000${HIP_FIRST_TARGET}") + if(HIP_FIRST_TARGET_PADDED STRGREATER_EQUAL "1000") + set(HIP_TARGET RDNA) + elseif(HIP_FIRST_TARGET_PADDED STRGREATER_EQUAL "090a") + set(HIP_TARGET MI210) + elseif(HIP_FIRST_TARGET_PADDED STRGREATER_EQUAL "0908") set(HIP_TARGET MI100) - message(STATUS "Using optimized HIP settings for MI210 GPU") + elseif(HIP_FIRST_TARGET_PADDED STRGREATER_EQUAL "0906") + set(HIP_TARGET VEGA) else() set(HIP_TARGET VEGA) - message(STATUS "Defaulting optimized HIP settings for VEGA GPU") endif() + message(STATUS "Using optimized HIP settings for ${HIP_TARGET} GPU (gfx${HIP_FIRST_TARGET})") if(backend STREQUAL "CUDA") # CUDA filter set(TARGET_ARCH "${CUDA_TARGET}" PARENT_SCOPE) From 6a9fd1e145c46fbe21c5a62999e59c8bc288a4b1 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sun, 8 Feb 2026 00:05:43 +0100 Subject: [PATCH 065/133] GPU CMake: Write separate headers for GPU device and non-device parameters, use same headers for compilation and for parameter file generation --- GPU/GPUTracking/Base/cuda/CMakeLists.txt | 2 +- GPU/GPUTracking/Base/hip/CMakeLists.txt | 2 +- GPU/GPUTracking/CMakeLists.txt | 13 ++- .../cmake/gpu_param_header_generator.cmake | 106 +++++++++--------- dependencies/FindO2GPU.cmake | 17 ++- 5 files changed, 72 insertions(+), 68 deletions(-) diff --git a/GPU/GPUTracking/Base/cuda/CMakeLists.txt b/GPU/GPUTracking/Base/cuda/CMakeLists.txt index 226bacbf88157..27c3d24cd079f 100644 --- a/GPU/GPUTracking/Base/cuda/CMakeLists.txt +++ b/GPU/GPUTracking/Base/cuda/CMakeLists.txt @@ -74,7 +74,7 @@ add_custom_command( COMMAND cat ${GPUDIR}/Base/GPUStdSystemHeaders.h >> ${GPU_RTC_BIN}.src COMMAND ${CMAKE_CUDA_COMPILER} ${GPU_RTC_DEFINES} ${GPU_RTC_INCLUDES} -std=c++${CMAKE_CUDA_STANDARD} -D__CUDA_ARCH__=${RTC_CUDA_ARCH} -Wno-deprecated-gpu-targets -D__CUDACC__ -x c++ -M -MD -MT ${GPU_RTC_BIN}.src -MF ${GPU_RTC_BIN}.src.d ${GPU_RTC_SRC} COMMAND ${CMAKE_CUDA_COMPILER} ${GPU_RTC_DEFINES} ${GPU_RTC_INCLUDES} -std=c++${CMAKE_CUDA_STANDARD} -D__CUDA_ARCH__=${RTC_CUDA_ARCH} -Wno-deprecated-gpu-targets -D__CUDACC__ -x c++ -E -Xcompiler "-nostdinc -P" ${GPU_RTC_SRC} >> ${GPU_RTC_BIN}.src - DEPENDS ${GPU_RTC_SRC} ${GPUDIR}/Base/GPUStdSystemHeaders.h ${GPUDIR}/Base/cuda/GPUReconstructionCUDAIncludesSystem.h ${GPUDIR}/Base/GPUStdSystemHeaders.h GPU_PARAM_HEADER_AUTO_ALL + DEPENDS ${GPU_RTC_SRC} ${GPUDIR}/Base/GPUStdSystemHeaders.h ${GPUDIR}/Base/cuda/GPUReconstructionCUDAIncludesSystem.h ${GPUDIR}/Base/GPUStdSystemHeaders.h GPU_PARAM_HEADER_TARGET DEPFILE ${GPU_RTC_BIN}.src.d COMMAND_EXPAND_LISTS COMMENT "Preparing CUDA RTC source file ${GPU_RTC_BIN}.src" diff --git a/GPU/GPUTracking/Base/hip/CMakeLists.txt b/GPU/GPUTracking/Base/hip/CMakeLists.txt index d148e376abca9..b459a78b5789e 100644 --- a/GPU/GPUTracking/Base/hip/CMakeLists.txt +++ b/GPU/GPUTracking/Base/hip/CMakeLists.txt @@ -125,7 +125,7 @@ add_custom_command( COMMAND cat ${GPUDIR}/Base/hip/GPUReconstructionHIPIncludesSystem.h | grep -v GPUStdSystemHeaders.h >> ${GPU_RTC_BIN}.src COMMAND cat ${GPUDIR}/Base/GPUStdSystemHeaders.h >> ${GPU_RTC_BIN}.src COMMAND ${CMAKE_HIP_COMPILER} ${GPU_RTC_DEFINES} ${GPU_RTC_INCLUDES} -std=c++${CMAKE_HIP_STANDARD} -D__HIPCC__ -D__HIP_DEVICE_COMPILE__ -x c++ -nostdinc -E -P ${GPU_RTC_SRC} -MD -MT ${GPU_RTC_BIN}.src -MF ${GPU_RTC_BIN}.src.d >> ${GPU_RTC_BIN}.src - DEPENDS ${GPU_RTC_SRC} ${GPUDIR}/Base/GPUStdSystemHeaders.h ${GPUDIR}/Base/hip/GPUReconstructionHIPIncludesSystem.h ${GPUDIR}/Base/GPUStdSystemHeaders.h ${MODULE}_HIPIFIED GPU_PARAM_HEADER_AUTO_ALL + DEPENDS ${GPU_RTC_SRC} ${GPUDIR}/Base/GPUStdSystemHeaders.h ${GPUDIR}/Base/hip/GPUReconstructionHIPIncludesSystem.h ${GPUDIR}/Base/GPUStdSystemHeaders.h ${MODULE}_HIPIFIED GPU_PARAM_HEADER_TARGET DEPFILE ${GPU_RTC_BIN}.src.d COMMAND_EXPAND_LISTS COMMENT "Preparing HIP RTC source file ${GPU_RTC_BIN}.src" diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index 4ff1355672d7c..786774c16971b 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -130,7 +130,8 @@ set(ON_THE_FLY_DIR ${CMAKE_CURRENT_BINARY_DIR}/include_gpu_onthefly) file(MAKE_DIRECTORY ${ON_THE_FLY_DIR}) include(cmake/gpu_param_header_generator.cmake) set(GPU_DEFAULT_PARAMS_HEADER ${ON_THE_FLY_DIR}/GPUDefParametersDefaults.h) -generate_gpu_param_header("AUTO" ${GPU_DEFAULT_PARAMS_HEADER}) # generate header with default GPU parameters, arch selected by CMake variables +set(GPU_DEFAULT_PARAMS_HEADER_DEVICE ${ON_THE_FLY_DIR}/GPUDefParametersDefaultsDevice.h) +generate_gpu_param_header("ALL" ${GPU_DEFAULT_PARAMS_HEADER} ${GPU_DEFAULT_PARAMS_HEADER_DEVICE} GPU_CONST_PARAM_ARCHITECTUES) # generate header with default GPU parameters, arch selected by CMake variables set(HDRS_INSTALL ${HDRS_CINT_O2} @@ -161,6 +162,7 @@ set(HDRS_INSTALL Debug/GPUROOTDump.h Definitions/GPUDefConstantsAndSettings.h ${GPU_DEFAULT_PARAMS_HEADER} + ${GPU_DEFAULT_PARAMS_HEADER_DEVICE} Definitions/GPUDefParametersWrapper.h Definitions/GPUDefParametersConstants.h Definitions/GPUDef.h @@ -449,20 +451,19 @@ if(CUDA_ENABLED OR OPENCL_ENABLED OR HIP_ENABLED) message(WARNING "GPU Tracking disabled on MacOS") else() make_directory(${CMAKE_CURRENT_BINARY_DIR}/genGPUArch) - set(GPU_CONST_PARAM_FILES "") - set(GPU_ARCH_PARAMS_HEADER ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch/GPUDefParametersDefaults_OnTheFly.h) - generate_gpu_param_header("ALL" ${GPU_ARCH_PARAMS_HEADER} "GPU_CONST_PARAM_ARCHITECTUES") + set(GPU_CONST_PARAM_FILES) foreach(GPU_ARCH ${GPU_CONST_PARAM_ARCHITECTUES}) set(PARAMFILE ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch/gpu_const_param_${GPU_ARCH}.par) add_custom_command( OUTPUT ${PARAMFILE} COMMAND bash -c - "echo -e '#define GPUCA_GPUTYPE_${GPU_ARCH}\\n#define PARAMETER_FILE \"${GPU_ARCH_PARAMS_HEADER}\"\\ngInterpreter->AddIncludePath(\"${CMAKE_CURRENT_SOURCE_DIR}/Definitions\");\\ngInterpreter->AddIncludePath(\"${ON_THE_FLY_DIR}\");\\n.x ${CMAKE_CURRENT_SOURCE_DIR}/Standalone/tools/dumpGPUDefParam.C(\"${PARAMFILE}\")\\n.q\\n'" + "echo -e '#define GPUCA_GPUTYPE_${GPU_ARCH}\\n#define PARAMETER_FILE \"GPUDefParametersDefaults.h\"\\ngInterpreter->AddIncludePath(\"${CMAKE_CURRENT_SOURCE_DIR}/Definitions\");\\ngInterpreter->AddIncludePath(\"${ON_THE_FLY_DIR}\");\\n.x ${CMAKE_CURRENT_SOURCE_DIR}/Standalone/tools/dumpGPUDefParam.C(\"${PARAMFILE}\")\\n.q\\n'" | root -l -b > /dev/null VERBATIM WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch MAIN_DEPENDENCY Standalone/tools/dumpGPUDefParam.C - DEPENDS ${GPU_ARCH_PARAMS_HEADER} + DEPENDS ${GPU_DEFAULT_PARAMS_HEADER} + ${GPU_DEFAULT_PARAMS_HEADER_DEVICE} ${ON_THE_FLY_DIR}/GPUDefParametersLoadPrepare.h ${ON_THE_FLY_DIR}/GPUDefParametersLoad.inc COMMENT "Generating GPU parameter set for architecture ${GPU_ARCH}") diff --git a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake index e79a96034103d..31d395615a5ed 100644 --- a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake +++ b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake @@ -12,17 +12,31 @@ # file gpu_param_header_generator.cmake # author Gabriele Cimador -function(generate_macros json_content output types arch_list arch_list_output) - foreach(arch IN LISTS arch_list) - set(OUTPUT_TMP_${arch} "") - endforeach() - set(arch_list_output_tmp) - list(FIND arch_list "ALL" do_all_architectures) - foreach(TYPE IN LISTS types) - string(JSON n_params LENGTH "${json_content}" "${TYPE}") +function(generate_gpu_param_header ARCH_LIST OUT_HEADER OUT_HEADER_DEVICE) + list(FIND ARCH_LIST "ALL" do_all_architectures) + list(FIND ARCH_LIST "AUTO" do_auto_architectures) + if(do_all_architectures GREATER -1 OR do_auto_architectures GREATER -1) + if(do_auto_architectures GREATER -1) + detect_gpu_arch("AUTO") + list(REMOVE_ITEM ARCH_LIST "AUTO") + else() + detect_gpu_arch("ALL") + endif() + list(APPEND ARCH_LIST ${TARGET_ARCH}) + endif() + file(READ "${GPU_PARAM_JSON}" JSON_CONTENT) + + # Types + set(TYPES CORE LB PAR) + set(ARCH_LIST_EXT "${ARCH_LIST};default;default_cpu") + # Per architecture definitions + set(JSON_ARCHITECTURES) + + foreach(TYPE IN LISTS TYPES) + string(JSON n_params LENGTH "${JSON_CONTENT}" "${TYPE}") math(EXPR last "${n_params} - 1") foreach(i RANGE 0 ${last}) - string(JSON param_name MEMBER "${json_content}" "${TYPE}" "${i}") + string(JSON param_name MEMBER "${JSON_CONTENT}" "${TYPE}" "${i}") string(JSON n_archs LENGTH "${JSON_CONTENT}" "${TYPE}" "${param_name}") math(EXPR last_arch "${n_archs} - 1") foreach(iArch RANGE 0 ${last_arch}) @@ -31,12 +45,12 @@ function(generate_macros json_content output types arch_list arch_list_output) message(FATAL_ERROR "Bogus entry ${param_name} for ${arch}") endif() if(do_all_architectures GREATER -1) - if(arch_list_output AND NOT arch MATCHES ^default) - list(APPEND arch_list_output_tmp "${arch}") + if(NOT arch MATCHES ^default) + list(APPEND JSON_ARCHITECTURES "${arch}") endif() set(list_idx 0) else() - list(FIND arch_list "${arch}" list_idx) + list(FIND ARCH_LIST_EXT "${arch}" list_idx) endif() if(list_idx GREATER -1) string(JSON param_values GET "${JSON_CONTENT}" "${TYPE}" "${param_name}" "${arch}") @@ -54,68 +68,58 @@ function(generate_macros json_content output types arch_list arch_list_output) set(MACRO_DEFINITION "#define ${MACRO_NAME} ${vals}") if(arch MATCHES ^default) # fallback defaults are wrapped in #ifndef - string(APPEND OUTPUT_TMP_${arch} "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") + string(APPEND generate_gpu_param_header_OUTPUT_TMP_${arch} "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") else() - string(APPEND OUTPUT_TMP_${arch} "${MACRO_DEFINITION}\n") + string(APPEND generate_gpu_param_header_OUTPUT_TMP_${arch} "${MACRO_DEFINITION}\n") endif() endif() endforeach() endforeach() endforeach() - foreach(arch IN LISTS arch_list) - set(${output}_${arch} "${OUTPUT_TMP_${arch}}" PARENT_SCOPE) - endforeach() - if(arch_list_output) - list(REMOVE_DUPLICATES arch_list_output_tmp) - list(SORT arch_list_output_tmp) - set(${arch_list_output} "${arch_list_output_tmp}" PARENT_SCOPE) - endif() -endfunction() -function(generate_gpu_param_header GPU_ARCH OUT_HEADER) - set(TARGET_ARCH "UNKNOWN") - if(GPU_ARCH STREQUAL "AUTO") - detect_gpu_arch("ALL") - else() - set(TARGET_ARCH ${GPU_ARCH}) + list(REMOVE_DUPLICATES JSON_ARCHITECTURES) + list(SORT JSON_ARCHITECTURES) + if(ARGC GREATER 3) + set(${ARGV3} "${JSON_ARCHITECTURES}" PARENT_SCOPE) endif() - file(READ "${GPU_PARAM_JSON}" JSON_CONTENT) + if(do_all_architectures GREATER -1) + list(REMOVE_ITEM ARCH_LIST "ALL") + list(APPEND ARCH_LIST ${JSON_ARCHITECTURES}) + endif() + list(REMOVE_DUPLICATES ARCH_LIST) + list(SORT ARCH_LIST) + + get_filename_component(DEVICE_HEADER_FILE "${OUT_HEADER_DEVICE}" NAME) + set(TMP_HEADER "#ifndef GPUDEFPARAMETERSDEFAULTS_H\n#define GPUDEFPARAMETERSDEFAULTS_H\n\n") + set(TMP_HEADER_DEVICE "#ifndef GPUDEFPARAMETERSDEFAULTSDEVICE_H\n#define GPUDEFPARAMETERSDEFAULTSDEVICE_H\n\n") string(APPEND TMP_HEADER "// This file is auto-generated from gpu_params.json. Do not edit directly.\n") - string(REPLACE "," ";" ARCH_LIST "${TARGET_ARCH}") - string(APPEND TMP_HEADER "// Architectures: ${TARGET_ARCH}\n\n") + string(APPEND TMP_HEADER_DEVICE "// This file is auto-generated from gpu_params.json. Do not edit directly.\n") + string(APPEND TMP_HEADER_DEVICE "// Architectures: ${TARGET_ARCH}\n\n") string(APPEND TMP_HEADER "#if defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS) // Avoid including for RTC generation besides normal include protection.\n\n") + string(APPEND TMP_HEADER "#include \"${DEVICE_HEADER_FILE}\"\n") - # Types - set(TYPES CORE LB PAR) - # Per architecture definitions - generate_macros("${JSON_CONTENT}" TMP_OUTPUT "${TYPES}" "${ARCH_LIST};default;default_cpu" "JSON_ARCHITECTURES") - list(FIND ARCH_LIST "ALL" do_all_architectures) - if(ARGC GREATER 2) - set(${ARGV2} "${JSON_ARCHITECTURES}" PARENT_SCOPE) - endif() - if(do_all_architectures GREATER -1) - set(ARCH_LIST ${JSON_ARCHITECTURES}) - endif() - string(APPEND TMP_HEADER "#if 0\n") + string(APPEND TMP_HEADER_DEVICE "#if 0\n") foreach(ARCH IN LISTS ARCH_LIST) - string(APPEND TMP_HEADER "\n#elif defined(GPUCA_GPUTYPE_${ARCH})\n") - string(APPEND TMP_HEADER ${TMP_OUTPUT_${ARCH}}) + string(APPEND TMP_HEADER_DEVICE "\n#elif defined(GPUCA_GPUTYPE_${ARCH})\n") + string(APPEND TMP_HEADER_DEVICE ${generate_gpu_param_header_OUTPUT_TMP_${ARCH}}) endforeach() - string(APPEND TMP_HEADER "#else\n#error GPU TYPE NOT SET\n#endif\n") + string(APPEND TMP_HEADER_DEVICE "#else\n#error GPU TYPE NOT SET\n#endif\n") # Default parameters string(APPEND TMP_HEADER "\n// Default parameters if not defined for the target architecture\n\n") - string(APPEND TMP_HEADER ${TMP_OUTPUT_default}) + string(APPEND TMP_HEADER ${generate_gpu_param_header_OUTPUT_TMP_default}) string(APPEND TMP_HEADER "#endif // defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS)\n\n") # CPU fallback string(APPEND TMP_HEADER "#ifndef GPUCA_GPUCODE_GENRTC // Defaults for non-LB parameters also for CPU fallback\n\n") - string(APPEND TMP_HEADER ${TMP_OUTPUT_default_cpu}) + string(APPEND TMP_HEADER ${generate_gpu_param_header_OUTPUT_TMP_default_cpu}) string(APPEND TMP_HEADER "\n#endif // GPUCA_GPUCODE_GENRTC\n") string(APPEND TMP_HEADER "\n#endif // GPUDEFPARAMETERSDEFAULTS_H\n") + string(APPEND TMP_HEADER_DEVICE "\n#endif // GPUDEFPARAMETERSDEFAULTSDEVICE_H\n") file(GENERATE OUTPUT "${OUT_HEADER}" CONTENT "${TMP_HEADER}") - message(STATUS "Generated ${OUT_HEADER}") - add_custom_target(GPU_PARAM_HEADER_${GPU_ARCH}_ALL ALL DEPENDS ${OUT_HEADER} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/gpu_param_header_generator.cmake ${GPU_PARAM_JSON}) + file(GENERATE OUTPUT "${OUT_HEADER_DEVICE}" CONTENT "${TMP_HEADER_DEVICE}") + message(STATUS "Generated ${OUT_HEADER} and ${OUT_HEADER_DEVICE}") + add_custom_target(GPU_PARAM_HEADER_TARGET ALL DEPENDS ${OUT_HEADER} ${OUT_HEADER_DEVICE} ${GPU_PARAM_JSON}) endfunction() diff --git a/dependencies/FindO2GPU.cmake b/dependencies/FindO2GPU.cmake index 928454f93b4f8..42d0162691c37 100644 --- a/dependencies/FindO2GPU.cmake +++ b/dependencies/FindO2GPU.cmake @@ -93,18 +93,17 @@ function(detect_gpu_arch backend) # Detect GPU architecture, optionally filterri set(TARGET_ARCH "${CUDA_TARGET}" PARENT_SCOPE) elseif(backend STREQUAL "HIP") # HIP filter set(TARGET_ARCH "${HIP_TARGET}" PARENT_SCOPE) - elseif(backend STREQUAL "ALL") # Return enabled backends - set(_archs "") - if(CUDA_ENABLED) - list(APPEND _archs "${CUDA_TARGET}") + elseif(backend STREQUAL "ALL" OR backend STREQUAL "AUTO") # Return all / enabled backends + set(TARGET_ARCH) + if(CUDA_ENABLED OR backend STREQUAL "ALL") + list(APPEND TARGET_ARCH "${CUDA_TARGET}") endif() - if(HIP_ENABLED) - list(APPEND _archs "${HIP_TARGET}") + if(HIP_ENABLED OR backend STREQUAL "ALL") + list(APPEND TARGET_ARCH "${HIP_TARGET}") endif() - if(OPENCL_ENABLED) - list(APPEND _archs "OPENCL") + if(OPENCL_ENABLED OR backend STREQUAL "ALL") + list(APPEND TARGET_ARCH "OPENCL") endif() - list(JOIN _archs "," TARGET_ARCH) set(TARGET_ARCH "${TARGET_ARCH}" PARENT_SCOPE) else() message(FATAL_ERROR "Unknown backend provided: ${backend}") From f4becde9083aa6bb2dda8837a2a254eec4c55bb9 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sun, 8 Feb 2026 23:25:16 +0100 Subject: [PATCH 066/133] GPU CMake: Clean up some targets, fix if JSON contains 0 architectures --- GPU/GPUTracking/Base/cuda/CMakeLists.txt | 2 +- GPU/GPUTracking/Base/hip/CMakeLists.txt | 2 +- GPU/GPUTracking/CMakeLists.txt | 55 ++++++++-------- .../cmake/gpu_param_header_generator.cmake | 65 ++++++++++--------- 4 files changed, 62 insertions(+), 62 deletions(-) diff --git a/GPU/GPUTracking/Base/cuda/CMakeLists.txt b/GPU/GPUTracking/Base/cuda/CMakeLists.txt index 27c3d24cd079f..6e54187332c9b 100644 --- a/GPU/GPUTracking/Base/cuda/CMakeLists.txt +++ b/GPU/GPUTracking/Base/cuda/CMakeLists.txt @@ -74,7 +74,7 @@ add_custom_command( COMMAND cat ${GPUDIR}/Base/GPUStdSystemHeaders.h >> ${GPU_RTC_BIN}.src COMMAND ${CMAKE_CUDA_COMPILER} ${GPU_RTC_DEFINES} ${GPU_RTC_INCLUDES} -std=c++${CMAKE_CUDA_STANDARD} -D__CUDA_ARCH__=${RTC_CUDA_ARCH} -Wno-deprecated-gpu-targets -D__CUDACC__ -x c++ -M -MD -MT ${GPU_RTC_BIN}.src -MF ${GPU_RTC_BIN}.src.d ${GPU_RTC_SRC} COMMAND ${CMAKE_CUDA_COMPILER} ${GPU_RTC_DEFINES} ${GPU_RTC_INCLUDES} -std=c++${CMAKE_CUDA_STANDARD} -D__CUDA_ARCH__=${RTC_CUDA_ARCH} -Wno-deprecated-gpu-targets -D__CUDACC__ -x c++ -E -Xcompiler "-nostdinc -P" ${GPU_RTC_SRC} >> ${GPU_RTC_BIN}.src - DEPENDS ${GPU_RTC_SRC} ${GPUDIR}/Base/GPUStdSystemHeaders.h ${GPUDIR}/Base/cuda/GPUReconstructionCUDAIncludesSystem.h ${GPUDIR}/Base/GPUStdSystemHeaders.h GPU_PARAM_HEADER_TARGET + DEPENDS ${GPU_RTC_SRC} ${GPUDIR}/Base/GPUStdSystemHeaders.h ${GPUDIR}/Base/cuda/GPUReconstructionCUDAIncludesSystem.h ${GPUDIR}/Base/GPUStdSystemHeaders.h DEPFILE ${GPU_RTC_BIN}.src.d COMMAND_EXPAND_LISTS COMMENT "Preparing CUDA RTC source file ${GPU_RTC_BIN}.src" diff --git a/GPU/GPUTracking/Base/hip/CMakeLists.txt b/GPU/GPUTracking/Base/hip/CMakeLists.txt index b459a78b5789e..50d710fd9d557 100644 --- a/GPU/GPUTracking/Base/hip/CMakeLists.txt +++ b/GPU/GPUTracking/Base/hip/CMakeLists.txt @@ -125,7 +125,7 @@ add_custom_command( COMMAND cat ${GPUDIR}/Base/hip/GPUReconstructionHIPIncludesSystem.h | grep -v GPUStdSystemHeaders.h >> ${GPU_RTC_BIN}.src COMMAND cat ${GPUDIR}/Base/GPUStdSystemHeaders.h >> ${GPU_RTC_BIN}.src COMMAND ${CMAKE_HIP_COMPILER} ${GPU_RTC_DEFINES} ${GPU_RTC_INCLUDES} -std=c++${CMAKE_HIP_STANDARD} -D__HIPCC__ -D__HIP_DEVICE_COMPILE__ -x c++ -nostdinc -E -P ${GPU_RTC_SRC} -MD -MT ${GPU_RTC_BIN}.src -MF ${GPU_RTC_BIN}.src.d >> ${GPU_RTC_BIN}.src - DEPENDS ${GPU_RTC_SRC} ${GPUDIR}/Base/GPUStdSystemHeaders.h ${GPUDIR}/Base/hip/GPUReconstructionHIPIncludesSystem.h ${GPUDIR}/Base/GPUStdSystemHeaders.h ${MODULE}_HIPIFIED GPU_PARAM_HEADER_TARGET + DEPENDS ${GPU_RTC_SRC} ${GPUDIR}/Base/GPUStdSystemHeaders.h ${GPUDIR}/Base/hip/GPUReconstructionHIPIncludesSystem.h ${GPUDIR}/Base/GPUStdSystemHeaders.h ${MODULE}_HIPIFIED DEPFILE ${GPU_RTC_BIN}.src.d COMMAND_EXPAND_LISTS COMMENT "Preparing HIP RTC source file ${GPU_RTC_BIN}.src" diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index 786774c16971b..e52fb80113c00 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -107,32 +107,6 @@ set(SRCS_NO_H SectorTracker/GPUTPCTrackerDump.cxx Global/GPUChainTrackingDebugAndProfiling.cxx Global/GPUChainTrackingIO.cxx) -if(GPUCA_OVERRIDE_PARAMETER_FILE) - set(GPU_PARAM_JSON ${GPUCA_OVERRIDE_PARAMETER_FILE}) -else() - set(GPU_PARAM_JSON ${CMAKE_CURRENT_SOURCE_DIR}/Definitions/Parameters/GPUParameters.csv) -endif() -set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${GPU_PARAM_JSON}") - -get_filename_component(GPU_PARAM_JSON_EXT ${GPU_PARAM_JSON} EXT) -string(TOLOWER "${GPU_PARAM_JSON_EXT}" GPU_PARAM_JSON_EXT) -if(GPU_PARAM_JSON_EXT STREQUAL .csv) - execute_process( - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/Definitions/Parameters/csv_to_json.sh "${GPU_PARAM_JSON}" - OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/gpu_parameters.json - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - ) - message(STATUS "Converted ${GPU_PARAM_JSON} to ${CMAKE_CURRENT_BINARY_DIR}/gpu_parameters.json") - set(GPU_PARAM_JSON ${CMAKE_CURRENT_BINARY_DIR}/gpu_parameters.json) -endif() - -set(ON_THE_FLY_DIR ${CMAKE_CURRENT_BINARY_DIR}/include_gpu_onthefly) -file(MAKE_DIRECTORY ${ON_THE_FLY_DIR}) -include(cmake/gpu_param_header_generator.cmake) -set(GPU_DEFAULT_PARAMS_HEADER ${ON_THE_FLY_DIR}/GPUDefParametersDefaults.h) -set(GPU_DEFAULT_PARAMS_HEADER_DEVICE ${ON_THE_FLY_DIR}/GPUDefParametersDefaultsDevice.h) -generate_gpu_param_header("ALL" ${GPU_DEFAULT_PARAMS_HEADER} ${GPU_DEFAULT_PARAMS_HEADER_DEVICE} GPU_CONST_PARAM_ARCHITECTUES) # generate header with default GPU parameters, arch selected by CMake variables - set(HDRS_INSTALL ${HDRS_CINT_O2} ${HDRS_CINT_DATATYPES} @@ -161,8 +135,6 @@ set(HDRS_INSTALL DataTypes/GPUO2ExternalUser.h Debug/GPUROOTDump.h Definitions/GPUDefConstantsAndSettings.h - ${GPU_DEFAULT_PARAMS_HEADER} - ${GPU_DEFAULT_PARAMS_HEADER_DEVICE} Definitions/GPUDefParametersWrapper.h Definitions/GPUDefParametersConstants.h Definitions/GPUDef.h @@ -258,6 +230,8 @@ if(ALIGPU_BUILD_TYPE STREQUAL "O2") DataTypes/GPUO2ConfigurableParam.cxx) endif() +set(ON_THE_FLY_DIR ${CMAKE_CURRENT_BINARY_DIR}/include_gpu_onthefly) +file(MAKE_DIRECTORY ${ON_THE_FLY_DIR}) set(TEMPLATE_HEADER_LIST Base/GPUReconstructionKernelList.template.h Base/GPUReconstructionKernelIncludes.template.h Base/GPUReconstructionIncludesDeviceAll.template.h @@ -288,6 +262,31 @@ add_custom_command( ) list(APPEND GENERATED_HEADERS_LIST ${ON_THE_FLY_DIR}/GPUDefParametersLoadPrepare.h) +if(GPUCA_OVERRIDE_PARAMETER_FILE) + set(GPU_PARAM_JSON ${GPUCA_OVERRIDE_PARAMETER_FILE}) +else() + set(GPU_PARAM_JSON ${CMAKE_CURRENT_SOURCE_DIR}/Definitions/Parameters/GPUParameters.csv) +endif() +set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${GPU_PARAM_JSON}") + +get_filename_component(GPU_PARAM_JSON_EXT ${GPU_PARAM_JSON} EXT) +string(TOLOWER "${GPU_PARAM_JSON_EXT}" GPU_PARAM_JSON_EXT) +if(GPU_PARAM_JSON_EXT STREQUAL .csv) + execute_process( + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/Definitions/Parameters/csv_to_json.sh "${GPU_PARAM_JSON}" + OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/gpu_parameters.json + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + message(STATUS "Converted ${GPU_PARAM_JSON} to ${CMAKE_CURRENT_BINARY_DIR}/gpu_parameters.json") + set(GPU_PARAM_JSON ${CMAKE_CURRENT_BINARY_DIR}/gpu_parameters.json) +endif() + +include(cmake/gpu_param_header_generator.cmake) +set(GPU_DEFAULT_PARAMS_HEADER ${ON_THE_FLY_DIR}/GPUDefParametersDefaults.h) +set(GPU_DEFAULT_PARAMS_HEADER_DEVICE ${ON_THE_FLY_DIR}/GPUDefParametersDefaultsDevice.h) +generate_gpu_param_header("ALL" ${GPU_DEFAULT_PARAMS_HEADER} ${GPU_DEFAULT_PARAMS_HEADER_DEVICE} GPU_CONST_PARAM_ARCHITECTUES) # generate header with default GPU parameters, arch selected by CMake variables +list(APPEND GENERATED_HEADERS_LIST ${GPU_DEFAULT_PARAMS_HEADER} ${GPU_DEFAULT_PARAMS_HEADER_DEVICE}) + set(HDRS_INSTALL ${HDRS_INSTALL} ${GENERATED_HEADERS_LIST}) include(kernels.cmake) diff --git a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake index 31d395615a5ed..0a7b234aa6a18 100644 --- a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake +++ b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake @@ -38,42 +38,44 @@ function(generate_gpu_param_header ARCH_LIST OUT_HEADER OUT_HEADER_DEVICE) foreach(i RANGE 0 ${last}) string(JSON param_name MEMBER "${JSON_CONTENT}" "${TYPE}" "${i}") string(JSON n_archs LENGTH "${JSON_CONTENT}" "${TYPE}" "${param_name}") + if(n_archs GREATER 0) math(EXPR last_arch "${n_archs} - 1") - foreach(iArch RANGE 0 ${last_arch}) - string(JSON arch MEMBER "${JSON_CONTENT}" "${TYPE}" "${param_name}" "${iArch}") - if(arch STREQUAL "default_cpu" AND NOT TYPE STREQUAL "PAR") - message(FATAL_ERROR "Bogus entry ${param_name} for ${arch}") - endif() - if(do_all_architectures GREATER -1) - if(NOT arch MATCHES ^default) - list(APPEND JSON_ARCHITECTURES "${arch}") + foreach(iArch RANGE 0 ${last_arch}) + string(JSON arch MEMBER "${JSON_CONTENT}" "${TYPE}" "${param_name}" "${iArch}") + if(arch STREQUAL "default_cpu" AND NOT TYPE STREQUAL "PAR") + message(FATAL_ERROR "Bogus entry ${param_name} for ${arch}") endif() - set(list_idx 0) - else() - list(FIND ARCH_LIST_EXT "${arch}" list_idx) - endif() - if(list_idx GREATER -1) - string(JSON param_values GET "${JSON_CONTENT}" "${TYPE}" "${param_name}" "${arch}") - if(TYPE STREQUAL "LB") - set(MACRO_NAME "GPUCA_LB_${param_name}") - elseif(TYPE STREQUAL "PAR") - set(MACRO_NAME "GPUCA_PAR_${param_name}") + if(do_all_architectures GREATER -1) + if(NOT arch MATCHES ^default) + list(APPEND JSON_ARCHITECTURES "${arch}") + endif() + set(list_idx 0) else() - set(MACRO_NAME "GPUCA_${param_name}") + list(FIND ARCH_LIST_EXT "${arch}" list_idx) endif() - set(vals "${param_values}") - string(REGEX REPLACE "^\\[ *" "" vals "${vals}") - string(REGEX REPLACE " *\\]$" "" vals "${vals}") - string(REGEX REPLACE "\"" "" vals "${vals}") - set(MACRO_DEFINITION "#define ${MACRO_NAME} ${vals}") - if(arch MATCHES ^default) - # fallback defaults are wrapped in #ifndef - string(APPEND generate_gpu_param_header_OUTPUT_TMP_${arch} "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") - else() - string(APPEND generate_gpu_param_header_OUTPUT_TMP_${arch} "${MACRO_DEFINITION}\n") + if(list_idx GREATER -1) + string(JSON param_values GET "${JSON_CONTENT}" "${TYPE}" "${param_name}" "${arch}") + if(TYPE STREQUAL "LB") + set(MACRO_NAME "GPUCA_LB_${param_name}") + elseif(TYPE STREQUAL "PAR") + set(MACRO_NAME "GPUCA_PAR_${param_name}") + else() + set(MACRO_NAME "GPUCA_${param_name}") + endif() + set(vals "${param_values}") + string(REGEX REPLACE "^\\[ *" "" vals "${vals}") + string(REGEX REPLACE " *\\]$" "" vals "${vals}") + string(REGEX REPLACE "\"" "" vals "${vals}") + set(MACRO_DEFINITION "#define ${MACRO_NAME} ${vals}") + if(arch MATCHES ^default) + # fallback defaults are wrapped in #ifndef + string(APPEND generate_gpu_param_header_OUTPUT_TMP_${arch} "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") + else() + string(APPEND generate_gpu_param_header_OUTPUT_TMP_${arch} "${MACRO_DEFINITION}\n") + endif() endif() - endif() - endforeach() + endforeach() + endif() endforeach() endforeach() @@ -121,5 +123,4 @@ function(generate_gpu_param_header ARCH_LIST OUT_HEADER OUT_HEADER_DEVICE) file(GENERATE OUTPUT "${OUT_HEADER}" CONTENT "${TMP_HEADER}") file(GENERATE OUTPUT "${OUT_HEADER_DEVICE}" CONTENT "${TMP_HEADER_DEVICE}") message(STATUS "Generated ${OUT_HEADER} and ${OUT_HEADER_DEVICE}") - add_custom_target(GPU_PARAM_HEADER_TARGET ALL DEPENDS ${OUT_HEADER} ${OUT_HEADER_DEVICE} ${GPU_PARAM_JSON}) endfunction() From f1175e1181e24441768ca3f97655786fcadee539 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 9 Feb 2026 00:03:02 +0100 Subject: [PATCH 067/133] GPU Parameters: Support multiple csv/json files, and merge the parameters into the header on the fly --- GPU/GPUTracking/CMakeLists.txt | 37 +++++--- .../cmake/gpu_param_header_generator.cmake | 95 ++++++++++--------- 2 files changed, 75 insertions(+), 57 deletions(-) diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index e52fb80113c00..082dc1f10b1d6 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -263,28 +263,39 @@ add_custom_command( list(APPEND GENERATED_HEADERS_LIST ${ON_THE_FLY_DIR}/GPUDefParametersLoadPrepare.h) if(GPUCA_OVERRIDE_PARAMETER_FILE) - set(GPU_PARAM_JSON ${GPUCA_OVERRIDE_PARAMETER_FILE}) + set(GPU_PARAM_JSON ${GPUCA_OVERRIDE_PARAMETER_FILE}) else() - set(GPU_PARAM_JSON ${CMAKE_CURRENT_SOURCE_DIR}/Definitions/Parameters/GPUParameters.csv) + set(GPU_PARAM_JSON ${CMAKE_CURRENT_SOURCE_DIR}/Definitions/Parameters/GPUParameters.csv) endif() set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${GPU_PARAM_JSON}") -get_filename_component(GPU_PARAM_JSON_EXT ${GPU_PARAM_JSON} EXT) -string(TOLOWER "${GPU_PARAM_JSON_EXT}" GPU_PARAM_JSON_EXT) -if(GPU_PARAM_JSON_EXT STREQUAL .csv) - execute_process( - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/Definitions/Parameters/csv_to_json.sh "${GPU_PARAM_JSON}" - OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/gpu_parameters.json - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +set(GPU_PARAM_JSON_FILES) +set(GPU_PARAM_JSON_N_FILES 0) +foreach(GPU_PARAM_JSON_FILE IN LISTS GPU_PARAM_JSON) + if(NOT EXISTS "${GPU_PARAM_JSON_FILE}") + message(FATAL_ERROR "Parameter file ${GPU_PARAM_JSON_FILE} does not exist") + endif() + get_filename_component(GPU_PARAM_JSON_EXT ${GPU_PARAM_JSON_FILE} EXT) + string(TOLOWER "${GPU_PARAM_JSON_EXT}" GPU_PARAM_JSON_EXT) + if(GPU_PARAM_JSON_EXT STREQUAL .csv) + get_filename_component(GPU_PARAM_JSON_NAME ${GPU_PARAM_JSON_FILE} NAME_WE) + set(CONVOUTFILE "GPUParameters_${GPU_PARAM_JSON_NAME}_${GPU_PARAM_JSON_N_FILES}.json") + execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/Definitions/Parameters/csv_to_json.sh "${GPU_PARAM_JSON_FILE}" + OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/${CONVOUTFILE} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) - message(STATUS "Converted ${GPU_PARAM_JSON} to ${CMAKE_CURRENT_BINARY_DIR}/gpu_parameters.json") - set(GPU_PARAM_JSON ${CMAKE_CURRENT_BINARY_DIR}/gpu_parameters.json) -endif() + message(STATUS "Converted ${GPU_PARAM_JSON_FILE} to ${CONVOUTFILE}") + list(APPEND GPU_PARAM_JSON_FILES ${CMAKE_CURRENT_BINARY_DIR}/${CONVOUTFILE}) + else() + list(APPEND GPU_PARAM_JSON_FILES ${GPU_PARAM_JSON_FILE}) + endif() + math(EXPR GPU_PARAM_JSON_N_FILES "${GPU_PARAM_JSON_N_FILES} + 1") +endforeach() include(cmake/gpu_param_header_generator.cmake) set(GPU_DEFAULT_PARAMS_HEADER ${ON_THE_FLY_DIR}/GPUDefParametersDefaults.h) set(GPU_DEFAULT_PARAMS_HEADER_DEVICE ${ON_THE_FLY_DIR}/GPUDefParametersDefaultsDevice.h) -generate_gpu_param_header("ALL" ${GPU_DEFAULT_PARAMS_HEADER} ${GPU_DEFAULT_PARAMS_HEADER_DEVICE} GPU_CONST_PARAM_ARCHITECTUES) # generate header with default GPU parameters, arch selected by CMake variables +generate_gpu_param_header("${GPU_PARAM_JSON_FILES}" "ALL" "${GPU_DEFAULT_PARAMS_HEADER}" "${GPU_DEFAULT_PARAMS_HEADER_DEVICE}" GPU_CONST_PARAM_ARCHITECTUES) # generate header with default GPU parameters, arch selected by CMake variables list(APPEND GENERATED_HEADERS_LIST ${GPU_DEFAULT_PARAMS_HEADER} ${GPU_DEFAULT_PARAMS_HEADER_DEVICE}) set(HDRS_INSTALL ${HDRS_INSTALL} ${GENERATED_HEADERS_LIST}) diff --git a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake index 0a7b234aa6a18..383d194aaa717 100644 --- a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake +++ b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake @@ -12,7 +12,7 @@ # file gpu_param_header_generator.cmake # author Gabriele Cimador -function(generate_gpu_param_header ARCH_LIST OUT_HEADER OUT_HEADER_DEVICE) +function(generate_gpu_param_header GPU_PARAM_JSON_FILES ARCH_LIST OUT_HEADER OUT_HEADER_DEVICE) list(FIND ARCH_LIST "ALL" do_all_architectures) list(FIND ARCH_LIST "AUTO" do_auto_architectures) if(do_all_architectures GREATER -1 OR do_auto_architectures GREATER -1) @@ -24,7 +24,6 @@ function(generate_gpu_param_header ARCH_LIST OUT_HEADER OUT_HEADER_DEVICE) endif() list(APPEND ARCH_LIST ${TARGET_ARCH}) endif() - file(READ "${GPU_PARAM_JSON}" JSON_CONTENT) # Types set(TYPES CORE LB PAR) @@ -32,57 +31,65 @@ function(generate_gpu_param_header ARCH_LIST OUT_HEADER OUT_HEADER_DEVICE) # Per architecture definitions set(JSON_ARCHITECTURES) - foreach(TYPE IN LISTS TYPES) - string(JSON n_params LENGTH "${JSON_CONTENT}" "${TYPE}") - math(EXPR last "${n_params} - 1") - foreach(i RANGE 0 ${last}) - string(JSON param_name MEMBER "${JSON_CONTENT}" "${TYPE}" "${i}") - string(JSON n_archs LENGTH "${JSON_CONTENT}" "${TYPE}" "${param_name}") - if(n_archs GREATER 0) - math(EXPR last_arch "${n_archs} - 1") - foreach(iArch RANGE 0 ${last_arch}) - string(JSON arch MEMBER "${JSON_CONTENT}" "${TYPE}" "${param_name}" "${iArch}") - if(arch STREQUAL "default_cpu" AND NOT TYPE STREQUAL "PAR") - message(FATAL_ERROR "Bogus entry ${param_name} for ${arch}") - endif() - if(do_all_architectures GREATER -1) - if(NOT arch MATCHES ^default) - list(APPEND JSON_ARCHITECTURES "${arch}") + set(GPU_PARAM_JSON_N_FILES 0) + foreach(GPU_PARAM_JSON_FILE IN LISTS GPU_PARAM_JSON_FILES) + file(READ "${GPU_PARAM_JSON_FILE}" JSON_CONTENT) + foreach(TYPE IN LISTS TYPES) + string(JSON n_params LENGTH "${JSON_CONTENT}" "${TYPE}") + math(EXPR last "${n_params} - 1") + foreach(i RANGE 0 ${last}) + string(JSON param_name MEMBER "${JSON_CONTENT}" "${TYPE}" "${i}") + string(JSON n_archs LENGTH "${JSON_CONTENT}" "${TYPE}" "${param_name}") + if(n_archs GREATER 0) + math(EXPR last_arch "${n_archs} - 1") + foreach(iArch RANGE 0 ${last_arch}) + string(JSON arch MEMBER "${JSON_CONTENT}" "${TYPE}" "${param_name}" "${iArch}") + if(arch STREQUAL "default_cpu" AND NOT TYPE STREQUAL "PAR") + message(FATAL_ERROR "Bogus entry ${param_name} for ${arch}") endif() - set(list_idx 0) - else() - list(FIND ARCH_LIST_EXT "${arch}" list_idx) - endif() - if(list_idx GREATER -1) - string(JSON param_values GET "${JSON_CONTENT}" "${TYPE}" "${param_name}" "${arch}") - if(TYPE STREQUAL "LB") - set(MACRO_NAME "GPUCA_LB_${param_name}") - elseif(TYPE STREQUAL "PAR") - set(MACRO_NAME "GPUCA_PAR_${param_name}") - else() - set(MACRO_NAME "GPUCA_${param_name}") + if(arch MATCHES ^default AND GPU_PARAM_JSON_N_FILES GREATER 0) + message(FATAL_ERROR "Defaults must be provided in first parameter file") endif() - set(vals "${param_values}") - string(REGEX REPLACE "^\\[ *" "" vals "${vals}") - string(REGEX REPLACE " *\\]$" "" vals "${vals}") - string(REGEX REPLACE "\"" "" vals "${vals}") - set(MACRO_DEFINITION "#define ${MACRO_NAME} ${vals}") - if(arch MATCHES ^default) - # fallback defaults are wrapped in #ifndef - string(APPEND generate_gpu_param_header_OUTPUT_TMP_${arch} "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") + if(do_all_architectures GREATER -1) + if(NOT arch MATCHES ^default) + list(APPEND JSON_ARCHITECTURES "${arch}") + endif() + set(list_idx 0) else() - string(APPEND generate_gpu_param_header_OUTPUT_TMP_${arch} "${MACRO_DEFINITION}\n") + list(FIND ARCH_LIST_EXT "${arch}" list_idx) + endif() + if(list_idx GREATER -1) + string(JSON param_values GET "${JSON_CONTENT}" "${TYPE}" "${param_name}" "${arch}") + if(TYPE STREQUAL "LB") + set(MACRO_NAME "GPUCA_LB_${param_name}") + elseif(TYPE STREQUAL "PAR") + set(MACRO_NAME "GPUCA_PAR_${param_name}") + else() + set(MACRO_NAME "GPUCA_${param_name}") + endif() + set(vals "${param_values}") + string(REGEX REPLACE "^\\[ *" "" vals "${vals}") + string(REGEX REPLACE " *\\]$" "" vals "${vals}") + string(REGEX REPLACE "\"" "" vals "${vals}") + set(MACRO_DEFINITION "#define ${MACRO_NAME} ${vals}") + if(arch MATCHES ^default) + # fallback defaults are wrapped in #ifndef + string(APPEND generate_gpu_param_header_OUTPUT_TMP_${arch} "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") + else() + string(APPEND generate_gpu_param_header_OUTPUT_TMP_${arch} "${MACRO_DEFINITION}\n") + endif() endif() - endif() - endforeach() - endif() + endforeach() + endif() + endforeach() endforeach() + math(EXPR GPU_PARAM_JSON_N_FILES "${GPU_PARAM_JSON_N_FILES} + 1") endforeach() list(REMOVE_DUPLICATES JSON_ARCHITECTURES) list(SORT JSON_ARCHITECTURES) - if(ARGC GREATER 3) - set(${ARGV3} "${JSON_ARCHITECTURES}" PARENT_SCOPE) + if(ARGC GREATER 4) + set(${ARGV4} "${JSON_ARCHITECTURES}" PARENT_SCOPE) endif() if(do_all_architectures GREATER -1) list(REMOVE_ITEM ARCH_LIST "ALL") From b77438bbd5aa8d07b4458b5eb80bf519ba9f6ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Mon, 9 Feb 2026 09:25:15 +0100 Subject: [PATCH 068/133] Utilities: Delete unused files (#15039) --- .../DataCompression/CodingModelDispatcher.h | 380 ------------ .../DataCompression/runtime_container.h | 583 ------------------ .../tpccluster_parameter_model.h | 101 --- .../internal/containers/HistogramInterface.h | 88 --- 4 files changed, 1152 deletions(-) delete mode 100644 Utilities/DataCompression/include/DataCompression/CodingModelDispatcher.h delete mode 100644 Utilities/DataCompression/include/DataCompression/runtime_container.h delete mode 100644 Utilities/DataCompression/tpccluster_parameter_model.h delete mode 100644 Utilities/rANS/include/rANS/internal/containers/HistogramInterface.h diff --git a/Utilities/DataCompression/include/DataCompression/CodingModelDispatcher.h b/Utilities/DataCompression/include/DataCompression/CodingModelDispatcher.h deleted file mode 100644 index 68fcc8360df2b..0000000000000 --- a/Utilities/DataCompression/include/DataCompression/CodingModelDispatcher.h +++ /dev/null @@ -1,380 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/* Local Variables: */ -/* mode: c++ */ -/* End: */ - -#ifndef CODINGMODELDISPATCHER_H -#define CODINGMODELDISPATCHER_H - -/// @file CodingModelDispatcher.h -/// @author Matthias Richter -/// @since 2016-09-11 -/// @brief Runtime dispatcher interface for probability model definitions - -#include "mpl_tools.h" -#include "runtime_container.h" -#include -#include -#include -#include - -using namespace gNeric; - -namespace o2 -{ -namespace data_compression -{ - -/** - * @class CodingModelDispatcher Runtime dispatcher interface - * @brief Runtime dispatcher interface for probability model definitions - * - * ModelDefinition single coding model or mpl sequence of models - * - * TODO: - * - consistency check for coding direction, all model definitions have to obey - * same direction - * - probably one should also require the same code type for all definitions, at - * least in the codec. Multiple code types do not make much sense in the codec - */ -template -class CodingModelDispatcher -{ - public: - CodingModelDispatcher() : mPosition(0), mContainer() {} - ~CodingModelDispatcher() = default; - - using self_type = CodingModelDispatcher; - - // make_mpl_vector traits makes sure that an mpl sequence is used further on - // if the original type is not a sequence it is wrapped into an mpl vector with - // the original type as the only element - using definition_type = typename mpl::make_mpl_vector::type; - - // the runtime container type is the heart of the dispatcher to runtime objects - // of the sequence of data types which define the probability model - using container_type = typename create_rtc>::type; - - using code_type = typename container_type::wrapped_type::code_type; - - /// get the number of models in the definition - static int getNumberOfModels() { return boost::mpl::size::value; } - - /// return highest stage of runtime container - container_type& operator*() { return mContainer; } - - /// functor to add weight to probability model at runtime container level - template - class addWeightFctr - { - public: - addWeightFctr(ValueType _v, WeightType _w) : value(_v), weight(_w) {} - ~addWeightFctr() {} - - using return_type = bool; - - template - return_type operator()(T& stage) - { - // the addWeight function belongs to the probability model as base - // of the specific model; funcions of the base can be accessed by - // static casting. This avoids an extra level of function calls. - return static_cast(*stage).addWeight(value, weight); - } - - private: - ValueType value; - WeightType weight; - }; - - /** - * add weight to current model - * - * Dispatcher increments to the next model definition after decoding if - * parameter switchToNextModel is true. - */ - template - bool addWeight(ValueType v, WeightType w, bool switchToNextModel = true) - { - bool result = mContainer.apply(mPosition, addWeightFctr(v, w)); - if (switchToNextModel && ++mPosition >= getNumberOfModels()) { - mPosition = 0; - } - return result; - } - - /** - * init model - */ - class initFctr - { - public: - initFctr(container_type& container) : mContainer(container) {} - ~initFctr() {} - - using return_type = int; - - template - return_type operator()(boost::type) - { - T& stage = static_cast(mContainer); - return (*stage).init(); - } - - private: - container_type& mContainer; - }; - - /** - * init dispatcher and models - */ - int init() - { - mPosition = 0; - boost::mpl::for_each>(initFctr(mContainer)); - return 0; - } - - /** - * TODO: this is tailored to HuffmanCodec for the moment, some generic interface - * has to come - */ - class generateFctr - { - public: - generateFctr(container_type& container) : mContainer(container) {} - ~generateFctr() {} - - using return_type = int; - - template - return_type operator()(boost::type) - { - T& stage = static_cast(mContainer); - return (*stage).GenerateHuffmanTree(); - } - - private: - container_type& mContainer; - }; - - /** - * TODO: maybe 'generate' is not the appropriate name - */ - int generate() - { - boost::mpl::for_each>(generateFctr(mContainer)); - return 0; - } - - /// functor to execute encoding on runtime container level - template - class encodeFctr - { - public: - encodeFctr(ValueType _v, CodeType& _code, uint16_t& _codeLength) : code(_code), value(_v), codeLength(_codeLength) - { - } - ~encodeFctr() {} - - using return_type = bool; - - template - return_type operator()(T& stage) - { - code = (*stage).Encode(value, codeLength); - return true; - } - - private: - CodeType& code; - ValueType value; - uint16_t& codeLength; - }; - - /** - * Encode a value - * - * Dispatcher increments to the next model definition after decoding if - * parameter switchToNextModel is true. - */ - template - bool encode(ValueType v, CodeType& code, uint16_t& codeLength, bool switchToNextModel = true) - { - bool result = mContainer.apply(mPosition, encodeFctr(v, code, codeLength)); - if (switchToNextModel && ++mPosition >= getNumberOfModels()) { - mPosition = 0; - } - return result; - } - - /// Functor to execute decoding on runtime container level - template - class decodeFctr - { - public: - decodeFctr(ValueType& _v, CodeType _code, uint16_t& _codeLength) : code(_code), value(_v), codeLength(_codeLength) - { - } - ~decodeFctr() {} - - using return_type = bool; - - template - return_type operator()(T& stage) - { - value = (*stage).Decode(code, codeLength); - return true; - } - - private: - CodeType code; - ValueType& value; - uint16_t& codeLength; - }; - - /** - * Decode a code sequence - * Code direction can be either from MSB to LSB or LSB to MSB, controlled - * by template parameter orderMSB of the probability model. - * - * Dispatcher increments to the next model definition after decoding if - * parameter switchToNextModel is true. - */ - template - bool decode(ValueType& v, CodeType code, uint16_t& codeLength, bool switchToNextModel = true) - { - bool result = mContainer.apply(mPosition, decodeFctr(v, code, codeLength)); - if (switchToNextModel && ++mPosition >= getNumberOfModels()) { - mPosition = 0; - } - return result; - } - - class getCodingDirectionFctr - { - public: - using return_type = bool; - template - return_type operator()(T& stage) - { - return T::wrapped_type::orderMSB; - } - }; - - /** - * Get coding direction for model at current position - */ - bool getCodingDirection() { return mContainer.apply(mPosition, getCodingDirectionFctr()); } - - /// write functor - class writeFctr - { - public: - writeFctr(std::ostream& out, container_type& container) : mOut(out), mContainer(container) {} - ~writeFctr() {} - - using return_type = std::ostream&; - - template - return_type operator()(boost::type) - { - T& stage = static_cast(mContainer); - if (T::level::value > 0) { - mOut << std::endl; // blank line between dumps - } - mOut << T::level::value << " " << (*stage).getName() << std::endl; - (*stage).write(mOut); - return mOut; - } - - private: - std::ostream& mOut; - container_type& mContainer; - }; - - /** - * Write configuration - * - * TODO: introduce a general storage policy, a text file is used for now - */ - int write(const char* filename = nullptr) - { - std::ofstream ofile(filename); - boost::mpl::for_each>( - writeFctr(ofile.good() ? ofile : std::cout, mContainer)); - ofile.close(); - return 0; - } - - /// read functor - class readFctr - { - public: - readFctr(std::istream& in, container_type& container) : mIn(in), mContainer(container) {} - ~readFctr() {} - - using return_type = bool; - - template - return_type operator()(boost::type) - { - T& stage = static_cast(mContainer); - std::string level, name, remaining; - mIn >> level; - mIn >> name; - if (!mIn) { - return false; - } - if (std::stoi(level) != T::level::value || name.compare((*stage).getName())) { - std::cerr << "Format error: expecting level '" << T::level::value << "' and name '" << (*stage).getName() - << "', got: " << level << " " << name << std::endl; - } - std::cout << "reading configuration for model " << name << std::endl; - std::getline(mIn, remaining); // flush the current line - (*stage).read(mIn); - return true; - } - - private: - std::istream& mIn; - container_type& mContainer; - }; - - /** - * Read configuration - * - * TODO: introduce a general storage policy, a text file is used for now - */ - int read(const char* filename) - { - std::ifstream input(filename); - if (!input.good()) { - return -1; - } - // TODO: probably need mpl fold here to propagate the return value - boost::mpl::for_each>(readFctr(input, mContainer)); - return 0; - } - - private: - /// position for cyclic dispatch - int mPosition; - /// the runtime container - container_type mContainer; -}; - -} // namespace data_compression -} // namespace o2 - -#endif diff --git a/Utilities/DataCompression/include/DataCompression/runtime_container.h b/Utilities/DataCompression/include/DataCompression/runtime_container.h deleted file mode 100644 index 363f4220e73f6..0000000000000 --- a/Utilities/DataCompression/include/DataCompression/runtime_container.h +++ /dev/null @@ -1,583 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -//-*- Mode: C++ -*- - -#ifndef RUNTIME_CONTAINER_H -#define RUNTIME_CONTAINER_H -//**************************************************************************** -//* This file is free software: you can redistribute it and/or modify * -//* it under the terms of the GNU General Public License as published by * -//* the Free Software Foundation, either version 3 of the License, or * -//* (at your option) any later version. * -//* * -//* Primary Author(s): Matthias Richter * -//* * -//* The authors make no claims about the suitability of this software for * -//* any purpose. It is provided "as is" without express or implied warranty. * -//**************************************************************************** - -/// @file runtime_container.h -/// @author Matthias Richter -/// @since 2016-09-11 -/// @brief A general runtime container for a compile time sequence -/// This file is part of https://github.com/matthiasrichter/gNeric - -// clang-format off - -// A general runtime container for a compile time sequence -// of types. A mixin class is used to represent a member of each data -// type. Every data type in the sequence describes a mixin on top of -// the previous one. The runtime container accumulates the type -// properties. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace boost::mpl::placeholders; - -namespace gNeric { - -/** - * @class DefaultInterface - * @brief The default interface for the RuntimeContainer - * - * The common interface for the mixin class. In order to allow entry - * points to the different levels of the mixin, none of the interface - * functions has to be declared virtual. The function implementation of - * the top most mixin would be called otherwise. - * - * The mixin technique requires a base class, but it mostly makes sense in - * the picture of runtime polymorphism and virtual interfaces. The runtime - * container application is purely using static polymorphism which makes the - * base interface just to a technical aspect. - */ -class DefaultInterface -{ -public: - DefaultInterface() {} - ~DefaultInterface() {} - - void print() const {} -}; - -/** - * @brief Default initializer does nothing - */ -struct default_initializer -{ - template - void operator()(T&) {} -}; - -/** - * @brief An initializer for simple types - * The initializer makes use of truncation for non-float types, and - * over- and underflow to produce different values in the member - * of the individual stages in the container. - * - float types keep the fraction - * - integral types truncate the fraction - * - unsigned types undergo an underflow and produce big numbers - * - 8 bit char produces the '*' character - * - * Mainly for testing and illustration purposes. - */ -struct funny_initializer -{ - template - void operator()(T& v) {v=0; v-=214.5;} -}; - -/** - * @brief Default printer prints nothing - */ -struct default_printer -{ - template - bool operator()(const T& v, int level = -1) {return false;} -}; - -/** - * @brief Verbose printer prints level and content - */ -template -struct verbose_printer_base -{ - template - bool operator()(const T& v, int level = -1) { - std::cout << "RC mixin level " - << std::setw(2) - << level << ": " << v << std::endl; - return recursive; - } -}; - -/** - * @brief Verbose printer to print levels recursively - */ -struct recursive_printer : verbose_printer_base {}; - -// preserve backward compatibility -typedef recursive_printer verbose_printer; - -/** - * @brief Verbose printer to print a single level - */ -struct single_printer : verbose_printer_base {}; - -/** - * @brief Setter functor, forwards to the container mixin's set function - */ -template -class set_value { -public: - typedef void return_type; - typedef U value_type; - - set_value(U u) : mValue(u) {} - template - return_type operator()(T& t) { - *t = mValue; - } - -private: - set_value(); // forbidden - U mValue; -}; - -/** - * @brief Adder functor - */ -template -class add_value { -public: - typedef void return_type; - typedef U value_type; - - add_value(U u) : mValue(u) {} - template - return_type operator()(T& t) { - *t += mValue; - } - -private: - add_value(); // forbidden - U mValue; -}; - -/** - * @brief Getter functor, forwards to the container mixin's get function - * - * TODO: make a type trait to either return t.get() if its a container - * instance or t directly if it is the member object - */ -template -class get_value { -public: - typedef U return_type; - typedef U value_type; - class NullType {}; -private: - /* could not solve the problem that one has to instantiate Traits - with a fixed number of template arguments where wrapped_type - would need to be provided already to go into the specialization - template - struct Traits { - typedef NullType container_type; - typedef InstanceType type; - static return_type apply(InstanceType& c) { - std::cout << "Traits"; - return c; - } - }; - // specialization for container instances - template - struct Traits { - typedef InstanceType container_type; - typedef typename InstanceType::wrapped_type type; - static return_type apply(InstanceType& c) { - std::cout << "specialized Traits"; - return c.get(); - } - }; - */ - -public: - template - return_type operator()(T& t) { - return t.get(); - //return (typename Traits::type)(t); - } -}; - - -/****************************************************************************** - * @brief apply functor to the wrapped member object in the runtime container - * This meta function recurses through the list while incrementing the index - * and calls the functor at the required position - * - * @note internal meta function for the RuntimeContainers' apply function - */ -template < - typename _ContainerT // container type - , typename _IndexT // data type of position index - , typename _Iterator // current iterator position - , typename _End // end iterator position - , _IndexT _Index // current index - , typename F // functor - > -struct rc_apply_at -{ - static typename F::return_type apply( _ContainerT& c, _IndexT position, F& f ) - { - if ( position == _Index ) { - // this is the queried position, make the type cast to the current - // stage of the runtime container and execute function for it. - // Terminate loop by forwarding _End as _Iterator and thus - // calling the specialization - typedef typename boost::mpl::deref< _Iterator >::type stagetype; - stagetype& stage = static_cast(c); - return f(stage); - } else { - // go to next element - return rc_apply_at< - _ContainerT - , _IndexT - , typename boost::mpl::next< _Iterator >::type - , _End - , _Index + 1 - , F - >::apply( c, position, f ); - } - } -}; -// specialization: end of recursive loop, kicks in if _Iterator matches -// _End. -// here we end up if the position parameter is out of bounds -template < - typename _ContainerT // container type - , typename _IndexT // data type of position index - , typename _End // end iterator position - , _IndexT _Index // current index - , typename F // functor - > -struct rc_apply_at<_ContainerT - , _IndexT - , _End - , _End - , _Index - , F - > -{ - static typename F::return_type apply( _ContainerT& c, _IndexT position, F& f ) - { - // TODO: this is probably the place to throw an exeption because - // we are out of bound - return typename F::return_type(0); - } -}; - -/** - * Apply functor to the specified container level - * - * Ignores parameter '_IndexT' - */ -template -struct rc_apply { - typedef typename _ContainerT::types types; - static typename F::return_type apply(_ContainerT& c, _IndexT /*ignored*/, F& f) - { - return f(static_cast<_StageT&>(c)); - } -}; - -/** - * Generalized dispatcher with the ability for code unrolling - * - * The optional template parameter 'Position' can be used to cast directly to - * the specified level in the runtime container and apply the functor without - * the recursive loop. The template call with default parameters forwards to - * the recursive call because 'Position' is set to out of list range. - */ -template - , typename _IndexT = int - > -struct rc_dispatcher { - typedef typename _ContainerT::types types; - typedef typename boost::mpl::if_< - boost::mpl::less > - , rc_apply<_ContainerT, typename boost::mpl::at::type, _IndexT, F> - , rc_apply_at< - _ContainerT - , _IndexT - , typename boost::mpl::begin::type - , typename boost::mpl::end::type - , 0 - , F - > - >::type type; - - static typename F::return_type apply(_ContainerT& c, _IndexT position, F& f) { - return type::apply(c, position, f); - } -}; - -/** - * @class RuntimeContainer The base for the mixin class - * @brief the technical base of the mixin class - * - * The class is necessary to provide the innermost functionality of the - * mixin. - * - * The level of the mixin is encoded in the type 'level' which is - * incremented in each mixin stage. - */ -template -struct RuntimeContainer : public InterfacePolicy -{ - InitializerPolicy _initializer; - PrinterPolicy _printer; - typedef boost::mpl::int_<-1> level; - typedef boost::mpl::vector<>::type types; - - /// get size which is 0 at this level - constexpr std::size_t size() const {return 0;} - - void print() { - const char* string = "base"; - _printer(string, level::value); - } - - // not yet clear if we need the setter and getter in the base class - // at least wrapped_type is not defined in the base - //void set(wrapped_type) {mMember = v;} - //wrapped_type get() const {return mMember;} - -}; - -/** - * @class rc_mixin Components for the mixin class - * @brief Mixin component is used with different data types - * - * Each mixin component has a member of the specified type. The container - * level exports the following data types to the outside: - * - wrapped_type the data type at this level - * - mixin_type composed type at this level - * - types mpl sequence containing all level types - * - level a data type containing the level - */ -template -class rc_mixin : public BASE -{ -public: - rc_mixin() : mMember() {BASE::_initializer(mMember);} - - /// each stage of the mixin class wraps one type - typedef T wrapped_type; - /// this is the self type - typedef rc_mixin mixin_type; - /// a vector of all mixin stage types so far - typedef typename boost::mpl::push_back::type types; - /// increment the level counter - typedef typename boost::mpl::plus< typename BASE::level, boost::mpl::int_<1> >::type level; - void print() { - // use the printer policy of this level, the policy returns - // a bool determining whether to call the underlying level - if (BASE::_printer(mMember, level::value)) { - BASE::print(); - } - } - - /// get size at this stage - constexpr std::size_t size() const {return level::value + 1;} - /// set member wrapped object - void set(wrapped_type v) {mMember = v;} - /// get wrapped object - wrapped_type get() const {return mMember;} - /// get wrapped object reference - wrapped_type& operator*() {return mMember;} - /// assignment operator to wrapped type - wrapped_type& operator=(const wrapped_type& v) {mMember = v; return mMember;} - /// type conversion to wrapped type - operator wrapped_type() const {return mMember;} - /// operator - wrapped_type& operator+=(const wrapped_type& v) {mMember += v; return mMember;} - /// operator - wrapped_type operator+(const wrapped_type& v) {return mMember + v;} - - /// a functor wrapper dereferencing the RC container instance - /// the idea is to use this extra wrapper to apply the functor directly to - /// the wrapped type, see the comment below - template - class member_apply_at { - public: - member_apply_at(F& f) : mFunctor(f) {} - typedef typename F::return_type return_type; - template - typename F::return_type operator()(_T& me) { - return mFunctor(*me); - } - private: - member_apply_at(); //forbidden - F& mFunctor; - }; - - /// apply functor to the runtime object at index - /// TODO: there is a performance issue with this solution, introducing another - /// level of functors makes the access much slower compared with applying to - /// container instance and using container member functions, tested with the - /// add_value functor and bench_runtime_container, also the actual operation - /// needs to be checked, the result is not correct for the last check of - /// 100000000 iterations - /* - template - typename F::return_type applyToMember(int index, F f) { - return apply(index, member_apply_at(f)); - } - */ - - /* - * Apply a functor to the runtime container at index - * - * For performance tests there is a template option to do an explicite loop - * unrolling for the first n (=10) elements. This is however only effective - * if the compiler optimization is switched of. This is in the end a nice - * demonstrator for the potential of compiler optimization. Unrolling is - * switched on with the compile time switch RC_UNROLL. - */ - template - typename F::return_type apply(int index, F f) { - if (unroll) {// this is a compile time switch - // do unrolling for the first n elements and forward to generic - // recursive function for the rest. - switch (index) { - case 0: return rc_dispatcher, int>::apply(*this, 0, f); - case 1: return rc_dispatcher, int>::apply(*this, 1, f); - case 2: return rc_dispatcher, int>::apply(*this, 2, f); - case 3: return rc_dispatcher, int>::apply(*this, 3, f); - case 4: return rc_dispatcher, int>::apply(*this, 4, f); - case 5: return rc_dispatcher, int>::apply(*this, 5, f); - case 6: return rc_dispatcher, int>::apply(*this, 6, f); - case 7: return rc_dispatcher, int>::apply(*this, 7, f); - case 8: return rc_dispatcher, int>::apply(*this, 8, f); - case 9: return rc_dispatcher, int>::apply(*this, 9, f); - } - } - return rc_dispatcher::apply(*this, index, f); - } - -private: - T mMember; -}; - -/** - * @brief Applying rc_mixin with the template parameters as placeholders - * The wrapping into an mpl lambda is necessary to separate placeholder scopes - * in the mpl fold operation. - */ -typedef typename boost::mpl::lambda< rc_mixin<_1, _2> >::type apply_rc_mixin; - -/** - * @brief check the mixin level to be below specified level - * - * @note: the number is specified as a type, e.g. boost::mpl:int_<3> - */ -template< typename T, typename N > struct rtc_less -: boost::mpl::bool_<(T::level::value < boost::mpl::minus>::value) > {}; - -template< typename T, typename N > struct rtc_equal -: boost::mpl::bool_::type> {}; - -/** - * @brief create the runtime container type - * The runtime container type is build from a list of data types, the recursive - * build can be optionally stopped at the level of argument N. - * - * Usage: typedef create_rtc::type container_type; - */ -template> -struct create_rtc -{ - typedef typename boost::mpl::lambda< - // mpl fold loops over all elements in the list of the first template - // parameter and provides this as placeholder _2; for every element the - // operation of the third template parameter is applied to the result of - // the previous stage which is provided as placeholder _1 to the operation - // and initialized to the second template argument for the very first - // operation - typename boost::mpl::fold< - // list of types, each element provided as placeholder _1 - Types - // initializer for the _1 placeholder - , Base - // recursively applied operation, depending on the outcome of rtc_less - // either the next mixin level is applied or the current state is used - , boost::mpl::if_< - rtc_less<_1, N > - // apply mixin level - , boost::mpl::apply2< boost::mpl::protect::type, _1, _2 > - // keep current state by identity - , boost::mpl::identity<_1> - > - >::type - >::type type; -}; - -/** - * @brief create an mpl vector of mixin types - * Every stage in the runtime container contains all the previous ones. - * The resulting mpl vector of this meta function contains all individual - * stages. - * - * Usage: typedef create_rtc_types::type container_types; - */ -template> -struct create_rtc_types -{ - typedef typename boost::mpl::fold< - boost::mpl::range_c - , boost::mpl::vector< > - , boost::mpl::push_back<_1, create_rtc>>> - >::type type; -}; - -};// namespace gNeric -// clang-format on - -#endif diff --git a/Utilities/DataCompression/tpccluster_parameter_model.h b/Utilities/DataCompression/tpccluster_parameter_model.h deleted file mode 100644 index e8455399f17c1..0000000000000 --- a/Utilities/DataCompression/tpccluster_parameter_model.h +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "DataCompression/dc_primitives.h" -#include "DataCompression/HuffmanCodec.h" -#include -#include -#include - -/** - * Parameter model definitions - * - boost mpl vector of alphabets - */ -using tpccluster_parameter = - boost::mpl::vector>, - BitRangeContiguousAlphabet>, - BitRangeContiguousAlphabet>, - BitRangeContiguousAlphabet>, - BitRangeContiguousAlphabet>, - BitRangeContiguousAlphabet>, - BitRangeContiguousAlphabet>>; -/** - * Definition of Huffman probability models for the above defined alphabets - * - * This is a temporary definition, the mpl sequence can be created automatically - * from the list of alphabet types, but did not manage so far (see below) - */ -template -using Model = o2::HuffmanModel>, - o2::HuffmanNode>, true>; - -using tpccluster_parameter_models = - boost::mpl::vector>, - Model>, - Model>, - Model>, - Model>, - Model>, - Model>>; - -/** new approach - using basemodels = foldtype - < tpccluster_parameter, - mpl::lambda>::type - >::type; - - using tpcmodels = foldtype - < basemodels, - mpl::lambda<_, o2::HuffmanNode>>::type - >::type; -*/ - -/** - * this was an attemp to create the vector of Huffman models directly - * from the vector of alphabets - * - * For the moment, the placeholders of mpl fold are not expanded, so there are - * unknown types in the end - */ -/// very first attemp -//using namespace boost::mpl::placeholders; -// -//typedef boost::mpl::fold< -// tpccluster_parameter, -// boost::mpl::vector<>, -// boost::mpl::push_back< -// _1 -// , AliceO2::HuffmanModel< ProbabilityModel< _2 >, AliceO2::HuffmanNode>, true> -// > -// >::type models_t; - -/// trying with additional lambda levels -//typedef boost::mpl::string<'T','e','s','t'>::type TestAlphabetName; -//typedef ContiguousAlphabet TestAlphabet; -// -//typedef typename boost::mpl::lambda< ProbabilityModel< _1 > > apply_alphabet; -//typedef boost::mpl::apply1::type TestAlphabetModel; -//typedef typename boost::mpl::lambda< AliceO2::HuffmanModel< _1, AliceO2::HuffmanNode>, true> > apply_probabilitymodel; -//typedef typename boost::mpl::apply1::type, TestAlphabetModel>::type TestHuffmanModel; -// -//TestAlphabetModel object; -//typedef TestAlphabetModel::value_type vtype; -// -//std::cout << object.getName() << std::endl; - -//typedef boost::mpl::fold< -// tpccluster_parameter, -// boost::mpl::vector<>, -// boost::mpl::push_back< -// _1 -// , boost::mpl::apply1< boost::mpl::protect::type, _2 > -// > -// >::type models_t; diff --git a/Utilities/rANS/include/rANS/internal/containers/HistogramInterface.h b/Utilities/rANS/include/rANS/internal/containers/HistogramInterface.h deleted file mode 100644 index 2c703ede64493..0000000000000 --- a/Utilities/rANS/include/rANS/internal/containers/HistogramInterface.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2019-2023 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file HistogramInterface.h -/// @author Michael Lettrich -/// @brief Operations that will be performed on a histogram - -#ifndef RANS_INTERNAL_CONTAINERS_HISTOGRAMINTERFACE_H_ -#define RANS_INTERNAL_CONTAINERS_HISTOGRAMINTERFACE_H_ - -#include - -#include "rANS/internal/common/utils.h" - -namespace o2::rans::internal -{ - -template -class HistogramInterface -{ - - public: - using source_type = source_T; - using value_type = value_T; - using difference_type = difference_T; - - // operations - template - inline derived_T& addSamples(source_IT begin, source_IT end) - { - static_assert(utils::isCompatibleIter_v); - - if (begin == end) { - return static_cast(*this); - } else { - return static_cast(this)->addSamples(begin, end); - } - }; - - inline derived_T& addSamples(gsl::span samples) - { - return addSamples(samples.data(), samples.data() + samples.size()); - }; - - template - inline derived_T& addFrequencies(freq_IT begin, freq_IT end, difference_type offset) - { - static_assert(utils::isCompatibleIter_v); - - if (begin == end) { - return static_cast(*this); - } else { - return static_cast(this)->addFrequencies(begin, end, offset); - } - }; - - inline derived_T& addFrequencies(gsl::span frequencies, difference_type offset) - { - return addFrequencies(frequencies.data(), frequencies.data() + frequencies.size(), offset); - }; - - derived_T& operator+(derived_T& other) - { - return addFrequencies(other.cbegin(), other.cbegin(), other.getOffset()); - }; - - protected: - HistogramInterface() = default; - - template - HistogramInterface(freq_IT begin, freq_IT end, difference_type offset) - { - static_assert(utils::isIntegralIter_v); - addFrequencies(begin, end, offset); - }; -}; - -} // namespace o2::rans::internal - -#endif /* RANS_INTERNAL_CONTAINERS_HISTOGRAMINTERFACE_H_ */ From c5ead8881e60c064d0c804889da12e1c55edaba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Mon, 9 Feb 2026 09:46:24 +0100 Subject: [PATCH 069/133] Algorithm: Delete unused files (#15025) --- Algorithm/include/Algorithm/BitstreamReader.h | 290 ------------------ Algorithm/test/test_BitstreamReader.cxx | 121 -------- 2 files changed, 411 deletions(-) delete mode 100644 Algorithm/include/Algorithm/BitstreamReader.h delete mode 100644 Algorithm/test/test_BitstreamReader.cxx diff --git a/Algorithm/include/Algorithm/BitstreamReader.h b/Algorithm/include/Algorithm/BitstreamReader.h deleted file mode 100644 index 0a112183ab5ef..0000000000000 --- a/Algorithm/include/Algorithm/BitstreamReader.h +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef BITSTREAMREADER_H -#define BITSTREAMREADER_H - -/// @file BitstreamReader.h -/// @author Matthias Richter -/// @since 2019-06-05 -/// @brief Utility class to provide bitstream access to an underlying resource - -#include -#include - -namespace o2 -{ -namespace algorithm -{ - -/// @class BitStreamReader -/// @brief Utility class to provide bitstream access to an underlying resource -/// -/// Allows to access bits of variable length, supports integral types and also -/// bitsets as target type. At the moment, the access is in direction MSB -> LSB. -/// -/// BitstreamReader reader(start, end); -/// while (reader.good() && not reader.eof()) { -/// // get an 8 bit value from the stream, moves the position -/// uint8_t ivalue; -/// reader.get(ivalue); -/// -/// // get a 13 bit bitset without moving the position -/// std::bitset<13> value; -/// reader.peek(value, value.size()); -/// // e.g. use 7 bits of the data -/// value >>= value.size() - 7; -/// // move position by the specific number of bits -/// reader.seek(7); -/// } -template -class BitstreamReader -{ - public: - using self_type = BitstreamReader; - // for the moment we simply use pointers, but with some traits this can be extended to - // containers - using value_type = BufferType; - using iterator = const value_type*; - static constexpr size_t value_size = sizeof(value_type) * 8; - BitstreamReader() = delete; - BitstreamReader(iterator start, iterator end) - : mStart(start), mEnd(end), mCurrent(mStart), mBitPosition(value_size) - { - } - ~BitstreamReader() = default; - - /// Check reader's state - /// @return true if not in error state - bool good() const - { - return mBitPosition > 0; - } - - /// Indicates end of data - /// @return true if end of resource is reached - bool eof() const - { - return mCurrent == mEnd && mBitPosition > 0; - } - - /// Reset the reader, start over at beginning - void reset() - { - mCurrent = mStart; - mBitPosition = value_size; - } - - /// Get the next N bits without moving the read position - /// if bitlength is smaller than the size of data type, result is aligned to LSB - /// TODO: this also works nicely for bitsets, but then the bitlength has to be specified - /// as template parameter, want to do a specific overload, but needs more work to catch - /// all cases. - /// @param v target variable passed by reference - /// @return number of poked bits - template - size_t peek(T& v) - { - static_assert(N <= sizeof(T) * 8); - return peek(v, N); - } - - /// Get the next n bits without moving the read position - /// if bitlength is smaller than the size of data type, result is aligned to LSB - /// @param v target variable passed by reference - /// @param bitlength number of bits to read - /// @return number of poked bits - template - size_t peek(T& v, size_t bitlength) - { - return peek(v, bitlength); - } - - /// Move read position - /// @param bitlength move count in number of bits - void seek(size_t bitlength) - { - while (good() && bitlength > 0 && mCurrent != mEnd) { - if (bitlength >= mBitPosition) { - bitlength -= mBitPosition; - mBitPosition = 0; - } else { - mBitPosition -= bitlength; - bitlength = 0; - } - if (mBitPosition == 0) { - mCurrent++; - mBitPosition = value_size; - } - } - - if (bitlength > 0) { - mBitPosition = 0; - } - } - - /// Get the next n bits and move the read position - template - T get() - { - T result; - peek(result); - seek(N); - return result; - } - - /// Get the next n and move the read position - template - T get(size_t bitlength = sizeof(T) * 8) - { - T result; - peek(result, bitlength); - seek(bitlength); - return result; - } - - /// @class Bits - /// @brief Helper class to get value of specified type which holds the number used bits - /// - /// The class holds both the extracted value access via peek method and the number of used - /// bits. The reader will be incremented when the object is destroyed. - /// The number of bits can be adjusted by using markUsed method - template - class Bits - { - public: - using field_type = FieldType; - static_assert(N <= sizeof(FieldType) * 8); - Bits() - : mParent(nullptr), mData(0), mLength(0) - { - } - Bits(ParentType* parent, FieldType&& data) - : mParent(parent), mData(std::move(data)), mLength(N) - { - } - Bits(Bits&& other) - : mParent(other.mParent), mData(std::move(other.mData)), mLength(other.mLength) - { - other.mParent = nullptr; - other.mLength = 0; - } - - ~Bits() - { - if (mParent) { - mParent->seek(mLength); - } - } - - auto& operator=(Bits&& other) - { - mParent = other.mParent; - mData = std::move(other.mData); - mLength = other.mLength; - other.mParent = nullptr; - other.mLength = 0; - - return *this; - } - - FieldType& operator*() - { - return mData; - } - - void markUsed(size_t length) - { - mLength = length; - } - - private: - ParentType* mParent; - FieldType mData; - size_t mLength; - }; - - /// Read an integral value from the stream - template ::value, int> = 0> - self_type& operator>>(T& target) - { - target = get(); - return *this; - } - - /// Read a bitstream value from the stream - template - self_type& operator>>(std::bitset& target) - { - target = get, N>(); - return *this; - } - - /// Read a Bits object from the stream - template - self_type& operator>>(Bits& target) - { - T bitfield; - peek(bitfield); - target = std::move(Bits(this, std::move(bitfield))); - return *this; - } - - private: - /// The internal peek method - template - size_t peek(T& result, size_t bitlength) - { - if constexpr (RuntimeCheck) { - // the runtime check is disabled if bitlength is derived at compile time - if (bitlength > sizeof(T) * 8) { - throw std::length_error(std::string("requested bit length ") + std::to_string(bitlength) + " does not fit size of result data type " + std::to_string(sizeof(T) * 8)); - } - } - result = 0; - size_t bitsToWrite = bitlength; - auto current = mCurrent; - auto bitsAvailable = mBitPosition; - while (bitsToWrite > 0 && current != mEnd) { - // extract available bits - value_type mask = ~value_type(0) >> (value_size - bitsAvailable); - if (bitsToWrite >= bitsAvailable) { - T value = (*current & mask) << (bitsToWrite - bitsAvailable); - result |= value; - bitsToWrite -= bitsAvailable; - bitsAvailable = 0; - } else { - value_type value = (*current & mask) >> (bitsAvailable - bitsToWrite); - result |= value; - bitsAvailable -= bitsToWrite; - bitsToWrite = 0; - } - if (bitsAvailable == 0) { - current++; - bitsAvailable = value_size; - } - } - - return bitlength - bitsToWrite; - } - - /// start of resource - iterator mStart; - /// end of resource - iterator mEnd; - /// current position in resource - iterator mCurrent; - /// bit position in current element - size_t mBitPosition; -}; -} // namespace algorithm -} // namespace o2 -#endif diff --git a/Algorithm/test/test_BitstreamReader.cxx b/Algorithm/test/test_BitstreamReader.cxx deleted file mode 100644 index 41e3b47f5f276..0000000000000 --- a/Algorithm/test/test_BitstreamReader.cxx +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file test_BitstreamReader.cxx -/// @author Matthias Richter -/// @since 2019-06-05 -/// @brief Test program for BitstreamReader utility - -#define BOOST_TEST_MODULE Algorithm BitstreamReader unit test -#define BOOST_TEST_MAIN -#define BOOST_TEST_DYN_LINK -#include -#include -#include -#include -#include -#include -#include "../include/Algorithm/BitstreamReader.h" - -namespace o2 -{ -namespace algorithm -{ - -BOOST_AUTO_TEST_CASE(test_BitstreamReader_basic) -{ - std::array data = {'d', 'e', 'a', 'd', 'b', 'e', 'e', 'f'}; - std::array expected7bit = {0x32, 0x19, 0x2c, 0x16, 0x23, 0x09, 0x4a, 0x65, 0x33, 0x0}; - auto reference = expected7bit.begin(); - constexpr size_t totalBits = data.size() * sizeof(decltype(data)::value_type) * 8; - size_t bitsRead = 0; - - BitstreamReader reader(data.data(), data.data() + data.size()); - while (bitsRead < totalBits) { - BOOST_REQUIRE(reference != expected7bit.end()); - BOOST_CHECK(reader.eof() == false); - uint8_t value; - reader.peek(value); - // we use 7 bits of the data - value >>= 1; - reader.seek(7); - bitsRead += 7; - // in the last call should there is not enough data - BOOST_CHECK(reader.good() == (bitsRead <= totalBits)); - BOOST_REQUIRE(reference != expected7bit.end()); - //std::cout << "value " << (int)value << " expected " << (int)*reference << std::endl; - BOOST_CHECK(value == *reference); - ++reference; - } -} - -BOOST_AUTO_TEST_CASE(test_BitstreamReader_operator) -{ - std::array data = {'d', 'e', 'a', 'd', 'b', 'e', 'e', 'f'}; - std::array expected7bit = {0x32, 0x19, 0x2c, 0x16, 0x23, 0x09, 0x4a, 0x65, 0x33, 0x0}; - auto reference = expected7bit.begin(); - constexpr size_t totalBits = data.size() * sizeof(decltype(data)::value_type) * 8; - size_t bitsRead = 0; - - BitstreamReader reader(data.data(), data.data() + data.size()); - while (bitsRead < totalBits) { - BOOST_REQUIRE(reference != expected7bit.end()); - BOOST_CHECK(reader.eof() == false); - { - decltype(reader)::Bits value; - reader >> value; - // we use 7 bits of the data - *value >>= 1; - value.markUsed(7); - //std::cout << "value " << (int)*value << " expected " << (int)*reference << std::endl; - BOOST_CHECK(*value == *reference); - } - bitsRead += 7; - // in the last call should there is not enough data - BOOST_CHECK(reader.good() == (bitsRead <= totalBits)); - BOOST_REQUIRE(reference != expected7bit.end()); - ++reference; - } -} - -BOOST_AUTO_TEST_CASE(test_BitstreamReader_bitset) -{ - std::array data = {'d', 'e', 'a', 'd', 'b', 'e', 'e', 'f'}; - std::array expected7bit = {0x32, 0x19, 0x2c, 0x16, 0x23, 0x09, 0x4a, 0x65, 0x33, 0x0}; - auto reference = expected7bit.begin(); - constexpr size_t totalBits = data.size() * sizeof(decltype(data)::value_type) * 8; - size_t bitsRead = 0; - - BitstreamReader reader(data.data(), data.data() + data.size()); - while (bitsRead < totalBits) { - BOOST_REQUIRE(reference != expected7bit.end()); - BOOST_CHECK(reader.eof() == false); - std::bitset<13> value; - reader.peek(value, value.size()); - // we use 7 bits of the data - value >>= value.size() - 7; - reader.seek(7); - bitsRead += 7; - // in the last call should there is not enough data - BOOST_CHECK(reader.good() == (bitsRead <= totalBits)); - BOOST_REQUIRE(reference != expected7bit.end()); - BOOST_CHECK_MESSAGE(value.to_ulong() == *reference, std::string("mismatch: value ") << value.to_ulong() << ", expected " << (int)*reference); - ++reference; - } - - reader.reset(); - std::bitset<16> aBitset; - reader >> aBitset; - BOOST_CHECK_MESSAGE(aBitset.to_ulong() == 0x6465, std::string("mismatch: value 0x") << std::hex << aBitset.to_ulong() << ", expected 0x6465"); -} - -} // namespace algorithm -} // namespace o2 From 7830e9c54db7ef479c0c8710c4b934c27312e2d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Mon, 9 Feb 2026 10:10:31 +0100 Subject: [PATCH 070/133] DataFormats: Delete unused files (#15029) --- .../include/Headers/SubframeMetadata.h | 68 ----- .../HLT/include/AliceHLT/TPCRawCluster.h | 232 ------------------ 2 files changed, 300 deletions(-) delete mode 100644 DataFormats/Headers/include/Headers/SubframeMetadata.h delete mode 100644 DataFormats/Legacy/HLT/include/AliceHLT/TPCRawCluster.h diff --git a/DataFormats/Headers/include/Headers/SubframeMetadata.h b/DataFormats/Headers/include/Headers/SubframeMetadata.h deleted file mode 100644 index 255fa0ceb8db6..0000000000000 --- a/DataFormats/Headers/include/Headers/SubframeMetadata.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef SUBFRAMEMETADATA_H -#define SUBFRAMEMETADATA_H - -#include - -namespace o2 -{ -namespace data_flow -{ - -struct SubframeMetadata { - // TODO: replace with timestamp struct - // IDEA: not timeframeID because can be calculcated with helper function - // QUESTION: isn't the duration set to ~22ms? - uint64_t startTime = ~(uint64_t)0; - uint64_t duration = ~(uint64_t)0; - - //further meta data to be added - - // putting data specific to FLP origin - int flpIndex; -}; - -// Helper function to derive the timeframe id from the actual timestamp. -// Timestamp is in nanoseconds. Each Timeframe is ~22ms i.e. 2^17 nanoseconds, -// so we can get a unique id by dividing by the timeframe period and masking -// the lower 16 bits. Overlaps will only happen every ~ 22 minutes. -constexpr uint16_t - timeframeIdFromTimestamp(uint64_t timestamp, uint64_t timeFrameDuration) -{ - return (timestamp / timeFrameDuration) & 0xffff; -} - -// A Mockup class to describe some TPC-like payload -struct TPCTestCluster { - float x = 0.f; - float y = 0.f; - float z = 1.5f; - float q = 0.; - uint64_t timeStamp; // the time this thing was digitized/recorded -}; - -struct TPCTestPayload { - std::vector clusters; -}; - -// a mockup class to describe some "ITS" payload -struct ITSRawData { - float x = -1.; - float y = 1.; - uint64_t timeStamp; -}; - -} // namespace data_flow -} // namespace o2 - -#endif diff --git a/DataFormats/Legacy/HLT/include/AliceHLT/TPCRawCluster.h b/DataFormats/Legacy/HLT/include/AliceHLT/TPCRawCluster.h deleted file mode 100644 index 59be2a86c3c2b..0000000000000 --- a/DataFormats/Legacy/HLT/include/AliceHLT/TPCRawCluster.h +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -//-*- Mode: C++ -*- - -#ifndef TPCRAWCLUSTER_H -#define TPCRAWCLUSTER_H -//**************************************************************************** -//* This file is free software: you can redistribute it and/or modify * -//* it under the terms of the GNU General Public License as published by * -//* the Free Software Foundation, either version 3 of the License, or * -//* (at your option) any later version. * -//* * -//* Primary Authors: Matthias Richter * -//* * -//* The authors make no claims about the suitability of this software for * -//* any purpose. It is provided "as is" without express or implied warranty. * -//**************************************************************************** - -// @file TPCRawCluster.h -// @author Matthias Richter -// @since 2015-09-27 -// @brief ALICE HLT TPC raw cluster structure and tools - -#include -#include // ifstream -#include // memcpy - -namespace o2 -{ -namespace AliceHLT -{ - -/** - * @struct RawCluster - * This is a redefinition from AliRoot/HLT/TPCLib/AliHLTTPCRawCluster.h for the - * sake of reading HLT TPC raw cluster files into O2. - * - * TODO: there is no dependence on AliRoot, however, a test needs to be added - * to check consistency if AliRoot is available in the build. - */ -struct RawCluster { - - int16_t GetPadRow() const { return fPadRow; } - float GetPad() const { return fPad; } - float GetTime() const { return fTime; } - float GetSigmaPad2() const { return fSigmaPad2; } - float GetSigmaTime2() const { return fSigmaTime2; } - int32_t GetCharge() const { return fCharge; } - int32_t GetQMax() const { return fQMax; } - bool GetFlagSplitPad() const { return (fFlags & (1 << 0)); } - bool GetFlagSplitTime() const { return (fFlags & (1 << 1)); } - bool GetFlagSplitAny() const { return (fFlags & 3); } - uint16_t GetFlags() const { return (fFlags); } - - int16_t fPadRow; - uint16_t fFlags; //Flags: (1 << 0): Split in pad direction - // (1 << 1): Split in time direction - //During cluster merging, flags are or'd - float fPad; - float fTime; - float fSigmaPad2; - float fSigmaTime2; - uint16_t fCharge; - uint16_t fQMax; -}; - -/** - * @struct RawClusterData - * Header data struct for a raw cluster block - */ -struct RawClusterData { - uint32_t fVersion; // version number - uint32_t fCount; // number of clusters - RawCluster fClusters[0]; // array of clusters -}; - -std::ostream& operator<<(std::ostream& stream, const RawCluster& cluster) -{ - stream << "TPCRawCluster:" - << " " << cluster.GetPadRow() - << " " << cluster.GetPad() - << " " << cluster.GetTime() - << " " << cluster.GetSigmaPad2() - << " " << cluster.GetSigmaTime2() - << " " << cluster.GetCharge() - << " " << cluster.GetQMax(); - return stream; -} - -/** - * @class RawClusterArray Wrapper to binary data block of HLT TPC raw clusters - * Container class which provides access to the content of a binary block of - * HLT TPC raw clusters. - */ -class RawClusterArray -{ - public: - RawClusterArray() : mBuffer(nullptr), mBufferSize(0), mNClusters(0), mClusters(NULL), mClustersEnd(NULL) {} - RawClusterArray(const char* filename) : mBuffer(nullptr), mBufferSize(0), mNClusters(0), mClusters(NULL), mClustersEnd(NULL) - { - init(filename); - } - RawClusterArray(unsigned char* buffer, int size) : mBuffer(nullptr), mBufferSize(0), mNClusters(0), mClusters(NULL), mClustersEnd(NULL) - { - init(buffer, size); - } - ~RawClusterArray() {} - - typedef uint8_t Buffer_t; - - int init(const char* filename) - { - std::ifstream input(filename, std::ifstream::binary); - clear(0); - if (input) { - // get length of file: - input.seekg(0, input.end); - int length = input.tellg(); - input.seekg(0, input.beg); - - // allocate memory: - mBuffer = new Buffer_t[length]; - mBufferSize = length; - - // read data as a block: - input.read(reinterpret_cast(mBuffer), length); - if (!input.good()) { - clear(-1); - std::cerr << "failed to read " << length << " byte(s) from file " << filename << std::endl; - } - - input.close(); - return init(); - } - std::cerr << "failed to open file " << filename << std::endl; - return -1; - } - - int init(unsigned char* buffer, int size) - { - if (!buffer || size <= 0) - return -1; - clear(0); - mBuffer = new Buffer_t[size]; - mBufferSize = size; - memcpy(mBuffer, buffer, size); - return init(); - } - - int GetNClusters() const { return mNClusters; } - - RawCluster* begin() { return mClusters; } - - RawCluster* end() { return mClustersEnd; } - - RawCluster& operator[](int i) - { - if (i + 1 > mNClusters) { - // runtime exeption? - static RawCluster dummy; - return dummy; - } - return *(mClusters + i); - } - - void print() { print(std::cout); } - - template - StreamT& print(StreamT& stream) - { - std::cout << "RawClusterArray: " << mNClusters << " cluster(s)" << std::endl; - for (RawCluster* cluster = mClusters; cluster != mClustersEnd; cluster++) { - std::cout << " " << *cluster << std::endl; - } - return stream; - } - - private: - int init() - { - if (mBuffer == nullptr || mBufferSize == 0) - return 0; - if (mBufferSize < sizeof(RawClusterData)) - return -1; - RawClusterData& clusterData = *reinterpret_cast(mBuffer); - - if (clusterData.fCount * sizeof(RawCluster) + sizeof(RawClusterData) > mBufferSize) { - std::cerr << "Format error, " << clusterData.fCount << " cluster(s) " - << "would require " - << (clusterData.fCount * sizeof(RawCluster) + sizeof(RawClusterData)) - << " byte(s), but only " << mBufferSize << " available" << std::endl; - return clear(-1); - } - - mNClusters = clusterData.fCount; - mClusters = clusterData.fClusters; - mClustersEnd = mClusters + mNClusters; - - return mNClusters; - } - - int clear(int returnValue) - { - mNClusters = 0; - mClusters = NULL; - mClustersEnd = NULL; - delete[] mBuffer; - mBuffer = nullptr; - mBufferSize = 0; - - return returnValue; - } - - Buffer_t* mBuffer; - int mBufferSize; - int mNClusters; - RawCluster* mClusters; - RawCluster* mClustersEnd; -}; - -}; // namespace AliceHLT -}; // namespace o2 -#endif From 99d03b950769c309102f0a7d2cb7805e329f15ee Mon Sep 17 00:00:00 2001 From: Ernst Hellbar Date: Fri, 9 Jan 2026 15:15:52 +0000 Subject: [PATCH 071/133] Event Display: remove return statements from handled filesystem exceptions --- EventVisualisation/Base/src/DirectoryLoader.cxx | 3 --- 1 file changed, 3 deletions(-) diff --git a/EventVisualisation/Base/src/DirectoryLoader.cxx b/EventVisualisation/Base/src/DirectoryLoader.cxx index f2f5a421c0ef9..50b3de61295a3 100644 --- a/EventVisualisation/Base/src/DirectoryLoader.cxx +++ b/EventVisualisation/Base/src/DirectoryLoader.cxx @@ -37,7 +37,6 @@ deque DirectoryLoader::load(const std::string& path, const std::string& } } catch (std::filesystem::filesystem_error const& ex) { LOGF(error, "filesystem problem during DirectoryLoader::load: %s", ex.what()); - return result; } // comparison with safety if marker not in the filename (-1+1 gives 0) std::sort(result.begin(), result.end(), @@ -62,7 +61,6 @@ bool DirectoryLoader::canCreateNextFile(const std::vector& paths, c } } catch (std::filesystem::filesystem_error const& ex) { LOGF(error, "filesystem problem during DirectoryLoader::canCreateNextFile: %s", ex.what()); - return false; } } @@ -103,7 +101,6 @@ deque DirectoryLoader::load(const std::vector& paths, const } } catch (std::filesystem::filesystem_error const& ex) { LOGF(error, "filesystem problem during DirectoryLoader::load: %s", ex.what()); - return result; } // comparison with safety if marker not in the filename (-1+1 gives 0) std::sort(result.begin(), result.end(), From 28dcfc4269c76c97a71081f8f4835d0e9ce7e196 Mon Sep 17 00:00:00 2001 From: Ernst Hellbar Date: Thu, 15 Jan 2026 15:17:28 +0100 Subject: [PATCH 072/133] Event Display: add OnlineMode and safety checks --- .../Base/src/DirectoryLoader.cxx | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/EventVisualisation/Base/src/DirectoryLoader.cxx b/EventVisualisation/Base/src/DirectoryLoader.cxx index 50b3de61295a3..e106eaf7ebb47 100644 --- a/EventVisualisation/Base/src/DirectoryLoader.cxx +++ b/EventVisualisation/Base/src/DirectoryLoader.cxx @@ -14,6 +14,8 @@ /// \author julian.myrcha@cern.ch #include "EventVisualisationBase/DirectoryLoader.h" +#include "Framework/DefaultsHelpers.h" +#include "Framework/DataTakingContext.h" #include #include #include @@ -65,10 +67,13 @@ bool DirectoryLoader::canCreateNextFile(const std::vector& paths, c } // comparison with safety if marker not in the filename (-1+1 gives 0) - std::ranges::sort(result.begin(), result.end(), - [marker](const std::string& a, const std::string& b) { - return a.substr(a.find_first_of(marker) + 1) > b.substr(b.find_first_of(marker) + 1); - }); + if (result.size() > 1) { + std::ranges::sort(result.begin(), result.end(), + [marker](const std::string& a, const std::string& b) { + return a.substr(a.find_first_of(marker) + 1) > b.substr(b.find_first_of(marker) + 1); + }); + } + unsigned long accumulatedSize = 0L; const std::regex delimiter{"_"}; for (auto const& file : result) { @@ -113,11 +118,15 @@ deque DirectoryLoader::load(const std::vector& paths, const std::vector DirectoryLoader::allFolders(const std::string& location) { - auto const pos = location.find_last_of('_'); std::vector folders; - folders.push_back(location.substr(0, pos) + "_PHYSICS"); - folders.push_back(location.substr(0, pos) + "_COSMICS"); - folders.push_back(location.substr(0, pos) + "_SYNTHETIC"); + if (o2::framework::DefaultsHelpers::deploymentMode() == o2::framework::DeploymentMode::OnlineDDS) { + auto const pos = location.find_last_of('_'); + folders.push_back(location.substr(0, pos) + "_PHYSICS"); + folders.push_back(location.substr(0, pos) + "_COSMICS"); + folders.push_back(location.substr(0, pos) + "_SYNTHETIC"); + } else { + folders.push_back(location); + } return folders; } From 1452f31b25e3ec1633e02e1a8f6151210b6bb70c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Mon, 9 Feb 2026 23:46:22 +0100 Subject: [PATCH 073/133] Framework: Delete unused files (#15038) --- .../Framework/DataProcessingStateManager.h | 35 ---- Framework/Foundation/src/Traits.cxx | 10 -- Framework/TestWorkflows/src/dummy.cxx | 10 -- Framework/TestWorkflows/src/o2_sim_its_ALP3.h | 25 --- Framework/TestWorkflows/src/o2_sim_tpc.cxx | 157 ------------------ Framework/TestWorkflows/src/o2_sim_tpc.h | 25 --- .../src/test_o2ITSCluserizer.cxx | 32 ---- .../src/test_o2TPCSimulation.cxx | 31 ---- Framework/Utils/test/DPLBroadcasterMerger.cxx | 147 ---------------- Framework/Utils/test/DPLBroadcasterMerger.h | 28 ---- Framework/Utils/test/DPLOutputTest.h | 28 ---- .../Utils/test/test_DPLBroadcasterMerger.cxx | 31 ---- Framework/Utils/test/test_DPLOutputTest.cxx | 31 ---- 13 files changed, 590 deletions(-) delete mode 100644 Framework/Core/include/Framework/DataProcessingStateManager.h delete mode 100644 Framework/Foundation/src/Traits.cxx delete mode 100644 Framework/TestWorkflows/src/dummy.cxx delete mode 100644 Framework/TestWorkflows/src/o2_sim_its_ALP3.h delete mode 100644 Framework/TestWorkflows/src/o2_sim_tpc.cxx delete mode 100644 Framework/TestWorkflows/src/o2_sim_tpc.h delete mode 100644 Framework/TestWorkflows/src/test_o2ITSCluserizer.cxx delete mode 100644 Framework/TestWorkflows/src/test_o2TPCSimulation.cxx delete mode 100644 Framework/Utils/test/DPLBroadcasterMerger.cxx delete mode 100644 Framework/Utils/test/DPLBroadcasterMerger.h delete mode 100644 Framework/Utils/test/DPLOutputTest.h delete mode 100644 Framework/Utils/test/test_DPLBroadcasterMerger.cxx delete mode 100644 Framework/Utils/test/test_DPLOutputTest.cxx diff --git a/Framework/Core/include/Framework/DataProcessingStateManager.h b/Framework/Core/include/Framework/DataProcessingStateManager.h deleted file mode 100644 index eaa1c8e4e5501..0000000000000 --- a/Framework/Core/include/Framework/DataProcessingStateManager.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef O2_DATAPROCESSINGSTATEMANAGER_H_ -#define O2_DATAPROCESSINGSTATEMANAGER_H_ - -#include -#include -#include - -struct DataProcessingStateManager { - struct StateIndex { - short id = -1; - short index = -1; - }; - struct StateInfo { - std::string name; - int64_t lastUpdate = 0; - int index = -1; - }; - - static constexpr int MAX_STATES = 1024; - std::vector> states = {}; - std::vector infos = {}; -}; - -#endif diff --git a/Framework/Foundation/src/Traits.cxx b/Framework/Foundation/src/Traits.cxx deleted file mode 100644 index faff430964e73..0000000000000 --- a/Framework/Foundation/src/Traits.cxx +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. diff --git a/Framework/TestWorkflows/src/dummy.cxx b/Framework/TestWorkflows/src/dummy.cxx deleted file mode 100644 index faff430964e73..0000000000000 --- a/Framework/TestWorkflows/src/dummy.cxx +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. diff --git a/Framework/TestWorkflows/src/o2_sim_its_ALP3.h b/Framework/TestWorkflows/src/o2_sim_its_ALP3.h deleted file mode 100644 index f9c465fcf5717..0000000000000 --- a/Framework/TestWorkflows/src/o2_sim_its_ALP3.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef WORKFLOWS_O2_SIM_ITS_ALP3 -#define WORKFLOWS_O2_SIM_ITS_ALP3 - -#include "Framework/DataProcessorSpec.h" - -namespace o2 -{ -namespace workflows -{ -o2::framework::DataProcessorSpec sim_its_ALP3(); -} -} // namespace o2 - -#endif // WORKFLOWS_O2_SIM_ITS_ALP3 diff --git a/Framework/TestWorkflows/src/o2_sim_tpc.cxx b/Framework/TestWorkflows/src/o2_sim_tpc.cxx deleted file mode 100644 index 4587c0fcb831f..0000000000000 --- a/Framework/TestWorkflows/src/o2_sim_tpc.cxx +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -#include "Framework/DataRefUtils.h" -#include "Framework/WorkflowSpec.h" -#include -#include "Framework/RootFileService.h" -#include "Framework/AlgorithmSpec.h" -#include "Framework/ConfigParamRegistry.h" - -#include "Framework/Logger.h" - -#include "FairRunSim.h" -#include -#include "FairRuntimeDb.h" -#include "FairPrimaryGenerator.h" -#include "FairBoxGenerator.h" -#include "FairParRootFileIo.h" - -#include "DetectorsPassive/Cave.h" -#include "Field/MagneticField.h" - -#include "DetectorsPassive/Cave.h" -#include "Generators/GeneratorFromFile.h" -#include "TPCSimulation/Detector.h" -#include "Framework/OutputSpec.h" -#include - -using namespace o2::framework; - -#define BOX_GENERATOR 1 - -namespace o2 -{ -namespace workflows -{ - -DataProcessorSpec sim_tpc() -{ - return { - "sim_tpc", - Inputs{}, - Outputs{OutputSpec{"TPC", "GEN"}}, - AlgorithmSpec{ - [](InitContext& setup) { - int nEvents = setup.options().get("nEvents"); - auto mcEngine = setup.options().get("mcEngine"); - - // FIXME: this should probably be part of some generic - // FairRunInitSpec - TString dir = getenv("VMCWORKDIR"); - TString geom_dir = dir + "/Detectors/Geometry/"; - gSystem->Setenv("GEOMPATH", geom_dir.Data()); - - TString tut_configdir = dir + "/Detectors/gconfig"; - gSystem->Setenv("CONFIG_DIR", tut_configdir.Data()); - - // Requiring a file is something which requires IO, and it's therefore - // delegated to the framework - auto& rfm = setup.services().get(); - // FIXME: We should propably have a service for FairRunSim, rather than - // for the root files themselves... - // Output file name - auto outFile = rfm.format("AliceO2_%s.tpc.mc_%i_event.root", mcEngine.c_str(), nEvents); - - // Parameter file name - auto parFile = rfm.format("AliceO2_%s.tpc.mc_%i_event.root", mcEngine.c_str(), nEvents); - - // Create simulation run - FairRunSim* run = new FairRunSim(); - - run->SetName(mcEngine.c_str()); - run->SetSink(new FairRootFileSink(outFile.c_str())); // Output file - FairRuntimeDb* rtdb = run->GetRuntimeDb(); - - // Create media - run->SetMaterials("media.geo"); // Materials - - // Create geometry - o2::passive::Cave* cave = new o2::passive::Cave("CAVE"); - cave->SetGeometryFileName("cave.geo"); - run->AddModule(cave); - - o2::field::MagneticField* magField = new o2::field::MagneticField("Maps", "Maps", -1., -1., o2::field::MagFieldParam::k5kG); - run->SetField(magField); - - // ===| Add TPC |============================================================ - o2::tpc::Detector* tpc = new o2::tpc::Detector(kTRUE); - tpc->SetGeoFileName("TPCGeometry.root"); - run->AddModule(tpc); - - // Create PrimaryGenerator - FairPrimaryGenerator* primGen = new FairPrimaryGenerator(); -#ifdef BOX_GENERATOR - FairBoxGenerator* boxGen = new FairBoxGenerator(211, 10); /*protons*/ - - //boxGen->SetThetaRange(0.0, 90.0); - boxGen->SetEtaRange(-0.9, 0.9); - boxGen->SetPRange(0.1, 5); - boxGen->SetPhiRange(0., 360.); - boxGen->SetDebug(kTRUE); - - primGen->AddGenerator(boxGen); -#else - // reading the events from a kinematics file (produced by AliRoot) - auto extGen = new o2::eventgen::GeneratorFromFile(params.get("extKinFile")); - extGen->SetStartEvent(params.get("startEvent")); - primGen->AddGenerator(extGen); -#endif - - run->SetGenerator(primGen); - - // store track trajectories - // run->SetStoreTraj(kTRUE); - - // Initialize simulation run - run->Init(); - - // Runtime database - Bool_t kParameterMerged = kTRUE; - FairParRootFileIo* parOut = new FairParRootFileIo(kParameterMerged); - parOut->open(parFile.c_str()); - rtdb->setOutput(parOut); - rtdb->saveOutput(); - rtdb->print(); - run->Run(nEvents); - - static bool once = true; - - // This is the actual inner loop for the device - return [run, nEvents](ProcessingContext& ctx) { - if (!once) { - run->Run(nEvents); - once = true; - } else { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - // FIXME: After we run we should readback events - // and push them as messages, for the next stage of - // processing. - }; - }}, - Options{ - {"mcEngine", VariantType::String, "TGeant3", {"Engine to use"}}, - {"nEvents", VariantType::Int, 10, {"Events to process"}}, - {"extKinFile", VariantType::String, "Kinematics.root", {"name of kinematics file for event generator from file (when applicable)"}}, - {"startEvent", VariantType::Int, 2, {"Events to skip"}}}}; -}; -} // namespace workflows -} // namespace o2 diff --git a/Framework/TestWorkflows/src/o2_sim_tpc.h b/Framework/TestWorkflows/src/o2_sim_tpc.h deleted file mode 100644 index e567fe89e0b38..0000000000000 --- a/Framework/TestWorkflows/src/o2_sim_tpc.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef WORKFLOWS_O2_SIM_TPC -#define WORKFLOWS_O2_SIM_TPC - -#include "Framework/DataProcessorSpec.h" - -namespace o2 -{ -namespace workflows -{ -o2::framework::DataProcessorSpec sim_tpc(); -} -} // namespace o2 - -#endif // WORKFLOWS_O2_SIM_TPC diff --git a/Framework/TestWorkflows/src/test_o2ITSCluserizer.cxx b/Framework/TestWorkflows/src/test_o2ITSCluserizer.cxx deleted file mode 100644 index d6d3cb1242f7c..0000000000000 --- a/Framework/TestWorkflows/src/test_o2ITSCluserizer.cxx +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -#include "Framework/DataRefUtils.h" -#include "Framework/ServiceRegistry.h" -#include "Framework/runDataProcessing.h" -#include -// FIXME: this should not be needed as the framework should be able to -// decode TClonesArray by itself. -#include "Framework/TMessageSerializer.h" -#include "o2_sim_its_ALP3.h" -#include "Framework/Logger.h" -#include -#include - -using namespace o2::framework; -using namespace o2::workflows; - -// This is how you can define your processing in a declarative way -WorkflowSpec defineDataProcessing(ConfigContext const&) -{ - return WorkflowSpec{ - sim_its_ALP3(), - }; -} diff --git a/Framework/TestWorkflows/src/test_o2TPCSimulation.cxx b/Framework/TestWorkflows/src/test_o2TPCSimulation.cxx deleted file mode 100644 index 403ad8bc7127b..0000000000000 --- a/Framework/TestWorkflows/src/test_o2TPCSimulation.cxx +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -#include "Framework/DataRefUtils.h" -#include "Framework/ServiceRegistry.h" -#include "Framework/runDataProcessing.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/DataRef.h" -// FIXME: this should not be needed as the framework should be able to -// decode TClonesArray by itself. -#include "Framework/TMessageSerializer.h" -#include "o2_sim_tpc.h" -#include "Framework/Logger.h" - -using namespace o2::framework; -using namespace o2::workflows; - -// This is how you can define your processing in a declarative way -WorkflowSpec defineDataProcessing(ConfigContext const& specs) -{ - return WorkflowSpec{ - sim_tpc(), - }; -} diff --git a/Framework/Utils/test/DPLBroadcasterMerger.cxx b/Framework/Utils/test/DPLBroadcasterMerger.cxx deleted file mode 100644 index bf793275d2f3f..0000000000000 --- a/Framework/Utils/test/DPLBroadcasterMerger.cxx +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \author Gabriele Gaetano Fronzé, gfronze@cern.ch - -#include -#include "DPLBroadcasterMerger.h" -#include "DPLUtils/Utils.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/ControlService.h" -#include "Framework/DataRefUtils.h" -#include "random" -#include "Framework/Logger.h" -#include - -namespace o2f = o2::framework; - -namespace o2::workflows -{ - -o2f::Inputs noInputs{}; -o2f::Outputs noOutputs{}; - -o2f::DataProcessorSpec defineGenerator(o2f::OutputSpec usrOutput) -{ - return {"Generator", // Device name - noInputs, // No inputs for a generator - o2f::Outputs{usrOutput}, // One simple output - - o2f::AlgorithmSpec{[usrOutput](o2f::InitContext&) { - int msgCounter = 0; - auto msgCounter_shptr = std::make_shared(msgCounter); - auto usrOutput_shptr = std::make_shared(getOutput(usrOutput)); - - LOG(info) << ">>>>>>>>>>>>>> Generator initialised"; - - // Processing context in captured from return on InitCallback - return [usrOutput_shptr, msgCounter_shptr](o2f::ProcessingContext& ctx) { - int msgIndex = (*msgCounter_shptr)++; - if (msgIndex > 10) { - ctx.services().get().endOfStream(); - } - LOG(info) << ">>> MSG:" << msgIndex; - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - - LOG(info) << ">>> Preparing MSG:" << msgIndex; - - auto& outputMsg = - ctx.outputs().newChunk(*usrOutput_shptr, (msgIndex + 1) * sizeof(uint32_t) / sizeof(char)); - - LOG(info) << ">>> Preparing1 MSG:" << msgIndex; - - auto payload = reinterpret_cast(outputMsg.data()); - - payload[0] = msgIndex; - - LOG(info) << ">>> Preparing2 MSG:" << msgIndex; - - for (int k = 0; k < msgIndex; ++k) { - payload[k + 1] = (uint32_t)32; - LOG(info) << ">>>>\t" << payload[k + 1]; - } - - return; - }; - }}}; -} - -o2f::DataProcessorSpec definePipeline(std::string devName, o2f::InputSpec usrInput, o2f::OutputSpec usrOutput) -{ - return {devName, // Device name - o2f::Inputs{usrInput}, // No inputs, for the moment - o2f::Outputs{usrOutput}, o2f::AlgorithmSpec{[usrOutput](o2f::InitContext&) { - auto output_sharedptr = std::make_shared(getOutput(usrOutput)); - - // Processing context in captured from return on InitCallback - return [output_sharedptr](o2f::ProcessingContext& ctx) { - auto inputMsg = ctx.inputs().getByPos(0); - auto msgSize = o2::framework::DataRefUtils::getPayloadSize(inputMsg); - - auto& fwdMsg = ctx.outputs().newChunk((*output_sharedptr), msgSize); - std::memcpy(fwdMsg.data(), inputMsg.payload, msgSize); - }; - }}}; -} - -o2f::DataProcessorSpec defineSink(o2f::InputSpec usrInput) -{ - return {"Sink", // Device name - o2f::Inputs{usrInput}, // No inputs, for the moment - noOutputs, - - o2f::AlgorithmSpec{[](o2f::InitContext&) { - // Processing context in captured from return on InitCallback - return [](o2f::ProcessingContext& ctx) { - LOG(info) << "Received message "; - - auto inputMsg = ctx.inputs().getByPos(0); - auto payload = reinterpret_cast(inputMsg.payload); - - LOG(info) << "Received message containing" << payload[0] << "elements"; - - for (int j = 0; j < payload[0]; ++j) { - LOG(info) << payload[j + 1] << "\t"; - } - LOG(info); - }; - }}}; -} - -o2::framework::WorkflowSpec DPLBroadcasterMergerWorkflow() -{ - auto lspec = o2f::WorkflowSpec(); - - // A generator of data - lspec.emplace_back(defineGenerator(o2f::OutputSpec{"TST", "ToBC", 0, o2f::Lifetime::Timeframe})); - - // A two-way broadcaster - lspec.emplace_back(defineBroadcaster("Broadcaster", - o2f::InputSpec{"input", "TST", "ToBC", 0, o2f::Lifetime::Timeframe}, - o2f::Outputs{{"TST", "BCAST0", 0, o2f::Lifetime::Timeframe}, - {"TST", "BCAST1", 0, o2f::Lifetime::Timeframe}})); - - // Two pipeline devices - lspec.emplace_back(definePipeline("pip0", o2f::InputSpec{"bc", "TST", "BCAST0", 0, o2f::Lifetime::Timeframe}, - o2f::OutputSpec{"TST", "PIP0", 0, o2f::Lifetime::Timeframe})); - lspec.emplace_back(definePipeline("pip1", o2f::InputSpec{"bc", "TST", "BCAST1", 0, o2f::Lifetime::Timeframe}, - o2f::OutputSpec{"TST", "PIP1", 0, o2f::Lifetime::Timeframe})); - - // A gatherer - lspec.emplace_back(defineMerger("Merger", o2f::Inputs{{"input1", "TST", "PIP0", 0, o2f::Lifetime::Timeframe}, {"input2", "TST", "PIP1", 0, o2f::Lifetime::Timeframe}}, - o2f::OutputSpec{"TST", "ToSink", 0, o2f::Lifetime::Timeframe})); - - // A sink which dumps messages - lspec.emplace_back(defineSink(o2f::InputSpec{"input", "TST", "ToSink", 0, o2f::Lifetime::Timeframe})); - return std::move(lspec); -} - -} // namespace o2::workflows diff --git a/Framework/Utils/test/DPLBroadcasterMerger.h b/Framework/Utils/test/DPLBroadcasterMerger.h deleted file mode 100644 index 4607d72a702b7..0000000000000 --- a/Framework/Utils/test/DPLBroadcasterMerger.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \author Gabriele Gaetano Fronzé, gfronze@cern.ch - -#ifndef DPLBROADCASTERMERGER_H -#define DPLBROADCASTERMERGER_H - -#include "Framework/WorkflowSpec.h" -#include "Framework/DataProcessorSpec.h" - -namespace o2 -{ -namespace workflows -{ -o2::framework::WorkflowSpec DPLBroadcasterMergerWorkflow(); -} -} // namespace o2 - -#endif // DPLBROADCASTERMERGER_H diff --git a/Framework/Utils/test/DPLOutputTest.h b/Framework/Utils/test/DPLOutputTest.h deleted file mode 100644 index ce776ffff1113..0000000000000 --- a/Framework/Utils/test/DPLOutputTest.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \author Gabriele Gaetano Fronzé, gfronze@cern.ch - -#ifndef DPLOUTPUTTEST_H -#define DPLOUTPUTTEST_H - -#include "Framework/WorkflowSpec.h" -#include "Framework/DataProcessorSpec.h" - -namespace o2 -{ -namespace workflows -{ -o2::framework::WorkflowSpec DPLOutputTest(); -} -} // namespace o2 - -#endif // DPLOUTPUTTEST_H diff --git a/Framework/Utils/test/test_DPLBroadcasterMerger.cxx b/Framework/Utils/test/test_DPLBroadcasterMerger.cxx deleted file mode 100644 index 6ff554e75f462..0000000000000 --- a/Framework/Utils/test/test_DPLBroadcasterMerger.cxx +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \author Gabriele Gaetano Fronzé, gfronze@cern.ch - -#include "Framework/DataRefUtils.h" -#include "Framework/ServiceRegistry.h" -#include "Framework/runDataProcessing.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/DataRef.h" -// FIXME: this should not be needed as the framework should be able to -// decode TClonesArray by itself. -#include "Framework/TMessageSerializer.h" -#include "DPLBroadcasterMerger.h" -#include "Framework/Logger.h" - -using namespace o2::framework; - -// This is how you can define your processing in a declarative way -WorkflowSpec defineDataProcessing(ConfigContext const&) -{ - return o2::workflows::DPLBroadcasterMergerWorkflow(); -} diff --git a/Framework/Utils/test/test_DPLOutputTest.cxx b/Framework/Utils/test/test_DPLOutputTest.cxx deleted file mode 100644 index e49bea3074dd1..0000000000000 --- a/Framework/Utils/test/test_DPLOutputTest.cxx +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \author Gabriele Gaetano Fronzé, gfronze@cern.ch - -#include "Framework/DataRefUtils.h" -#include "Framework/ServiceRegistry.h" -#include "Framework/runDataProcessing.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/DataRef.h" -// FIXME: this should not be needed as the framework should be able to -// decode TClonesArray by itself. -#include "Framework/TMessageSerializer.h" -#include "DPLOutputTest.h" -#include "Framework/Logger.h" - -using namespace o2::framework; - -// This is how you can define your processing in a declarative way -WorkflowSpec defineDataProcessing(ConfigContext const&) -{ - return o2::workflows::DPLOutputTest(); -} From 1fd232899d168aab1eaa344aab5562589f7195a2 Mon Sep 17 00:00:00 2001 From: Stefano Cannito <143754257+scannito@users.noreply.github.com> Date: Tue, 10 Feb 2026 01:50:33 +0100 Subject: [PATCH 074/133] ALICE3 Sensor orientation fix + first try to close in-stave gaps (#15043) --- .../ALICE3/TRK/simulation/src/Detector.cxx | 2 +- .../ALICE3/TRK/simulation/src/TRKLayer.cxx | 35 +++++++++++-------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index e0fc6ef1ed35b..06fd2d9670b67 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -128,7 +128,7 @@ void Detector::buildTRKMiddleOuterLayers() LOGP(info, "TRKLayer created. Name: {}", GeometryTGeo::getTRKLayerPattern() + std::to_string(0)); mLayers.emplace_back(1, GeometryTGeo::getTRKLayerPattern() + std::to_string(1), 11.f, 10, 100.e-3); mLayers.emplace_back(2, GeometryTGeo::getTRKLayerPattern() + std::to_string(2), 15.f, 10, 100.e-3); - mLayers.emplace_back(3, GeometryTGeo::getTRKLayerPattern() + std::to_string(3), 19.f, 10, 100.e-3); + mLayers.emplace_back(3, GeometryTGeo::getTRKLayerPattern() + std::to_string(3), 20.f, 10, 100.e-3); mLayers.emplace_back(4, GeometryTGeo::getTRKLayerPattern() + std::to_string(4), 30.f, 10, 100.e-3); mLayers.emplace_back(5, GeometryTGeo::getTRKLayerPattern() + std::to_string(5), 45.f, 20, 100.e-3); mLayers.emplace_back(6, GeometryTGeo::getTRKLayerPattern() + std::to_string(6), 60.f, 20, 100.e-3); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx index c4683f28918d0..8d30cf9759e40 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx @@ -168,7 +168,7 @@ TGeoVolume* TRKLayer::createChip(std::string type) TGeoVolume* TRKLayer::createModule(std::string type) { - TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); + TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); std::string moduleName = GeometryTGeo::getTRKModulePattern() + std::to_string(mLayerNumber); TGeoShape* module; @@ -176,7 +176,7 @@ TGeoVolume* TRKLayer::createModule(std::string type) if (type == "cylinder") { module = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, (constants::moduleMLOT::length * mNumberOfModules) / 2); - moduleVol = new TGeoVolume(moduleName.c_str(), module, medAir); + moduleVol = new TGeoVolume(moduleName.c_str(), module, medSi); TGeoVolume* chipVol = createChip("cylinder"); LOGP(debug, "Inserting {} in {} ", chipVol->GetName(), moduleVol->GetName()); @@ -186,7 +186,7 @@ TGeoVolume* TRKLayer::createModule(std::string type) double moduleLength = constants::moduleMLOT::length; module = new TGeoBBox(moduleWidth / 2, mChipThickness / 2, moduleLength / 2); // TO BE CHECKED !!! - moduleVol = new TGeoVolume(moduleName.c_str(), module, medAir); + moduleVol = new TGeoVolume(moduleName.c_str(), module, medSi); for (int iChip = 0; iChip < mHalfNumberOfChips; iChip++) { TGeoVolume* chipVolLeft = createChip("flat"); @@ -223,7 +223,7 @@ TGeoVolume* TRKLayer::createModule(std::string type) TGeoVolume* TRKLayer::createHalfStave(std::string type) { - TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); + TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); std::string halfStaveName = GeometryTGeo::getTRKHalfStavePattern() + std::to_string(mLayerNumber); TGeoShape* halfStave; @@ -231,7 +231,7 @@ TGeoVolume* TRKLayer::createHalfStave(std::string type) if (type == "cylinder") { halfStave = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, mChipLength / 2); - halfStaveVol = new TGeoVolume(halfStaveName.c_str(), halfStave, medAir); + halfStaveVol = new TGeoVolume(halfStaveName.c_str(), halfStave, medSi); TGeoVolume* moduleVol = createModule("cylinder"); LOGP(debug, "Inserting {} in {} ", moduleVol->GetName(), halfStaveVol->GetName()); @@ -242,7 +242,7 @@ TGeoVolume* TRKLayer::createHalfStave(std::string type) double halfStaveLength = constants::moduleMLOT::length * mNumberOfModules; halfStave = new TGeoBBox(halfStaveWidth / 2, mChipThickness / 2, halfStaveLength / 2); - halfStaveVol = new TGeoVolume(halfStaveName.c_str(), halfStave, medAir); + halfStaveVol = new TGeoVolume(halfStaveName.c_str(), halfStave, medSi); for (int iModule = 0; iModule < mNumberOfModules; iModule++) { TGeoVolume* moduleVol = createModule("flat"); @@ -257,6 +257,9 @@ TGeoVolume* TRKLayer::createHalfStave(std::string type) halfStaveVol->AddNode(moduleVol, iModule, trans); } } + + halfStaveVol->SetLineColor(kYellow); + return halfStaveVol; } @@ -296,11 +299,11 @@ TGeoVolume* TRKLayer::createStave(std::string type) staveVol->AddNode(moduleVol, iModule, trans); } } else if (type == "staggered") { - /*double moduleWidth = constants::moduleMLOT::width; - double moduleLength = constants::moduleMLOT::length;*/ + double overlap = constants::moduleMLOT::gaps::outerEdgeLongSide + constants::moduleMLOT::chip::passiveEdgeReadOut + 0.1; // 1.5mm outer-edge + 1mm deadzone + 1mm (true)overlap + double shift = overlap / 2; - double halfstaveWidth = constants::ML::width; - double staveWidth = constants::OT::width; // Each stave has two modules (based on the LOI design) + double halfstaveWidth = constants::OT::halfstave::width; + double staveWidth = constants::OT::width - overlap; double staveLength = constants::moduleMLOT::length * mNumberOfModules; stave = new TGeoBBox(staveWidth / 2, mLogicalVolumeThickness / 2, staveLength / 2); @@ -311,12 +314,12 @@ TGeoVolume* TRKLayer::createStave(std::string type) TGeoVolume* halfStaveVolRight = createHalfStave("flat"); TGeoCombiTrans* transLeft = new TGeoCombiTrans(); - transLeft->SetTranslation(-halfstaveWidth / 2 + 0.05, 0, 0); // TO BE CHECKED !!! 1mm overlap between the modules + transLeft->SetTranslation(-halfstaveWidth / 2 + shift, 0, 0); // TO BE CHECKED !!! 1mm overlap between the modules LOGP(debug, "Inserting {} in {} ", halfStaveVolLeft->GetName(), staveVol->GetName()); staveVol->AddNode(halfStaveVolLeft, 0, transLeft); TGeoCombiTrans* transRight = new TGeoCombiTrans(); - transRight->SetTranslation(halfstaveWidth / 2 - 0.05, 0.2, 0); // TO BE CHECKED !!! 1mm overlap between the modules + transRight->SetTranslation(halfstaveWidth / 2 - shift, 0.2, 0); // TO BE CHECKED !!! 1mm overlap between the modules LOGP(debug, "Inserting {} in {} ", halfStaveVolRight->GetName(), staveVol->GetName()); staveVol->AddNode(halfStaveVolRight, 1, transRight); } else { @@ -377,7 +380,7 @@ void TRKLayer::createLayer(TGeoVolume* motherVolume) // Put the staves in the correct position and orientation TGeoCombiTrans* trans = new TGeoCombiTrans(); double theta = 360. * iStave / nStaves; - TGeoRotation* rot = new TGeoRotation("rot", theta + 90 + 3, 0, 0); + TGeoRotation* rot = new TGeoRotation("rot", theta - 90 + 3, 0, 0); trans->SetRotation(rot); trans->SetTranslation(mInnerRadius * std::cos(2. * TMath::Pi() * iStave / nStaves), mInnerRadius * std::sin(2 * TMath::Pi() * iStave / nStaves), 0); @@ -385,13 +388,15 @@ void TRKLayer::createLayer(TGeoVolume* motherVolume) layerVol->AddNode(staveVol, iStave, trans); } } else if (mLayout == kStaggered) { + double overlapInStave = constants::moduleMLOT::gaps::outerEdgeLongSide + constants::moduleMLOT::chip::passiveEdgeReadOut + 0.1; // 1.5mm outer-edge + 1mm deadzone + 1mm (true)overlap + double layerLength = constants::moduleMLOT::length * mNumberOfModules; + double staveWidth = constants::OT::width - overlapInStave; layer = new TGeoTube(mInnerRadius - 0.333 * layerThickness, mInnerRadius + 0.667 * layerThickness, layerLength / 2); layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); // Compute the number of staves - double staveWidth = constants::OT::width; // Each stave has two modules (based on the LOI design) int nStaves = (int)std::ceil(mInnerRadius * 2 * TMath::Pi() / staveWidth); nStaves += nStaves % 2; // Require an even number of staves @@ -410,7 +415,7 @@ void TRKLayer::createLayer(TGeoVolume* motherVolume) // Put the staves in the correct position and orientation TGeoCombiTrans* trans = new TGeoCombiTrans(); double theta = 360. * iStave / nStaves; - TGeoRotation* rot = new TGeoRotation("rot", theta + 90, 0, 0); + TGeoRotation* rot = new TGeoRotation("rot", theta - 90 + 3, 0, 0); trans->SetRotation(rot); trans->SetTranslation(mInnerRadius * std::cos(2. * TMath::Pi() * iStave / nStaves), mInnerRadius * std::sin(2 * TMath::Pi() * iStave / nStaves), 0); From d43ba29f8b05b6cb5e0914686830dd0740c87f80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Thu, 5 Feb 2026 22:38:04 +0100 Subject: [PATCH 075/133] SimulationDataFormat: Delete unused files --- .../ProcessingEventInfo.h | 36 ------------------- 1 file changed, 36 deletions(-) delete mode 100644 DataFormats/simulation/include/SimulationDataFormat/ProcessingEventInfo.h diff --git a/DataFormats/simulation/include/SimulationDataFormat/ProcessingEventInfo.h b/DataFormats/simulation/include/SimulationDataFormat/ProcessingEventInfo.h deleted file mode 100644 index 150a8272c7714..0000000000000 --- a/DataFormats/simulation/include/SimulationDataFormat/ProcessingEventInfo.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file ProcessingEventInfo.h -/// \brief Encapsulated meta information about current event being processed by FairRoot (analysis) tasks -/// \author Sandro Wenzel - -#ifndef ALICEO2_DATA_EVENTINFO_H_ -#define ALICEO2_DATA_EVENTINFO_H_ - -namespace o2 -{ - -// A class encapsulating meta information about events being process -// and the data being sent by run classes such as FairRunAna. -// Can be send to processing tasks for usage so that they do no longer -// need to access the FairRootManager directly. -struct ProcessingEventInfo { - double eventTime; //! time of the current event - int eventNumber; //! the current entry - int sourceNumber; //! the current source number - bool numberSources; //! number of sources - // can be extended further -}; - -} // namespace o2 - -#endif From 1f4624047d09e5bcf08c1e67c665593e4f178ef8 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Tue, 10 Feb 2026 10:50:56 +0100 Subject: [PATCH 076/133] Update OpenMP detection for macOS (#15040) Updated OpenMP detection for macOS with hints to brew library paths and set required compile flags. --- dependencies/FindOpenMPMacOS.cmake | 47 +++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/dependencies/FindOpenMPMacOS.cmake b/dependencies/FindOpenMPMacOS.cmake index 264ce5398a331..9bdeb35ecd46d 100644 --- a/dependencies/FindOpenMPMacOS.cmake +++ b/dependencies/FindOpenMPMacOS.cmake @@ -1,28 +1,53 @@ +# Copyright 2019-2026 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + find_library(OpenMP_LIBRARY - NAMES omp + NAMES omp libomp + HINTS + /opt/homebrew/opt/libomp/lib + /usr/local/opt/libomp/lib ) find_path(OpenMP_INCLUDE_DIR - omp.h + NAMES omp.h + HINTS + /opt/homebrew/opt/libomp/include + /usr/local/opt/libomp/include ) mark_as_advanced(OpenMP_LIBRARY OpenMP_INCLUDE_DIR) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(OpenMP DEFAULT_MSG - OpenMP_LIBRARY OpenMP_INCLUDE_DIR) +find_package_handle_standard_args( + OpenMPMacOS + DEFAULT_MSG + OpenMP_LIBRARY OpenMP_INCLUDE_DIR +) -if (OpenMP_FOUND) +if (OpenMPMacOS_FOUND) set(OpenMP_LIBRARIES ${OpenMP_LIBRARY}) set(OpenMP_INCLUDE_DIRS ${OpenMP_INCLUDE_DIR}) - set(OpenMP_COMPILE_OPTIONS -Xpreprocessor -fopenmp) - set(OpenMP_CXX_FOUND True) - set(OpenMPMacOS_FOUND True) - add_library(OpenMP::OpenMP_CXX SHARED IMPORTED) + set(OpenMP_CXX_FOUND TRUE) + set(OpenMP_FOUND TRUE) + + add_library(OpenMP::OpenMP_CXX INTERFACE IMPORTED) set_target_properties(OpenMP::OpenMP_CXX PROPERTIES - IMPORTED_LOCATION ${OpenMP_LIBRARIES} INTERFACE_INCLUDE_DIRECTORIES "${OpenMP_INCLUDE_DIRS}" - INTERFACE_COMPILE_OPTIONS "${OpenMP_COMPILE_OPTIONS}" + INTERFACE_COMPILE_OPTIONS "-Xclang;-fopenmp" + INTERFACE_LINK_LIBRARIES "${OpenMP_LIBRARIES}" + ) + message(STATUS + "Found OpenMP (macOS workaround): " + "library=${OpenMP_LIBRARY}, " + "include=${OpenMP_INCLUDE_DIR}" ) endif() From a63c9c11727258f79489f5dc8801a55a6483d7ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Thu, 5 Feb 2026 22:53:21 +0100 Subject: [PATCH 077/133] TRD: Delete unused files --- .../TRD/base/src/CalSingleChamberStatus.cxx | 154 ------------------ 1 file changed, 154 deletions(-) delete mode 100644 Detectors/TRD/base/src/CalSingleChamberStatus.cxx diff --git a/Detectors/TRD/base/src/CalSingleChamberStatus.cxx b/Detectors/TRD/base/src/CalSingleChamberStatus.cxx deleted file mode 100644 index f054d49766461..0000000000000 --- a/Detectors/TRD/base/src/CalSingleChamberStatus.cxx +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/////////////////////////////////////////////////////////////////////////////// -// // -// Calibration base class for a single ROC // -// Contains one char value per pad // -// // -/////////////////////////////////////////////////////////////////////////////// - -#include "TRDBase/CalSingleChamberStatus.h" - -using namespace o2::trd; - -//_____________________________________________________________________________ -CalSingleChamberStatus::CalSingleChamberStatus() = default; - -//_____________________________________________________________________________ -CalSingleChamberStatus::CalSingleChamberStatus(Int_t p, Int_t c, Int_t cols) - : mPla(p), mCha(c), mNcols(cols) -{ - // - // Constructor that initializes a given pad plane type - // - - // - // The pad plane parameter - // - switch (p) { - case 0: - if (c == 2) { - // L0C0 type - mNrows = 12; - } else { - // L0C1 type - mNrows = 16; - } - break; - case 1: - if (c == 2) { - // L1C0 type - mNrows = 12; - } else { - // L1C1 type - mNrows = 16; - } - break; - case 2: - if (c == 2) { - // L2C0 type - mNrows = 12; - } else { - // L2C1 type - mNrows = 16; - } - break; - case 3: - if (c == 2) { - // L3C0 type - mNrows = 12; - } else { - // L3C1 type - mNrows = 16; - } - break; - case 4: - if (c == 2) { - // L4C0 type - mNrows = 12; - } else { - // L4C1 type - mNrows = 16; - } - break; - case 5: - if (c == 2) { - // L5C0 type - mNrows = 12; - } else { - // L5C1 type - mNrows = 16; - } - break; - }; - - mNchannels = mNrows * mNcols; - if (mNchannels != 0) { - mData.resize(mNchannels); - } - memset(&mData[0], 0, sizeof(mData[0]) * mData.size()); -} - -//_____________________________________________________________________________ -CalSingleChamberStatus::CalSingleChamberStatus(const CalSingleChamberStatus& c) - : mPla(c.mPla), mCha(c.mCha), mNrows(c.mNrows), mNcols(c.mNcols), mNchannels(c.mNchannels) -{ - // - // CalSingleChamberStatus copy constructor - // - - mData = c.mData; -} - -//_____________________________________________________________________________ -CalSingleChamberStatus::~CalSingleChamberStatus() = default; - -//_____________________________________________________________________________ -CalSingleChamberStatus& CalSingleChamberStatus::operator=(const CalSingleChamberStatus& c) -{ - // - // Assignment operator - // - - if (this == &c) { - return *this; - } - - mPla = c.mPla; - mCha = c.mCha; - mNrows = c.mNrows; - mNcols = c.mNcols; - mNchannels = c.mNchannels; - mData = c.mData; - - return *this; -} - -//_____________________________________________________________________________ -void CalSingleChamberStatus::Copy(CalSingleChamberStatus& c) const -{ - // - // Copy function - // - - Int_t iBin = 0; - - c.mPla = mPla; - c.mCha = mCha; - - c.mNrows = mNrows; - c.mNcols = mNcols; - - c.mNchannels = mNchannels; - - c.mData = mData; -} From d9bbbfccdc95fd9779cb9b08b7df78a8271e2565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Thu, 5 Feb 2026 22:42:28 +0100 Subject: [PATCH 078/133] GlobalTrackingWorkflow: Delete unused files --- .../tofworkflow/src/RecoWorkflowSpec.cxx | 189 ------------------ 1 file changed, 189 deletions(-) delete mode 100644 Detectors/GlobalTrackingWorkflow/tofworkflow/src/RecoWorkflowSpec.cxx diff --git a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/RecoWorkflowSpec.cxx b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/RecoWorkflowSpec.cxx deleted file mode 100644 index ab4f90464b31b..0000000000000 --- a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/RecoWorkflowSpec.cxx +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "TOFWorkflow/RecoWorkflowSpec.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/DataRefUtils.h" -#include "Framework/Lifetime.h" -#include "Framework/Task.h" -#include "Framework/SerializationMethods.h" -#include "Headers/DataHeader.h" -#include "DataFormatsTOF/Cluster.h" -#include "GlobalTracking/MatchTOF.h" -#include "ReconstructionDataFormats/TrackTPCITS.h" -#include "DetectorsBase/GeometryManager.h" -#include "DetectorsBase/Propagator.h" -#include "DetectorsBase/GRPGeomHelper.h" -#include "CommonUtils/NameConf.h" -#include -#include "TStopwatch.h" -#include "TPCCalibration/VDriftHelper.h" - -// from FIT -#include "DataFormatsFT0/RecPoints.h" - -#include // for make_shared, make_unique, unique_ptr -#include - -using namespace o2::framework; - -namespace o2 -{ -namespace tof -{ - -// use the tasking system of DPL -// just need to implement 2 special methods init + run (there is no need to inherit from anything) -class TOFDPLRecoWorkflowTask -{ - using evIdx = o2::dataformats::EvIndex; - using MatchOutputType = std::vector; - - bool mUseMC = true; - bool mUseFIT = false; - - public: - explicit TOFDPLRecoWorkflowTask(std::shared_ptr gr, bool useMC, bool useFIT) : mGGCCDBRequest(gr), mUseMC(useMC), mUseFIT(useFIT) {} - - void init(framework::InitContext& ic) - { - o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); - mTimer.Stop(); - mTimer.Reset(); - } - - void run(framework::ProcessingContext& pc) - { - mTimer.Start(false); - updateTimeDependentParams(pc); - //>>>---------- attach input data --------------->>> - const auto clustersRO = pc.inputs().get>("tofcluster"); - const auto tracksRO = pc.inputs().get>("globaltrack"); - - if (mUseFIT) { - // Note: the particular variable will go out of scope, but the span is passed by copy to the - // worker and the underlying memory is valid throughout the whole computation - auto recPoints = std::move(pc.inputs().get>("fitrecpoints")); - mMatcher.setFITRecPoints(recPoints); - LOG(info) << "TOF Reco Workflow pulled " << recPoints.size() << " FIT RecPoints"; - } - - // we do a copy of the input but we are looking for a way to avoid it (current problem in conversion form unique_ptr to *) - - gsl::span itstpclab; - o2::dataformats::MCTruthContainer toflab; - if (mUseMC) { - const auto toflabel = pc.inputs().get*>("tofclusterlabel"); - itstpclab = pc.inputs().get>("itstpclabel"); - toflab = std::move(*toflabel); - } - - mMatcher.run(tracksRO, clustersRO, toflab, itstpclab); - - // in run_match_tof aggiugnere esplicitamente la chiamata a fill del tree (nella classe MatchTOF) e il metodo per leggere i vettori di output - - //... - // LOG(info) << "TOF CLUSTERER : TRANSFORMED " << digits->size() - // << " DIGITS TO " << mClustersArray.size() << " CLUSTERS"; - - // send matching-info - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MTC_ITSTPC", 0}, mMatcher.getMatchedTrackVector()); - if (mUseMC) { - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MCMATCHTOF", 0}, mMatcher.getMatchedTOFLabelsVector()); - } - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "CALIBDATA", 0}, mMatcher.getCalibVector()); - mTimer.Stop(); - } - - void endOfStream(EndOfStreamContext& ec) - { - LOGF(info, "TOF Matching total timing: Cpu: %.3e Real: %.3e s in %d slots", - mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); - } - - void updateTimeDependentParams(ProcessingContext& pc) - { - o2::base::GRPGeomHelper::instance().checkUpdates(pc); - mTPCVDriftHelper.extractCCDBInputs(pc); - static bool initOnceDone = false; - if (!initOnceDone) { // this params need to be queried only once - initOnceDone = true; - // put here init-once stuff - } - // we may have other params which need to be queried regularly - if (mTPCVDriftHelper.isUpdated()) { - LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} from source {}", - mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getSourceName()); - mMatcher.setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); - mTPCVDriftHelper.acknowledgeUpdate(); - } - } - - void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) - { - if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { - return; - } - if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { - return; - } - } - - private: - o2::globaltracking::MatchTOF mMatcher; ///< Cluster finder - std::shared_ptr mGGCCDBRequest; - o2::tpc::VDriftHelper mTPCVDriftHelper{}; - TStopwatch mTimer; -}; - -o2::framework::DataProcessorSpec getTOFRecoWorkflowSpec(bool useMC, bool useFIT) -{ - std::vector inputs; - std::vector outputs; - inputs.emplace_back("tofcluster", o2::header::gDataOriginTOF, "CLUSTERS", 0, Lifetime::Timeframe); - inputs.emplace_back("globaltrack", "GLO", "TPCITS", 0, Lifetime::Timeframe); - if (useMC) { - inputs.emplace_back("tofclusterlabel", o2::header::gDataOriginTOF, "CLUSTERSMCTR", 0, Lifetime::Timeframe); - inputs.emplace_back("itstpclabel", "GLO", "TPCITS_MC", 0, Lifetime::Timeframe); - } - - if (useFIT) { - inputs.emplace_back("fitrecpoints", o2::header::gDataOriginFT0, "RECPOINTS", 0, Lifetime::Timeframe); - } - auto ggRequest = std::make_shared(false, // orbitResetTime - true, // GRPECS=true - false, // GRPLHCIF - true, // GRPMagField - true, // askMatLUT - o2::base::GRPGeomRequest::Aligned, // geometry - inputs, - true); - o2::tpc::VDriftHelper::requestCCDBInputs(inputs); - - outputs.emplace_back(o2::header::gDataOriginTOF, "MTC_ITSTPC", 0, Lifetime::Timeframe); - if (useMC) { - outputs.emplace_back(o2::header::gDataOriginTOF, "MCMATCHTOF", 0, Lifetime::Timeframe); - } - outputs.emplace_back(o2::header::gDataOriginTOF, "CALIBDATA", 0, Lifetime::Timeframe); - - return DataProcessorSpec{ - "TOFRecoWorkflow", - inputs, - outputs, - AlgorithmSpec{adaptFromTask(ggRequest, useMC, useFIT)}, - Options{ - {"material-lut-path", VariantType::String, "", {"Path of the material LUT file"}}}}; -} - -} // end namespace tof -} // end namespace o2 From aa7e258b79b263eff919e23579e9455ffe1c9c0b Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 9 Feb 2026 09:48:53 +0100 Subject: [PATCH 079/133] FindO2GPU.cmake: be less verbose --- dependencies/FindO2GPU.cmake | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/dependencies/FindO2GPU.cmake b/dependencies/FindO2GPU.cmake index 42d0162691c37..3e8f012fea4b5 100644 --- a/dependencies/FindO2GPU.cmake +++ b/dependencies/FindO2GPU.cmake @@ -10,7 +10,7 @@ # or submit itself to any jurisdiction. # NOTE!!!! - Whenever this file is changed, move it over to alidist/resources -# FindO2GPU.cmake Version 11 +# FindO2GPU.cmake Version 13 set(CUDA_COMPUTETARGET_DEFAULT_FULL 80-real 86-real 89-real 120-real 75-virtual) set(HIP_AMDGPUTARGET_DEFAULT_FULL gfx906;gfx908) @@ -65,7 +65,6 @@ function(detect_gpu_arch backend) # Detect GPU architecture, optionally filterri else() set(CUDA_TARGET TESLA) endif() - message(STATUS "Using optimized CUDA settings for ${CUDA_TARGET} GPU (sm_${CUDA_FIRST_TARGET})") string(REGEX MATCH "^[ \t\r\n]*gfx[0-9]+" HIP_FIRST_TARGET "${HIP_AMDGPUTARGET}") string(STRIP "${HIP_FIRST_TARGET}" HIP_FIRST_TARGET) @@ -87,12 +86,13 @@ function(detect_gpu_arch backend) # Detect GPU architecture, optionally filterri else() set(HIP_TARGET VEGA) endif() - message(STATUS "Using optimized HIP settings for ${HIP_TARGET} GPU (gfx${HIP_FIRST_TARGET})") if(backend STREQUAL "CUDA") # CUDA filter + message(STATUS "Using optimized CUDA settings for ${CUDA_TARGET} GPU (sm_${CUDA_FIRST_TARGET})") set(TARGET_ARCH "${CUDA_TARGET}" PARENT_SCOPE) elseif(backend STREQUAL "HIP") # HIP filter set(TARGET_ARCH "${HIP_TARGET}" PARENT_SCOPE) + message(STATUS "Using optimized HIP settings for ${HIP_TARGET} GPU (gfx${HIP_FIRST_TARGET})") elseif(backend STREQUAL "ALL" OR backend STREQUAL "AUTO") # Return all / enabled backends set(TARGET_ARCH) if(CUDA_ENABLED OR backend STREQUAL "ALL") @@ -194,8 +194,6 @@ if(ENABLE_CUDA) if(THRUST_INCLUDE_DIR STREQUAL "THRUST_INCLUDE_DIR-NOTFOUND") message(${FAILURE_SEVERITY} "CUDA found but thrust not available, looked under: ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES}") set(CMAKE_CUDA_COMPILER OFF) - else() - message(STATUS "Thrust found in the path: ${THRUST_INCLUDE_DIR}") endif() if (NOT CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "12.8") message(${FAILURE_SEVERITY} "CUDA Version too old: ${CMAKE_CUDA_COMPILER_VERSION}, 12.8 required") @@ -278,15 +276,14 @@ if(ENABLE_OPENCL) AND NOT LLVM_SPIRV STREQUAL "LLVM_SPIRV-NOTFOUND" AND OPENCL_COMPATIBLE_CLANG_FOUND) set(OPENCL_ENABLED_SPIRV ON) - message(STATUS "Using CLANG ${LLVM_CLANG} and ${LLVM_SPIRV} for SPIR-V compilation") endif () if(OPENCL_COMPATIBLE_CLANG_FOUND AND (OpenCL_VERSION_STRING VERSION_GREATER_EQUAL 2.2 OR OPENCL_ENABLED_SPIRV)) set(OPENCL_ENABLED ON) - message(STATUS "Found OpenCL 2 (${OpenCL_VERSION_STRING} SPIR-V ${OPENCL_ENABLED_SPIRV} with CLANG ${LLVM_PACKAGE_VERSION})") + message(STATUS "Found OpenCL ${OpenCL_VERSION_STRING} (SPIR-V ${OPENCL_ENABLED_SPIRV} ${LLVM_CLANG} ${LLVM_PACKAGE_VERSION} ${LLVM_SPIRV})") elseif(NOT ENABLE_OPENCL STREQUAL "AUTO") - message(FATAL_ERROR "OpenCL 2.x not available") + message(FATAL_ERROR "OpenCL >= 2.x not available") else() set(OPENCL_ENABLED OFF) endif() @@ -347,7 +344,6 @@ if(ENABLE_HIP) set(CMAKE_HIP_HOST_COMPILER "$ENV{GCC_TOOLCHAIN_ROOT}/bin/gcc") endif() enable_language(HIP) - message(STATUS "HIP language enabled: ${CMAKE_HIP_COMPILER}") endif() elseif(NOT ENABLE_HIP STREQUAL "AUTO") message(FATAL_ERROR "HIP requested, but CMAKE_PREFIX_PATH env variable does not contain rocm folder!") @@ -373,7 +369,7 @@ if(ENABLE_HIP) if(HIP_AMDGPUTARGET) set(CMAKE_HIP_ARCHITECTURES "${HIP_AMDGPUTARGET}") endif() - message(STATUS "HIP Found (${hip_HIPCC_EXECUTABLE} version ${hip_VERSION}, Architectures ${CMAKE_HIP_ARCHITECTURES})") + message(STATUS "HIP Found (${hip_HIPCC_EXECUTABLE} version ${hip_VERSION}, ${CMAKE_HIP_COMPILER}, Architectures ${CMAKE_HIP_ARCHITECTURES})") else() set(HIP_ENABLED OFF) endif() From 2d96089c502e6c582a108e4a0f0ed1cbb7a21e69 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 9 Feb 2026 09:49:15 +0100 Subject: [PATCH 080/133] GPU Parameters: Add script to generate parameter file from parameter list csv/json and architecture --- GPU/GPUTracking/CMakeLists.txt | 4 +- .../gpu_param_header_generator.cmake | 4 +- .../Standalone/Benchmark/CMakeLists.txt | 2 + GPU/GPUTracking/Standalone/CMakeLists.txt | 1 - .../Standalone/tools/dumpGPUParamByArch.sh | 60 +++++++++++++++++++ GPU/GPUTracking/display/CMakeLists.txt | 9 ++- .../display/filterMacros/setinclude.sh.in | 2 +- 7 files changed, 72 insertions(+), 10 deletions(-) rename GPU/GPUTracking/{cmake => Definitions/Parameters}/gpu_param_header_generator.cmake (96%) create mode 100755 GPU/GPUTracking/Standalone/tools/dumpGPUParamByArch.sh diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index 082dc1f10b1d6..dfee81b398a79 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -292,10 +292,10 @@ foreach(GPU_PARAM_JSON_FILE IN LISTS GPU_PARAM_JSON) math(EXPR GPU_PARAM_JSON_N_FILES "${GPU_PARAM_JSON_N_FILES} + 1") endforeach() -include(cmake/gpu_param_header_generator.cmake) +include(Definitions/Parameters/gpu_param_header_generator.cmake) set(GPU_DEFAULT_PARAMS_HEADER ${ON_THE_FLY_DIR}/GPUDefParametersDefaults.h) set(GPU_DEFAULT_PARAMS_HEADER_DEVICE ${ON_THE_FLY_DIR}/GPUDefParametersDefaultsDevice.h) -generate_gpu_param_header("${GPU_PARAM_JSON_FILES}" "ALL" "${GPU_DEFAULT_PARAMS_HEADER}" "${GPU_DEFAULT_PARAMS_HEADER_DEVICE}" GPU_CONST_PARAM_ARCHITECTUES) # generate header with default GPU parameters, arch selected by CMake variables +generate_gpu_param_header("${GPU_PARAM_JSON_FILES}" "ALL" "${GPU_DEFAULT_PARAMS_HEADER}" "${GPU_DEFAULT_PARAMS_HEADER_DEVICE}" GPU_CONST_PARAM_ARCHITECTUES) # generate header with default GPU parameters for all architectures list(APPEND GENERATED_HEADERS_LIST ${GPU_DEFAULT_PARAMS_HEADER} ${GPU_DEFAULT_PARAMS_HEADER_DEVICE}) set(HDRS_INSTALL ${HDRS_INSTALL} ${GENERATED_HEADERS_LIST}) diff --git a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake b/GPU/GPUTracking/Definitions/Parameters/gpu_param_header_generator.cmake similarity index 96% rename from GPU/GPUTracking/cmake/gpu_param_header_generator.cmake rename to GPU/GPUTracking/Definitions/Parameters/gpu_param_header_generator.cmake index 383d194aaa717..d0e36e7f15026 100644 --- a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake +++ b/GPU/GPUTracking/Definitions/Parameters/gpu_param_header_generator.cmake @@ -110,6 +110,9 @@ function(generate_gpu_param_header GPU_PARAM_JSON_FILES ARCH_LIST OUT_HEADER OUT string(APPEND TMP_HEADER_DEVICE "#if 0\n") foreach(ARCH IN LISTS ARCH_LIST) + if(do_all_architectures EQUAL -1 AND do_auto_architectures EQUAL -1 AND NOT generate_gpu_param_header_OUTPUT_TMP_${ARCH}) + message(FATAL_ERROR "No parameters defined for architecture ${ARCH}") + endif() string(APPEND TMP_HEADER_DEVICE "\n#elif defined(GPUCA_GPUTYPE_${ARCH})\n") string(APPEND TMP_HEADER_DEVICE ${generate_gpu_param_header_OUTPUT_TMP_${ARCH}}) endforeach() @@ -129,5 +132,4 @@ function(generate_gpu_param_header GPU_PARAM_JSON_FILES ARCH_LIST OUT_HEADER OUT string(APPEND TMP_HEADER_DEVICE "\n#endif // GPUDEFPARAMETERSDEFAULTSDEVICE_H\n") file(GENERATE OUTPUT "${OUT_HEADER}" CONTENT "${TMP_HEADER}") file(GENERATE OUTPUT "${OUT_HEADER_DEVICE}" CONTENT "${TMP_HEADER_DEVICE}") - message(STATUS "Generated ${OUT_HEADER} and ${OUT_HEADER_DEVICE}") endfunction() diff --git a/GPU/GPUTracking/Standalone/Benchmark/CMakeLists.txt b/GPU/GPUTracking/Standalone/Benchmark/CMakeLists.txt index eeafcfc44142d..9f28fd8cc6fe9 100644 --- a/GPU/GPUTracking/Standalone/Benchmark/CMakeLists.txt +++ b/GPU/GPUTracking/Standalone/Benchmark/CMakeLists.txt @@ -30,6 +30,8 @@ if(ALIGPU_BUILD_TYPE STREQUAL "Standalone") target_link_libraries(${targetName} PUBLIC GPUTracking) endif() +install(DIRECTORY ../tools DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/GPU) +install(DIRECTORY ../../Definitions/Parameters/ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/GPU/tools FILES_MATCHING REGEX "\\.(python|sh|cmake)") target_compile_definitions(${targetName} PRIVATE $) if(ROOT_FOUND) diff --git a/GPU/GPUTracking/Standalone/CMakeLists.txt b/GPU/GPUTracking/Standalone/CMakeLists.txt index 48fbd77c62786..0cf72fd2b4c3e 100644 --- a/GPU/GPUTracking/Standalone/CMakeLists.txt +++ b/GPU/GPUTracking/Standalone/CMakeLists.txt @@ -246,4 +246,3 @@ install(TARGETS ca TPCFastTransformation standalone_support) install(FILES "cmake/makefile" DESTINATION "${CMAKE_INSTALL_PREFIX}") install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${O2_DIR} ${CMAKE_INSTALL_PREFIX}/src)") install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_BINARY_DIR}/config.cmake ${CMAKE_INSTALL_PREFIX}/config.cmake)") -install(DIRECTORY tools DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/GPU) diff --git a/GPU/GPUTracking/Standalone/tools/dumpGPUParamByArch.sh b/GPU/GPUTracking/Standalone/tools/dumpGPUParamByArch.sh new file mode 100755 index 0000000000000..0a4f5f5c1656f --- /dev/null +++ b/GPU/GPUTracking/Standalone/tools/dumpGPUParamByArch.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +if [[ -z $3 ]]; then + echo "Usage: dumpGPUParamByArch.sh [JSON or CSV parameter file] [Architecture] [Output File]" + exit 1 +fi + +if ! command -v root &> /dev/null; then + echo "Cannot run root, please make sure ROOT is available and in the parh" + exit 1 +fi + +if [[ ! -f $1 ]]; then + echo "Input file $1 does not exist" + exit 1 +fi + +if [[ -f "include/GPU/GPUDefParametersLoad.inc" ]]; then + LOADDIR=$(realpath "include/GPU") +elif [[ -f "$O2_ROOT/include/GPU/GPUDefParametersLoad.inc" ]]; then + LOADDIR=$(realpath "$O2_ROOT/include/GPU/") +else + echo "Cannot find GPUDefParametersLoad.inc, please run from standalone benchmark folder or set \$O2_ROOT to the standalone or O2 installation" + exit 1 +fi + +set -e + +TMPDIR=$(mktemp -d) +if [[ $? != 0 ]]; then + echo "Failed to create a temporary directory" + exit 1 +fi + +BASE_DIR=$(dirname $(realpath ${BASH_SOURCE[0]})) + +if [[ $1 =~ \.csv$ ]]; then + "${BASE_DIR}"/../../Definitions/Parameters/csv_to_json.sh $1 > "$TMPDIR"/temp.json + JSON_FILE="$TMPDIR"/temp.json +else + JSON_FILE=$(realpath $1) +fi + +cat < "${TMPDIR}"/CMakeLists.txt +cmake_minimum_required(VERSION 3.16 FATAL_ERROR) +project(DumpGPUParam NONE) +include($BASE_DIR/../../Definitions/Parameters/gpu_param_header_generator.cmake) +generate_gpu_param_header("${JSON_FILE}" "$2" "${TMPDIR}/GPUDefParametersDefaultsOnTheFly.h" "${TMPDIR}/GPUDefParametersDefaultsDeviceOnTheFly.h") +EOT + +cmake -B "${TMPDIR}" -S"${TMPDIR}" + +echo -e "#define GPUCA_GPUTYPE_$2\n" \ + "#define PARAMETER_FILE \"${TMPDIR}/GPUDefParametersDefaultsOnTheFly.h\"\n" \ + "gInterpreter->AddIncludePath(\"${TMPDIR}\");gInterpreter->AddIncludePath(\"${LOADDIR}\");\n" \ + ".x $BASE_DIR/dumpGPUDefParam.C(\"$3\")\n.q\n" | root -l -b + +echo -e "\nCreated $3 with parameters for $2 architecture from $1" + +rm -Rf "${TMPDIR}" diff --git a/GPU/GPUTracking/display/CMakeLists.txt b/GPU/GPUTracking/display/CMakeLists.txt index 32d25ee08b729..82ce0d4a9b190 100644 --- a/GPU/GPUTracking/display/CMakeLists.txt +++ b/GPU/GPUTracking/display/CMakeLists.txt @@ -151,14 +151,13 @@ if(ALIGPU_BUILD_TYPE STREQUAL "Standalone") add_library(O2::${MODULE} ALIAS ${MODULE}) target_link_libraries(${targetName} PUBLIC O2::GPUTracking) install(TARGETS ${MODULE}) - - install(DIRECTORY filterMacros/ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/GPU/displayTrackFilter FILES_MATCHING PATTERN "*.C") - get_property(GPU_DISPLAY_INCLUDE_PATH DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES) - configure_file(filterMacros/setinclude.sh.in setinclude.sh @ONLY) - install(FILES ${CMAKE_CURRENT_BINARY_DIR}/setinclude.sh PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/GPU/displayTrackFilter) endif() install(FILES ${HDRS} ${HDRS_INSTALL} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/GPU) +install(DIRECTORY filterMacros/ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/GPU/displayTrackFilter FILES_MATCHING PATTERN "*.C") +get_property(GPU_DISPLAY_INCLUDE_PATH DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES) +configure_file(filterMacros/setinclude.sh.in setinclude.sh @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/setinclude.sh PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/GPU/displayTrackFilter) target_compile_definitions(${targetName} PRIVATE $) diff --git a/GPU/GPUTracking/display/filterMacros/setinclude.sh.in b/GPU/GPUTracking/display/filterMacros/setinclude.sh.in index c588923db4b43..0a301537bba0e 100755 --- a/GPU/GPUTracking/display/filterMacros/setinclude.sh.in +++ b/GPU/GPUTracking/display/filterMacros/setinclude.sh.in @@ -1,2 +1,2 @@ #!/bin/bash -export ROOC_INCLUDE_PATH="@GPU_DISPLAY_INCLUDE_PATH@" +export ROOT_INCLUDE_PATH="@GPU_DISPLAY_INCLUDE_PATH@" From bf8a4027b3eb50ce57d54dc0b796afa1cb6a2fcf Mon Sep 17 00:00:00 2001 From: David Rohr Date: Tue, 10 Feb 2026 14:21:16 +0100 Subject: [PATCH 081/133] Fix codechecker violation --- Detectors/EMCAL/base/src/ClusterFactory.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Detectors/EMCAL/base/src/ClusterFactory.cxx b/Detectors/EMCAL/base/src/ClusterFactory.cxx index 970f7979ef86d..1752e5c0e98ee 100644 --- a/Detectors/EMCAL/base/src/ClusterFactory.cxx +++ b/Detectors/EMCAL/base/src/ClusterFactory.cxx @@ -528,8 +528,9 @@ void ClusterFactory::evalNExMax(gsl::span inputsIndices, A // loop over all other cells in cluster for (size_t j = 0; j < n; j++) { - if (i == j) + if (i == j) { continue; + } // adjacent cell is any cell with adjacent phi or eta index if (std::abs(rows[i] - rows[j]) <= 1 && From f10bf6ecd0328e6a96700cc6de8d1afd57e66875 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 11 Feb 2026 10:02:55 +0100 Subject: [PATCH 082/133] DPL: oldest possible timeframe triggered CompletionPolicy (#15046) This will trigger the processing whenever a given slot will not receive data anymore in virtue of its timeslice being past the oldest possible timeframe. --- .../Framework/CompletionPolicyHelpers.h | 8 ++++- .../Core/src/CompletionPolicyHelpers.cxx | 29 ++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/Framework/Core/include/Framework/CompletionPolicyHelpers.h b/Framework/Core/include/Framework/CompletionPolicyHelpers.h index 7f77e4a96f76f..9fce626854e5b 100644 --- a/Framework/Core/include/Framework/CompletionPolicyHelpers.h +++ b/Framework/Core/include/Framework/CompletionPolicyHelpers.h @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -54,6 +54,12 @@ struct CompletionPolicyHelpers { } static CompletionPolicy consumeWhenAny(std::string matchName); + // Consume all the data captured until the oldest possible timeframe + // in input indicates that nothing else can be added to this timeslice. + // Useful in case of wildcards which multiplex multiple subspecs on the + // same input. + static CompletionPolicy consumeWhenPastOldestPossibleTimeframe(const char* name, CompletionPolicy::Matcher matcher); + /// When any of the parts of the record have been received, consume them. static CompletionPolicy consumeWhenAnyWithAllConditions(const char* name, CompletionPolicy::Matcher matcher); /// Default matcher applies for all devices diff --git a/Framework/Core/src/CompletionPolicyHelpers.cxx b/Framework/Core/src/CompletionPolicyHelpers.cxx index 67c726b7f4368..2b49b8dfa9acd 100644 --- a/Framework/Core/src/CompletionPolicyHelpers.cxx +++ b/Framework/Core/src/CompletionPolicyHelpers.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -11,6 +11,7 @@ #include "Framework/CompletionPolicyHelpers.h" #include "Framework/CompletionPolicy.h" +#include "Framework/DataProcessingHeader.h" #include "Framework/InputSpan.h" #include "Framework/DeviceSpec.h" #include "Framework/CompilerBuiltins.h" @@ -263,6 +264,32 @@ CompletionPolicy CompletionPolicyHelpers::consumeWhenAnyZeroCount(const char* na return CompletionPolicy{name, matcher, callback, false}; } +CompletionPolicy CompletionPolicyHelpers::consumeWhenPastOldestPossibleTimeframe(const char* name, CompletionPolicy::Matcher matcher) +{ + auto callback = [](InputSpan const& inputs, std::vector const&, ServiceRegistryRef& ref) -> CompletionPolicy::CompletionOp { + size_t currentTimeslice = -1; + for (auto& input : inputs) { + if (input.header == nullptr) { + continue; + } + o2::framework::DataProcessingHeader const* dph = o2::header::get(input.header); + if (dph && !TimingInfo::timesliceIsTimer(dph->startTime)) { + currentTimeslice = dph->startTime; + break; + } + } + + auto& timesliceIndex = ref.get(); + auto oldestPossibleTimeslice = timesliceIndex.getOldestPossibleInput().timeslice.value; + + if (currentTimeslice >= oldestPossibleTimeslice) { + return CompletionPolicy::CompletionOp::Retry; + } + return CompletionPolicy::CompletionOp::Consume; + }; + return CompletionPolicy{name, matcher, callback, false}; +} + CompletionPolicy CompletionPolicyHelpers::consumeWhenAny(const char* name, CompletionPolicy::Matcher matcher) { auto callback = [](InputSpan const& inputs, std::vector const&, ServiceRegistryRef& ref) -> CompletionPolicy::CompletionOp { From 331f2cc815bd213df04c40ecb9359422f11edd8d Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Wed, 11 Feb 2026 11:18:17 +0100 Subject: [PATCH 083/133] fix topology adjust corner case (#15053) --- Framework/Core/src/ArrowSupport.cxx | 4 +++- run/o2sim_kine_publisher.cxx | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index 31cddc9803d69..450f31f4ba7d3 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -581,7 +581,6 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() auto spawner = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { return spec.name.starts_with("internal-dpl-aod-spawner"); }); auto analysisCCDB = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { return spec.name.starts_with("internal-dpl-aod-ccdb"); }); auto builder = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { return spec.name.starts_with("internal-dpl-aod-index-builder"); }); - auto reader = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { return spec.name.starts_with("internal-dpl-aod-reader"); }); auto writer = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { return spec.name.starts_with("internal-dpl-aod-writer"); }); auto& dec = ctx.services().get(); dec.requestedAODs.clear(); @@ -659,6 +658,9 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() workflow.erase(writer); } + // removing writer would invalidate the reader iterator if it was created before + auto reader = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { return spec.name.starts_with("internal-dpl-aod-reader"); }); + if (reader != workflow.end()) { // If reader and/or builder were adjusted, remove unneeded outputs // update currently requested AODs diff --git a/run/o2sim_kine_publisher.cxx b/run/o2sim_kine_publisher.cxx index f72dd6eebaaf0..cfbea6ae02a5f 100644 --- a/run/o2sim_kine_publisher.cxx +++ b/run/o2sim_kine_publisher.cxx @@ -13,7 +13,6 @@ #include "Framework/AnalysisTask.h" #include "Monitoring/Monitoring.h" #include "Framework/CommonDataProcessors.h" -#include "SimulationDataFormat/MCTrack.h" #include "Steer/MCKinematicsReader.h" #include "Framework/runDataProcessing.h" @@ -64,6 +63,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) spec.outputs.emplace_back("MC", "MCHEADER", 0, Lifetime::Timeframe); spec.outputs.emplace_back("MC", "MCTRACKS", 0, Lifetime::Timeframe); spec.requiredServices.push_back(o2::framework::ArrowSupport::arrowBackendSpec()); - spec.algorithm = CommonDataProcessors::wrapWithRateLimiting(spec.algorithm); + spec.algorithm = CommonDataProcessors::wrapWithTimesliceConsumption(spec.algorithm); return {spec}; } From 970ed8ea7328d3828694ba4770429ea680ff2524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiktor=20Piero=C5=BCak?= <94726725+wpierozak@users.noreply.github.com> Date: Wed, 11 Feb 2026 14:41:38 +0100 Subject: [PATCH 084/133] EventsPerBC calibration task for FT0 (O2-6563) (#14986) Implements the EventsPerBC calibration task for FT0, which generates a histogram of VTX events above a defined amplitude threshold plotted against the BC. * FT0: created first sketch of implementation of generation of TVX per Event calibration object * FT0: Updated CMakeLists for calibration * FT0: Added missing entry in FT0CalibrationLinkDef.h * FT0 calibration: fixed ROOT directory compilation, fixed CCDB output * FT0: refined logs in EventsPerBc calibration, fixed setting TF info in run method * FT0: Added readme to calibrations * FT0: Changed calibration object name, implemented missing OrbitReset fetching * FT0 EventsPerBc calibration: storing histograms in float format, updated readme * Changed type of EventsPerBc calibration object to std::array * FT0: corrected macro FT0readEventsPerBc, corrected typo in calibration README * Created CCDB object class for EvetnsPerBC calibration * FT0: formatted EvensPerBc.h * FT0: removed amplitudes thresholds from EventsPerBc * FT0: Removed from EventsPerBc calibarion option to define slot lenght in TFs; Small code cleaning * Changed default value of min number of entries in EventsPerBcProcessor from 5000 to 5000u --- DataFormats/Detectors/FIT/FT0/CMakeLists.txt | 1 + .../FT0/include/DataFormatsFT0/EventsPerBc.h | 25 ++++ .../FIT/FT0/src/DataFormatsFT0LinkDef.h | 2 + Detectors/FIT/FT0/calibration/CMakeLists.txt | 70 ++++++---- Detectors/FIT/FT0/calibration/README.md | 62 +++++++++ .../FT0Calibration/EventsPerBcCalibrator.h | 81 ++++++++++++ .../calibration/src/EventsPerBcCalibrator.cxx | 81 ++++++++++++ .../calibration/src/FT0CalibrationLinkDef.h | 4 +- .../FT0EventsPerBcProcessor-Workflow.cxx | 47 +++++++ .../calibration/workflow/FT0EventsPerBcSpec.h | 124 ++++++++++++++++++ Detectors/FIT/FT0/macros/CMakeLists.txt | 19 ++- Detectors/FIT/FT0/macros/FT0readEventsPerBc.C | 52 ++++++++ 12 files changed, 538 insertions(+), 30 deletions(-) create mode 100644 DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/EventsPerBc.h create mode 100644 Detectors/FIT/FT0/calibration/README.md create mode 100644 Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h create mode 100644 Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx create mode 100644 Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx create mode 100644 Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h create mode 100644 Detectors/FIT/FT0/macros/FT0readEventsPerBc.C diff --git a/DataFormats/Detectors/FIT/FT0/CMakeLists.txt b/DataFormats/Detectors/FIT/FT0/CMakeLists.txt index e5331b7b739b2..f7d6a111f4348 100644 --- a/DataFormats/Detectors/FIT/FT0/CMakeLists.txt +++ b/DataFormats/Detectors/FIT/FT0/CMakeLists.txt @@ -47,4 +47,5 @@ o2_target_root_dictionary(DataFormatsFT0 include/DataFormatsFT0/GlobalOffsetsCalibrationObject.h include/DataFormatsFT0/SpectraInfoObject.h include/DataFormatsFT0/SlewingCoef.h + include/DataFormatsFT0/EventsPerBc.h ) diff --git a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/EventsPerBc.h b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/EventsPerBc.h new file mode 100644 index 0000000000000..9fcd1318914bd --- /dev/null +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/EventsPerBc.h @@ -0,0 +1,25 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef _FT0_EVENTS_PER_BC_CALIB_OBJECT +#define _FT0_EVENTS_PER_BC_CALIB_OBJECT + +#include "CommonConstants/LHCConstants.h" +#include + +namespace o2::ft0 +{ +struct EventsPerBc { + std::array histogram; + ClassDefNV(EventsPerBc, 1); +}; +} // namespace o2::ft0 +#endif \ No newline at end of file diff --git a/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h b/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h index 0d3491224180c..7f8c17a0cd191 100644 --- a/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h +++ b/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h @@ -56,4 +56,6 @@ #pragma link C++ class std::pair < std::vector < double>, std::vector < double>> + ; #pragma link C++ class o2::ft0::SlewingCoef + ; +#pragma link C++ class o2::ft0::EventsPerBc + ; + #endif diff --git a/Detectors/FIT/FT0/calibration/CMakeLists.txt b/Detectors/FIT/FT0/calibration/CMakeLists.txt index d103b4a9a18b6..bee0493d300c1 100644 --- a/Detectors/FIT/FT0/calibration/CMakeLists.txt +++ b/Detectors/FIT/FT0/calibration/CMakeLists.txt @@ -10,26 +10,50 @@ # or submit itself to any jurisdiction. o2_add_library(FT0Calibration - SOURCES - src/FT0TimeOffsetSlotContainer.cxx - PUBLIC_LINK_LIBRARIES - O2::DataFormatsFT0 - O2::CommonDataFormat - O2::DetectorsCalibration - ) - o2_target_root_dictionary(FT0Calibration - HEADERS - include/FT0Calibration/FT0TimeOffsetSlotContainer.h - ) - o2_add_executable(ft0-time-offset-calib - COMPONENT_NAME calibration - SOURCES workflow/FT0TimeOffsetCalibration-Workflow.cxx - PUBLIC_LINK_LIBRARIES - O2::FT0Calibration O2::FITCalibration - ) - o2_add_executable(ft0-time-spectra-processor - COMPONENT_NAME calibration - SOURCES workflow/FT0TimeSpectraProcessor-Workflow.cxx - PUBLIC_LINK_LIBRARIES - O2::FT0Calibration - ) + SOURCES + src/FT0TimeOffsetSlotContainer.cxx + src/EventsPerBcCalibrator.cxx + PUBLIC_LINK_LIBRARIES + O2::DetectorsCalibration + O2::Framework + O2::CommonUtils + Microsoft.GSL::GSL + O2::DataFormatsFT0 + O2::CommonDataFormat + O2::Steer + O2::CCDB + ROOT::Minuit + ROOT::Hist + ) + +o2_target_root_dictionary(FT0Calibration + HEADERS + include/FT0Calibration/FT0TimeOffsetSlotContainer.h + include/FT0Calibration/EventsPerBcCalibrator.h + ) + +o2_add_executable(ft0-time-offset-calib + COMPONENT_NAME calibration + SOURCES + workflow/FT0TimeOffsetCalibration-Workflow.cxx + PUBLIC_LINK_LIBRARIES + O2::FT0Calibration O2::FITCalibration + ) + +o2_add_executable(ft0-time-spectra-processor + COMPONENT_NAME calibration + SOURCES + workflow/FT0TimeSpectraProcessor-Workflow.cxx + PUBLIC_LINK_LIBRARIES + O2::FT0Calibration + ) + +o2_add_executable(ft0-events-per-bc-processor + COMPONENT_NAME calibration + SOURCES + workflow/FT0EventsPerBcProcessor-Workflow.cxx + PUBLIC_LINK_LIBRARIES + O2::FT0Calibration + O2::Framework + O2::CCDB +) \ No newline at end of file diff --git a/Detectors/FIT/FT0/calibration/README.md b/Detectors/FIT/FT0/calibration/README.md new file mode 100644 index 0000000000000..78b0f980400d2 --- /dev/null +++ b/Detectors/FIT/FT0/calibration/README.md @@ -0,0 +1,62 @@ +# Calibrations + +## Events per BC Calibration +### Description +Generates histograms of **Events per Bunch Crossing (BC)**. Events can be filtered by applying amplitude thresholds to the **A-side** and **C-side**. + +### Command-Line Options +| Option | Default | Description | +| :--- | :--- | :--- | +| `--slot-len-sec` | `3600` | Duration of each slot in seconds. | +| `--slot-len-tf` | `0` | Slot length in Time Frames (TFs). | +| `--one-object-per-run` | — | If set, the workflow creates only one calibration object per run. | +| `--min-entries-number` | `0` | Minimum number of entries required for a slot to be valid. | +| `--min-ampl-side-a` | `-2147483648` | Amplitude threshold for Side A events. | +| `--min-ampl-side-c` | `-2147483648` | Amplitude threshold for Side C events. | + +--- + +## How to Run + +### Simulation Data +First, it is important to digitize data with a non-zero run number, orbit, and timestamp. To set these parameters, one can use the `--configKeyValues` option, as shown in the example below. +``` +o2-sim-digitizer-workflow \ +--onlyDet FT0 \ +--configKeyValues="HBFUtils.nHBFPerTF=128;HBFUtils.orbitFirst=128;HBFUtils.orbitFirstSampled=256;HBFUtils.runNumber=560560;HBFUtils.startTime=1768464099000" +``` + +To process simulation data, digits must first be converted to RAW format. The `o2-ft0-digi2raw` tool performs this conversion and generates the required configuration file. + +Once converted, you can run the calibration either as a single integrated workflow or by spawning as the sender and receiver components separately. + +#### Single Workflow Example +Execute the following command within the simulation directory: +``` +o2-raw-file-reader-workflow --input-conf FT0raw.cfg --loop -1 \ +| o2-ft0-flp-dpl-workflow --condition-backend=http://localhost:8080 \ +| o2-calibration-ft0-events-per-bc-processor --FT0EventsPerBcProcessor "--slot-len-sec=10" \ +| o2-calibration-ccdb-populator-workflow --ccdb-path=http://localhost:8080 +``` + +Sender example (in simulation directory): +``` +o2-raw-file-reader-workflow --input-conf FT0raw.cfg --loop -1 \ +| o2-ft0-flp-dpl-workflow --condition-backend=http://localhost:8080 \ +| o2-dpl-output-proxy --channel-config "name=downstream,method=connect,address=tcp://localhost:30453,type=push,transport=zeromq" --dataspec "downstream:FT0/DIGITSBC" +``` + +Receiver example: +``` +o2-dpl-raw-proxy --channel-config "name=readout-proxy,type=pull,method=bind,address=tcp://localhost:30453,rateLogging=1,transport=zeromq" --dataspec "A:FT0/DIGITSBC/0" \ +| o2-calibration-ft0-events-per-bc-processor --FT0EventsPerBcProcessor "--slot-len-sec=10 --min-ampl-side-a=0" \ +| o2-calibration-ccdb-populator-workflow --ccdb-path=http://localhost:8080/ +``` + +### CTF Data +Example: +``` +o2-ctf-reader-workflow --ctf-input ctf.root --onlyDet FT0 \ +| o2-calibration-ft0-events-per-bc-processor --FT0EventsPerBcProcessor "--slot-len-sec=10" \ +| o2-calibration-ccdb-populator-workflow --ccdb-path=http://localhost:8080/ +``` \ No newline at end of file diff --git a/Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h b/Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h new file mode 100644 index 0000000000000..f44824517f258 --- /dev/null +++ b/Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h @@ -0,0 +1,81 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_FT0TVXPERBCID +#define O2_FT0TVXPERBCID + +#include +#include +#include +#include + +#include "CommonDataFormat/FlatHisto2D.h" +#include "CommonConstants/LHCConstants.h" +#include "DataFormatsFT0/SpectraInfoObject.h" +#include "DataFormatsFT0/Digit.h" +#include "DataFormatsFT0/EventsPerBc.h" +#include "DetectorsCalibration/TimeSlotCalibration.h" +#include "DetectorsCalibration/TimeSlot.h" +#include "CommonDataFormat/TFIDInfo.h" +#include "TH1F.h" +#include "Rtypes.h" + +namespace o2::ft0 +{ +struct EventsPerBcContainer { + EventsPerBcContainer(int32_t minAmplitudeSideA, int32_t minAmplitudeSideC) : mMinAmplitudeSideA(minAmplitudeSideA), mMinAmplitudeSideC(minAmplitudeSideC) {} + + size_t getEntries() const { return entries; } + void print() const; + void fill(const o2::dataformats::TFIDInfo& ti, const gsl::span data); + void merge(const EventsPerBcContainer* prev); + + const int32_t mMinAmplitudeSideA; + const int32_t mMinAmplitudeSideC; + + std::array mTvx{0.0}; + size_t entries{0}; + long startTimeStamp{0}; + long stopTimeStamp{0}; + + ClassDefNV(EventsPerBcContainer, 1); +}; + +class EventsPerBcCalibrator final : public o2::calibration::TimeSlotCalibration +{ + using Slot = o2::calibration::TimeSlot; + using TFType = o2::calibration::TFType; + using EventsHistogram = std::array; + + public: + EventsPerBcCalibrator(uint32_t minNumberOfEntries, int32_t minAmplitudeSideA, int32_t minAmplitudeSideC); + + bool hasEnoughData(const Slot& slot) const override; + void initOutput() override; + void finalizeSlot(Slot& slot) override; + Slot& emplaceNewSlot(bool front, TFType tstart, TFType tend) override; + + const std::vector& getTvxPerBc() { return mTvxPerBcs; } + std::vector>& getTvxPerBcCcdbInfo() { return mTvxPerBcInfos; } + + private: + const uint32_t mMinNumberOfEntries; + const int32_t mMinAmplitudeSideA; + const int32_t mMinAmplitudeSideC; + + std::vector mTvxPerBcs; + std::vector> mTvxPerBcInfos; + + ClassDefOverride(EventsPerBcCalibrator, 1); +}; +} // namespace o2::ft0 + +#endif diff --git a/Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx b/Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx new file mode 100644 index 0000000000000..a2230f51dc4ea --- /dev/null +++ b/Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx @@ -0,0 +1,81 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "FT0Calibration/EventsPerBcCalibrator.h" +#include "CommonUtils/MemFileHelper.h" + +namespace o2::ft0 +{ +void EventsPerBcContainer::print() const +{ + LOG(info) << entries << " entries"; +} + +void EventsPerBcContainer::fill(const o2::dataformats::TFIDInfo& ti, const gsl::span data) +{ + size_t oldEntries = entries; + for (const auto& digit : data) { + if (digit.mTriggers.getVertex() && digit.mTriggers.getAmplA() >= mMinAmplitudeSideA && digit.mTriggers.getAmplC() >= mMinAmplitudeSideC) { + mTvx[digit.mIntRecord.bc]++; + entries++; + } + } + LOG(debug) << "Container is filled with " << entries - oldEntries << " new events"; +} + +void EventsPerBcContainer::merge(const EventsPerBcContainer* prev) +{ + for (int bc = 0; bc < o2::constants::lhc::LHCMaxBunches; bc++) { + mTvx[bc] += prev->mTvx[bc]; + } + entries += prev->entries; +} + +void EventsPerBcCalibrator::initOutput() +{ + mTvxPerBcs.clear(); + mTvxPerBcInfos.clear(); +} + +EventsPerBcCalibrator::EventsPerBcCalibrator(uint32_t minNumberOfEntries, int32_t minAmplitudeSideA, int32_t minAmplitudeSideC) : mMinNumberOfEntries(minNumberOfEntries), mMinAmplitudeSideA(minAmplitudeSideA), mMinAmplitudeSideC(minAmplitudeSideC) +{ + LOG(info) << "Defined threshold for number of entires per slot: " << mMinNumberOfEntries; + LOG(info) << "Defined threshold for side A amplitude for event: " << mMinAmplitudeSideA; + LOG(info) << "Defined threshold for side C amplitude for event: " << mMinAmplitudeSideC; +} + +bool EventsPerBcCalibrator::hasEnoughData(const EventsPerBcCalibrator::Slot& slot) const +{ + return slot.getContainer()->entries > mMinNumberOfEntries; +} + +void EventsPerBcCalibrator::finalizeSlot(EventsPerBcCalibrator::Slot& slot) +{ + LOG(info) << "Finalizing slot from " << slot.getStartTimeMS() << " to " << slot.getEndTimeMS(); + o2::ft0::EventsPerBcContainer* data = slot.getContainer(); + mTvxPerBcs.emplace_back(data->mTvx); + + auto clName = o2::utils::MemFileHelper::getClassName(mTvxPerBcs.back()); + auto flName = o2::ccdb::CcdbApi::generateFileName(clName); + + std::map metaData; + mTvxPerBcInfos.emplace_back(std::make_unique("FT0/Calib/EventsPerBc", clName, flName, metaData, slot.getStartTimeMS(), slot.getEndTimeMS())); + LOG(info) << "Created object valid from " << mTvxPerBcInfos.back()->getStartValidityTimestamp() << " to " << mTvxPerBcInfos.back()->getEndValidityTimestamp(); +} + +EventsPerBcCalibrator::Slot& EventsPerBcCalibrator::emplaceNewSlot(bool front, TFType tstart, TFType tend) +{ + auto& cont = getSlots(); + auto& slot = front ? cont.emplace_front(tstart, tend) : cont.emplace_back(tstart, tend); + slot.setContainer(std::make_unique(mMinAmplitudeSideA, mMinAmplitudeSideC)); + return slot; +} +} // namespace o2::ft0 \ No newline at end of file diff --git a/Detectors/FIT/FT0/calibration/src/FT0CalibrationLinkDef.h b/Detectors/FIT/FT0/calibration/src/FT0CalibrationLinkDef.h index 49f72e8cbdfff..11b1ce25e9353 100644 --- a/Detectors/FIT/FT0/calibration/src/FT0CalibrationLinkDef.h +++ b/Detectors/FIT/FT0/calibration/src/FT0CalibrationLinkDef.h @@ -16,7 +16,9 @@ #pragma link off all functions; #pragma link C++ class o2::ft0::FT0TimeOffsetSlotContainer + ; +#pragma link C++ class o2::ft0::EventsPerBcCalibrator + ; #pragma link C++ class o2::calibration::TimeSlot < o2::ft0::FT0TimeOffsetSlotContainer>; #pragma link C++ class o2::calibration::TimeSlotCalibration < o2::ft0::FT0TimeOffsetSlotContainer>; - +#pragma link C++ class o2::calibration::TimeSlot < o2::ft0::EventsPerBcContainer> + ; +#pragma link C++ class o2::calibration::TimeSlotCalibration < o2::ft0::EventsPerBcContainer> + ; #endif diff --git a/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx new file mode 100644 index 0000000000000..ac7a8e52f53b1 --- /dev/null +++ b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx @@ -0,0 +1,47 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "FT0EventsPerBcSpec.h" +#include "Framework/Lifetime.h" +#include + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +{ + using namespace o2::framework; + using o2::calibration::FT0EventsPerBcProcessor; + std::vector inputs; + inputs.emplace_back("digits", "FT0", "DIGITSBC", Lifetime::Timeframe); + auto ccdbRequest = std::make_shared(true, // orbitResetTime + false, // GRPECS=true + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::None, // geometry + inputs); + std::vector outputs; + outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "EventsPerBc"}, Lifetime::Timeframe); + outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, "EventsPerBc"}, Lifetime::Timeframe); + DataProcessorSpec dataProcessorSpec{ + "FT0EventsPerBcProcessor", + inputs, + outputs, + AlgorithmSpec(adaptFromTask(ccdbRequest)), + Options{ + {"slot-len-sec", VariantType::UInt32, 3600u, {"Duration of each slot in seconds"}}, + {"one-object-per-run", VariantType::Bool, false, {"If set, workflow creates only one calibration object per run"}}, + {"min-entries-number", VariantType::UInt32, 5000u, {"Minimum number of entries required for a slot to be valid"}}, + {"min-ampl-side-a", VariantType::Int, 0, {"Amplitude threshold for Side A events"}}, + {"min-ampl-side-c", VariantType::Int, 0, {"Amplitude threshold for Side C events"}}}}; + + WorkflowSpec workflow; + workflow.emplace_back(dataProcessorSpec); + return workflow; +} \ No newline at end of file diff --git a/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h new file mode 100644 index 0000000000000..c587ab58fcd90 --- /dev/null +++ b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h @@ -0,0 +1,124 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_CALIBRATION_FT0_EVENTS_PER_BC_CALIBRATOR_H +#define O2_CALIBRATION_FT0_EVENTS_PER_BC_CALIBRATOR_H + +#include "Framework/runDataProcessing.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" +#include +#include "Framework/DeviceSpec.h" +#include "Framework/WorkflowSpec.h" +#include "Framework/Task.h" +#include "DetectorsCalibration/Utils.h" +#include "DetectorsBase/GRPGeomHelper.h" + +#include "DataFormatsFT0/Digit.h" +#include "FT0Calibration/EventsPerBcCalibrator.h" + +namespace o2::calibration +{ +class FT0EventsPerBcProcessor final : public o2::framework::Task +{ + public: + FT0EventsPerBcProcessor(std::shared_ptr request) : mCCDBRequest(request) {} + + void init(o2::framework::InitContext& ic) final + { + o2::base::GRPGeomHelper::instance().setRequest(mCCDBRequest); + if (ic.options().hasOption("slot-len-sec")) { + mSlotLenSec = ic.options().get("slot-len-sec"); + } + if (ic.options().hasOption("one-object-per-run")) { + mOneObjectPerRun = ic.options().get("one-object-per-run"); + } + if (ic.options().hasOption("min-entries-number")) { + mMinNumberOfEntries = ic.options().get("min-entries-number"); + } + if (ic.options().hasOption("min-ampl-side-a")) { + mMinAmplitudeSideA = ic.options().get("min-ampl-side-a"); + } + if (ic.options().hasOption("min-ampl-side-c")) { + mMinAmplitudeSideC = ic.options().get("min-ampl-side-c"); + } + + mCalibrator = std::make_unique(mMinNumberOfEntries, mMinAmplitudeSideA, mMinAmplitudeSideC); + + if (mOneObjectPerRun) { + LOG(info) << "Only one object will be created at the end of run"; + mCalibrator->setUpdateAtTheEndOfRunOnly(); + } + if (mOneObjectPerRun == false) { + LOG(info) << "Defined slot interval to " << mSlotLenSec << " seconds"; + mCalibrator->setSlotLengthInSeconds(mSlotLenSec); + } + } + + void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) + { + o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); + } + + void run(o2::framework::ProcessingContext& pc) final + { + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + auto digits = pc.inputs().get>("digits"); + o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mCalibrator->getCurrentTFInfo()); + if (digits.size() == 0) { + return; + } + mCalibrator->process(digits); + if (mOneObjectPerRun == false) { + sendOutput(pc.outputs()); + } + } + + void endOfStream(o2::framework::EndOfStreamContext& ec) final + { + LOG(info) << "Received end-of-stream, checking for slot to finalize..."; + mCalibrator->checkSlotsToFinalize(); + sendOutput(ec.outputs()); + mCalibrator->initOutput(); + } + + void sendOutput(o2::framework::DataAllocator& output) + { + using o2::framework::Output; + const auto& tvxHists = mCalibrator->getTvxPerBc(); + auto& infos = mCalibrator->getTvxPerBcCcdbInfo(); + for (unsigned int idx = 0; idx < tvxHists.size(); idx++) { + auto& info = infos[idx]; + const auto& payload = tvxHists[idx]; + + auto image = o2::ccdb::CcdbApi::createObjectImage(&payload, info.get()); + LOG(info) << "Sending object " << info->getPath() << "/" << info->getFileName() << " of size " << image->size() + << " bytes, valid for " << info->getStartValidityTimestamp() << " : " << info->getEndValidityTimestamp(); + output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBPayload, "EventsPerBc", idx}, *image.get()); + output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBWrapper, "EventsPerBc", idx}, *info.get()); + } + + if (tvxHists.size()) { + mCalibrator->initOutput(); + } + } + + private: + std::shared_ptr mCCDBRequest; + std::unique_ptr mCalibrator; + bool mOneObjectPerRun; + uint32_t mSlotLenSec; + uint32_t mMinNumberOfEntries; + int32_t mMinAmplitudeSideA; + int32_t mMinAmplitudeSideC; +}; +} // namespace o2::calibration +#endif \ No newline at end of file diff --git a/Detectors/FIT/FT0/macros/CMakeLists.txt b/Detectors/FIT/FT0/macros/CMakeLists.txt index c4ed27d2513ba..17491ca4962c1 100644 --- a/Detectors/FIT/FT0/macros/CMakeLists.txt +++ b/Detectors/FIT/FT0/macros/CMakeLists.txt @@ -1,14 +1,21 @@ -# Copyright CERN and copyright holders of ALICE O2. This software is distributed -# under the terms of the GNU General Public License v3 (GPL Version 3), copied -# verbatim in the file "COPYING". +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. # -# See http://alice-o2.web.cern.ch/license for full licensing information. +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". # # In applying this license CERN does not waive the privileges and immunities -# granted to it by virtue of its status as an Intergovernmental Organization or -# submit itself to any jurisdiction. +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. o2_add_test_root_macro(FT0Misaligner.C PUBLIC_LINK_LIBRARIES O2::CCDB O2::FT0Simulation LABELS ft0) + +o2_add_test_root_macro(FT0readEventsPerBc.C + PUBLIC_LINK_LIBRARIES + O2::CCDB + O2::DataFormatsFT0 + LABELS ft0) diff --git a/Detectors/FIT/FT0/macros/FT0readEventsPerBc.C b/Detectors/FIT/FT0/macros/FT0readEventsPerBc.C new file mode 100644 index 0000000000000..c6afc86389b9b --- /dev/null +++ b/Detectors/FIT/FT0/macros/FT0readEventsPerBc.C @@ -0,0 +1,52 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#endif + +#include "CCDB/CcdbApi.h" +#include "CCDB/CCDBTimeStampUtils.h" +#include "TH1F.h" +#include "DataFormatsFT0/EventsPerBc.h" +#include "Framework/Logger.h" +#include "CommonConstants/LHCConstants.h" + +std::unique_ptr hist; +std::unique_ptr canvas; + +void FT0readEventsPerBc(std::string ccdbUrl, long timestamp) +{ + o2::ccdb::CcdbApi ccdbApi; + ccdbApi.init(ccdbUrl); + const std::string ccdbPath = "FT0/Calib/EventsPerBc"; + std::map metadata; + + if (timestamp < 0) { + timestamp = o2::ccdb::getCurrentTimestamp(); + } + + std::unique_ptr events(ccdbApi.retrieveFromTFileAny(ccdbPath, metadata, timestamp)); + + if (!events) { + LOGP(fatal, "EventsPerBc object not found in {}/{} for timestamp {}.", ccdbUrl, ccdbPath, timestamp); + return; + } + + hist = std::make_unique("eventsPerBcHist", "Events per BC", o2::constants::lhc::LHCMaxBunches, 0, o2::constants::lhc::LHCMaxBunches - 1); + for (int idx = 0; idx < o2::constants::lhc::LHCMaxBunches; idx++) { + hist->Fill(idx, events->histogram[idx]); + } + canvas = std::make_unique(); + hist->Draw(); + canvas->Draw(); +} \ No newline at end of file From 8d3541adfc2ff3fb78615f86cc2a234eb8bd60a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Thu, 5 Feb 2026 23:27:12 +0100 Subject: [PATCH 085/133] Vertexing: Delete unused files --- .../DetectorsVertexing/FwdDCAFitterN.h | 1297 ----------------- Detectors/Vertexing/src/FwdDCAFitterN.cxx | 33 - 2 files changed, 1330 deletions(-) delete mode 100644 Detectors/Vertexing/include/DetectorsVertexing/FwdDCAFitterN.h delete mode 100644 Detectors/Vertexing/src/FwdDCAFitterN.cxx diff --git a/Detectors/Vertexing/include/DetectorsVertexing/FwdDCAFitterN.h b/Detectors/Vertexing/include/DetectorsVertexing/FwdDCAFitterN.h deleted file mode 100644 index d5bc6631575af..0000000000000 --- a/Detectors/Vertexing/include/DetectorsVertexing/FwdDCAFitterN.h +++ /dev/null @@ -1,1297 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file FwdDCAFitterN.h -/// \brief Defintions for N-prongs secondary vertex fit -/// \author ruben.shahoyan@cern.ch, adapted from central barrel to fwd rapidities by Rita Sadek, rita.sadek@cern.ch -/// For the formulae derivation see /afs/cern.ch/user/s/shahoian/public/O2/DCAFitter/DCAFitterN.pdf - -#ifndef _ALICEO2_DCA_FWDFITTERN_ -#define _ALICEO2_DCA_FWDFITTERN_ -#include -#include "MathUtils/Cartesian.h" -#include "ReconstructionDataFormats/TrackFwd.h" -#include "ReconstructionDataFormats/Track.h" -#include "ReconstructionDataFormats/HelixHelper.h" -#include -#include "DetectorsBase/Propagator.h" -#include "DetectorsBase/GeometryManager.h" - -namespace o2 -{ -namespace vertexing -{ - -///__________________________________________________________________________________ -///< Fwd Inverse cov matrix (augmented by a dummy Z error) of the point defined by the track -struct FwdTrackCovI { - float sxx, syy, sxy, szz; - - FwdTrackCovI(const o2::track::TrackParCovFwd& trc, float zerrFactor = 1.) { set(trc, zerrFactor); } - FwdTrackCovI() = default; - void set(const o2::track::TrackParCovFwd& trc, float zerrFactor = 1) - { - float cxx = trc.getSigma2X(), cyy = trc.getSigma2Y(), cxy = trc.getSigmaXY(), czz = cyy * zerrFactor; - float detXY = cxx * cyy - cxy * cxy; - if (detXY > 0.) { - auto detXYI = 1. / detXY; - sxx = cyy * detXYI; - syy = cxx * detXYI; - sxy = -cxy * detXYI; - szz = 1. / czz; - } else { - throw std::runtime_error("invalid track covariance"); - } - } -}; - -///__________________________________________________________________________ -///< Fwd derivative (up to 2) of the TrackParam position over its running param Z -struct FwdTrackDeriv { - float dxdz, dydz, d2xdz2, d2ydz2; - FwdTrackDeriv() = default; - FwdTrackDeriv(const o2::track::TrackParFwd& trc, float bz) { set(trc, bz); } - void set(const o2::track::TrackParFwd& trc, float bz) - { - float snp = trc.getSnp(), csp = std::sqrt((1. - snp) * (1. + snp)), cspI = 1. / csp, crv2c = trc.getCurvature(bz), tgl = trc.getTanl(), tglI = 1. / tgl; - if (crv2c == 0.) { - crv2c = (trc.getCharge()) * 0.3 * bz * (-1e-3); - } - - dxdz = csp * tglI; - dydz = snp * tglI; - d2xdz2 = crv2c * snp * tglI * tglI; - d2ydz2 = -crv2c * csp * tglI * tglI; - } -}; - -template -class FwdDCAFitterN -{ - static constexpr double NMin = 2; - static constexpr double NMax = 4; - static constexpr double NInv = 1. / N; - static constexpr int MAXHYP = 2; - static constexpr float ZerrFactor = 5.; // factor for conversion of track covXX to dummy covZZ - using Track = o2::track::TrackParCovFwd; - using TrackAuxPar = o2::track::TrackAuxPar; - using CrossInfo = o2::track::CrossInfo; - using Vec3D = ROOT::Math::SVector; - using VecND = ROOT::Math::SVector; - using MatSym3D = ROOT::Math::SMatrix>; - using MatStd3D = ROOT::Math::SMatrix>; - using MatSymND = ROOT::Math::SMatrix>; - using MatStdND = ROOT::Math::SMatrix>; - using SMatrix55 = ROOT::Math::SMatrix>; - using TrackCoefVtx = MatStd3D; - using ArrTrack = std::array; // container for prongs (tracks) at single vertex cand. - using ArrTrackCovI = std::array; // container for inv.cov.matrices at single vertex cand. - using ArrTrCoef = std::array; // container of TrackCoefVtx coefficients at single vertex cand. - using ArrTrDer = std::array; // container of Track 1st and 2nd derivative over their Z param - using ArrTrPos = std::array; // container of Track positions - - public: - static constexpr int getNProngs() { return N; } - - FwdDCAFitterN() = default; - FwdDCAFitterN(float bz, bool useAbsDCA, bool prop2DCA) : mBz(bz), mUseAbsDCA(useAbsDCA), mPropagateToPCA(prop2DCA) - { - static_assert(N >= NMin && N <= NMax, "N prongs outside of allowed range"); - } - - //========================================================================= - ///< return PCA candidate, by default best on is provided (no check for the index validity) - const Vec3D& getPCACandidate(int cand = 0) const { return mPCA[mOrder[cand]]; } - const auto getPCACandidatePos(int cand = 0) const - { - const auto& vd = mPCA[mOrder[cand]]; - return std::array{float(vd[0]), float(vd[1]), float(vd[2])}; - } - - ///< return Chi2 at PCA candidate (no check for its validity) - float getChi2AtPCACandidate(int cand = 0) const { return mChi2[mOrder[cand]]; } - - ///< prepare copies of tracks at the V0 candidate (no check for the candidate validity) - /// must be called before getTrack(i,cand) query - bool FwdpropagateTracksToVertex(int cand = 0); - - ///< check if propagation of tracks to candidate vertex was done - bool isPropagateTracksToVertexDone(int cand = 0) const { return mTrPropDone[mOrder[cand]]; } - - ///< track param propagated to V0 candidate (no check for the candidate validity) - /// propagateTracksToVertex must be called in advance - Track& getTrack(int i, int cand = 0) - { - if (!mTrPropDone[mOrder[cand]]) { - throw std::runtime_error("propagateTracksToVertex was not called yet"); - } - return mCandTr[mOrder[cand]][i]; - } - - ///< calculate on the fly track param (no cov mat) at candidate - o2::track::TrackParFwd FwdgetTrackParamAtPCA(int i, int cand = 0) const; - - MatSym3D calcPCACovMatrix(int cand = 0) const; - - std::array calcPCACovMatrixFlat(int cand = 0) const - { - auto m = calcPCACovMatrix(cand); - return {float(m(0, 0)), float(m(1, 0)), float(m(1, 1)), float(m(2, 0)), float(m(2, 1)), float(m(2, 2))}; - } - - const Track* getOrigTrackPtr(int i) const { return mOrigTrPtr[i]; } - - ///< return number of iterations during minimization (no check for its validity) - int getNIterations(int cand = 0) const { return mNIters[mOrder[cand]]; } - void setPropagateToPCA(bool v = true) { mPropagateToPCA = v; } - void setMaxIter(int n = 60) { mMaxIter = n > 2 ? n : 2; } - void setMaxR(float r = 200.) { mMaxR2 = r * r; } - void setMaxDXIni(float d = 4.) { mMaxDXIni = d; } - void setMaxChi2(float chi2 = 999.) { mMaxChi2 = chi2; } - void setBz(float bz) { mBz = std::abs(bz) > o2::constants::math::Almost0 ? bz : 0.f; } - void setMinParamChange(float x = 1e-3) { mMinParamChange = x > 1e-4 ? x : 1.e-4; } - void setMinRelChi2Change(float r = 0.9) { mMinRelChi2Change = r > 0.1 ? r : 999.; } - void setUseAbsDCA(bool v) { mUseAbsDCA = v; } - void setMatLUT(const o2::base::MatLayerCylSet* m) - { - mMatLUT = m; - mUseMatBudget = true; - } - void setTGeoMat(bool v = true) { mTGeoFallBackAllowed = v; } - void setMaxDistance2ToMerge(float v) { mMaxDist2ToMergeSeeds = v; } - - int getNCandidates() const { return mCurHyp; } - int getMaxIter() const { return mMaxIter; } - float getMaxR() const { return std::sqrt(mMaxR2); } - float getMaxDXIni() const { return mMaxDXIni; } - float getMaxChi2() const { return mMaxChi2; } - float getMinParamChange() const { return mMinParamChange; } - float getBz() const { return mBz; } - double getK(double b) const { return std::abs(o2::constants::math::B2C * b); } - double getHz(double b) const { return std::copysign(1, b); } - - float getMaxDistance2ToMerge() const { return mMaxDist2ToMergeSeeds; } - bool getUseAbsDCA() const { return mUseAbsDCA; } - bool getPropagateToPCA() const { return mPropagateToPCA; } - - template - int process(const Tr&... args); - void print() const; - - protected: - bool FwdcalcPCACoefs(); - bool FwdcalcInverseWeight(); - void FwdcalcResidDerivatives(); - void FwdcalcResidDerivativesNoErr(); - void FwdcalcChi2Derivatives(); - void FwdcalcChi2DerivativesNoErr(); - void FwdcalcPCA(); - void FwdcalcPCANoErr(); - void FwdcalcTrackResiduals(); - void calcTrackDerivatives(); - float findZatXY(int cand = 0); - void findZatXY_mid(int cand = 0); - void findZatXY_lineApprox(int cand = 0); - void findZatXY_quad(int cand = 0); - void findZatXY_linear(int cand = 0); - double FwdcalcChi2() const; - double FwdcalcChi2NoErr() const; - bool FwdcorrectTracks(const VecND& corrZ); - bool minimizeChi2(); - bool minimizeChi2NoErr(); - bool roughDXCut() const; - bool closerToAlternative() const; - static double getAbsMax(const VecND& v); - bool propagateToVtx(o2::track::TrackParCovFwd& t, const std::array& p, const std::array& cov) const; - - ///< track param positions at V0 candidate (no check for the candidate validity) - const Vec3D& getTrackPos(int i, int cand = 0) const { return mTrPos[mOrder[cand]][i]; } - - ///< track Z-param at V0 candidate (no check for the candidate validity) - float getTrackZ(int i, int cand = 0) const { return getTrackPos(i, cand)[2]; } - - MatStd3D getTrackRotMatrix(int i) const // generate 3D matrix for track rotation to global frame - // no rotation for fwd: mat=I - { - MatStd3D mat; - mat(0, 0) = 1; - mat(1, 1) = 1; - mat(2, 2) = 1; - return mat; - } - - MatSym3D getTrackCovMatrix(int i, int cand = 0) const // generate covariance matrix of track position, adding fake Z error - { - const auto& trc = mCandTr[mOrder[cand]][i]; - MatSym3D mat; - mat(0, 0) = trc.getSigma2X(); - mat(1, 1) = trc.getSigma2Y(); - mat(1, 0) = trc.getSigmaXY(); - mat(2, 2) = trc.getSigma2Y() * ZerrFactor; - return mat; - } - - void assign(int) {} - template - void assign(int i, const T& t, const Tr&... args) - { - static_assert(std::is_convertible(), "Wrong track type"); - mOrigTrPtr[i] = &t; - assign(i + 1, args...); - } - - void clear() - { - mCurHyp = 0; - mAllowAltPreference = true; - } - - static void setTrackPos(Vec3D& pnt, const Track& tr) - { - pnt[0] = tr.getX(); - pnt[1] = tr.getY(); - pnt[2] = tr.getZ(); - } - - private: - // vectors of 1st derivatives of track local residuals over Z parameters - std::array, N> mDResidDz; - // vectors of 1nd derivatives of track local residuals over Z parameters - std::array, N> mD2ResidDz2; - VecND mDChi2Dz; // 1st derivatives of chi2 over tracks Z params - MatSymND mD2Chi2Dz2; // 2nd derivatives of chi2 over tracks Z params (symmetric matrix) - - std::array mOrigTrPtr; - std::array mTrAux; // Aux track info for each track at each cand. vertex - CrossInfo mCrossings; // info on track crossing - - std::array mTrcEInv; // errors for each track at each cand. vertex - std::array mCandTr; // tracks at each cond. vertex (Note: Errors are at seed XY point) - std::array mTrCFVT; // TrackCoefVtx for each track at each cand. vertex - std::array mTrDer; // Track derivativse - std::array mTrPos; // Track positions - std::array mTrRes; // Track residuals - std::array mPCA; // PCA for each vertex candidate - std::array mChi2 = {0}; // Chi2 at PCA candidate - std::array mNIters; // number of iterations for each seed - std::array mTrPropDone; // Flag that the tracks are fully propagated to PCA - MatSym3D mWeightInv; // inverse weight of single track, [sum{M^T E M}]^-1 in EQ.T - std::array mOrder{0}; - int mCurHyp = 0; - int mCrossIDCur = 0; - int mCrossIDAlt = -1; - bool mAllowAltPreference = true; // if the fit converges to alternative PCA seed, abandon the current one - bool mUseAbsDCA = false; // use abs. distance minimization rather than chi2 - bool mPropagateToPCA = true; // create tracks version propagated to PCA - bool mUseMatBudget = false; // include MCS effects in track propagation - bool mTGeoFallBackAllowed = true; // use TGeo for precise estimate of mat. budget - int mMaxIter = 60; // max number of iterations - float mBz = 0; // bz field, to be set by user - float mMaxR2 = 200. * 200.; // reject PCA's above this radius - float mMaxDXIni = 4.; // reject (if>0) PCA candidate if tracks DZ exceeds threshold - float mMinParamChange = 1e-5; // stop iterations if largest change of any X is smaller than this - float mMinRelChi2Change = 0.98; // stop iterations is chi2/chi2old > this - float mMaxChi2 = 100; // abs cut on chi2 or abs distance - float mMaxDist2ToMergeSeeds = 1.; // merge 2 seeds to their average if their distance^2 is below the threshold - const o2::base::MatLayerCylSet* mMatLUT = nullptr; // use to compute material budget to include MCS effects - - ClassDefNV(FwdDCAFitterN, 1); -}; - -///_________________________________________________________________________ -template -template -int FwdDCAFitterN::process(const Tr&... args) -{ - - static_assert(sizeof...(args) == N, "incorrect number of input tracks"); - assign(0, args...); - clear(); - - for (int i = 0; i < N; i++) { - mTrAux[i].set(*mOrigTrPtr[i], mBz); - } - - if (!mCrossings.set(mTrAux[0], *mOrigTrPtr[0], mTrAux[1], *mOrigTrPtr[1])) { // even for N>2 it should be enough to test just 1 loop - return 0; // no crossing - } - - if (mCrossings.nDCA == MAXHYP) { // if there are 2 candidates - auto dst2 = (mCrossings.xDCA[0] - mCrossings.xDCA[1]) * (mCrossings.xDCA[0] - mCrossings.xDCA[1]) + - (mCrossings.yDCA[0] - mCrossings.yDCA[1]) * (mCrossings.yDCA[0] - mCrossings.yDCA[1]); - - if (dst2 < mMaxDist2ToMergeSeeds) { - mCrossings.nDCA = 1; - mCrossings.xDCA[0] = 0.5 * (mCrossings.xDCA[0] + mCrossings.xDCA[1]); - mCrossings.yDCA[0] = 0.5 * (mCrossings.yDCA[0] + mCrossings.yDCA[1]); - } - } - - // check all crossings - for (int ic = 0; ic < mCrossings.nDCA; ic++) { - // check if radius is acceptable - if (mCrossings.xDCA[ic] * mCrossings.xDCA[ic] + mCrossings.yDCA[ic] * mCrossings.yDCA[ic] > mMaxR2) { - continue; - } - - mCrossIDCur = ic; - mCrossIDAlt = (mCrossings.nDCA == 2 && mAllowAltPreference) ? 1 - ic : -1; // works for max 2 crossings - mNIters[mCurHyp] = 0; - mTrPropDone[mCurHyp] = false; - mChi2[mCurHyp] = -1.; - - findZatXY_mid(mCurHyp); - - if (mUseAbsDCA ? minimizeChi2NoErr() : minimizeChi2()) { - mOrder[mCurHyp] = mCurHyp; - if (mPropagateToPCA && !FwdpropagateTracksToVertex(mCurHyp)) { - continue; - } - mCurHyp++; - } - } - - for (int i = mCurHyp; i--;) { // order in quality - for (int j = i; j--;) { - if (mChi2[mOrder[i]] < mChi2[mOrder[j]]) { - std::swap(mOrder[i], mOrder[j]); - } - } - } - - return mCurHyp; -} - -//__________________________________________________________________________ -template -bool FwdDCAFitterN::FwdcalcPCACoefs() -{ - //< calculate Ti matrices for global vertex decomposition to V = sum_{0 -bool FwdDCAFitterN::FwdcalcInverseWeight() -{ - //< calculate [sum_{0 -void FwdDCAFitterN::FwdcalcResidDerivatives() -{ - //< calculate matrix of derivatives for weighted chi2: residual i vs parameter Z of track j - MatStd3D matMT; - for (int i = N; i--;) { // residual being differentiated - // const auto& taux = mTrAux[i]; - for (int j = N; j--;) { // track over which we differentiate - const auto& matT = mTrCFVT[mCurHyp][j]; // coefficient matrix for track J - const auto& trDz = mTrDer[mCurHyp][j]; // track point derivs over track Z param - auto& dr1 = mDResidDz[i][j]; - auto& dr2 = mD2ResidDz2[i][j]; - // calculate M_i^transverse * T_j , M_i^transverse=I -> MT=T - matMT[0][0] = matT[0][0]; - matMT[0][1] = matT[0][1]; - matMT[0][2] = matT[0][2]; - matMT[1][0] = matT[1][0]; - matMT[1][1] = matT[1][1]; - matMT[1][2] = matT[1][2]; - matMT[2][0] = matT[2][0]; - matMT[2][1] = matT[2][1]; - matMT[2][2] = matT[2][2]; - - // calculate DResid_i/Dz_j = (delta_ij - M_i^tr * T_j) * DTrack_k/Dz_k - dr1[0] = -(matMT[0][0] * trDz.dxdz + matMT[0][1] * trDz.dydz + matMT[0][2]); - dr1[1] = -(matMT[1][0] * trDz.dxdz + matMT[1][1] * trDz.dydz + matMT[1][2]); - dr1[2] = -(matMT[2][0] * trDz.dxdz + matMT[2][1] * trDz.dydz + matMT[2][2]); - - // calculate D2Resid_I/(Dz_J Dz_K) = (delta_ijk - M_i^tr * T_j * delta_jk) * D2Track_k/dz_k^2 - dr2[0] = -(matMT[0][1] * trDz.d2ydz2 + matMT[0][0] * trDz.d2xdz2); - dr2[1] = -(matMT[1][1] * trDz.d2ydz2 + matMT[1][0] * trDz.d2xdz2); - dr2[2] = -(matMT[2][1] * trDz.d2ydz2 + matMT[2][0] * trDz.d2xdz2); - - if (i == j) { - dr1[0] += trDz.dxdz; - dr1[1] += trDz.dydz; - dr1[2] += 1.; - - dr2[0] += trDz.d2xdz2; - dr2[1] += trDz.d2ydz2; - } - } // track over which we differentiate - } // residual being differentiated -} - -//__________________________________________________________________________ -template -void FwdDCAFitterN::FwdcalcResidDerivativesNoErr() -{ - //< calculate matrix of derivatives for absolute distance chi2: residual i vs parameter Z of track j - constexpr double NInv1 = 1. - NInv; // profit from Rii = I/Ninv - for (int i = N; i--;) { // residual being differentiated - const auto& trDzi = mTrDer[mCurHyp][i]; // track point derivs over track Z param - auto& dr1ii = mDResidDz[i][i]; - auto& dr2ii = mD2ResidDz2[i][i]; - - dr1ii[0] = NInv1 * trDzi.dxdz; - dr1ii[1] = NInv1 * trDzi.dydz; - dr1ii[2] = NInv1; - - dr2ii[0] = NInv1 * trDzi.d2xdz2; - dr2ii[1] = NInv1 * trDzi.d2ydz2; - dr2ii[2] = 0; - - for (int j = i; j--;) { // track over which we differentiate - auto& dr1ij = mDResidDz[i][j]; - auto& dr1ji = mDResidDz[j][i]; - const auto& trDzj = mTrDer[mCurHyp][j]; // track point derivs over track Z param - - // calculate DResid_i/Dz_j = (delta_ij - R_ij) * DTrack_j/Dz_j for j -void FwdDCAFitterN::FwdcalcChi2Derivatives() -{ - //< calculate 1st and 2nd derivatives of wighted DCA (chi2) over track parameters Z - std::array, N> covIDrDz; // tempory vectors of covI_j * dres_j/dz_i - - // chi2 1st derivative - for (int i = N; i--;) { - auto& dchi1 = mDChi2Dz[i]; // DChi2/Dz_i = sum_j { res_j * covI_j * Dres_j/Dz_i } - dchi1 = 0; - for (int j = N; j--;) { - const auto& res = mTrRes[mCurHyp][j]; // vector of residuals of track j - const auto& covI = mTrcEInv[mCurHyp][j]; // inverse cov matrix of track j - const auto& dr1 = mDResidDz[j][i]; // vector of j-th residuals 1st derivative over Z param of track i - auto& cidr = covIDrDz[i][j]; // vector covI_j * dres_j/dz_i, save for 2nd derivative calculation - cidr[0] = covI.sxx * dr1[0] + covI.sxy * dr1[1]; - cidr[1] = covI.sxy * dr1[0] + covI.syy * dr1[1]; - cidr[2] = covI.szz * dr1[2]; - - dchi1 += ROOT::Math::Dot(res, cidr); - } - } - - // chi2 2nd derivative - for (int i = N; i--;) { - for (int j = i + 1; j--;) { // symmetric matrix - auto& dchi2 = mD2Chi2Dz2[i][j]; // D2Chi2/Dz_i/Dz_j = sum_k { Dres_k/Dz_j * covI_k * Dres_k/Dz_i + res_k * covI_k * D2res_k/Dz_i/Dz_j } - dchi2 = 0; - for (int k = N; k--;) { - const auto& dr1j = mDResidDz[k][j]; // vector of k-th residuals 1st derivative over Z param of track j - const auto& cidrkj = covIDrDz[i][k]; // vector covI_k * dres_k/dz_i - dchi2 += ROOT::Math::Dot(dr1j, cidrkj); - if (k == j) { - const auto& res = mTrRes[mCurHyp][k]; // vector of residuals of track k - const auto& covI = mTrcEInv[mCurHyp][k]; // inverse cov matrix of track k - const auto& dr2ij = mD2ResidDz2[k][j]; // vector of k-th residuals 2nd derivative over Z params of track j - dchi2 += res[0] * (covI.sxx * dr2ij[0] + covI.sxy * dr2ij[1]) + res[1] * (covI.sxy * dr2ij[0] + covI.syy * dr2ij[1]) + res[2] * covI.szz * dr2ij[2]; - } - } - } - } -} - -//__________________________________________________________________________ -template -void FwdDCAFitterN::FwdcalcChi2DerivativesNoErr() -{ - //< calculate 1st and 2nd derivatives of abs DCA (chi2) over track parameters Z - for (int i = N; i--;) { - auto& dchi1 = mDChi2Dz[i]; // DChi2/Dz_i = sum_j { res_j * Dres_j/Dz_i } - dchi1 = 0; // chi2 1st derivative - for (int j = N; j--;) { - const auto& res = mTrRes[mCurHyp][j]; // vector of residuals of track j - const auto& dr1 = mDResidDz[j][i]; // vector of j-th residuals 1st derivative over Z param of track i - dchi1 += ROOT::Math::Dot(res, dr1); - if (i >= j) { // symmetrix matrix - // chi2 2nd derivative - auto& dchi2 = mD2Chi2Dz2[i][j]; // D2Chi2/Dz_i/Dz_j = sum_k { Dres_k/Dz_j * covI_k * Dres_k/Dz_i + res_k * covI_k * D2res_k/Dz_i/Dz_j } - dchi2 = ROOT::Math::Dot(mTrRes[mCurHyp][i], mD2ResidDz2[i][j]); - for (int k = N; k--;) { - dchi2 += ROOT::Math::Dot(mDResidDz[k][i], mDResidDz[k][j]); - } - } - } - } -} - -//___________________________________________________________________ -template -void FwdDCAFitterN::FwdcalcPCA() -{ - // calculate point of closest approach for N prongs - // calculating V = sum (Ti*Pi) - mPCA[mCurHyp] = mTrCFVT[mCurHyp][N - 1] * mTrPos[mCurHyp][N - 1]; - for (int i = N - 1; i--;) { - mPCA[mCurHyp] += mTrCFVT[mCurHyp][i] * mTrPos[mCurHyp][i]; - } -} - -//___________________________________________________________________ -template -void FwdDCAFitterN::FwdcalcPCANoErr() -{ - // calculate point of closest approach for N prongs w/o errors - auto& pca = mPCA[mCurHyp]; - - pca[0] = mTrPos[mCurHyp][N - 1][0]; - pca[1] = mTrPos[mCurHyp][N - 1][1]; - pca[2] = mTrPos[mCurHyp][N - 1][2]; - - for (int i = N - 1; i--;) { - pca[0] += mTrPos[mCurHyp][i][0]; - pca[1] += mTrPos[mCurHyp][i][1]; - pca[2] += mTrPos[mCurHyp][i][2]; - } - pca[0] *= NInv; - pca[1] *= NInv; - pca[2] *= NInv; -} - -//___________________________________________________________________ -template -ROOT::Math::SMatrix> FwdDCAFitterN::calcPCACovMatrix(int cand) const -{ - // calculate covariance matrix for the point of closest approach - MatSym3D covm; - for (int i = N; i--;) { - covm += ROOT::Math::Similarity(mUseAbsDCA ? getTrackRotMatrix(i) : mTrCFVT[mOrder[cand]][i], getTrackCovMatrix(i, cand)); - } - return covm; -} - -//___________________________________________________________________ -template -void FwdDCAFitterN::FwdcalcTrackResiduals() -{ - // calculate residuals, res = Pi - V - Vec3D vtxLoc; - for (int i = N; i--;) { - mTrRes[mCurHyp][i] = mTrPos[mCurHyp][i]; - vtxLoc = mPCA[mCurHyp]; - mTrRes[mCurHyp][i] -= vtxLoc; - } -} - -//___________________________________________________________________ -template -inline void FwdDCAFitterN::calcTrackDerivatives() -{ - // calculate track derivatives over Z param - for (int i = N; i--;) { - mTrDer[mCurHyp][i].set(mCandTr[mCurHyp][i], mBz); - } -} - -//___________________________________________________________________ -template -inline double FwdDCAFitterN::FwdcalcChi2() const -{ - // calculate current chi2 - double chi2 = 0; - for (int i = N; i--;) { - const auto& res = mTrRes[mCurHyp][i]; - const auto& covI = mTrcEInv[mCurHyp][i]; - chi2 += res[0] * res[0] * covI.sxx + res[1] * res[1] * covI.syy + res[2] * res[2] * covI.szz + 2. * res[0] * res[1] * covI.sxy; - } - return chi2; -} - -//___________________________________________________________________ -template -inline double FwdDCAFitterN::FwdcalcChi2NoErr() const -{ - // calculate current chi2 of abs. distance minimization - double chi2 = 0; - for (int i = N; i--;) { - const auto& res = mTrRes[mCurHyp][i]; - chi2 += res[0] * res[0] + res[1] * res[1] + res[2] * res[2]; - } - return chi2; -} - -//___________________________________________________________________ -template -bool FwdDCAFitterN::FwdcorrectTracks(const VecND& corrZ) -{ - // propagate tracks to updated Z - for (int i = N; i--;) { - const auto& trDer = mTrDer[mCurHyp][i]; - auto dz2h = 0.5 * corrZ[i] * corrZ[i]; - mTrPos[mCurHyp][i][0] -= trDer.dxdz * corrZ[i] - dz2h * trDer.d2xdz2; - mTrPos[mCurHyp][i][1] -= trDer.dydz * corrZ[i] - dz2h * trDer.d2ydz2; - mTrPos[mCurHyp][i][2] -= corrZ[i]; - } - - return true; -} - -//___________________________________________________________________ -template -bool FwdDCAFitterN::FwdpropagateTracksToVertex(int icand) -{ - // propagate on z axis to vertex - int ord = mOrder[icand]; - if (mTrPropDone[ord]) { - return true; - } - const Vec3D& pca = mPCA[ord]; - std::array covMatrixPCA = calcPCACovMatrixFlat(ord); - std::array cov = {covMatrixPCA[0], covMatrixPCA[2]}; - for (int i = N; i--;) { - mCandTr[ord][i] = *mOrigTrPtr[i]; // fetch the track again, as mCandTr might have been propagated w/o errors - auto& trc = mCandTr[ord][i]; - const std::array p = {(float)pca[0], (float)pca[1], (float)pca[2]}; - if (!propagateToVtx(trc, p, cov)) { - return false; - } - } - - mTrPropDone[ord] = true; - return true; -} - -//___________________________________________________________________ -template -float FwdDCAFitterN::findZatXY(int mCurHyp) // Between 2 tracks -{ - - double step = 0.001; // initial step - double startPoint = 20.; // first MFT disk - - double z[2] = {startPoint, startPoint}; - double newX[2], newY[2]; - - double X = mPCA[mCurHyp][0]; // X seed - double Y = mPCA[mCurHyp][1]; // Y seed - - mCandTr[mCurHyp][0] = *mOrigTrPtr[0]; - mCandTr[mCurHyp][1] = *mOrigTrPtr[1]; - - double dstXY[2][3] = {{999., 999., 999.}, {999., 999., 999.}}; - - double Z[2]; - double finalZ[2]; - - double newDstXY; - - for (int i = 0; i < 2; i++) { - - while (z[i] > -10) { - - mCandTr[mCurHyp][i].propagateParamToZquadratic(z[i], mBz); - newX[i] = mCandTr[mCurHyp][i].getX(); - newY[i] = mCandTr[mCurHyp][i].getY(); - - newDstXY = std::sqrt((newX[i] - X) * (newX[i] - X) + - (newY[i] - Y) * (newY[i] - Y)); - - // Update points - dstXY[i][0] = dstXY[i][1]; - dstXY[i][1] = dstXY[i][2]; - dstXY[i][2] = newDstXY; - - if (dstXY[i][2] > dstXY[i][1] && dstXY[i][1] < dstXY[i][0]) { - finalZ[i] = z[i] + step; - break; - } - - z[i] -= step; - } - } - - float rez = 0.5 * (finalZ[0] + finalZ[1]); - return rez; -} - -//___________________________________________________________________ -template -void FwdDCAFitterN::findZatXY_mid(int mCurHyp) -{ - // look into dXY of T0 - T1 between 2 points(0,40cm); the one with the highest dXY is moved to mid - - double startPoint = -40.; - double endPoint = 50.; - double midPoint = 0.5 * (startPoint + endPoint); - - double z[2][2] = {{startPoint, endPoint}, {startPoint, endPoint}}; // z for tracks 0/1 on starting poing and endpoint - - double DeltaZ = std::abs(endPoint - startPoint); - - double newX[2][2]; - double newY[2][2]; - - double epsilon = 0.0001; - - double X = mPCA[mCurHyp][0]; // X seed - double Y = mPCA[mCurHyp][1]; // Y seed - - mCandTr[mCurHyp][0] = *mOrigTrPtr[0]; - mCandTr[mCurHyp][1] = *mOrigTrPtr[1]; - - double finalZ; - - double dstXY[2]; // 0 -> distance btwn both tracks at startPoint - - while (DeltaZ > epsilon) { - - midPoint = 0.5 * (startPoint + endPoint); - - for (int i = 0; i < 2; i++) { - mCandTr[mCurHyp][i].propagateParamToZquadratic(startPoint, mBz); - newX[i][0] = mCandTr[mCurHyp][i].getX(); - newY[i][0] = mCandTr[mCurHyp][i].getY(); - - mCandTr[mCurHyp][i].propagateParamToZquadratic(endPoint, mBz); - newX[i][1] = mCandTr[mCurHyp][i].getX(); - newY[i][1] = mCandTr[mCurHyp][i].getY(); - } - - dstXY[0] = (newX[0][0] - newX[1][0]) * (newX[0][0] - newX[1][0]) + - (newY[0][0] - newY[1][0]) * (newY[0][0] - newY[1][0]); - - dstXY[1] = (newX[0][1] - newX[1][1]) * (newX[0][1] - newX[1][1]) + - (newY[0][1] - newY[1][1]) * (newY[0][1] - newY[1][1]); - - DeltaZ = std::abs(endPoint - startPoint); - - if (DeltaZ < epsilon) { - finalZ = 0.5 * (startPoint + endPoint); - break; - } - - // chose new start and end Point according to the smallest D_XY - if (dstXY[1] > dstXY[0]) { - endPoint = midPoint; - } else { - startPoint = midPoint; - } - } - - mPCA[mCurHyp][2] = finalZ; -} - -//___________________________________________________________________ -template -void FwdDCAFitterN::findZatXY_lineApprox(int mCurHyp) -{ - // approx method: z=(b-b')/(a'-a) -> tracks to lines with y0,1=az0,1+b for each track (in YZ and XZ plane) - - double startPoint = 1.; - double endPoint = 50.; // first disk - - double X = mPCA[mCurHyp][0]; // X seed - double Y = mPCA[mCurHyp][1]; // Y seed - - mCandTr[mCurHyp][0] = *mOrigTrPtr[0]; - mCandTr[mCurHyp][1] = *mOrigTrPtr[1]; - - double y[2][2]; // Y00: y track 0 at point 0; Y01: y track 0 at point 1 - double z[2][2]; - double x[2][2]; - - double aYZ[2]; - double bYZ[2]; - - double aXZ[2]; - double bXZ[2]; - - double finalZ; - - // find points of the tracks = 2 straight lines - for (int i = 0; i < 2; i++) { - - mCandTr[mCurHyp][i].propagateToZquadratic(startPoint, mBz); - // mCandTr[mCurHyp][i].propagateToZlinear(startPoint); - z[i][0] = startPoint; - y[i][0] = mCandTr[mCurHyp][i].getY(); - x[i][0] = mCandTr[mCurHyp][i].getX(); - - mCandTr[mCurHyp][i].propagateToZquadratic(endPoint, mBz); - // mCandTr[mCurHyp][i].propagateToZlinear(endPoint); - z[i][1] = endPoint; - y[i][1] = mCandTr[mCurHyp][i].getY(); - x[i][1] = mCandTr[mCurHyp][i].getX(); - - bYZ[i] = (y[i][1] - y[i][0] * z[i][1] / z[i][0]) / (1 - z[i][1] / z[i][0]); - aYZ[i] = (y[i][0] - bYZ[i]) / z[i][0]; - - bXZ[i] = (x[i][1] - x[i][0] * z[i][1] / z[i][0]) / (1 - z[i][1] / z[i][0]); - aXZ[i] = (x[i][0] - bXZ[i]) / z[i][0]; - } - - // z seed: equ. for intersection of these lines - finalZ = 0.5 * ((bYZ[0] - bYZ[1]) / (aYZ[1] - aYZ[0]) + (bXZ[0] - bXZ[1]) / (aXZ[1] - aXZ[0])); - - mPCA[mCurHyp][2] = finalZ; -} - -//___________________________________________________________________ -template -void FwdDCAFitterN::findZatXY_quad(int mCurHyp) -{ - double startPoint = 0.; - double endPoint = 40.; // first disk - - double X = mPCA[mCurHyp][0]; // X seed - double Y = mPCA[mCurHyp][1]; // Y seed - - mCandTr[mCurHyp][0] = *mOrigTrPtr[0]; - mCandTr[mCurHyp][1] = *mOrigTrPtr[1]; - - double x[2]; - double y[2]; - double sinPhi0[2]; - double cosPhi0[2]; - double tanL0[2]; - double qpt0[2]; - - double k[2]; // B2C *abs(mBz) - double Hz[2]; // mBz/abs(mBz) - - double Ax[2], Bx[2], Cx[2]; - double Ay[2], By[2], Cy[2]; - - double deltaX[2], deltaY[2]; - - bool posX[2], nulX[2], negX[2]; - double z1X[2], z2X[2], z12X[2]; - - bool posY[2], nulY[2], negY[2]; - double z1Y[2], z2Y[2], z12Y[2]; - - double finalZ[2]; - - // find all variables for 2 tracks at z0 = startPoint - // set A, B, C variables for x/y equation for 2 tracks - // calculate Deltax/y for both and roots - - for (int i = 0; i < 2; i++) { - mCandTr[mCurHyp][i].propagateToZquadratic(startPoint, mBz); - x[i] = mCandTr[mCurHyp][i].getX(); - y[i] = mCandTr[mCurHyp][i].getY(); - sinPhi0[i] = mCandTr[mCurHyp][i].getSnp(); - cosPhi0[i] = std::sqrt((1. - sinPhi0[i]) * (1. + sinPhi0[i])); - tanL0[i] = mCandTr[mCurHyp][i].getTanl(); - qpt0[i] = mCandTr[mCurHyp][i].getInvQPt(); - k[i] = getK(mBz); - Hz[i] = getHz(mBz); - - Ax[i] = qpt0[i] * Hz[i] * k[i] * sinPhi0[i] / (2 * tanL0[i] * tanL0[i]); - Bx[i] = cosPhi0[i] / tanL0[i]; - Cx[i] = x[i] - X; - - Ay[i] = -qpt0[i] * Hz[i] * k[i] * cosPhi0[i] / (2 * tanL0[i] * tanL0[i]); - By[i] = sinPhi0[i] / tanL0[i]; - Cy[i] = y[i] - Y; // - - deltaX[i] = Bx[i] * Bx[i] - 4 * Ax[i] * Cx[i]; - deltaY[i] = By[i] * By[i] - 4 * Ay[i] * Cy[i]; - - if (deltaX[i] > 0) { - posX[i] = true; - z1X[i] = (-Bx[i] - std::sqrt(deltaX[i])) / (2 * Ax[i]); - z2X[i] = (-Bx[i] + std::sqrt(deltaX[i])) / (2 * Ax[i]); - } else if (deltaX[i] == 0) { - nulX[i] = true; - z12X[i] = -Bx[i] / (2 * Ax[i]); - } else { - negX[i] = true; - z12X[i] = 0; - } // discard - - if (deltaY[i] > 0) { - posY[i] = true; - z1Y[i] = (-By[i] - std::sqrt(deltaY[i])) / (2 * Ay[i]); - z2Y[i] = (-By[i] + std::sqrt(deltaY[i])) / (2 * Ay[i]); - } else if (deltaX[i] == 0) { - nulY[i] = true; - z12Y[i] = -By[i] / (2 * Ay[i]); - } else { - negY[i] = true; - z12Y[i] = 0; - } - - // find the z located in an acceptable interval - if (posX[i]) { - if (z1X[i] < endPoint && z1X[i] > startPoint) { - z12X[i] = z1X[i]; - } else { - z12X[i] = z2X[i]; - } - } - - if (posY[i]) { - if (z1Y[i] < endPoint && z1Y[i] > startPoint) { - z12Y[i] = z1Y[i]; - } else { - z12Y[i] = z2Y[i]; - } - } - - finalZ[i] = 0.5 * (z12X[i] + z12Y[i]); - } - - mPCA[mCurHyp][2] = 0.5 * (finalZ[0] + finalZ[1]); -} - -//___________________________________________________________________ -template -void FwdDCAFitterN::findZatXY_linear(int mCurHyp) -{ - - double startPoint = 0.; - - double X = mPCA[mCurHyp][0]; // X seed - double Y = mPCA[mCurHyp][1]; // Y seed - - mCandTr[mCurHyp][0] = *mOrigTrPtr[0]; - mCandTr[mCurHyp][1] = *mOrigTrPtr[1]; - - double x[2]; - double y[2]; - double sinPhi0[2]; - double cosPhi0[2]; - double tanL0[2]; - - double Ax[2], Bx[2]; - double Ay[2], By[2]; - - double z12X[2]; - double z12Y[2]; - - double finalZ[2]; - - // find all variables for 2 tracks at z0 = startPoint - // set A, B variables for x/y equation for 2 tracks - // calculate root - - for (int i = 0; i < 2; i++) { - mCandTr[mCurHyp][i].propagateToZlinear(startPoint); - x[i] = mCandTr[mCurHyp][i].getX(); - y[i] = mCandTr[mCurHyp][i].getY(); - sinPhi0[i] = mCandTr[mCurHyp][i].getSnp(); - cosPhi0[i] = std::sqrt((1. - sinPhi0[i]) * (1. + sinPhi0[i])); - tanL0[i] = mCandTr[mCurHyp][i].getTanl(); - - Ax[i] = cosPhi0[i] / tanL0[i]; - Bx[i] = x[i] - X; - - Ay[i] = sinPhi0[i] / tanL0[i]; - By[i] = y[i] - Y; - - z12X[i] = -Bx[i] / Ax[i]; - z12Y[i] = -By[i] / Ay[i]; - - finalZ[i] = 0.5 * (z12X[i] + z12Y[i]); - } - - mPCA[mCurHyp][2] = 0.5 * (finalZ[0] + finalZ[1]); -} - -//___________________________________________________________________ -template -inline o2::track::TrackParFwd FwdDCAFitterN::FwdgetTrackParamAtPCA(int i, int icand) const -{ - // propagate tracks param only to current vertex (if not already done) - int ord = mOrder[icand]; - o2::track::TrackParFwd trc(mCandTr[ord][i]); - if (!mTrPropDone[ord]) { - auto z = mPCA[ord][2]; - trc.propagateParamToZquadratic(z, mBz); - } - - return {trc}; -} - -//___________________________________________________________________ -template -inline double FwdDCAFitterN::getAbsMax(const VecND& v) -{ - double mx = -1; - for (int i = N; i--;) { - auto vai = std::abs(v[i]); - if (mx < vai) { - mx = vai; - } - } - return mx; -} - -//___________________________________________________________________ -template -bool FwdDCAFitterN::minimizeChi2() -{ - // find best chi2 (weighted DCA) of N tracks in the vicinity of the seed PCA - double x[2], y[2]; - double sumX = 0.; - double sumY = 0.; - - for (int i = N; i--;) { - mCandTr[mCurHyp][i] = *mOrigTrPtr[i]; - auto z = mPCA[mCurHyp][2]; - - mCandTr[mCurHyp][i].propagateToZquadratic(z, mBz); - - x[i] = mCandTr[mCurHyp][i].getX(); - y[i] = mCandTr[mCurHyp][i].getY(); - - setTrackPos(mTrPos[mCurHyp][i], mCandTr[mCurHyp][i]); // prepare positions - mTrcEInv[mCurHyp][i].set(mCandTr[mCurHyp][i], ZerrFactor); // prepare inverse cov.matrices at starting point - - sumX = sumX + x[i]; - sumY = sumY + y[i]; - } - - mPCA[mCurHyp][0] = sumX / N; - mPCA[mCurHyp][1] = sumY / N; - - if (mMaxDXIni > 0 && !roughDXCut()) { // apply rough cut on tracks X difference - return false; - } - - if (!FwdcalcPCACoefs()) { // prepare tracks contribution matrices to the global PCA - return false; - } - FwdcalcPCA(); // current PCA - FwdcalcTrackResiduals(); // current track residuals - float chi2Upd, chi2 = FwdcalcChi2(); - do { - calcTrackDerivatives(); // current track derivatives (1st and 2nd) - FwdcalcResidDerivatives(); // current residals derivatives (1st and 2nd) - FwdcalcChi2Derivatives(); // current chi2 derivatives (1st and 2nd) to proceed for dz calculation - - // do Newton-Rapson iteration with corrections = - dchi2/d{x0..xN} * [ d^2chi2/d{x0..xN}^2 ]^-1 - if (!mD2Chi2Dz2.Invert()) { - return false; - } - - VecND dz = mD2Chi2Dz2 * mDChi2Dz; - - if (!FwdcorrectTracks(dz)) { // calculate new Pi (mTrPos) following Newton-Rapson iteration - return false; - } - - FwdcalcPCA(); // updated mPCA (new V coordinates with new mTrPos (Pi)) - if (mCrossIDAlt >= 0 && closerToAlternative()) { - mAllowAltPreference = false; - return false; - } - - FwdcalcTrackResiduals(); // updated residuals - chi2Upd = FwdcalcChi2(); // updated chi2 - - if (getAbsMax(dz) < mMinParamChange || chi2Upd > chi2 * mMinRelChi2Change) { - chi2 = chi2Upd; - break; // converged - } - - chi2 = chi2Upd; - } while (++mNIters[mCurHyp] < mMaxIter); - - mChi2[mCurHyp] = chi2 * NInv; - return mChi2[mCurHyp] < mMaxChi2; -} - -//___________________________________________________________________ -template -bool FwdDCAFitterN::minimizeChi2NoErr() -{ - // find best chi2 (absolute DCA) of N tracks in the vicinity of the PCA seed - double x[2], y[2]; - double sumX = 0.; - double sumY = 0.; - - for (int i = N; i--;) { - - mCandTr[mCurHyp][i] = *mOrigTrPtr[i]; - - auto z = mPCA[mCurHyp][2]; - mCandTr[mCurHyp][i].propagateParamToZquadratic(z, mBz); - - x[i] = mCandTr[mCurHyp][i].getX(); - y[i] = mCandTr[mCurHyp][i].getY(); - - mPCA[mCurHyp][2] = z; - - setTrackPos(mTrPos[mCurHyp][i], mCandTr[mCurHyp][i]); // prepare positions - - sumX = sumX + x[i]; - sumY = sumY + y[i]; - } - - mPCA[mCurHyp][0] = sumX / N; - mPCA[mCurHyp][1] = sumY / N; - - if (mMaxDXIni > 0 && !roughDXCut()) { // apply rough cut on tracks Z difference - return false; - } - - FwdcalcPCANoErr(); // current PCA - FwdcalcTrackResiduals(); // current track residuals - float chi2Upd, chi2 = FwdcalcChi2NoErr(); - do { - calcTrackDerivatives(); // current track derivatives (1st and 2nd) - FwdcalcResidDerivativesNoErr(); // current residals derivatives (1st and 2nd) - FwdcalcChi2DerivativesNoErr(); // current chi2 derivatives (1st and 2nd) - - // do Newton-Rapson iteration with corrections = - dchi2/d{x0..xN} * [ d^2chi2/d{x0..xN}^2 ]^-1 - if (!mD2Chi2Dz2.Invert()) { - return false; - } - VecND dz = mD2Chi2Dz2 * mDChi2Dz; - - if (!FwdcorrectTracks(dz)) { - return false; - } - FwdcalcPCANoErr(); // updated PCA - if (mCrossIDAlt >= 0 && closerToAlternative()) { - mAllowAltPreference = false; - return false; - } - FwdcalcTrackResiduals(); // updated residuals - chi2Upd = FwdcalcChi2NoErr(); // updated chi2 - if (getAbsMax(dz) < mMinParamChange || chi2Upd > chi2 * mMinRelChi2Change) { - chi2 = chi2Upd; - break; // converged - } - chi2 = chi2Upd; - } while (++mNIters[mCurHyp] < mMaxIter); - // - mChi2[mCurHyp] = chi2 * NInv; - return mChi2[mCurHyp] < mMaxChi2; -} - -//___________________________________________________________________ -template -bool FwdDCAFitterN::roughDXCut() const -{ - // apply rough cut on DX between the tracks in the seed point - - bool accept = true; - for (int i = N; accept && i--;) { - for (int j = i; j--;) { - if (std::abs(mCandTr[mCurHyp][i].getX() - mCandTr[mCurHyp][j].getX()) > mMaxDXIni) { - accept = false; - break; - } - } - } - return accept; -} - -//___________________________________________________________________ -template -bool FwdDCAFitterN::closerToAlternative() const -{ - // check if the point current PCA point is closer to the seeding XY point being tested or to alternative see (if any) - auto dxCur = mPCA[mCurHyp][0] - mCrossings.xDCA[mCrossIDCur], dyCur = mPCA[mCurHyp][1] - mCrossings.yDCA[mCrossIDCur]; - auto dxAlt = mPCA[mCurHyp][0] - mCrossings.xDCA[mCrossIDAlt], dyAlt = mPCA[mCurHyp][1] - mCrossings.yDCA[mCrossIDAlt]; - return dxCur * dxCur + dyCur * dyCur > dxAlt * dxAlt + dyAlt * dyAlt; -} - -//___________________________________________________________________ -template -void FwdDCAFitterN::print() const -{ - LOG(info) << N << "-prong vertex fitter in " << (mUseAbsDCA ? "abs." : "weighted") << " distance minimization mode"; - LOG(info) << "Bz: " << mBz << " MaxIter: " << mMaxIter << " MaxChi2: " << mMaxChi2; - LOG(info) << "Stopping condition: Max.param change < " << mMinParamChange << " Rel.Chi2 change > " << mMinRelChi2Change; - LOG(info) << "Discard candidates for : Rvtx > " << getMaxR() << " DZ between tracks > " << mMaxDXIni; -} -//___________________________________________________________________ -template -inline bool FwdDCAFitterN::propagateToVtx(o2::track::TrackParCovFwd& t, const std::array& p, const std::array& cov) const -{ - // propagate track to vertex including MCS effects if material budget included, simple propagation to Z otherwise - float x2x0 = 0; - if (mUseMatBudget) { - auto mb = mMatLUT->getMatBudget(t.getX(), t.getY(), t.getZ(), p[0], p[1], p[2]); - x2x0 = (float)mb.meanX2X0; - return t.propagateToVtxhelixWithMCS(p[2], {p[0], p[1]}, cov, mBz, x2x0); - } else if (mTGeoFallBackAllowed) { - auto geoMan = o2::base::GeometryManager::meanMaterialBudget(t.getX(), t.getY(), t.getZ(), p[0], p[1], p[2]); - x2x0 = (float)geoMan.meanX2X0; - return t.propagateToVtxhelixWithMCS(p[2], {p[0], p[1]}, cov, mBz, x2x0); - } else { - t.propagateToZhelix(p[2], mBz); - return true; - } -} - -using FwdDCAFitter2 = FwdDCAFitterN<2, o2::track::TrackParCovFwd>; -using FwdDCAFitter3 = FwdDCAFitterN<3, o2::track::TrackParCovFwd>; - -} // namespace vertexing -} // namespace o2 -#endif // _ALICEO2_DCA_FWDFITTERN_ diff --git a/Detectors/Vertexing/src/FwdDCAFitterN.cxx b/Detectors/Vertexing/src/FwdDCAFitterN.cxx deleted file mode 100644 index f7176aa5039fd..0000000000000 --- a/Detectors/Vertexing/src/FwdDCAFitterN.cxx +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file DCAFitterN.cxx -/// \brief Defintions for N-prongs secondary vertex fit -/// \author ruben.shahoyan@cern.ch, adapted from central barrel to fwd rapidities by Rita Sadek, rita.sadek@cern.ch - -#include "DetectorsVertexing/FwdDCAFitterN.h" - -namespace o2 -{ -namespace vertexing -{ - -void __test_instance__() -{ - FwdDCAFitter2 ft2; - FwdDCAFitter3 ft3; - o2::track::TrackParCovFwd tr; - ft2.process(tr, tr); - ft3.process(tr, tr, tr); -} - -} // namespace vertexing -} // namespace o2 From 35c99fbf9c4a2c42e6cc220c119441ad22dfd059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Wed, 11 Feb 2026 18:05:45 +0100 Subject: [PATCH 086/133] ITSMFT: Delete unused files (#15034) --- .../MFTCalibration/NoiseSlotCalibrator.h | 101 - .../MFT/calibration/src/MchAlignment.cxx | 1660 ----------------- .../calibration/src/NoiseSlotCalibrator.cxx | 145 -- 3 files changed, 1906 deletions(-) delete mode 100644 Detectors/ITSMFT/MFT/calibration/include/MFTCalibration/NoiseSlotCalibrator.h delete mode 100644 Detectors/ITSMFT/MFT/calibration/src/MchAlignment.cxx delete mode 100644 Detectors/ITSMFT/MFT/calibration/src/NoiseSlotCalibrator.cxx diff --git a/Detectors/ITSMFT/MFT/calibration/include/MFTCalibration/NoiseSlotCalibrator.h b/Detectors/ITSMFT/MFT/calibration/include/MFTCalibration/NoiseSlotCalibrator.h deleted file mode 100644 index a8280467b14c9..0000000000000 --- a/Detectors/ITSMFT/MFT/calibration/include/MFTCalibration/NoiseSlotCalibrator.h +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file NoiseSlotCalibrator.h - -#ifndef O2_MFT_NOISESLOTCALIBRATOR -#define O2_MFT_NOISESLOTCALIBRATOR - -#include - -#include "DetectorsCalibration/TimeSlotCalibration.h" -#include "DetectorsCalibration/TimeSlot.h" - -#include "DataFormatsITSMFT/CompCluster.h" -#include "DataFormatsITSMFT/Digit.h" -#include "DataFormatsITSMFT/NoiseMap.h" -#include "gsl/span" - -namespace o2 -{ - -namespace itsmft -{ -class ROFRecord; -} // namespace itsmft - -namespace mft -{ - -class NoiseSlotCalibrator : public o2::calibration::TimeSlotCalibration -{ - using Slot = calibration::TimeSlot; - - public: - NoiseSlotCalibrator() { setUpdateAtTheEndOfRunOnly(); } - NoiseSlotCalibrator(float prob, float relErr) : mProbabilityThreshold(prob), mProbRelErr(relErr) - { - setUpdateAtTheEndOfRunOnly(); - setSlotLength(INFINITE_TF); - mMinROFs = 1.1 * o2::itsmft::NoiseMap::getMinROFs(prob, relErr); - LOGP(info, "At least {} ROFs needed to apply threshold {} with relative error {}", mMinROFs, mProbabilityThreshold, mProbRelErr); - } - ~NoiseSlotCalibrator() final = default; - - void setThreshold(unsigned int t) { mThreshold = t; } - - bool processTimeFrame(calibration::TFType tf, - gsl::span const& digits, - gsl::span const& rofs); - - bool processTimeFrame(calibration::TFType tf, - gsl::span const& clusters, - gsl::span const& patterns, - gsl::span const& rofs); - - void setMinROFs(long n) { mMinROFs = n; } - - void finalize() - { - LOG(info) << "Number of processed strobes is " << mNumberOfStrobes; - auto& slot = getSlots().back(); - slot.getContainer()->applyProbThreshold(mProbabilityThreshold, mNumberOfStrobes); - } - - const o2::itsmft::NoiseMap& getNoiseMap(long& start, long& end) - { - const auto& slot = getSlots().back(); - start = slot.getTFStart(); - end = slot.getTFEnd(); - return *(slot.getContainer()); - } - - // Functions overloaded from the calibration framework - bool process(calibration::TFType tf, const gsl::span data) final; - - // Functions required by the calibration framework - void initOutput() final {} - Slot& emplaceNewSlot(bool, calibration::TFType, calibration::TFType) final; - void finalizeSlot(Slot& slot) final; - bool hasEnoughData(const Slot& slot) const final; - - private: - float mProbabilityThreshold = 1e-6f; - float mProbRelErr = 0.2; // relative error on channel noise to apply the threshold - long mMinROFs = 0; - unsigned int mThreshold = 100; - unsigned int mNumberOfStrobes = 0; -}; - -} // namespace mft -} // namespace o2 - -#endif /* O2_MFT_NOISESLOTCALIBRATOR */ diff --git a/Detectors/ITSMFT/MFT/calibration/src/MchAlignment.cxx b/Detectors/ITSMFT/MFT/calibration/src/MchAlignment.cxx deleted file mode 100644 index b9e590cca0b63..0000000000000 --- a/Detectors/ITSMFT/MFT/calibration/src/MchAlignment.cxx +++ /dev/null @@ -1,1660 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -//----------------------------------------------------------------------------- -/// \file Alignment -/// Alignment class for the ALICE DiMuon spectrometer -/// -/// MUON specific alignment class which interface to AliMillepede. -/// For each track ProcessTrack calculates the local and global derivatives -/// at each cluster and fill the corresponding local equations. Provide methods -/// for fixing or constraining detection elements for best results. -/// -/// \author Javier Castillo Castellanos -//----------------------------------------------------------------------------- - -#include "MCHAlign/Alignment.h" -#include "MCHAlign/MillePede2.h" -#include "MCHAlign/MillePedeRecord.h" -#include - -#include "MCHTracking/Track.h" -#include "MCHTracking/TrackParam.h" -#include "MCHTracking/Cluster.h" -#include "TGeoManager.h" - -// #include "DataFormatsMCH/ROFRecord.h" -// #include "DataFormatsMCH/TrackMCH.h" -// #include "DataFormatsMCH/Cluster.h" -// #include "DataFormatsMCH/Digit.h" - -// #include "AliMUONGeometryTransformer.h" -// #include "AliMUONGeometryModuleTransformer.h" -// #include "MCHAlign/AliMUONGeometryDetElement.h" -// #include "AliMUONGeometryBuilder.h" -#include "MCHGeometryCreator/Geometry.h" -#include "MCHGeometryTest/Helpers.h" -#include "MCHGeometryTransformer/Transformations.h" -#include "TGeoManager.h" - -// #include "Align/Millepede2Record.h" //to be replaced -// #include "AliMpExMap.h" -// #include "AliMpExMapIterator.h" - -#include "DetectorsCommonDataFormats/AlignParam.h" -#include "Framework/Logger.h" - -#include -#include -#include -#include -#include -#include - -namespace o2 -{ -namespace mch -{ - -using namespace std; - -//_____________________________________________________________________ -// static variables -const Int_t Alignment::fgNDetElemCh[Alignment::fgNCh] = {4, 4, 4, 4, 18, 18, 26, 26, 26, 26}; -const Int_t Alignment::fgSNDetElemCh[Alignment::fgNCh + 1] = {0, 4, 8, 12, 16, 34, 52, 78, 104, 130, 156}; - -// number of detector elements in each half-chamber -const Int_t Alignment::fgNDetElemHalfCh[Alignment::fgNHalfCh] = {2, 2, 2, 2, 2, 2, 2, 2, 9, 9, 9, 9, 13, 13, 13, 13, 13, 13, 13, 13}; - -// list of detector elements for each half chamber -const Int_t Alignment::fgDetElemHalfCh[Alignment::fgNHalfCh][Alignment::fgNDetHalfChMax] = - { - {100, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {101, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - - {200, 203, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {201, 202, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - - {300, 303, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {301, 302, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - - {400, 403, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {401, 402, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - - {500, 501, 502, 503, 504, 514, 515, 516, 517, 0, 0, 0, 0}, - {505, 506, 507, 508, 509, 510, 511, 512, 513, 0, 0, 0, 0}, - - {600, 601, 602, 603, 604, 614, 615, 616, 617, 0, 0, 0, 0}, - {605, 606, 607, 608, 609, 610, 611, 612, 613, 0, 0, 0, 0}, - - {700, 701, 702, 703, 704, 705, 706, 720, 721, 722, 723, 724, 725}, - {707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719}, - - {800, 801, 802, 803, 804, 805, 806, 820, 821, 822, 823, 824, 825}, - {807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819}, - - {900, 901, 902, 903, 904, 905, 906, 920, 921, 922, 923, 924, 925}, - {907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919}, - - {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1020, 1021, 1022, 1023, 1024, 1025}, - {1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019} - -}; - -//_____________________________________________________________________ -/// self initialized array, used for adding constraints -class Array -{ - - public: - /// contructor - Array(void) - { - for (Int_t i = 0; i < Alignment::fNGlobal; ++i) { - values[i] = 0; - } - } - - /// array - Double_t values[Alignment::fNGlobal]; - - private: - /// Not implemented - Array(const Array&); - - /// Not implemented - Array& operator=(const Array&); -}; - -//________________________________________________________________________ -Double_t Square(Double_t x) { return x * x; } - -//_____________________________________________________________________ -Alignment::Alignment() - : TObject(), - fInitialized(kFALSE), - fRunNumber(0), - fBFieldOn(kFALSE), - fRefitStraightTracks(kFALSE), - fStartFac(256), - fResCutInitial(100), - fResCut(100), - fMillepede(0L), // to be modified - fCluster(0L), - fNStdDev(3), - fDetElemNumber(0), - fTrackRecord(), - fTransformCreator(), - fGeoCombiTransInverse(), - fDoEvaluation(kFALSE), - fTrackParamOrig(0), - fTrackParamNew(0), - fTFile(0), - fTTree(0) -{ - /// constructor - fSigma[0] = 1.5e-1; - fSigma[1] = 1.0e-2; - - // default allowed variations - fAllowVar[0] = 0.5; // x - fAllowVar[1] = 0.5; // y - fAllowVar[2] = 0.01; // phi_z - fAllowVar[3] = 5; // z - - // initialize millepede - fMillepede = new MillePede2(); - // fMillepede = new o2::align::Mille("theMilleFile.txt"); // To be replaced by MillePede2 - - // initialize degrees of freedom - // by default all parameters are free - for (Int_t iPar = 0; iPar < fNGlobal; ++iPar) { - fGlobalParameterStatus[iPar] = kFreeParId; - } - - // initialize local equations - for (int i = 0; i < fNLocal; ++i) { - fLocalDerivatives[i] = 0.0; - } - - for (int i = 0; i < fNGlobal; ++i) { - fGlobalDerivatives[i] = 0.0; - } -} - -//_____________________________________________________________________ -// Alignment::~Alignment() -//{ -// /// destructor -//} -// Alignment::~Alignment() = default; -//_____________________________________________________________________ -void Alignment::init(void) -{ - - /// initialize - /** - initialize millipede - must be called after necessary detectors have been fixed, - but before constrains are added and before global parameters initial value are set - */ - if (fInitialized) { - LOG(fatal) << "Millepede already initialized"; - } - - // assign proper groupID to free parameters - Int_t nGlobal = 0; - for (Int_t iPar = 0; iPar < fNGlobal; ++iPar) { - - if (fGlobalParameterStatus[iPar] == kFixedParId) { - // fixed parameters are left unchanged - continue; - - } else if (fGlobalParameterStatus[iPar] == kFreeParId || fGlobalParameterStatus[iPar] == kGroupBaseId) { - - // free parameters or first element of group are assigned a new group id - fGlobalParameterStatus[iPar] = nGlobal++; - continue; - - } else if (fGlobalParameterStatus[iPar] < kGroupBaseId) { - - // get detector element id from status, get chamber parameter id - const Int_t iDeBase(kGroupBaseId - 1 - fGlobalParameterStatus[iPar]); - const Int_t iParBase = iPar % fgNParCh; - - // check - if (iDeBase < 0 || iDeBase >= iPar / fgNParCh) { - LOG(fatal) << "Group for parameter index " << iPar << " has wrong base detector element: " << iDeBase; - } - - // assign identical group id to current - fGlobalParameterStatus[iPar] = fGlobalParameterStatus[iDeBase * fgNParCh + iParBase]; - LOG(info) << "Parameter " << iPar << " grouped to detector " << iDeBase << " (" << GetParameterMaskString(1 << iParBase).Data() << ")"; - - } else - LOG(fatal) << "Unrecognized parameter status for index " << iPar << ": " << fGlobalParameterStatus[iPar]; - } - - LOG(info) << "Free Parameters: " << nGlobal << " out of " << fNGlobal; - - // initialize millepede - // fMillepede->InitMille(fNGlobal, fNLocal, fNStdDev, fResCut, fResCutInitial, fGlobalParameterStatus); - fMillepede->InitMille(fNGlobal, fNLocal, fNStdDev, fResCut, fResCutInitial); // MillePede2 implementation - - fInitialized = kTRUE; - - // some debug output - for (Int_t iPar = 0; iPar < fgNParCh; ++iPar) { - LOG(info) << "fAllowVar[" << iPar << "]= " << fAllowVar[iPar]; - } - - // set allowed variations for all parameters - for (Int_t iDet = 0; iDet < fgNDetElem; ++iDet) { - for (Int_t iPar = 0; iPar < fgNParCh; ++iPar) { - fMillepede->SetParSigma(iDet * fgNParCh + iPar, fAllowVar[iPar]); - } - } - - // Set iterations - if (fStartFac > 1) { - fMillepede->SetIterations(fStartFac); - } - // setup monitoring TFile - if (fDoEvaluation && fRefitStraightTracks) { - fTFile = new TFile("Alignment.root", "RECREATE"); - fTTree = new TTree("TreeE", "Evaluation"); - - const Int_t kSplitlevel = 98; - const Int_t kBufsize = 32000; - - fTrackParamOrig = new LocalTrackParam(); - fTTree->Branch("fTrackParamOrig", "LocalTrackParam", &fTrackParamOrig, kBufsize, kSplitlevel); - - fTrackParamNew = new LocalTrackParam(); - fTTree->Branch("fTrackParamNew", "LocalTrackParam", &fTrackParamNew, kBufsize, kSplitlevel); - } -} - -//_____________________________________________________ -void Alignment::terminate(void) -{ - LOG(info) << "Closing Evaluation TFile"; - if (fTFile && fTTree) { - fTFile->cd(); - fTTree->Write(); - fTFile->Close(); - } -} - -//_____________________________________________________ -MillePedeRecord* Alignment::ProcessTrack(Track& track, Bool_t doAlignment, Double_t weight) -{ - /// process track for alignment minimization - /** - returns the alignment records for this track. - They can be stored in some output for later reprocessing. - */ - - // reset track records - fTrackRecord.Reset(); - if (fMillepede->GetRecord()) { - fMillepede->GetRecord()->Reset(); - } - - // loop over clusters to get starting values - Bool_t first(kTRUE); - // if (!trackParam) - // continue; - for (auto itTrackParam(track.begin()); itTrackParam != track.end(); ++itTrackParam) { - - // get cluster - const Cluster* Cluster = itTrackParam->getClusterPtr(); - if (!cluster) - continue; - - // for first valid cluster, save track position as "starting" values - if (first) { - - first = kFALSE; - FillTrackParamData(&*itTrackParam); - fTrackPos0[0] = fTrackPos[0]; - fTrackPos0[1] = fTrackPos[1]; - fTrackPos0[2] = fTrackPos[2]; - fTrackSlope0[0] = fTrackSlope[0]; - fTrackSlope0[1] = fTrackSlope[1]; - - break; - } - } - - // redo straight track fit - if (fRefitStraightTracks) { - - // refit straight track - const LocalTrackParam trackParam(RefitStraightTrack(track, fTrackPos0[2])); - - // fill evaluation tree - if (fTrackParamOrig) { - fTrackParamOrig->fTrackX = fTrackPos0[0]; - fTrackParamOrig->fTrackY = fTrackPos0[1]; - fTrackParamOrig->fTrackZ = fTrackPos0[2]; - fTrackParamOrig->fTrackSlopeX = fTrackSlope[0]; - fTrackParamOrig->fTrackSlopeY = fTrackSlope[1]; - } - - // new ones - if (fTrackParamNew) { - fTrackParamNew->fTrackX = trackParam.fTrackX; - fTrackParamNew->fTrackY = trackParam.fTrackY; - fTrackParamNew->fTrackZ = trackParam.fTrackZ; - fTrackParamNew->fTrackSlopeX = trackParam.fTrackSlopeX; - fTrackParamNew->fTrackSlopeY = trackParam.fTrackSlopeY; - } - - if (fTTree) - fTTree->Fill(); - - /* - copy new parameters to stored ones for derivatives calculation - this is done only if BFieldOn is false, for which these parameters are used - */ - if (!fBFieldOn) { - fTrackPos0[0] = trackParam.fTrackX; - fTrackPos0[1] = trackParam.fTrackY; - fTrackPos0[2] = trackParam.fTrackZ; - fTrackSlope[0] = trackParam.fTrackSlopeX; - fTrackSlope[1] = trackParam.fTrackSlopeY; - } - } - - // second loop to perform alignment - for (auto itTrackParam(track.begin()); itTrackParam != track.end(); ++itTrackParam) { - - // get track parameters - if (!&*itTrackParam) - continue; - - // get cluster - const Cluster* cluster = itTrackParam->getClusterPtr(); - if (!cluster) - continue; - - // fill local variables for this position --> one measurement - FillDetElemData(cluster); - FillRecPointData(cluster); - FillTrackParamData(&*itTrackParam); - - // 'inverse' (GlobalToLocal) rotation matrix - const Double_t* r(fGeoCombiTransInverse.GetRotationMatrix()); - - // calculate measurements - if (fBFieldOn) { - - // use residuals (cluster - track) for measurement - fMeas[0] = r[0] * (fClustPos[0] - fTrackPos[0]) + r[1] * (fClustPos[1] - fTrackPos[1]); - fMeas[1] = r[3] * (fClustPos[0] - fTrackPos[0]) + r[4] * (fClustPos[1] - fTrackPos[1]); - - } else { - - // use cluster position for measurement - fMeas[0] = (r[0] * fClustPos[0] + r[1] * fClustPos[1]); - fMeas[1] = (r[3] * fClustPos[0] + r[4] * fClustPos[1]); - } - - // Set local equations - LocalEquationX(); - LocalEquationY(); - } - - // copy track record - fMillepede->SetRecordRun(fRunNumber); - fMillepede->SetRecordWeight(weight); - fTrackRecord = *fMillepede->GetRecord(); - - // save record data - if (doAlignment) { - fMillepede->SaveRecordData(); - fMillepede->CloseDataRecStorage(); - } - - // return record - return &fTrackRecord; -} - -//______________________________________________________________________________ -void Alignment::ProcessTrack(MillePedeRecord* trackRecord) -{ - LOG(fatal) << __PRETTY_FUNCTION__ << " is disabled"; - - /// process track record - if (!trackRecord) - return; - - // // make sure record storage is initialized - if (!fMillepede->GetRecord()) { - fMillepede->InitDataRecStorage(kFalse); - } - // // copy content - *fMillepede->GetRecord() = *trackRecord; - - // save record - fMillepede->SaveRecordData(); - // write to local file - fMillepede->CloseDataRecStorage(); - - return; -} - -//_____________________________________________________________________ -void Alignment::FixAll(UInt_t mask) -{ - /// fix parameters matching mask, for all chambers - LOG(info) << "Fixing " << GetParameterMaskString(mask).Data() << " for all detector elements"; - - // fix all stations - for (Int_t i = 0; i < fgNDetElem; ++i) { - if (mask & ParX) - FixParameter(i, 0); - if (mask & ParY) - FixParameter(i, 1); - if (mask & ParTZ) - FixParameter(i, 2); - if (mask & ParZ) - FixParameter(i, 3); - } -} - -//_____________________________________________________________________ -void Alignment::FixChamber(Int_t iCh, UInt_t mask) -{ - /// fix parameters matching mask, for all detector elements in a given chamber, counting from 1 - - // check boundaries - if (iCh < 1 || iCh > 10) { - LOG(fatal) << "Invalid chamber index " << iCh; - } - - // get first and last element - const Int_t iDetElemFirst = fgSNDetElemCh[iCh - 1]; - const Int_t iDetElemLast = fgSNDetElemCh[iCh]; - for (Int_t i = iDetElemFirst; i < iDetElemLast; ++i) { - - LOG(info) << "Fixing " << GetParameterMaskString(mask).Data() << " for detector element " << i; - - if (mask & ParX) - FixParameter(i, 0); - if (mask & ParY) - FixParameter(i, 1); - if (mask & ParTZ) - FixParameter(i, 2); - if (mask & ParZ) - FixParameter(i, 3); - } -} - -//_____________________________________________________________________ -void Alignment::FixDetElem(Int_t iDetElemId, UInt_t mask) -{ - /// fix parameters matching mask, for a given detector element, counting from 0 - const Int_t iDet(GetDetElemNumber(iDetElemId)); - if (mask & ParX) - FixParameter(iDet, 0); - if (mask & ParY) - FixParameter(iDet, 1); - if (mask & ParTZ) - FixParameter(iDet, 2); - if (mask & ParZ) - FixParameter(iDet, 3); -} - -//_____________________________________________________________________ -void Alignment::FixHalfSpectrometer(const Bool_t* lChOnOff, UInt_t sidesMask, UInt_t mask) -{ - - /// Fix parameters matching mask for all detectors in selected chambers and selected sides of the spectrometer - for (Int_t i = 0; i < fgNDetElem; ++i) { - - // get chamber matching detector - const Int_t iCh(GetChamberId(i)); - if (!lChOnOff[iCh - 1]) - continue; - - // get detector element in chamber - Int_t lDetElemNumber = i - fgSNDetElemCh[iCh - 1]; - - // skip detector if its side is off - // stations 1 and 2 - if (iCh >= 1 && iCh <= 4) { - if (lDetElemNumber == 0 && !(sidesMask & SideTopRight)) - continue; - if (lDetElemNumber == 1 && !(sidesMask & SideTopLeft)) - continue; - if (lDetElemNumber == 2 && !(sidesMask & SideBottomLeft)) - continue; - if (lDetElemNumber == 3 && !(sidesMask & SideBottomRight)) - continue; - } - - // station 3 - if (iCh >= 5 && iCh <= 6) { - if (lDetElemNumber >= 0 && lDetElemNumber <= 4 && !(sidesMask & SideTopRight)) - continue; - if (lDetElemNumber >= 5 && lDetElemNumber <= 10 && !(sidesMask & SideTopLeft)) - continue; - if (lDetElemNumber >= 11 && lDetElemNumber <= 13 && !(sidesMask & SideBottomLeft)) - continue; - if (lDetElemNumber >= 14 && lDetElemNumber <= 17 && !(sidesMask & SideBottomRight)) - continue; - } - - // stations 4 and 5 - if (iCh >= 7 && iCh <= 10) { - if (lDetElemNumber >= 0 && lDetElemNumber <= 6 && !(sidesMask & SideTopRight)) - continue; - if (lDetElemNumber >= 7 && lDetElemNumber <= 13 && !(sidesMask & SideTopLeft)) - continue; - if (lDetElemNumber >= 14 && lDetElemNumber <= 19 && !(sidesMask & SideBottomLeft)) - continue; - if (lDetElemNumber >= 20 && lDetElemNumber <= 25 && !(sidesMask & SideBottomRight)) - continue; - } - - // detector is accepted, fix it - FixDetElem(i, mask); - } -} - -//______________________________________________________________________ -void Alignment::FixParameter(Int_t iPar) -{ - - /// fix a given parameter, counting from 0 - if (fInitialized) { - LOG(fatal) << "Millepede already initialized"; - } - - fGlobalParameterStatus[iPar] = kFixedParId; -} - -//_____________________________________________________________________ -void Alignment::ReleaseChamber(Int_t iCh, UInt_t mask) -{ - /// release parameters matching mask, for all detector elements in a given chamber, counting from 1 - - // check boundaries - if (iCh < 1 || iCh > 10) { - LOG(fatal) << "Invalid chamber index " << iCh; - } - - // get first and last element - const Int_t iDetElemFirst = fgSNDetElemCh[iCh - 1]; - const Int_t iDetElemLast = fgSNDetElemCh[iCh]; - for (Int_t i = iDetElemFirst; i < iDetElemLast; ++i) { - - LOG(info) << "Releasing " << GetParameterMaskString(mask).Data() << " for detector element " << i; - - if (mask & ParX) - ReleaseParameter(i, 0); - if (mask & ParY) - ReleaseParameter(i, 1); - if (mask & ParTZ) - ReleaseParameter(i, 2); - if (mask & ParZ) - ReleaseParameter(i, 3); - } -} - -//_____________________________________________________________________ -void Alignment::ReleaseDetElem(Int_t iDetElemId, UInt_t mask) -{ - /// release parameters matching mask, for a given detector element, counting from 0 - const Int_t iDet(GetDetElemNumber(iDetElemId)); - if (mask & ParX) - ReleaseParameter(iDet, 0); - if (mask & ParY) - ReleaseParameter(iDet, 1); - if (mask & ParTZ) - ReleaseParameter(iDet, 2); - if (mask & ParZ) - ReleaseParameter(iDet, 3); -} - -//______________________________________________________________________ -void Alignment::ReleaseParameter(Int_t iPar) -{ - - /// release a given parameter, counting from 0 - if (fInitialized) { - LOG(fatal) << "Millepede already initialized"; - } - - fGlobalParameterStatus[iPar] = kFreeParId; -} - -//_____________________________________________________________________ -void Alignment::GroupChamber(Int_t iCh, UInt_t mask) -{ - /// group parameters matching mask for all detector elements in a given chamber, counting from 1 - if (iCh < 1 || iCh > fgNCh) { - LOG(fatal) << "Invalid chamber index " << iCh; - } - - const Int_t detElemMin = 100 * iCh; - const Int_t detElemMax = 100 * iCh + fgNDetElemCh[iCh] - 1; - GroupDetElems(detElemMin, detElemMax, mask); -} - -//_____________________________________________________________________ -void Alignment::GroupHalfChamber(Int_t iCh, Int_t iHalf, UInt_t mask) -{ - /// group parameters matching mask for all detector elements in a given tracking module (= half chamber), counting from 0 - if (iCh < 1 || iCh > fgNCh) { - LOG(fatal) << "Invalid chamber index " << iCh; - } - - if (iHalf < 0 || iHalf > 1) { - LOG(fatal) << "Invalid half chamber index " << iHalf; - } - - const Int_t iHalfCh = 2 * (iCh - 1) + iHalf; - GroupDetElems(&fgDetElemHalfCh[iHalfCh][0], fgNDetElemHalfCh[iHalfCh], mask); -} - -//_____________________________________________________________________ -void Alignment::GroupDetElems(Int_t detElemMin, Int_t detElemMax, UInt_t mask) -{ - /// group parameters matching mask for all detector elements between min and max - // check number of detector elements - const Int_t nDetElem = detElemMax - detElemMin + 1; - if (nDetElem < 2) { - LOG(fatal) << "Requested group of DEs " << detElemMin << "-" << detElemMax << " contains less than 2 DE's"; - } - - // create list - Int_t* detElemList = new int[nDetElem]; - for (Int_t i = 0; i < nDetElem; ++i) { - detElemList[i] = detElemMin + i; - } - - // group - GroupDetElems(detElemList, nDetElem, mask); - delete[] detElemList; -} - -//_____________________________________________________________________ -void Alignment::GroupDetElems(const Int_t* detElemList, Int_t nDetElem, UInt_t mask) -{ - /// group parameters matching mask for all detector elements in list - if (fInitialized) { - LOG(fatal) << "Millepede already initialized"; - } - - const Int_t iDeBase(GetDetElemNumber(detElemList[0])); - for (Int_t i = 0; i < nDetElem; ++i) { - const Int_t iDeCurrent(GetDetElemNumber(detElemList[i])); - if (mask & ParX) - fGlobalParameterStatus[iDeCurrent * fgNParCh + 0] = (i == 0) ? kGroupBaseId : (kGroupBaseId - iDeBase - 1); - if (mask & ParY) - fGlobalParameterStatus[iDeCurrent * fgNParCh + 1] = (i == 0) ? kGroupBaseId : (kGroupBaseId - iDeBase - 1); - if (mask & ParTZ) - fGlobalParameterStatus[iDeCurrent * fgNParCh + 2] = (i == 0) ? kGroupBaseId : (kGroupBaseId - iDeBase - 1); - if (mask & ParZ) - fGlobalParameterStatus[iDeCurrent * fgNParCh + 3] = (i == 0) ? kGroupBaseId : (kGroupBaseId - iDeBase - 1); - - if (i == 0) - LOG(info) << "Creating new group for detector " << detElemList[i] << " and variable " << GetParameterMaskString(mask).Data(); - else - LOG(info) << "Adding detector element " << detElemList[i] << " to current group"; - } -} - -//______________________________________________________________________ -void Alignment::SetChamberNonLinear(Int_t iCh, UInt_t mask) -{ - /// Set parameters matching mask as non linear, for all detector elements in a given chamber, counting from 1 - const Int_t iDetElemFirst = fgSNDetElemCh[iCh - 1]; - const Int_t iDetElemLast = fgSNDetElemCh[iCh]; - for (Int_t i = iDetElemFirst; i < iDetElemLast; ++i) { - - if (mask & ParX) - SetParameterNonLinear(i, 0); - if (mask & ParY) - SetParameterNonLinear(i, 1); - if (mask & ParTZ) - SetParameterNonLinear(i, 2); - if (mask & ParZ) - SetParameterNonLinear(i, 3); - } -} - -//_____________________________________________________________________ -void Alignment::SetDetElemNonLinear(Int_t iDetElemId, UInt_t mask) -{ - /// Set parameters matching mask as non linear, for a given detector element, counting from 0 - const Int_t iDet(GetDetElemNumber(iDetElemId)); - if (mask & ParX) - SetParameterNonLinear(iDet, 0); - if (mask & ParY) - SetParameterNonLinear(iDet, 1); - if (mask & ParTZ) - SetParameterNonLinear(iDet, 2); - if (mask & ParZ) - SetParameterNonLinear(iDet, 3); -} - -//______________________________________________________________________ -void Alignment::SetParameterNonLinear(Int_t iPar) -{ - /// Set nonlinear flag for parameter iPar - if (!fInitialized) { - LOG(fatal) << "Millepede not initialized"; - } - - fMillepede->SetNonLinear(iPar); - LOG(info) << "Parameter " << iPar << " set to non linear "; -} - -//______________________________________________________________________ -void Alignment::AddConstraints(const Bool_t* lChOnOff, UInt_t mask) -{ - /// Add constraint equations for selected chambers and degrees of freedom - - Array fConstraintX; - Array fConstraintY; - Array fConstraintTZ; - Array fConstraintZ; - - for (Int_t i = 0; i < fgNDetElem; ++i) { - - // get chamber matching detector - const Int_t iCh(GetChamberId(i)); - if (lChOnOff[iCh - 1]) { - - if (mask & ParX) - fConstraintX.values[i * fgNParCh + 0] = 1.0; - if (mask & ParY) - fConstraintY.values[i * fgNParCh + 1] = 1.0; - if (mask & ParTZ) - fConstraintTZ.values[i * fgNParCh + 2] = 1.0; - if (mask & ParZ) - fConstraintTZ.values[i * fgNParCh + 3] = 1.0; - } - } - - if (mask & ParX) - AddConstraint(fConstraintX.values, 0.0); - if (mask & ParY) - AddConstraint(fConstraintY.values, 0.0); - if (mask & ParTZ) - AddConstraint(fConstraintTZ.values, 0.0); - if (mask & ParZ) - AddConstraint(fConstraintZ.values, 0.0); -} - -//______________________________________________________________________ -void Alignment::AddConstraints(const Bool_t* lChOnOff, const Bool_t* lVarXYT, UInt_t sidesMask) -{ - /* - questions: - - is there not redundancy/inconsistency between lDetTLBR and lSpecLROnOff ? shouldn't we use only lDetTLBR ? - - why is weight ignored for ConstrainT and ConstrainB - - why is there no constrain on z - */ - - /// Add constraint equations for selected chambers, degrees of freedom and detector half - Double_t lMeanY = 0.; - Double_t lSigmaY = 0.; - Double_t lMeanZ = 0.; - Double_t lSigmaZ = 0.; - Int_t lNDetElem = 0; - - for (Int_t i = 0; i < fgNDetElem; ++i) { - - // get chamber matching detector - const Int_t iCh(GetChamberId(i)); - - // skip detector if chamber is off - if (lChOnOff[iCh - 1]) - continue; - - // get detector element id from detector element number - const Int_t lDetElemNumber = i - fgSNDetElemCh[iCh - 1]; - const Int_t lDetElemId = iCh * 100 + lDetElemNumber; - - // skip detector if its side is off - // stations 1 and 2 - if (iCh >= 1 && iCh <= 4) { - if (lDetElemNumber == 0 && !(sidesMask & SideTopRight)) - continue; - if (lDetElemNumber == 1 && !(sidesMask & SideTopLeft)) - continue; - if (lDetElemNumber == 2 && !(sidesMask & SideBottomLeft)) - continue; - if (lDetElemNumber == 3 && !(sidesMask & SideBottomRight)) - continue; - } - - // station 3 - if (iCh >= 5 && iCh <= 6) { - if (lDetElemNumber >= 0 && lDetElemNumber <= 4 && !(sidesMask & SideTopRight)) - continue; - if (lDetElemNumber >= 5 && lDetElemNumber <= 10 && !(sidesMask & SideTopLeft)) - continue; - if (lDetElemNumber >= 11 && lDetElemNumber <= 13 && !(sidesMask & SideBottomLeft)) - continue; - if (lDetElemNumber >= 14 && lDetElemNumber <= 17 && !(sidesMask & SideBottomRight)) - continue; - } - - // stations 4 and 5 - if (iCh >= 7 && iCh <= 10) { - if (lDetElemNumber >= 0 && lDetElemNumber <= 6 && !(sidesMask & SideTopRight)) - continue; - if (lDetElemNumber >= 7 && lDetElemNumber <= 13 && !(sidesMask & SideTopLeft)) - continue; - if (lDetElemNumber >= 14 && lDetElemNumber <= 19 && !(sidesMask & SideBottomLeft)) - continue; - if (lDetElemNumber >= 20 && lDetElemNumber <= 25 && !(sidesMask & SideBottomRight)) - continue; - } - - // get global x, y and z position - Double_t lDetElemGloX = 0.; - Double_t lDetElemGloY = 0.; - Double_t lDetElemGloZ = 0.; - - auto fTransform = fTransformCreator(lDetElemId); - o2::math_utils::Point3D SlatPos{0.0, 0.0, 0.0}; - o2::math_utils::Point3D GlobalPos; - - fTransform.LocalToMaster(SlatPos, GlobalPos); - lDetElemGloX = GlobalPos.x(); - lDetElemGloY = GlobalPos.y(); - lDetElemGloZ = GlobalPos.z(); - // fTransform->Local2Global(lDetElemId, 0, 0, 0, lDetElemGloX, lDetElemGloY, lDetElemGloZ); - - // increment mean Y, mean Z, sigmas and number of accepted detectors - lMeanY += lDetElemGloY; - lSigmaY += lDetElemGloY * lDetElemGloY; - lMeanZ += lDetElemGloZ; - lSigmaZ += lDetElemGloZ * lDetElemGloZ; - lNDetElem++; - } - - // calculate mean values - lMeanY /= lNDetElem; - lSigmaY /= lNDetElem; - lSigmaY = TMath::Sqrt(lSigmaY - lMeanY * lMeanY); - lMeanZ /= lNDetElem; - lSigmaZ /= lNDetElem; - lSigmaZ = TMath::Sqrt(lSigmaZ - lMeanZ * lMeanZ); - LOG(info) << "Used " << lNDetElem << " DetElem, MeanZ= " << lMeanZ << ", SigmaZ= " << lSigmaZ; - - // create all possible arrays - Array fConstraintX[4]; // Array for constraint equation X - Array fConstraintY[4]; // Array for constraint equation Y - Array fConstraintP[4]; // Array for constraint equation P - Array fConstraintXZ[4]; // Array for constraint equation X vs Z - Array fConstraintYZ[4]; // Array for constraint equation Y vs Z - Array fConstraintPZ[4]; // Array for constraint equation P vs Z - - // do we really need these ? - Array fConstraintXY[4]; // Array for constraint equation X vs Y - Array fConstraintYY[4]; // Array for constraint equation Y vs Y - Array fConstraintPY[4]; // Array for constraint equation P vs Y - - // fill Bool_t sides array based on masks, for convenience - Bool_t lDetTLBR[4]; - lDetTLBR[0] = sidesMask & SideTop; - lDetTLBR[1] = sidesMask & SideLeft; - lDetTLBR[2] = sidesMask & SideBottom; - lDetTLBR[3] = sidesMask & SideRight; - - for (Int_t i = 0; i < fgNDetElem; ++i) { - - // get chamber matching detector - const Int_t iCh(GetChamberId(i)); - - // skip detector if chamber is off - if (!lChOnOff[iCh - 1]) - continue; - - // get detector element id from detector element number - const Int_t lDetElemNumber = i - fgSNDetElemCh[iCh - 1]; - const Int_t lDetElemId = iCh * 100 + lDetElemNumber; - - // get global x, y and z position - Double_t lDetElemGloX = 0.; - Double_t lDetElemGloY = 0.; - Double_t lDetElemGloZ = 0.; - - auto fTransform = fTransformCreator(lDetElemId); - o2::math_utils::Point3D SlatPos{0.0, 0.0, 0.0}; - o2::math_utils::Point3D GlobalPos; - - fTransform.LocalToMaster(SlatPos, GlobalPos); - lDetElemGloX = GlobalPos.x(); - lDetElemGloY = GlobalPos.y(); - lDetElemGloZ = GlobalPos.z(); - // fTransform->Local2Global(lDetElemId, 0, 0, 0, lDetElemGloX, lDetElemGloY, lDetElemGloZ); - - // loop over sides - for (Int_t iSide = 0; iSide < 4; iSide++) { - - // skip if side is not selected - if (!lDetTLBR[iSide]) - continue; - - // skip detector if it is not in the selected side - // stations 1 and 2 - if (iCh >= 1 && iCh <= 4) { - if (lDetElemNumber == 0 && !(iSide == 0 || iSide == 3)) - continue; // top-right - if (lDetElemNumber == 1 && !(iSide == 0 || iSide == 1)) - continue; // top-left - if (lDetElemNumber == 2 && !(iSide == 2 || iSide == 1)) - continue; // bottom-left - if (lDetElemNumber == 3 && !(iSide == 2 || iSide == 3)) - continue; // bottom-right - } - - // station 3 - if (iCh >= 5 && iCh <= 6) { - if (lDetElemNumber >= 0 && lDetElemNumber <= 4 && !(iSide == 0 || iSide == 3)) - continue; // top-right - if (lDetElemNumber >= 5 && lDetElemNumber <= 9 && !(iSide == 0 || iSide == 1)) - continue; // top-left - if (lDetElemNumber >= 10 && lDetElemNumber <= 13 && !(iSide == 2 || iSide == 1)) - continue; // bottom-left - if (lDetElemNumber >= 14 && lDetElemNumber <= 17 && !(iSide == 2 || iSide == 3)) - continue; // bottom-right - } - - // stations 4 and 5 - if (iCh >= 7 && iCh <= 10) { - if (lDetElemNumber >= 0 && lDetElemNumber <= 6 && !(iSide == 0 || iSide == 3)) - continue; // top-right - if (lDetElemNumber >= 7 && lDetElemNumber <= 13 && !(iSide == 0 || iSide == 1)) - continue; // top-left - if (lDetElemNumber >= 14 && lDetElemNumber <= 19 && !(iSide == 2 || iSide == 1)) - continue; // bottom-left - if (lDetElemNumber >= 20 && lDetElemNumber <= 25 && !(iSide == 2 || iSide == 3)) - continue; // bottom-right - } - - // constrain x - if (lVarXYT[0]) - fConstraintX[iSide].values[i * fgNParCh + 0] = 1; - - // constrain y - if (lVarXYT[1]) - fConstraintY[iSide].values[i * fgNParCh + 1] = 1; - - // constrain phi (rotation around z) - if (lVarXYT[2]) - fConstraintP[iSide].values[i * fgNParCh + 2] = 1; - - // x-z shearing - if (lVarXYT[3]) - fConstraintXZ[iSide].values[i * fgNParCh + 0] = (lDetElemGloZ - lMeanZ) / lSigmaZ; - - // y-z shearing - if (lVarXYT[4]) - fConstraintYZ[iSide].values[i * fgNParCh + 1] = (lDetElemGloZ - lMeanZ) / lSigmaZ; - - // phi-z shearing - if (lVarXYT[5]) - fConstraintPZ[iSide].values[i * fgNParCh + 2] = (lDetElemGloZ - lMeanZ) / lSigmaZ; - - // x-y shearing - if (lVarXYT[6]) - fConstraintXY[iSide].values[i * fgNParCh + 0] = (lDetElemGloY - lMeanY) / lSigmaY; - - // y-y shearing - if (lVarXYT[7]) - fConstraintYY[iSide].values[i * fgNParCh + 1] = (lDetElemGloY - lMeanY) / lSigmaY; - - // phi-y shearing - if (lVarXYT[8]) - fConstraintPY[iSide].values[i * fgNParCh + 2] = (lDetElemGloY - lMeanY) / lSigmaY; - } - } - - // pass constraints to millepede - for (Int_t iSide = 0; iSide < 4; iSide++) { - // skip if side is not selected - if (!lDetTLBR[iSide]) - continue; - - if (lVarXYT[0]) - AddConstraint(fConstraintX[iSide].values, 0.0); - if (lVarXYT[1]) - AddConstraint(fConstraintY[iSide].values, 0.0); - if (lVarXYT[2]) - AddConstraint(fConstraintP[iSide].values, 0.0); - if (lVarXYT[3]) - AddConstraint(fConstraintXZ[iSide].values, 0.0); - if (lVarXYT[4]) - AddConstraint(fConstraintYZ[iSide].values, 0.0); - if (lVarXYT[5]) - AddConstraint(fConstraintPZ[iSide].values, 0.0); - if (lVarXYT[6]) - AddConstraint(fConstraintXY[iSide].values, 0.0); - if (lVarXYT[7]) - AddConstraint(fConstraintYY[iSide].values, 0.0); - if (lVarXYT[8]) - AddConstraint(fConstraintPY[iSide].values, 0.0); - } -} - -//______________________________________________________________________ -void Alignment::InitGlobalParameters(Double_t* par) -{ - /// Initialize global parameters with par array - if (!fInitialized) { - LOG(fatal) << "Millepede is not initialized"; - } - - fMillepede->SetGlobalParameters(par); -} - -//______________________________________________________________________ -void Alignment::SetAllowedVariation(Int_t iPar, Double_t value) -{ - /// "Encouraged" variation for degrees of freedom - // check initialization - if (fInitialized) { - LOG(fatal) << "Millepede already initialized"; - } - - // check initialization - if (!(iPar >= 0 && iPar < fgNParCh)) { - LOG(fatal) << "Invalid index: " << iPar; - } - - fAllowVar[iPar] = value; -} - -//______________________________________________________________________ -void Alignment::SetSigmaXY(Double_t sigmaX, Double_t sigmaY) -{ - - /// Set expected measurement resolution - fSigma[0] = sigmaX; - fSigma[1] = sigmaY; - - // print - for (Int_t i = 0; i < 2; ++i) { - LOG(info) << "fSigma[" << i << "] =" << fSigma[i]; - } -} - -//_____________________________________________________ -void Alignment::GlobalFit(Double_t* parameters, Double_t* errors, Double_t* pulls) -{ - - /// Call global fit; Global parameters are stored in parameters - fMillepede->GlobalFit(parameters, errors, pulls); - - LOG(info) << "Done fitting global parameters"; - for (int iDet = 0; iDet < fgNDetElem; ++iDet) { - LOG(info) << iDet << " " << parameters[iDet * fgNParCh + 0] << " " << parameters[iDet * fgNParCh + 1] << " " << parameters[iDet * fgNParCh + 3] << " " << parameters[iDet * fgNParCh + 2]; - } -} - -//_____________________________________________________ -void Alignment::PrintGlobalParameters() const -{ - fMillepede->PrintGlobalParameters(); -} - -//_____________________________________________________ -Double_t Alignment::GetParError(Int_t iPar) const -{ - return fMillepede->GetParError(iPar); -} - -// //______________________________________________________________________ -// AliMUONGeometryTransformer* Alignment::ReAlign( -// const AliMUONGeometryTransformer* transformer, -// const double* misAlignments, Bool_t) -// { - -// /// Returns a new AliMUONGeometryTransformer with the found misalignments -// /// applied. - -// // Takes the internal geometry module transformers, copies them -// // and gets the Detection Elements from them. -// // Takes misalignment parameters and applies these -// // to the local transform of the Detection Element -// // Obtains the global transform by multiplying the module transformer -// // transformation with the local transformation -// // Applies the global transform to a new detection element -// // Adds the new detection element to a new module transformer -// // Adds the new module transformer to a new geometry transformer -// // Returns the new geometry transformer - -// Double_t lModuleMisAlignment[fgNParCh] = {0}; -// Double_t lDetElemMisAlignment[fgNParCh] = {0}; -// const TClonesArray* oldMisAlignArray(transformer->GetMisAlignmentData()); - -// AliMUONGeometryTransformer* newGeometryTransformer = new AliMUONGeometryTransformer(); -// for (Int_t iMt = 0; iMt < transformer->GetNofModuleTransformers(); ++iMt) { - -// // module transformers -// const AliMUONGeometryModuleTransformer* kModuleTransformer = transformer->GetModuleTransformer(iMt, kTRUE); - -// AliMUONGeometryModuleTransformer* newModuleTransformer = new AliMUONGeometryModuleTransformer(iMt); -// newGeometryTransformer->AddModuleTransformer(newModuleTransformer); - -// // get transformation -// TGeoHMatrix deltaModuleTransform(DeltaTransform(lModuleMisAlignment)); - -// // update module -// TGeoHMatrix moduleTransform(*kModuleTransformer->GetTransformation()); -// TGeoHMatrix newModuleTransform(AliMUONGeometryBuilder::Multiply(deltaModuleTransform, moduleTransform)); -// newModuleTransformer->SetTransformation(newModuleTransform); - -// // Get matching old alignment and update current matrix accordingly -// if (oldMisAlignArray) { - -// const AliAlignObjMatrix* oldAlignObj(0); -// const Int_t moduleId(kModuleTransformer->GetModuleId()); -// const Int_t volId = AliGeomManager::LayerToVolUID(AliGeomManager::kMUON, moduleId); -// for (Int_t pos = 0; pos < oldMisAlignArray->GetEntriesFast(); ++pos) { - -// const AliAlignObjMatrix* localAlignObj(dynamic_cast(oldMisAlignArray->At(pos))); -// if (localAlignObj && localAlignObj->GetVolUID() == volId) { -// oldAlignObj = localAlignObj; -// break; -// } -// } - -// // multiply -// if (oldAlignObj) { - -// TGeoHMatrix oldMatrix; -// oldAlignObj->GetMatrix(oldMatrix); -// deltaModuleTransform.Multiply(&oldMatrix); -// } -// } - -// // Create module mis alignment matrix -// newGeometryTransformer->AddMisAlignModule(kModuleTransformer->GetModuleId(), deltaModuleTransform); - -// AliMpExMap* detElements = kModuleTransformer->GetDetElementStore(); - -// TIter next(detElements->CreateIterator()); -// AliMUONGeometryDetElement* detElement; -// Int_t iDe(-1); -// while ((detElement = static_cast(next()))) { -// ++iDe; -// // make a new detection element -// AliMUONGeometryDetElement* newDetElement = new AliMUONGeometryDetElement(detElement->GetId(), detElement->GetVolumePath()); -// TString lDetElemName(detElement->GetDEName()); -// lDetElemName.ReplaceAll("DE", ""); - -// // store detector element id and number -// const Int_t iDetElemId = lDetElemName.Atoi(); -// if (DetElemIsValid(iDetElemId)) { - -// const Int_t iDetElemNumber(GetDetElemNumber(iDetElemId)); - -// for (int i = 0; i < fgNParCh; ++i) { -// lDetElemMisAlignment[i] = 0.0; -// if (iMt < fgNTrkMod) { -// lDetElemMisAlignment[i] = misAlignments[iDetElemNumber * fgNParCh + i]; -// } -// } - -// // get transformation -// TGeoHMatrix deltaGlobalTransform(DeltaTransform(lDetElemMisAlignment)); - -// // update module -// TGeoHMatrix globalTransform(*detElement->GetGlobalTransformation()); -// TGeoHMatrix newGlobalTransform(AliMUONGeometryBuilder::Multiply(deltaGlobalTransform, globalTransform)); -// newDetElement->SetGlobalTransformation(newGlobalTransform); -// newModuleTransformer->GetDetElementStore()->Add(newDetElement->GetId(), newDetElement); - -// // Get matching old alignment and update current matrix accordingly -// if (oldMisAlignArray) { - -// const AliAlignObjMatrix* oldAlignObj(0); -// const int detElemId(detElement->GetId()); -// const Int_t volId = AliGeomManager::LayerToVolUID(AliGeomManager::kMUON, detElemId); -// for (Int_t pos = 0; pos < oldMisAlignArray->GetEntriesFast(); ++pos) { - -// const AliAlignObjMatrix* localAlignObj(dynamic_cast(oldMisAlignArray->At(pos))); -// if (localAlignObj && localAlignObj->GetVolUID() == volId) { -// oldAlignObj = localAlignObj; -// break; -// } -// } - -// // multiply -// if (oldAlignObj) { - -// TGeoHMatrix oldMatrix; -// oldAlignObj->GetMatrix(oldMatrix); -// deltaGlobalTransform.Multiply(&oldMatrix); -// } -// } - -// // Create misalignment matrix -// newGeometryTransformer->AddMisAlignDetElement(detElement->GetId(), deltaGlobalTransform); - -// } else { - -// // "invalid" detector elements come from MTR and are left unchanged -// Aliinfo(Form("Keeping detElement %i unchanged", iDetElemId)); - -// // update module -// TGeoHMatrix globalTransform(*detElement->GetGlobalTransformation()); -// newDetElement->SetGlobalTransformation(globalTransform); -// newModuleTransformer->GetDetElementStore()->Add(newDetElement->GetId(), newDetElement); - -// // Get matching old alignment and update current matrix accordingly -// if (oldMisAlignArray) { - -// const AliAlignObjMatrix* oldAlignObj(0); -// const int detElemId(detElement->GetId()); -// const Int_t volId = AliGeomManager::LayerToVolUID(AliGeomManager::kMUON, detElemId); -// for (Int_t pos = 0; pos < oldMisAlignArray->GetEntriesFast(); ++pos) { - -// const AliAlignObjMatrix* localAlignObj(dynamic_cast(oldMisAlignArray->At(pos))); -// if (localAlignObj && localAlignObj->GetVolUID() == volId) { -// oldAlignObj = localAlignObj; -// break; -// } -// } - -// // multiply -// if (oldAlignObj) { - -// TGeoHMatrix oldMatrix; -// oldAlignObj->GetMatrix(oldMatrix); -// newGeometryTransformer->AddMisAlignDetElement(detElement->GetId(), oldMatrix); -// } -// } -// } -// } - -// newGeometryTransformer->AddModuleTransformer(newModuleTransformer); -// } - -// return newGeometryTransformer; -// } - -//______________________________________________________________________ -void Alignment::SetAlignmentResolution(const TClonesArray* misAlignArray, Int_t rChId, Double_t chResX, Double_t chResY, Double_t deResX, Double_t deResY) -{ - - /// Set alignment resolution to misalign objects to be stored in CDB - /// if rChId is > 0 set parameters for this chamber only, counting from 1 - TMatrixDSym mChCorrMatrix(6); - mChCorrMatrix[0][0] = chResX * chResX; - mChCorrMatrix[1][1] = chResY * chResY; - - TMatrixDSym mDECorrMatrix(6); - mDECorrMatrix[0][0] = deResX * deResX; - mDECorrMatrix[1][1] = deResY * deResY; - - o2::detectors::AlignParam* alignMat = 0x0; - - for (Int_t chId = 0; chId <= 9; ++chId) { - - // skip chamber if selection is valid, and does not match - if (rChId > 0 && chId + 1 != rChId) - continue; - - TString chName1; - TString chName2; - if (chId < 4) { - - chName1 = Form("GM%d", chId); - chName2 = Form("GM%d", chId); - - } else { - - chName1 = Form("GM%d", 4 + (chId - 4) * 2); - chName2 = Form("GM%d", 4 + (chId - 4) * 2 + 1); - } - - for (int i = 0; i < misAlignArray->GetEntries(); ++i) { - - alignMat = (o2::detectors::AlignParam*)misAlignArray->At(i); - TString volName(alignMat->getSymName()); - if ((volName.Contains(chName1) && - ((volName.Last('/') == volName.Index(chName1) + chName1.Length()) || - (volName.Length() == volName.Index(chName1) + chName1.Length()))) || - (volName.Contains(chName2) && - ((volName.Last('/') == volName.Index(chName2) + chName2.Length()) || - (volName.Length() == volName.Index(chName2) + chName2.Length())))) { - - volName.Remove(0, volName.Last('/') + 1); - // if (volName.Contains("GM")){ - // alignMat->SetCorrMatrix(mChCorrMatrix); - // }else if (volName.Contains("DE")){ - // alignMat->SetCorrMatrix(mDECorrMatrix); - // } - } - } - } -} - -//_____________________________________________________ -LocalTrackParam Alignment::RefitStraightTrack(Track& track, Double_t z0) const -{ - - // initialize matrices - TMatrixD AtGASum(4, 4); - AtGASum.Zero(); - - TMatrixD AtGMSum(4, 1); - AtGMSum.Zero(); - - // loop over clusters - for (auto itTrackParam(track.begin()); itTrackParam != track.end(); ++itTrackParam) { - - // get track parameters - if (!&*itTrackParam) - continue; - - // get cluster - const Cluster* cluster = itTrackParam->getClusterPtr(); - if (!cluster) - continue; - - // projection matrix - TMatrixD A(2, 4); - A.Zero(); - A(0, 0) = 1; - A(0, 2) = (cluster->getZ() - z0); - A(1, 1) = 1; - A(1, 3) = (cluster->getZ() - z0); - - TMatrixD At(TMatrixD::kTransposed, A); - - // gain matrix - TMatrixD G(2, 2); - G.Zero(); - G(0, 0) = 1.0 / Square(cluster->getEx()); - G(1, 1) = 1.0 / Square(cluster->getEy()); - - const TMatrixD AtG(At, TMatrixD::kMult, G); - const TMatrixD AtGA(AtG, TMatrixD::kMult, A); - AtGASum += AtGA; - - // measurement - TMatrixD M(2, 1); - M(0, 0) = cluster->getX(); - M(1, 0) = cluster->getY(); - const TMatrixD AtGM(AtG, TMatrixD::kMult, M); - AtGMSum += AtGM; - } - - // perform inversion - TMatrixD AtGASumInv(TMatrixD::kInverted, AtGASum); - TMatrixD X(AtGASumInv, TMatrixD::kMult, AtGMSum); - - // // TODO: compare with initial track parameters - // Aliinfo( Form( "x: %.3f vs %.3f", fTrackPos0[0], X(0,0) ) ); - // Aliinfo( Form( "y: %.3f vs %.3f", fTrackPos0[1], X(1,0) ) ); - // Aliinfo( Form( "dxdz: %.6g vs %.6g", fTrackSlope0[0], X(2,0) ) ); - // Aliinfo( Form( "dydz: %.6g vs %.6g\n", fTrackSlope0[1], X(3,0) ) ); - - // fill output parameters - LocalTrackParam out; - out.fTrackX = X(0, 0); - out.fTrackY = X(1, 0); - out.fTrackZ = z0; - out.fTrackSlopeX = X(2, 0); - out.fTrackSlopeY = X(3, 0); - - return out; -} - -//_____________________________________________________ -void Alignment::FillDetElemData(const Cluster* cluster) -{ - // LOG(fatal) << __PRETTY_FUNCTION__ << " is disabled"; - LOG(info) << __PRETTY_FUNCTION__ << " is enabled"; - - /// Get information of current detection element - // get detector element number from Alice ID - const Int_t detElemId = cluster->getDEId(); - fDetElemNumber = GetDetElemNumber(detElemId); - - // get detector element - // const AliMUONGeometryDetElement detElement(detElemId); - auto fTransform = fTransformCreator(detElemId); - /* - get the global transformation matrix and store its inverse, in order to manually perform - the global to Local transformations needed to calculate the derivatives - */ - // fTransform = fTransform.Inverse(); - // fTransform.GetTransformMatrix(fGeoCombiTransInverse); -} - -//______________________________________________________________________ -void Alignment::FillRecPointData(const Cluster* cluster) -{ - - /// Get information of current cluster - fClustPos[0] = cluster->getX(); - fClustPos[1] = cluster->getY(); - fClustPos[2] = cluster->getZ(); -} - -//______________________________________________________________________ -void Alignment::FillTrackParamData(const TrackParam* trackParam) -{ - - /// Get information of current track at current cluster - fTrackPos[0] = trackParam->getNonBendingCoor(); - fTrackPos[1] = trackParam->getBendingCoor(); - fTrackPos[2] = trackParam->getZ(); - fTrackSlope[0] = trackParam->getNonBendingSlope(); - fTrackSlope[1] = trackParam->getBendingSlope(); -} - -//______________________________________________________________________ -void Alignment::LocalEquationX(void) -{ - /// local equation along X - - // 'inverse' (GlobalToLocal) rotation matrix - const Double_t* r(fGeoCombiTransInverse.GetRotationMatrix()); - - // local derivatives - SetLocalDerivative(0, r[0]); - SetLocalDerivative(1, r[0] * (fTrackPos[2] - fTrackPos0[2])); - SetLocalDerivative(2, r[1]); - SetLocalDerivative(3, r[1] * (fTrackPos[2] - fTrackPos0[2])); - - // global derivatives - /* - alignment parameters are - 0: delta_x - 1: delta_y - 2: delta_phiz - 3: delta_z - */ - - SetGlobalDerivative(fDetElemNumber * fgNParCh + 0, -r[0]); - SetGlobalDerivative(fDetElemNumber * fgNParCh + 1, -r[1]); - - if (fBFieldOn) { - - // use local position for derivatives vs 'delta_phi_z' - SetGlobalDerivative(fDetElemNumber * fgNParCh + 2, -r[1] * fTrackPos[0] + r[0] * fTrackPos[1]); - - // use local slopes for derivatives vs 'delta_z' - SetGlobalDerivative(fDetElemNumber * fgNParCh + 3, r[0] * fTrackSlope[0] + r[1] * fTrackSlope[1]); - - } else { - - // local copy of extrapolated track positions - const Double_t trackPosX = fTrackPos0[0] + fTrackSlope0[0] * (fTrackPos[2] - fTrackPos0[2]); - const Double_t trackPosY = fTrackPos0[1] + fTrackSlope0[1] * (fTrackPos[2] - fTrackPos0[2]); - - // use properly extrapolated position for derivatives vs 'delta_phi_z' - SetGlobalDerivative(fDetElemNumber * fgNParCh + 2, -r[1] * trackPosX + r[0] * trackPosY); - - // use slopes at origin for derivatives vs 'delta_z' - SetGlobalDerivative(fDetElemNumber * fgNParCh + 3, r[0] * fTrackSlope0[0] + r[1] * fTrackSlope0[1]); - } - - // store local equation - fMillepede->SetLocalEquation(fGlobalDerivatives, fLocalDerivatives, fMeas[0], fSigma[0]); -} - -//______________________________________________________________________ -void Alignment::LocalEquationY(void) -{ - /// local equation along Y - - // 'inverse' (GlobalToLocal) rotation matrix - const Double_t* r(fGeoCombiTransInverse.GetRotationMatrix()); - - // store local derivatives - SetLocalDerivative(0, r[3]); - SetLocalDerivative(1, r[3] * (fTrackPos[2] - fTrackPos0[2])); - SetLocalDerivative(2, r[4]); - SetLocalDerivative(3, r[4] * (fTrackPos[2] - fTrackPos0[2])); - - // set global derivatives - SetGlobalDerivative(fDetElemNumber * fgNParCh + 0, -r[3]); - SetGlobalDerivative(fDetElemNumber * fgNParCh + 1, -r[4]); - - if (fBFieldOn) { - - // use local position for derivatives vs 'delta_phi' - SetGlobalDerivative(fDetElemNumber * fgNParCh + 2, -r[4] * fTrackPos[0] + r[3] * fTrackPos[1]); - - // use local slopes for derivatives vs 'delta_z' - SetGlobalDerivative(fDetElemNumber * fgNParCh + 3, r[3] * fTrackSlope[0] + r[4] * fTrackSlope[1]); - - } else { - - // local copy of extrapolated track positions - const Double_t trackPosX = fTrackPos0[0] + fTrackSlope0[0] * (fTrackPos[2] - fTrackPos0[2]); - const Double_t trackPosY = fTrackPos0[1] + fTrackSlope0[1] * (fTrackPos[2] - fTrackPos0[2]); - - // use properly extrapolated position for derivatives vs 'delta_phi' - SetGlobalDerivative(fDetElemNumber * fgNParCh + 2, -r[4] * trackPosX + r[3] * trackPosY); - - // use slopes at origin for derivatives vs 'delta_z' - SetGlobalDerivative(fDetElemNumber * fgNParCh + 3, r[3] * fTrackSlope0[0] + r[4] * fTrackSlope0[1]); - } - - // store local equation - fMillepede->SetLocalEquation(fGlobalDerivatives, fLocalDerivatives, fMeas[1], fSigma[1]); -} - -//_________________________________________________________________________ -TGeoCombiTrans Alignment::DeltaTransform(const double* lMisAlignment) const -{ - /// Get Delta Transformation, based on alignment parameters - - // translation - const TGeoTranslation deltaTrans(lMisAlignment[0], lMisAlignment[1], lMisAlignment[3]); - - // rotation - TGeoRotation deltaRot; - deltaRot.RotateZ(lMisAlignment[2] * 180. / TMath::Pi()); - - // combined rotation and translation. - return TGeoCombiTrans(deltaTrans, deltaRot); -} - -//______________________________________________________________________ -void Alignment::AddConstraint(Double_t* par, Double_t value) -{ - /// Constrain equation defined by par to value - if (!fInitialized) { - LOG(fatal) << "Millepede is not initialized"; - } - - fMillepede->SetGlobalConstraint(par, value); -} - -//______________________________________________________________________ -Bool_t Alignment::DetElemIsValid(Int_t iDetElemId) const -{ - /// return true if given detector element is valid (and belongs to muon tracker) - const Int_t iCh = iDetElemId / 100; - const Int_t iDet = iDetElemId % 100; - return (iCh > 0 && iCh <= fgNCh && iDet < fgNDetElemCh[iCh - 1]); -} - -//______________________________________________________________________ -Int_t Alignment::GetDetElemNumber(Int_t iDetElemId) const -{ - /// get det element number from ID - // get chamber and element number in chamber - const Int_t iCh = iDetElemId / 100; - const Int_t iDet = iDetElemId % 100; - - // make sure detector index is valid - if (!(iCh > 0 && iCh <= fgNCh && iDet < fgNDetElemCh[iCh - 1])) { - LOG(fatal) << "Invalid detector element id: " << iDetElemId; - } - - // add number of detectors up to this chamber - return iDet + fgSNDetElemCh[iCh - 1]; -} - -//______________________________________________________________________ -Int_t Alignment::GetChamberId(Int_t iDetElemNumber) const -{ - /// get chamber (counting from 1) matching a given detector element id - Int_t iCh(0); - for (iCh = 0; iCh < fgNCh; iCh++) { - if (iDetElemNumber < fgSNDetElemCh[iCh]) - break; - } - - return iCh; -} - -//______________________________________________________________________ -TString Alignment::GetParameterMaskString(UInt_t mask) const -{ - TString out; - if (mask & ParX) - out += "X"; - if (mask & ParY) - out += "Y"; - if (mask & ParZ) - out += "Z"; - if (mask & ParTZ) - out += "T"; - return out; -} - -//______________________________________________________________________ -TString Alignment::GetSidesMaskString(UInt_t mask) const -{ - TString out; - if (mask & SideTop) - out += "T"; - if (mask & SideLeft) - out += "L"; - if (mask & SideBottom) - out += "B"; - if (mask & SideRight) - out += "R"; - return out; -} - -} // namespace mch -} // namespace o2 \ No newline at end of file diff --git a/Detectors/ITSMFT/MFT/calibration/src/NoiseSlotCalibrator.cxx b/Detectors/ITSMFT/MFT/calibration/src/NoiseSlotCalibrator.cxx deleted file mode 100644 index 13d6f3b3f567b..0000000000000 --- a/Detectors/ITSMFT/MFT/calibration/src/NoiseSlotCalibrator.cxx +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file NoiseSlotCalibrator.cxx - -#include "MFTCalibration/NoiseSlotCalibrator.h" - -#include -#include "TFile.h" -#include "DataFormatsITSMFT/Digit.h" -#include "DataFormatsITSMFT/ClusterPattern.h" -#include "DataFormatsITSMFT/ROFRecord.h" - -namespace o2 -{ -using Slot = calibration::TimeSlot; - -namespace mft -{ -bool NoiseSlotCalibrator::processTimeFrame(calibration::TFType nTF, - gsl::span const& digits, - gsl::span const& rofs) -{ - LOG(detail) << "Processing TF# " << nTF; - - auto& slotTF = getSlotForTF(nTF); - auto& noiseMap = *(slotTF.getContainer()); - - for (const auto& rof : rofs) { - auto digitsInFrame = rof.getROFData(digits); - for (const auto& d : digitsInFrame) { - auto id = d.getChipIndex(); - auto row = d.getRow(); - auto col = d.getColumn(); - - noiseMap.increaseNoiseCount(id, row, col); - } - } - noiseMap.addStrobes(rofs.size()); - mNumberOfStrobes += rofs.size(); - return hasEnoughData(slotTF); -} - -bool NoiseSlotCalibrator::processTimeFrame(calibration::TFType nTF, - gsl::span const& clusters, - gsl::span const& patterns, - gsl::span const& rofs) -{ - LOG(detail) << "Processing TF# " << nTF; - - auto& slotTF = getSlotForTF(nTF); - auto& noiseMap = *(slotTF.getContainer()); - - auto pattIt = patterns.begin(); - for (const auto& rof : rofs) { - auto clustersInFrame = rof.getROFData(clusters); - for (const auto& c : clustersInFrame) { - if (c.getPatternID() != o2::itsmft::CompCluster::InvalidPatternID) { - // For the noise calibration, we use "pass1" clusters... - continue; - } - o2::itsmft::ClusterPattern patt(pattIt); - - auto id = c.getSensorID(); - auto row = c.getRow(); - auto col = c.getCol(); - auto colSpan = patt.getColumnSpan(); - auto rowSpan = patt.getRowSpan(); - - // Fast 1-pixel calibration - if ((rowSpan == 1) && (colSpan == 1)) { - noiseMap.increaseNoiseCount(id, row, col); - continue; - } - - // All-pixel calibration - auto nBits = rowSpan * colSpan; - int ic = 0, ir = 0; - for (unsigned int i = 2; i < patt.getUsedBytes() + 2; i++) { - unsigned char tempChar = patt.getByte(i); - int s = 128; // 0b10000000 - while (s > 0) { - if ((tempChar & s) != 0) { - noiseMap.increaseNoiseCount(id, row + ir, col + ic); - } - ic++; - s >>= 1; - if ((ir + 1) * ic == nBits) { - break; - } - if (ic == colSpan) { - ic = 0; - ir++; - } - } - if ((ir + 1) * ic == nBits) { - break; - } - } - } - } - noiseMap.addStrobes(rofs.size()); - mNumberOfStrobes += rofs.size(); - return hasEnoughData(slotTF); -} - -// Functions overloaded from the calibration framework -bool NoiseSlotCalibrator::process(calibration::TFType tf, const gsl::span data) -{ - LOG(warning) << "Only 1-pix noise calibraton is possible !"; - return calibration::TimeSlotCalibration::process(tf, data); -} - -// Functions required by the calibration framework - -Slot& NoiseSlotCalibrator::emplaceNewSlot(bool front, calibration::TFType tstart, calibration::TFType tend) -{ - auto& cont = getSlots(); - auto& slot = front ? cont.emplace_front(tstart, tend) : cont.emplace_back(tstart, tend); - slot.setContainer(std::make_unique(936)); - return slot; -} - -bool NoiseSlotCalibrator::hasEnoughData(const Slot& slot) const -{ - return slot.getContainer()->getNumberOfStrobes() > mMinROFs ? true : false; -} - -void NoiseSlotCalibrator::finalizeSlot(Slot& slot) -{ - o2::itsmft::NoiseMap* map = slot.getContainer(); - LOG(info) << "Number of processed strobes is " << map->getNumberOfStrobes(); - map->applyProbThreshold(mProbabilityThreshold, map->getNumberOfStrobes(), mProbRelErr); -} - -} // namespace mft -} // namespace o2 From 6d11591da421e8c200b3368a41727752882e4db1 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Wed, 11 Feb 2026 10:09:52 +0100 Subject: [PATCH 087/133] ITS: GPU: use mean vertex constraint for gpu processing I noticed that in the Pb-Pb production we did not add the mean vertex constraint to be used. --- prodtests/full-system-test/dpl-workflow.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index f559fcdf91cf5..a8f01a3ef1822 100755 --- a/prodtests/full-system-test/dpl-workflow.sh +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -572,6 +572,9 @@ if [[ $CTFINPUT == 0 && $DIGITINPUT == 0 ]]; then fi has_detector_gpu ITS && GPU_INPUT+=",its-clusters" +if [[ $BEAMTYPE != "cosmic" && $SYNCMODE != 1 ]]; then + has_detector_gpu ITS && GPU_INPUT+=",its-mean-vertex" +fi has_detector_gpu ITS && GPU_OUTPUT+=",its-tracks" # --------------------------------------------------------------------------------------------------------------------- From 7c79e17a0c694491c65c3033f976084f85cca8d7 Mon Sep 17 00:00:00 2001 From: Matthias Kleiner Date: Wed, 11 Feb 2026 09:44:56 +0100 Subject: [PATCH 088/133] TPC: time gain calibration optimizations - bug fix: using bin centre of the dE/dx instead of lower bin edge - add option to not perform per sector scaling (needed for MC) --- .../DataFormatsTPC/CalibdEdxCorrection.h | 3 +++ .../Detectors/TPC/src/CalibdEdxCorrection.cxx | 13 +++++++++++++ .../include/TPCCalibration/CalibdEdx.h | 4 +++- Detectors/TPC/calibration/src/CalibdEdx.cxx | 17 +++++++++++------ 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h index 1d7b10dc965f7..024d6189593e9 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h @@ -115,6 +115,9 @@ class CalibdEdxCorrection /// Single fit parameters averaged over all sectors for a stack type float getMeanEntries(const GEMstack stack, ChargeType charge) const; + /// set all corrections to 1, used for default initialization and to reset corrections + void setUnity(); + #endif private: diff --git a/DataFormats/Detectors/TPC/src/CalibdEdxCorrection.cxx b/DataFormats/Detectors/TPC/src/CalibdEdxCorrection.cxx index 0991c8693d8e8..152feacb41937 100644 --- a/DataFormats/Detectors/TPC/src/CalibdEdxCorrection.cxx +++ b/DataFormats/Detectors/TPC/src/CalibdEdxCorrection.cxx @@ -168,3 +168,16 @@ float CalibdEdxCorrection::getMeanEntries(const GEMstack stack, ChargeType charg return mean / (SECTORSPERSIDE * SIDES); } + +void CalibdEdxCorrection::setUnity() +{ + for (int i = 0; i < FitSize; ++i) { + for (int j = 0; j < ParamSize; ++j) { + mParams[i][j] = 0.f; + } + mParams[i][0] = 1.f; // constant term = 1 + mChi2[i] = 0.f; + mEntries[i] = 0; + } + mDims = 0; +} diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CalibdEdx.h b/Detectors/TPC/calibration/include/TPCCalibration/CalibdEdx.h index 20e470702a89a..ff7c763efcd2b 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/CalibdEdx.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/CalibdEdx.h @@ -129,7 +129,9 @@ class CalibdEdx /// Compute MIP position from dEdx histograms and save result in the correction container. /// To retrieve the correction call `CalibdEdx::getCalib()` /// \param useGausFits make gaussian fits of dEdx vs tgl instead of fitting the mean dEdx - void finalize(const bool useGausFits = true); + /// \param averageSectors If true, the correction is averaged over all sectors. + /// In this case, no mean-sector scaling is applied when statistics are low. + void finalize(const bool useGausFits = true, const bool averageSectors = false); /// Return calib data histogram const Hist& getHist() const { return mHist; } diff --git a/Detectors/TPC/calibration/src/CalibdEdx.cxx b/Detectors/TPC/calibration/src/CalibdEdx.cxx index 4eb29c8833565..938ab8ae91065 100644 --- a/Detectors/TPC/calibration/src/CalibdEdx.cxx +++ b/Detectors/TPC/calibration/src/CalibdEdx.cxx @@ -351,7 +351,7 @@ auto ProjectBoostHistoXFastAllSectors(const Hist& hist, std::vector& bin_in // access the bin content specified by bin_indices const float counts = hist.at(bin_indices); - float dEdx = hist.axis(ax::dEdx).value(i); + float dEdx = hist.axis(ax::dEdx).bin(i).center(); // scale the dedx to the mean if (stackMean != nullptr) { @@ -532,7 +532,7 @@ void CalibdEdx::fitHistGaus(TLinearFitter& fitter, CalibdEdxCorrection& corr, co LOGP(info, "Calibration fits took: {}", time.count()); } -void CalibdEdx::finalize(const bool useGausFits) +void CalibdEdx::finalize(const bool useGausFits, const bool averageSectors) { const float entries = minStackEntries(); mCalib.clear(); @@ -565,10 +565,15 @@ void CalibdEdx::finalize(const bool useGausFits) // get mean of each GEM stack CalibdEdxCorrection meanCorr{}; meanCorr.setDims(0); - TLinearFitter meanFitter(0); - meanFitter.SetFormula("1"); - // get the mean dEdx for each stack - fitHist(mHist, meanCorr, meanFitter, mFitCut, mFitLowCutFactor, mFitPasses); + if (averageSectors) { + // set mean dEdx per stack to unity + meanCorr.setUnity(); + } else { + // get the mean dEdx for each stack + TLinearFitter meanFitter(0); + meanFitter.SetFormula("1"); + fitHist(mHist, meanCorr, meanFitter, mFitCut, mFitLowCutFactor, mFitPasses, nullptr, mDebugOutputStreamer.get()); + } if (!useGausFits) { // get higher dimension corrections with projected sectors fitHist(mHist, mCalib, fitter, mFitCut, mFitLowCutFactor, mFitPasses, &meanCorr, mDebugOutputStreamer.get()); From a1b356bc9af763d7c8bd7893f04b17d7202f8457 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 12 Feb 2026 00:11:38 +0100 Subject: [PATCH 089/133] GPU Vulkan Display: fix DEPFILE path --- GPU/GPUTracking/cmake/vulkan_display.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/GPUTracking/cmake/vulkan_display.cmake b/GPU/GPUTracking/cmake/vulkan_display.cmake index 7cbfd0328c323..7859742363755 100644 --- a/GPU/GPUTracking/cmake/vulkan_display.cmake +++ b/GPU/GPUTracking/cmake/vulkan_display.cmake @@ -27,7 +27,7 @@ function(add_glslc_shader TARGET SHADER) OUTPUT ${spirv-file} COMMAND ${Vulkan_GLSLC_EXECUTABLE} -o ${spirv-file} ${input-file-abs} -MD -MT ${spirv-file} -MF ${spirv-file}.d DEPENDS ${input-file-abs} - DEPFILE ${input-file-abs}.d + DEPFILE ${spirv-file}.d COMMENT "Compiling GLSL to SPIRV: ${SHADER}" VERBATIM ) From bf2a3feb26a00127d99b08b9185b3a7a1fc30e15 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 11 Feb 2026 15:52:03 +0100 Subject: [PATCH 090/133] DCS: Fix undefined behavior and invalid pointer access --- Detectors/DCS/src/DataPointCreator.cxx | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/Detectors/DCS/src/DataPointCreator.cxx b/Detectors/DCS/src/DataPointCreator.cxx index 06b0321ad2c94..0bfb5bcd7d387 100644 --- a/Detectors/DCS/src/DataPointCreator.cxx +++ b/Detectors/DCS/src/DataPointCreator.cxx @@ -37,10 +37,9 @@ DataPointCompositeObject createDataPointCompositeObject(const std::string& alias template <> DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, float val, uint32_t seconds, uint16_t msec, uint16_t flags) { - float tmp[2]; - tmp[0] = val; - tmp[1] = 0; - return createDPCOM(alias, reinterpret_cast(&tmp[0]), seconds, msec, flags, DeliveryType::DPVAL_FLOAT); + uint64_t tmp = 0; + memcpy(&tmp, &val, sizeof(val)); + return createDPCOM(alias, &tmp, seconds, msec, flags, DeliveryType::DPVAL_FLOAT); } template <> @@ -54,36 +53,38 @@ template <> DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, uint32_t val, uint32_t seconds, uint16_t msec, uint16_t flags) { uint64_t tmp{val}; - return createDPCOM(alias, reinterpret_cast(&tmp), seconds, msec, flags, DeliveryType::DPVAL_UINT); + return createDPCOM(alias, &tmp, seconds, msec, flags, DeliveryType::DPVAL_UINT); } template <> DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, long long val, uint32_t seconds, uint16_t msec, uint16_t flags) { uint64_t tmp{static_cast(val)}; - return createDPCOM(alias, reinterpret_cast(&tmp), seconds, msec, flags, DeliveryType::DPVAL_UINT); + return createDPCOM(alias, &tmp, seconds, msec, flags, DeliveryType::DPVAL_UINT); } template <> DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, char val, uint32_t seconds, uint16_t msec, uint16_t flags) { - return createDPCOM(alias, reinterpret_cast(&val), seconds, msec, flags, DeliveryType::DPVAL_CHAR); + uint64_t tmp = 0; + memcpy(&tmp, &val, 1); + return createDPCOM(alias, &tmp, seconds, msec, flags, DeliveryType::DPVAL_CHAR); } template <> DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, bool val, uint32_t seconds, uint16_t msec, uint16_t flags) { uint64_t tmp{val}; - return createDPCOM(alias, reinterpret_cast(&tmp), seconds, msec, flags, DeliveryType::DPVAL_BOOL); + return createDPCOM(alias, &tmp, seconds, msec, flags, DeliveryType::DPVAL_BOOL); } template <> DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, std::string val, uint32_t seconds, uint16_t msec, uint16_t flags) { constexpr int N{56}; - char str[N]; - strncpy(str, val.c_str(), N); - return createDPCOM(alias, reinterpret_cast(&str[0]), seconds, msec, flags, DeliveryType::DPVAL_STRING); + uint64_t tmp[N / sizeof(uint64_t)]; + strncpy((char*)tmp, val.c_str(), N); + return createDPCOM(alias, tmp, seconds, msec, flags, DeliveryType::DPVAL_STRING); } } // namespace o2::dcs From 6ef43cf21196279d3c6e7cb507e51e4274b015ac Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Thu, 12 Feb 2026 09:01:29 +0100 Subject: [PATCH 091/133] ITS3: load chip response functions from ccdb (#15051) Signed-off-by: Felix Schlepper --- .../ITS3/macros/test/CheckChipResponseFile.C | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/Detectors/Upgrades/ITS3/macros/test/CheckChipResponseFile.C b/Detectors/Upgrades/ITS3/macros/test/CheckChipResponseFile.C index 32d5bad87ce21..5bc053c516079 100644 --- a/Detectors/Upgrades/ITS3/macros/test/CheckChipResponseFile.C +++ b/Detectors/Upgrades/ITS3/macros/test/CheckChipResponseFile.C @@ -22,6 +22,7 @@ #include #include +#include "CCDB/BasicCCDBManager.h" #define ENABLE_UPGRADES #include "ITSMFTSimulation/AlpideSimResponse.h" #include "ITS3Simulation/ChipSimResponse.h" @@ -37,16 +38,12 @@ double cm2um(double cm) { return cm * 1e+4; } std::unique_ptr mAlpSimResp0, mAlpSimResp1, mAptSimResp1; -std::unique_ptr loadResponse(const std::string& fileName, const std::string& respName) +std::unique_ptr loadResponse(const std::string& path) { - TFile* f = TFile::Open(fileName.data()); - if (!f) { - std::cerr << fileName << " not found" << std::endl; - return nullptr; - } - auto base = f->Get(respName.c_str()); + auto& cdb = o2::ccdb::BasicCCDBManager::instance(); + o2::itsmft::AlpideSimResponse* base = cdb.get(path); if (!base) { - std::cerr << respName << " not found in " << fileName << std::endl; + std::cerr << path << " not found in " << '\n'; return nullptr; } return std::make_unique(base); @@ -54,24 +51,24 @@ std::unique_ptr loadResponse(const std::string& fileN void LoadRespFunc() { - std::string AptsFile = "$(O2_ROOT)/share/Detectors/Upgrades/ITS3/data/ITS3ChipResponseData/APTSResponseData.root"; - std::string AlpideFile = "$(O2_ROOT)/share/Detectors/ITSMFT/data/AlpideResponseData/AlpideResponseData.root"; + auto& cdb = o2::ccdb::BasicCCDBManager::instance(); + cdb.setURL("https://alice-ccdb.cern.ch/"); std::cout << "=====================\n"; LOGP(info, "ALPIDE Vbb=0V response"); - mAlpSimResp0 = loadResponse(AlpideFile, "response0"); // Vbb=0V + mAlpSimResp0 = loadResponse("ITSMFT/Calib/ALPIDEResponseVbb0"); // Vbb=0V mAlpSimResp0->computeCentreFromData(); mAlpSimResp0->print(); LOGP(info, "Response Centre {}", mAlpSimResp0->getRespCentreDep()); std::cout << "=====================\n"; LOGP(info, "ALPIDE Vbb=-3V response"); - mAlpSimResp1 = loadResponse(AlpideFile, "response1"); // Vbb=-3V + mAlpSimResp1 = loadResponse("ITSMFT/Calib/ALPIDEResponseVbbM3"); // Vbb=-3V mAlpSimResp1->computeCentreFromData(); mAlpSimResp1->print(); LOGP(info, "Response Centre {}", mAlpSimResp1->getRespCentreDep()); std::cout << "=====================\n"; LOGP(info, "APTS response"); - mAptSimResp1 = loadResponse(AptsFile, "response1"); // APTS + mAptSimResp1 = loadResponse("IT3/Calib/APTSResponse"); // APTS mAptSimResp1->computeCentreFromData(); mAptSimResp1->print(); LOGP(info, "Response Centre {}", mAptSimResp1->getRespCentreDep()); From 93cae7b66005fa79f1ecb5bd58e49d52e7cb33e8 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Thu, 12 Feb 2026 09:01:54 +0100 Subject: [PATCH 092/133] ITS3: split longerons, improving stepping speed (#15052) Signed-off-by: Felix Schlepper --- .../ITS3/base/include/ITS3Base/SpecsV2.h | 2 +- .../include/ITS3Simulation/ITS3Layer.h | 5 +- .../ITS3/simulation/src/ITS3Layer.cxx | 68 +++++++++---------- 3 files changed, 37 insertions(+), 38 deletions(-) diff --git a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h index a7422c55e72b8..937fa8d2e982c 100644 --- a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h +++ b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h @@ -104,7 +104,7 @@ namespace carbonfoam // TODO: Waiting for the further information from WP5(Corrado) constexpr double HringLength{6.0 * mm}; // from blueprint constexpr double longeronsWidth{2.0 * mm}; // what is the height of the longerons? -constexpr double longeronsLength{segment::length - 2 * HringLength}; // 263mm from blueprint; overrriden to be consitent +constexpr double longeronsLength{segment::length - (2 * HringLength)}; // 263mm from blueprint; overrriden to be consitent constexpr double edgeBetwChipAndFoam{1.0 * mm}; // from blueprint but not used cause forms are already overlapping constexpr double gapBetwHringsLongerons{0.05 * mm}; // from blueprint constexpr std::array nHoles{11, 11, 11}; // how many holes for each layer? diff --git a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ITS3Layer.h b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ITS3Layer.h index fd9195f9ee228..f45a4469ae2b8 100644 --- a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ITS3Layer.h +++ b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ITS3Layer.h @@ -26,7 +26,7 @@ namespace o2::its3 { /// This class defines the geometry for the ITS3 IB layers. -class ITS3Layer +class ITS3Layer final { // The hierarchy will be the following: // ITS2 -> ITS3 @@ -76,7 +76,6 @@ class ITS3Layer void buildPartial(TGeoVolume* motherVolume, TGeoMatrix* mat = nullptr, BuildLevel level = BuildLevel::kAll, bool createMaterials = false); private: - bool mBuilt{false}; TGeoMedium* mSilicon{nullptr}; TGeoMedium* mAir{nullptr}; TGeoMedium* mCarbon{nullptr}; @@ -91,7 +90,7 @@ class ITS3Layer void createSegment(); void createChip(); void createCarbonForm(); - TGeoCompositeShape* getHringShape(TGeoTubeSeg* Hring); + TGeoCompositeShape* getHringShape(TGeoTubeSeg* Hring) const; void createLayerImpl(); uint8_t mNLayer{0}; // Layer number diff --git a/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx b/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx index e0be011096450..c0f8fdc19d03b 100644 --- a/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx +++ b/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx @@ -67,11 +67,11 @@ void ITS3Layer::createLayer(TGeoVolume* motherVolume) // Create one layer of ITS3 and attach it to the motherVolume. getMaterials(); createLayerImpl(); - mBuilt = true; if (motherVolume == nullptr) { return; } + // Add it to motherVolume auto* trans = new TGeoTranslation(0, 0, -constants::segment::lengthSensitive / 2.); motherVolume->AddNode(mLayer, 0, trans); @@ -122,8 +122,8 @@ void ITS3Layer::createTile() mTile->AddNode(mPixelArray, 0, phiRotPixelArray); // Biasing - double biasPhi1 = constants::pixelarray::width / mR * o2m::Rad2Deg + readoutPhi2; - double biasPhi2 = biasing::width / mR * o2m::Rad2Deg + biasPhi1; + double biasPhi1 = (constants::pixelarray::width / mR * o2m::Rad2Deg) + readoutPhi2; + double biasPhi2 = (biasing::width / mR * o2m::Rad2Deg) + biasPhi1; auto biasing = new TGeoTubeSeg(mRmin, mRmax, biasing::length / 2, biasPhi1, biasPhi2); auto biasingVol = new TGeoVolume(Form("biasing%d", mNLayer), biasing, mSilicon); biasingVol->SetLineColor(biasing::color); @@ -131,9 +131,9 @@ void ITS3Layer::createTile() mTile->AddNode(biasingVol, 0); // Power Switches are on the side right side of the pixel array and biasing. - auto zMovePowerSwitches = new TGeoTranslation(0, 0, +powerswitches::length / 2. + constants::pixelarray::length / 2.); + auto zMovePowerSwitches = new TGeoTranslation(0, 0, (+powerswitches::length / 2.) + (constants::pixelarray::length / 2.)); double powerPhi1 = readoutPhi2; - double powerPhi2 = powerswitches::width / mR * o2m::Rad2Deg + powerPhi1; + double powerPhi2 = (powerswitches::width / mR * o2m::Rad2Deg) + powerPhi1; auto powerSwitches = new TGeoTubeSeg(mRmin, mRmax, powerswitches::length / 2, powerPhi1, powerPhi2); auto powerSwitchesVol = new TGeoVolume(Form("powerswitches%d", mNLayer), powerSwitches, mSilicon); powerSwitchesVol->SetLineColor(powerswitches::color); @@ -166,7 +166,7 @@ void ITS3Layer::createRSU() // Lower Left auto zMoveLL1 = new TGeoTranslation(0, 0, constants::tile::length); auto zMoveLL2 = new TGeoTranslation(0, 0, constants::tile::length * 2.); - auto zMoveLLDB = new TGeoTranslation(0, 0, -databackbone::length / 2. - constants::pixelarray::length / 2.); + auto zMoveLLDB = new TGeoTranslation(0, 0, (-databackbone::length / 2.) - (constants::pixelarray::length / 2.)); // Lets attach the tiles to the QS. mRSU->AddNode(mTile, nCopyRSU++, nullptr); mRSU->AddNode(mTile, nCopyRSU++, zMoveLL1); @@ -175,9 +175,9 @@ void ITS3Layer::createRSU() // Lower Right auto zMoveLR0 = new TGeoTranslation(0, 0, +length / 2.); - auto zMoveLR1 = new TGeoTranslation(0, 0, constants::tile::length + length / 2.); - auto zMoveLR2 = new TGeoTranslation(0, 0, constants::tile::length * 2. + length / 2.); - auto zMoveLRDB = new TGeoTranslation(0, 0, -databackbone::length / 2. + length / 2. - constants::pixelarray::length / 2.); + auto zMoveLR1 = new TGeoTranslation(0, 0, constants::tile::length + (length / 2.)); + auto zMoveLR2 = new TGeoTranslation(0, 0, (constants::tile::length * 2.) + (length / 2.)); + auto zMoveLRDB = new TGeoTranslation(0, 0, (-databackbone::length / 2.) + (length / 2.) - (constants::pixelarray::length / 2.)); // Lets attach the tiles to the QS. mRSU->AddNode(mTile, nCopyRSU++, zMoveLR0); mRSU->AddNode(mTile, nCopyRSU++, zMoveLR1); @@ -192,7 +192,7 @@ void ITS3Layer::createRSU() // Upper Left auto zMoveUL1 = new TGeoCombiTrans(0, 0, constants::tile::length, rot); auto zMoveUL2 = new TGeoCombiTrans(0, 0, constants::tile::length * 2., rot); - auto zMoveULDB = new TGeoCombiTrans(0, 0, -databackbone::length / 2. - constants::pixelarray::length / 2., rot); + auto zMoveULDB = new TGeoCombiTrans(0, 0, (-databackbone::length / 2.) - (constants::pixelarray::length / 2.), rot); // Lets attach the tiles to the QS. mRSU->AddNode(mTile, nCopyRSU++, rot); mRSU->AddNode(mTile, nCopyRSU++, zMoveUL1); @@ -201,9 +201,9 @@ void ITS3Layer::createRSU() // Upper Right auto zMoveUR0 = new TGeoCombiTrans(0, 0, +length / 2., rot); - auto zMoveUR1 = new TGeoCombiTrans(0, 0, constants::tile::length + length / 2., rot); - auto zMoveUR2 = new TGeoCombiTrans(0, 0, constants::tile::length * 2. + length / 2., rot); - auto zMoveURDB = new TGeoCombiTrans(0, 0, -databackbone::length / 2. + length / 2. - constants::pixelarray::length / 2., rot); + auto zMoveUR1 = new TGeoCombiTrans(0, 0, constants::tile::length + (length / 2.), rot); + auto zMoveUR2 = new TGeoCombiTrans(0, 0, (constants::tile::length * 2.) + (length / 2.), rot); + auto zMoveURDB = new TGeoCombiTrans(0, 0, (-databackbone::length / 2.) + (length / 2.) - (constants::pixelarray::length / 2.), rot); // Lets attach the tiles to the QS. mRSU->AddNode(mTile, nCopyRSU++, zMoveUR0); mRSU->AddNode(mTile, nCopyRSU++, zMoveUR1); @@ -225,9 +225,9 @@ void ITS3Layer::createSegment() mSegment = new TGeoVolumeAssembly(its3TGeo::getITS3SegmentPattern(mNLayer)); mSegment->VisibleDaughters(); - for (size_t i{0}; i < nRSUs; ++i) { - auto zMove = new TGeoTranslation(0, 0, +i * constants::rsu::length + constants::rsu::databackbone::length + constants::pixelarray::length / 2.); - mSegment->AddNode(mRSU, i, zMove); + for (unsigned int i{0}; i < nRSUs; ++i) { + auto zMove = new TGeoTranslation(0, 0, (i * constants::rsu::length) + constants::rsu::databackbone::length + (constants::pixelarray::length / 2.)); + mSegment->AddNode(mRSU, (int)i, zMove); } // LEC @@ -242,7 +242,7 @@ void ITS3Layer::createSegment() mSegment->AddNode(lecVol, 0, zMoveLEC); // REC; reuses lecPhi1,2 - auto zMoveREC = new TGeoTranslation(0, 0, nRSUs * constants::rsu::length + rec::length / 2.); + auto zMoveREC = new TGeoTranslation(0, 0, (nRSUs * constants::rsu::length) + (rec::length / 2.)); auto rec = new TGeoTubeSeg(mRmin, mRmax, rec::length / 2., lecPhi1, lecPhi2); auto recVol = new TGeoVolume(Form("rec%d", mNLayer), rec, mSilicon); @@ -266,11 +266,11 @@ void ITS3Layer::createChip() auto phiOffset = constants::segment::width / mR * o2m::Rad2Deg; for (unsigned int i{0}; i < constants::nSegments[mNLayer]; ++i) { auto rot = new TGeoRotation(Form("its3PhiSegmentOffset_%d_%d", mNLayer, i), 0, 0, phiOffset * i); - mChip->AddNode(mSegment, i, rot); + mChip->AddNode(mSegment, (int)i, rot); } // Add metal stack positioned radially outward - auto zMoveMetal = new TGeoTranslation(0, 0, constants::metalstack::length / 2. - constants::segment::lec::length); + auto zMoveMetal = new TGeoTranslation(0, 0, (constants::metalstack::length / 2.) - constants::segment::lec::length); auto metal = new TGeoTubeSeg(mRmax, mRmax + constants::metalstack::thickness, constants::metalstack::length / 2., 0, constants::nSegments[mNLayer] * phiOffset); auto metalVol = new TGeoVolume(Form("metal%d", mNLayer), metal, mCopper); metalVol->SetLineColor(constants::metalstack::color); @@ -296,7 +296,7 @@ void ITS3Layer::createCarbonForm() dRadius = constants::carbonfoam::thicknessOuterFoam; // TODO: lack of carbon foam radius for layer 2, use 0.7 cm as a temporary value } double phiSta = edgeBetwChipAndFoam / (0.5 * constants::radii[mNLayer + 1] + constants::radii[mNLayer]) * o2m::Rad2Deg; - double phiEnd = (constants::nSegments[mNLayer] * constants::segment::width) / constants::radii[mNLayer] * o2m::Rad2Deg - phiSta; + double phiEnd = ((constants::nSegments[mNLayer] * constants::segment::width) / constants::radii[mNLayer] * o2m::Rad2Deg) - phiSta; double phiLongeronsCover = longeronsWidth / (0.5 * constants::radii[mNLayer + 1] + constants::radii[mNLayer]) * o2m::Rad2Deg; // H-rings foam @@ -308,35 +308,37 @@ void ITS3Layer::createCarbonForm() HringCVol->SetLineColor(color); auto HringAVol = new TGeoVolume(Form("hringA%d", mNLayer), HringAWithHoles, mCarbon); HringAVol->SetLineColor(color); - auto zMoveHringC = new TGeoTranslation(0, 0, -constants::segment::lec::length + HringLength / 2.); - auto zMoveHringA = new TGeoTranslation(0, 0, -constants::segment::lec::length + HringLength / 2. + constants::segment::length - HringLength); + auto zMoveHringC = new TGeoTranslation(0, 0, -constants::segment::lec::length + (HringLength / 2.)); + auto zMoveHringA = new TGeoTranslation(0, 0, -constants::segment::lec::length + (HringLength / 2.) + constants::segment::length - HringLength); // Longerons are made by same material + // added separately to make navigation faster [[maybe_unused]] auto longeronR = new TGeoTubeSeg(Form("longeronR%d", mNLayer), mRmax, mRmax + dRadius, longeronsLength / 2., phiSta, phiSta + phiLongeronsCover); [[maybe_unused]] auto longeronL = new TGeoTubeSeg(Form("longeronL%d", mNLayer), mRmax, mRmax + dRadius, longeronsLength / 2., phiEnd - phiLongeronsCover, phiEnd); - TString nameLongerons = Form("longeronR%d + longeronL%d", mNLayer, mNLayer); - auto longerons = new TGeoCompositeShape(nameLongerons); - auto longeronsVol = new TGeoVolume(Form("longerons%d", mNLayer), longerons, mCarbon); - longeronsVol->SetLineColor(color); - auto zMoveLongerons = new TGeoTranslation(0, 0, -constants::segment::lec::length + constants::segment::length / 2.); + auto longeronRVol = new TGeoVolume(Form("longeronR%d", mNLayer), longeronR, mCarbon); + longeronRVol->SetLineColor(color); + auto longeronLVol = new TGeoVolume(Form("longeronL%d", mNLayer), longeronL, mCarbon); + longeronLVol->SetLineColor(color); + auto zMoveLongerons = new TGeoTranslation(0, 0, -constants::segment::lec::length + (constants::segment::length / 2.)); mCarbonForm->AddNode(HringCVol, 0, zMoveHringC); mCarbonForm->AddNode(HringAVol, 0, zMoveHringA); - mCarbonForm->AddNode(longeronsVol, 0, zMoveLongerons); + mCarbonForm->AddNode(longeronRVol, 0, zMoveLongerons); + mCarbonForm->AddNode(longeronLVol, 0, zMoveLongerons); mCarbonForm->AddNode(mChip, 0); } -TGeoCompositeShape* ITS3Layer::getHringShape(TGeoTubeSeg* Hring) +TGeoCompositeShape* ITS3Layer::getHringShape(TGeoTubeSeg* Hring) const { // Function to dig holes in H-rings using namespace constants::carbonfoam; double stepPhiHoles = (Hring->GetPhi2() - Hring->GetPhi1()) / (nHoles[mNLayer]); - double phiHolesSta = Hring->GetPhi1() + stepPhiHoles / 2.; + double phiHolesSta = Hring->GetPhi1() + (stepPhiHoles / 2.); double radiusHring = 0.5 * (Hring->GetRmin() + Hring->GetRmax()); TGeoCompositeShape* HringWithHoles = nullptr; TString nameAllHoles = ""; for (int iHoles = 0; iHoles < nHoles[mNLayer]; iHoles++) { - double phiHole = phiHolesSta + stepPhiHoles * iHoles; + double phiHole = phiHolesSta + (stepPhiHoles * iHoles); TString nameHole = Form("hole_%d_%d", iHoles, mNLayer); [[maybe_unused]] auto hole = new TGeoTube(nameHole, 0, radiusHoles[mNLayer], 3 * Hring->GetDz()); // move hole to the hring radius @@ -376,9 +378,7 @@ void ITS3Layer::createLayerImpl() void ITS3Layer::buildPartial(TGeoVolume* motherVolume, TGeoMatrix* mat, BuildLevel level, bool createMaterials) { - if (!mBuilt) { - getMaterials(createMaterials); - } + getMaterials(createMaterials); switch (level) { case BuildLevel::kPixelArray: createPixelArray(); From f926fb83e768fb8b60656bc8e9ed668f8debb7c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Thu, 12 Feb 2026 10:34:03 +0100 Subject: [PATCH 093/133] MUON: Delete unused files (#15027) --- .../Detectors/MUON/MCH/src/DsChannelGroup.cxx | 16 - .../SegContour/src/SegmentationSVGWriter.cxx | 117 --- .../Raw/Encoder/Payload/RefBufferCRUBare.cxx | 938 ------------------ .../Encoder/Payload/RefBufferCRUUserLogic.cxx | 67 -- .../Raw/Encoder/Payload/RefBufferGBTBare.cxx | 239 ----- .../Encoder/Payload/RefBufferGBTUserLogic.cxx | 34 - .../MUON/MID/Filtering/test/bench_Filter.cxx | 98 -- .../include/MIDWorkflow/DecodedDataDumpSpec.h | 30 - .../include/MIDWorkflow/RawAggregatorSpec.h | 30 - .../MID/Workflow/src/DecodedDataDumpSpec.cxx | 84 -- .../src/decoded-data-dump-workflow.cxx | 65 -- 11 files changed, 1718 deletions(-) delete mode 100644 DataFormats/Detectors/MUON/MCH/src/DsChannelGroup.cxx delete mode 100644 Detectors/MUON/MCH/Mapping/SegContour/src/SegmentationSVGWriter.cxx delete mode 100644 Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferCRUBare.cxx delete mode 100644 Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferCRUUserLogic.cxx delete mode 100644 Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferGBTBare.cxx delete mode 100644 Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferGBTUserLogic.cxx delete mode 100644 Detectors/MUON/MID/Filtering/test/bench_Filter.cxx delete mode 100644 Detectors/MUON/MID/Workflow/include/MIDWorkflow/DecodedDataDumpSpec.h delete mode 100644 Detectors/MUON/MID/Workflow/include/MIDWorkflow/RawAggregatorSpec.h delete mode 100644 Detectors/MUON/MID/Workflow/src/DecodedDataDumpSpec.cxx delete mode 100644 Detectors/MUON/MID/Workflow/src/decoded-data-dump-workflow.cxx diff --git a/DataFormats/Detectors/MUON/MCH/src/DsChannelGroup.cxx b/DataFormats/Detectors/MUON/MCH/src/DsChannelGroup.cxx deleted file mode 100644 index bcf10d74c95ff..0000000000000 --- a/DataFormats/Detectors/MUON/MCH/src/DsChannelGroup.cxx +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "DataFormatsMCH/DsChannelGroup.h" - -std::string o2::mch::DsChannelId() const -{ -} diff --git a/Detectors/MUON/MCH/Mapping/SegContour/src/SegmentationSVGWriter.cxx b/Detectors/MUON/MCH/Mapping/SegContour/src/SegmentationSVGWriter.cxx deleted file mode 100644 index b614346f1a42a..0000000000000 --- a/Detectors/MUON/MCH/Mapping/SegContour/src/SegmentationSVGWriter.cxx +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// @author Laurent Aphecetche - -#include "MCHMappingSegContour/CathodeSegmentationSVGWriter.h" -#include "MCHMappingInterface/CathodeSegmentation.h" -#include "MCHMappingSegContour/CathodeSegmentationContours.h" -#include "MCHContour/SVGWriter.h" -#include - -using namespace o2::mch::contour; - -namespace o2 -{ -namespace mch -{ -namespace mapping -{ - -std::string svgCathodeSegmentationDefaultStyle() -{ - return R"( -.pads { - fill: #EEEEEE; - stroke-width: 0.025px; - stroke: #AAAAAA; -} -.padchannels { - font-size: 0.4px; - font-family: arial; - fill: blue; - text-anchor: middle; -} -.dualsampas { - fill:none; - stroke-width: 0.025px; - stroke: #333333; -} -.detectionelements { - fill:none; - stroke-width:0.025px; - stroke: #000000; -} -.testpoints { - fill:red; - stroke-width:0.025px; - stroke: black; - opacity: 0.5; -} -)"; -} - -void svgCathodeSegmentation(const CathodeSegmentation& seg, SVGWriter& w, bool showdes, bool showdualsampas, bool showpads, - bool showpadchannels) -{ - std::vector> dualSampaContours = getDualSampaContours(seg); - std::vector>> dualSampaPads = getPadPolygons(seg); - std::vector> dualSampaPadChannels = getPadChannels(seg); - - if (dualSampaPadChannels.size() != dualSampaPads.size()) { - throw std::runtime_error("gouze"); - } - - auto deContour = getEnvelop(seg); - auto box = getBBox(seg); - - if (showpads) { - w.svgGroupStart("pads"); - for (auto& dsp : dualSampaPads) { - for (auto& p : dsp) { - w.polygon(p); - } - } - w.svgGroupEnd(); - } - - if (showpadchannels) { - w.svgGroupStart("padchannels"); - for (auto i = 0; i < dualSampaPads.size(); ++i) { - auto& dsp = dualSampaPads[i]; - auto& dspch = dualSampaPadChannels[i]; - for (auto j = 0; j < dsp.size(); j++) { - auto bbox = getBBox(dsp[j]); - w.text(std::to_string(dspch[j]), bbox.xcenter(), - bbox.ymax() - 0.05 * bbox.height()); // SVG text y position is the bottom of the text - } - } - w.svgGroupEnd(); - } - - if (showdualsampas) { - w.svgGroupStart("dualsampas"); - for (auto& dsp : dualSampaContours) { - w.contour(dsp); - } - w.svgGroupEnd(); - } - - if (showdes) { - w.svgGroupStart("detectionelements"); - w.contour(deContour); - } -} - -} // namespace mapping -} // namespace mch -} // namespace o2 diff --git a/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferCRUBare.cxx b/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferCRUBare.cxx deleted file mode 100644 index 52e4581da1a71..0000000000000 --- a/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferCRUBare.cxx +++ /dev/null @@ -1,938 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "RefBuffers.h" -#include -#include "MCHRawCommon/DataFormats.h" - -extern std::array REF_BUFFER_CRU_BARE_CHARGESUM; -template <> -gsl::span REF_BUFFER_CRU() -{ - return gsl::span(reinterpret_cast(&REF_BUFFER_CRU_BARE_CHARGESUM[0]), REF_BUFFER_CRU_BARE_CHARGESUM.size()); -} -std::array REF_BUFFER_CRU_BARE_CHARGESUM = { - // clang-format off -0x04, 0x40, 0x00, 0x00, 0x1B, 0x01, 0x00, 0x00, 0xB0, 0x12, 0xB0, 0x12, -0x00, 0x00, 0x0D, 0x10, 0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, -0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAB, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA9, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00, -0x1B, 0x01, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, 0x01, 0x0D, 0x10, -0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, 0x03, 0x08, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x04, 0x40, 0x00, 0x00, 0x1E, 0x01, 0x00, 0x00, 0x40, 0x0A, 0x40, 0x0A, -0x00, 0x00, 0x0F, 0x00, 0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, -0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA6, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAE, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA6, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA6, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA6, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF3, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA6, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAE, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAE, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF3, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA6, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA6, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00, -0x1E, 0x01, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, 0x01, 0x0F, 0x00, -0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, 0x03, 0x08, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x04, 0x40, 0x00, 0x00, 0x1B, 0x01, 0x00, 0x00, 0x10, 0x0D, 0x10, 0x0D, -0x07, 0x00, 0x0D, 0x10, 0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, -0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFD, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAB, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFD, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAB, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA9, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA9, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFE, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAB, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA9, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00, -0x1B, 0x01, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, 0x07, 0x01, 0x0D, 0x10, -0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, 0x03, 0x08, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - - // clang-format on -}; diff --git a/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferCRUUserLogic.cxx b/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferCRUUserLogic.cxx deleted file mode 100644 index 3c3781460f4d1..0000000000000 --- a/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferCRUUserLogic.cxx +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "RefBuffers.h" -#include -#include "MCHRawCommon/DataFormats.h" - -extern std::array REF_BUFFER_CRU_USERLOGIC_CHARGESUM; -template <> -gsl::span REF_BUFFER_CRU() -{ - return gsl::span(reinterpret_cast(&REF_BUFFER_CRU_USERLOGIC_CHARGESUM[0]), REF_BUFFER_CRU_USERLOGIC_CHARGESUM.size()); -} -std::array REF_BUFFER_CRU_USERLOGIC_CHARGESUM = { - // clang-format off -0x04, 0x40, 0x00, 0x00, 0x1E, 0x01, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, -0x0F, 0x00, 0x0F, 0x00, 0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, -0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x13, 0x01, 0xF0, 0x40, 0x55, 0x55, 0xA1, 0x00, -0x03, 0x12, 0x00, 0xE3, 0x46, 0x00, 0xA0, 0x00, 0x01, 0x60, 0xD0, 0x00, -0x00, 0x58, 0xA2, 0x00, 0x04, 0x40, 0xBB, 0x11, 0x00, 0x01, 0xA0, 0x00, -0x18, 0x14, 0x02, 0x40, 0x90, 0x04, 0xA0, 0x00, 0x70, 0x6F, 0x04, 0x40, -0x00, 0x18, 0xA0, 0x00, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x00, -0xED, 0xDE, 0xED, 0xFE, 0xED, 0xDE, 0xED, 0xFE, 0x04, 0x40, 0x00, 0x00, -0x1E, 0x01, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, 0x0F, 0x01, 0x0F, 0x00, -0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, 0x03, 0x08, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x04, 0x40, 0x00, 0x00, 0x1B, 0x01, 0x00, 0x00, 0xF0, 0x00, 0xF0, 0x00, -0x0F, 0x00, 0x0D, 0x10, 0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, -0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x13, 0x01, 0xF0, 0x40, 0x55, 0x55, 0x81, 0x38, -0x1A, 0x12, 0x80, 0xE0, 0x46, 0x00, 0x80, 0x38, 0x01, 0x60, 0xA0, 0x00, -0x00, 0x4D, 0x82, 0x38, 0x04, 0x60, 0xB8, 0x11, 0x00, 0x01, 0x80, 0x38, -0x18, 0x50, 0x00, 0x80, 0x90, 0x04, 0x80, 0x38, 0x28, 0x6E, 0x04, 0x40, -0x00, 0x18, 0x80, 0x38, 0x1E, 0x00, 0x50, 0x21, 0x01, 0x38, 0x82, 0x38, -0x1B, 0x01, 0x10, 0x00, 0x06, 0x28, 0x80, 0x38, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x80, 0x38, 0xED, 0xDE, 0xED, 0xFE, 0xED, 0xDE, 0xED, 0xFE, -0x13, 0x01, 0xF0, 0x40, 0x55, 0x55, 0x01, 0x04, 0x03, 0x12, 0x40, 0xF6, -0x46, 0x00, 0x00, 0x04, 0x01, 0x60, 0x40, 0x1A, 0x00, 0x54, 0x02, 0x04, -0x04, 0xD0, 0xBD, 0x11, 0x00, 0x01, 0x00, 0x04, 0x18, 0xB8, 0x06, 0x00, -0x96, 0x04, 0x00, 0x04, 0x84, 0x6F, 0x04, 0x40, 0x00, 0x18, 0x00, 0x04, -0xB8, 0x01, 0xF0, 0x20, 0x01, 0x94, 0x03, 0x04, 0x1B, 0x01, 0x10, 0x00, -0x06, 0xC2, 0x01, 0x04, 0x00, 0x00, 0x48, 0x00, 0xE9, 0x1B, 0x01, 0x04, -0x00, 0x04, 0x80, 0x01, 0x73, 0x00, 0x00, 0x04, 0x48, 0x12, 0x50, 0xEA, -0x46, 0x00, 0x00, 0x04, 0x01, 0x60, 0x40, 0x1A, 0x00, 0x00, 0x00, 0x04, -0x04, 0x40, 0x00, 0x00, 0x1B, 0x01, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, -0x0F, 0x01, 0x0D, 0x10, 0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, -0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00 - // clang-format on -}; diff --git a/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferGBTBare.cxx b/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferGBTBare.cxx deleted file mode 100644 index 89b1602cb0489..0000000000000 --- a/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferGBTBare.cxx +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "RefBuffers.h" -#include -#include "MCHRawCommon/DataFormats.h" - -extern std::array REF_BUFFER_GBT_BARE_CHARGESUM; -template <> -gsl::span REF_BUFFER_GBT() -{ - return gsl::span(reinterpret_cast(&REF_BUFFER_GBT_BARE_CHARGESUM[0]), REF_BUFFER_GBT_BARE_CHARGESUM.size()); -} -std::array REF_BUFFER_GBT_BARE_CHARGESUM = { - // clang-format off -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xBC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xBC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x69, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xEB, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xBD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAB, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x3C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x7D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xEB, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x3C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x3C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAB, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6A, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xE8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xE8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x3E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAB, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00 - // clang-format on -}; diff --git a/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferGBTUserLogic.cxx b/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferGBTUserLogic.cxx deleted file mode 100644 index 9487037328ad2..0000000000000 --- a/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferGBTUserLogic.cxx +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "RefBuffers.h" -#include -#include "MCHRawCommon/DataFormats.h" - -extern std::array REF_BUFFER_GBT_USERLOGIC_CHARGESUM; -template <> -gsl::span REF_BUFFER_GBT() -{ - return gsl::span(reinterpret_cast(&REF_BUFFER_GBT_USERLOGIC_CHARGESUM[0]), REF_BUFFER_GBT_USERLOGIC_CHARGESUM.size()); -} -std::array REF_BUFFER_GBT_USERLOGIC_CHARGESUM = { - // clang-format off -0x13, 0x01, 0xF0, 0x40, 0x55, 0x55, 0x01, 0x58, 0x0C, 0x12, 0x00, 0xA0, -0x50, 0x03, 0x00, 0x58, 0x01, 0x30, 0xA0, 0x00, 0x00, 0x5B, 0x02, 0x58, -0x04, 0xC0, 0x2F, 0xD4, 0x00, 0x01, 0x00, 0x58, 0x0C, 0x80, 0x02, 0x00, -0x00, 0x00, 0x00, 0x58, 0x13, 0x01, 0xF0, 0x40, 0x55, 0x55, 0x61, 0x58, -0x19, 0x12, 0x60, 0xAD, 0x50, 0x03, 0x60, 0x58, 0x01, 0x30, 0xD0, 0x00, -0x00, 0x09, 0x62, 0x58, 0x04, 0x5C, 0x28, 0xD4, 0x00, 0x01, 0x60, 0x58, -0x0C, 0x14, 0x02, 0x40, 0x82, 0x04, 0x60, 0x58, 0xF7, 0x0B, 0x35, 0x40, -0x00, 0x0C, 0x60, 0x58, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x58 - - // clang-format on -}; diff --git a/Detectors/MUON/MID/Filtering/test/bench_Filter.cxx b/Detectors/MUON/MID/Filtering/test/bench_Filter.cxx deleted file mode 100644 index a54ea9c1733a8..0000000000000 --- a/Detectors/MUON/MID/Filtering/test/bench_Filter.cxx +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file MID/Tracking/test/bench_Tracker.cxx -/// \brief Benchmark tracker device for MID -/// \author Diego Stocco -/// \date 17 March 2018 - -#include "benchmark/benchmark.h" -#include -#include "DataFormatsMID/Cluster.h" -#include "DataFormatsMID/Track.h" -#include "MIDBase/HitFinder.h" -#include "MIDBase/Mapping.h" -#include "MIDBase/MpArea.h" -#include "MIDTestingSimTools/TrackGenerator.h" -#include "MIDTracking/Tracker.h" - -std::vector generateTestData(int nTracks, o2::mid::TrackGenerator& trackGen, - const o2::mid::HitFinder& hitFinder, const o2::mid::Mapping& mapping) -{ - o2::mid::Mapping::MpStripIndex stripIndex; - o2::mid::MpArea area; - std::vector clusters; - o2::mid::Cluster cl; - std::vector tracks = trackGen.generate(nTracks); - for (auto& track : tracks) { - for (int ich = 0; ich < 4; ++ich) { - auto hits = hitFinder.getLocalPositions(track, ich); - bool isFired = false; - for (auto& hit : hits) { - int deId = hit.deId; - float xPos = hit.xCoor; - float yPos = hit.yCoor; - stripIndex = mapping.stripByPosition(xPos, yPos, 0, deId, false); - if (!stripIndex.isValid()) { - continue; - } - cl.deId = deId; - area = mapping.stripByLocation(stripIndex.strip, 0, stripIndex.line, stripIndex.column, deId); - cl.yCoor = area.getCenterY(); - cl.yErr = area.getHalfSizeY() / std::sqrt(3.); - stripIndex = mapping.stripByPosition(xPos, yPos, 1, deId, false); - area = mapping.stripByLocation(stripIndex.strip, 1, stripIndex.line, stripIndex.column, deId); - cl.xCoor = area.getCenterX(); - cl.xErr = area.getHalfSizeX() / std::sqrt(3.); - clusters.push_back(cl); - } // loop on fired pos - } // loop on chambers - } // loop on tracks - return clusters; -} - -static void BM_TRACKER(benchmark::State& state) -{ - o2::mid::GeometryTransformer geoTrans = o2::mid::createDefaultTransformer(); - o2::mid::TrackGenerator trackGen; - o2::mid::HitFinder hitFinder(geoTrans); - o2::mid::Mapping mapping; - o2::mid::Tracker tracker(geoTrans); - - int nTracksPerEvent = state.range(0); - tracker.init((state.range(1) == 1)); - double num{0}; - - std::vector inputData; - - for (auto _ : state) { - state.PauseTiming(); - inputData = generateTestData(nTracksPerEvent, trackGen, hitFinder, mapping); - state.ResumeTiming(); - tracker.process(inputData); - ++num; - } - - state.counters["num"] = benchmark::Counter(num, benchmark::Counter::kIsRate); -} - -static void CustomArguments(benchmark::internal::Benchmark* bench) -{ - for (int itrack = 1; itrack <= 8; ++itrack) { - for (int imethod = 0; imethod < 2; ++imethod) { - bench->Args({itrack, imethod}); - } - } -} - -BENCHMARK(BM_TRACKER)->Apply(CustomArguments)->Unit(benchmark::kNanosecond); - -BENCHMARK_MAIN(); diff --git a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/DecodedDataDumpSpec.h b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/DecodedDataDumpSpec.h deleted file mode 100644 index 4d104aacac15c..0000000000000 --- a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/DecodedDataDumpSpec.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file MIDWorkflow/RawDumpSpec.h -/// \brief Device to dump decoded raw data -/// \author Diego Stocco -/// \date 17 February 2022 - -#ifndef O2_MID_RAWDUMPSPEC_H -#define O2_MID_RAWDUMPSPEC_H - -#include "Framework/DataProcessorSpec.h" - -namespace o2 -{ -namespace mid -{ -framework::DataProcessorSpec getRawDumpSpec(); -} // namespace mid -} // namespace o2 - -#endif // O2_MID_RAWDUMPSPEC_H diff --git a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/RawAggregatorSpec.h b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/RawAggregatorSpec.h deleted file mode 100644 index b5a6b33530c8f..0000000000000 --- a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/RawAggregatorSpec.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file MIDWorkflow/RawAggregatorSpec.h -/// \brief Data processor spec for MID raw data aggregator devices -/// \author Diego Stocco -/// \date 26 February 2020 - -#ifndef O2_MID_RAWAGGREGATORSPEC_H -#define O2_MID_RAWAGGREGATORSPEC_H - -#include "Framework/DataProcessorSpec.h" - -namespace o2 -{ -namespace mid -{ -framework::DataProcessorSpec getRawAggregatorSpec(); -} // namespace mid -} // namespace o2 - -#endif //O2_MID_RAWAGGREGATORSPEC_H diff --git a/Detectors/MUON/MID/Workflow/src/DecodedDataDumpSpec.cxx b/Detectors/MUON/MID/Workflow/src/DecodedDataDumpSpec.cxx deleted file mode 100644 index 77d05a8b3374f..0000000000000 --- a/Detectors/MUON/MID/Workflow/src/DecodedDataDumpSpec.cxx +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file MID/Workflow/src/RawDumpSpec.cxx -/// \brief Device to dump decoded raw data -/// \author Diego Stocco -/// \date 17 February 2022 - -#include "MIDWorkflow/RawDumpSpec.h" - -#include -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/Logger.h" -#include "Framework/Task.h" -#include "fmt/format.h" -#include "DataFormatsMID/ROBoard.h" -#include "DataFormatsMID/ROFRecord.h" - -namespace o2 -{ -namespace mid -{ - -class RawDumpDeviceDPL -{ - public: - void init(o2::framework::InitContext& ic) - { - auto outFilename = ic.options().get("mid-dump-outfile"); - - if (!outFilename.empty()) { - mOutFile.open(outFilename.c_str()); - } - } - - void - run(o2::framework::ProcessingContext& pc) - { - - auto data = pc.inputs().get>("mid_decoded"); - auto dataROFs = pc.inputs().get>("mid_decoded_rof"); - std::stringstream ss; - for (auto& rof : dataROFs) { - ss << fmt::format("BCid: 0x{:x} Orbit: 0x{:x} EvtType: {:d}", rof.interactionRecord.bc, rof.interactionRecord.orbit, static_cast(rof.eventType)) << std::endl; - for (auto colIt = data.begin() + rof.firstEntry, end = data.begin() + rof.getEndIndex(); colIt != end; ++colIt) { - ss << *colIt << std::endl; - } - } - if (mOutFile.is_open()) { - mOutFile << ss.str(); - } else { - LOG(info) << ss.str(); - } - } - - private: - std::ofstream mOutFile; /// Output file -}; - -framework::DataProcessorSpec getRawDumpSpec() -{ - std::vector inputSpecs{ - o2::framework::InputSpec{"mid_decoded", header::gDataOriginMID, "DECODED", 0, o2::framework::Lifetime::Timeframe}, - o2::framework::InputSpec{"mid_decoded_rof", header::gDataOriginMID, "DECODEDROF", 0, o2::framework::Lifetime::Timeframe}}; - - return o2::framework::DataProcessorSpec{ - "MIDRawDataDumper", - {inputSpecs}, - {}, - o2::framework::AlgorithmSpec{o2::framework::adaptFromTask()}, - o2::framework::Options{{"mid-dump-outfile", o2::framework::VariantType::String, "", {"Dump output to file"}}}}; -} - -} // namespace mid -} // namespace o2 diff --git a/Detectors/MUON/MID/Workflow/src/decoded-data-dump-workflow.cxx b/Detectors/MUON/MID/Workflow/src/decoded-data-dump-workflow.cxx deleted file mode 100644 index 036b63bc75338..0000000000000 --- a/Detectors/MUON/MID/Workflow/src/decoded-data-dump-workflow.cxx +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file MID/Workflow/src/raw-dump-workflow.cxx -/// \brief MID raw dump workflow -/// \author Diego Stocco -/// \date 17 February 2022 - -#include -#include -#include "Framework/Variant.h" -#include "Framework/ConfigParamSpec.h" -#include "MIDRaw/CrateMasks.h" -#include "MIDRaw/ElectronicsDelay.h" -#include "MIDRaw/FEEIdConfig.h" -#include "MIDWorkflow/RawDumpSpec.h" -#include "MIDWorkflow/RawDecoderSpec.h" - -using namespace o2::framework; - -// add workflow options, note that customization needs to be declared before -// including Framework/runDataProcessing -void customize(std::vector& workflowOptions) -{ - std::vector - options{ - {"feeId-config-file", VariantType::String, "", {"Filename with crate FEE ID correspondence"}}, - {"crate-masks-file", VariantType::String, "", {"Filename with crate masks"}}, - {"electronics-delay-file", VariantType::String, "", {"Filename with electronics delay"}}}; - workflowOptions.insert(workflowOptions.end(), options.begin(), options.end()); -} - -#include "Framework/runDataProcessing.h" - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - auto feeIdConfigFilename = cfgc.options().get("feeId-config-file"); - o2::mid::FEEIdConfig feeIdConfig; - if (!feeIdConfigFilename.empty()) { - feeIdConfig = o2::mid::FEEIdConfig(feeIdConfigFilename.c_str()); - } - auto crateMasksFilename = cfgc.options().get("crate-masks-file"); - o2::mid::CrateMasks crateMasks; - if (!crateMasksFilename.empty()) { - crateMasks = o2::mid::CrateMasks(crateMasksFilename.c_str()); - } - auto electronicsDelayFilename = cfgc.options().get("electronics-delay-file"); - o2::mid::ElectronicsDelay electronicsDelay; - if (!electronicsDelayFilename.empty()) { - electronicsDelay = o2::mid::readElectronicsDelay(electronicsDelayFilename.c_str()); - } - - WorkflowSpec specs; - specs.emplace_back(o2::mid::getRawDecoderSpec(true, feeIdConfig, crateMasks, electronicsDelay, false)); - specs.emplace_back(o2::mid::getRawDumpSpec()); - return specs; -} From 87fa1f6c3befc55da9d538d04dab3b2811793c91 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Thu, 12 Feb 2026 11:08:37 +0100 Subject: [PATCH 094/133] ITSMFT: scaffolding for staggered clusterization (#15004) * ITS staggered clusterization Signed-off-by: Felix Schlepper * Simplify getMaxBCDiffToSquashBias function --------- Signed-off-by: Felix Schlepper --- .../src/StrangenessTrackingSpec.cxx | 2 - Detectors/ITSMFT/ITS/workflow/CMakeLists.txt | 2 - .../include/ITSWorkflow/ClustererSpec.h | 65 ---- .../ITS/workflow/src/ClusterWriterSpec.cxx | 72 ---- .../workflow/src/ClusterWriterWorkflow.cxx | 4 +- .../ITSMFT/ITS/workflow/src/ClustererSpec.cxx | 218 ------------ .../ITSMFT/ITS/workflow/src/RecoWorkflow.cxx | 8 +- Detectors/ITSMFT/MFT/workflow/CMakeLists.txt | 2 - .../include/MFTWorkflow/ClusterWriterSpec.h | 31 -- .../MFT/workflow/src/ClusterWriterSpec.cxx | 72 ---- .../ITSMFT/MFT/workflow/src/ClustererSpec.cxx | 212 ------------ .../ITSMFT/MFT/workflow/src/RecoWorkflow.cxx | 8 +- .../src/mft-cluster-writer-workflow.cxx | 4 +- .../include/ITSMFTReconstruction/Clusterer.h | 14 +- .../ITSMFTReconstruction/ClustererParam.h | 22 +- .../ITSMFTReconstruction/DigitPixelReader.h | 3 +- .../common/reconstruction/src/Clusterer.cxx | 36 +- .../reconstruction/src/DigitPixelReader.cxx | 11 + .../ITSMFT/common/workflow/CMakeLists.txt | 2 + .../ITSMFTWorkflow/ClusterReaderSpec.h | 52 ++- .../ITSMFTWorkflow}/ClusterWriterSpec.h | 16 +- .../include/ITSMFTWorkflow}/ClustererSpec.h | 28 +- .../common/workflow/src/ClusterReaderSpec.cxx | 166 +++++---- .../common/workflow/src/ClusterWriterSpec.cxx | 107 ++++++ .../common/workflow/src/ClustererSpec.cxx | 325 ++++++++++++++++++ .../ITS3/workflow/src/RecoWorkflow.cxx | 4 +- 26 files changed, 648 insertions(+), 838 deletions(-) delete mode 100644 Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClustererSpec.h delete mode 100644 Detectors/ITSMFT/ITS/workflow/src/ClusterWriterSpec.cxx delete mode 100644 Detectors/ITSMFT/ITS/workflow/src/ClustererSpec.cxx delete mode 100644 Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClusterWriterSpec.h delete mode 100644 Detectors/ITSMFT/MFT/workflow/src/ClusterWriterSpec.cxx delete mode 100644 Detectors/ITSMFT/MFT/workflow/src/ClustererSpec.cxx rename Detectors/ITSMFT/{ITS/workflow/include/ITSWorkflow => common/workflow/include/ITSMFTWorkflow}/ClusterWriterSpec.h (73%) rename Detectors/ITSMFT/{MFT/workflow/include/MFTWorkflow => common/workflow/include/ITSMFTWorkflow}/ClustererSpec.h (64%) create mode 100644 Detectors/ITSMFT/common/workflow/src/ClusterWriterSpec.cxx create mode 100644 Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx diff --git a/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingSpec.cxx index 849964aeaf871..e313940b0a91e 100644 --- a/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingSpec.cxx @@ -17,10 +17,8 @@ #include "DataFormatsGlobalTracking/RecoContainer.h" #include "StrangenessTracking/StrangenessTrackingConfigParam.h" #include "GlobalTrackingWorkflow/StrangenessTrackingSpec.h" -#include "ITSWorkflow/ClusterWriterSpec.h" #include "ITSWorkflow/TrackerSpec.h" #include "ITSWorkflow/TrackReaderSpec.h" -#include "ITSMFTWorkflow/ClusterReaderSpec.h" #include "Framework/CCDBParamSpec.h" #include "DataFormatsParameters/GRPObject.h" diff --git a/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt b/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt index f0d50e59493d4..10e16e49d92b5 100644 --- a/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt @@ -13,8 +13,6 @@ o2_add_library(ITSWorkflow TARGETVARNAME targetName SOURCES src/RecoWorkflow.cxx src/ClusterWriterWorkflow.cxx - src/ClustererSpec.cxx - src/ClusterWriterSpec.cxx src/TrackerSpec.cxx src/TrackWriterSpec.cxx src/TrackReaderSpec.cxx diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClustererSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClustererSpec.h deleted file mode 100644 index c5038c87fa467..0000000000000 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClustererSpec.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file ClustererSpec.h - -#ifndef O2_ITS_CLUSTERERDPL -#define O2_ITS_CLUSTERERDPL - -#include -#include "DetectorsBase/GRPGeomHelper.h" -#include "ITSMFTReconstruction/Clusterer.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" - -using namespace o2::framework; - -namespace o2 -{ - -namespace itsmft -{ -class Clusterer; -} - -namespace its -{ - -class ClustererDPL : public Task -{ - public: - ClustererDPL(std::shared_ptr gr, bool useMC) : mGGCCDBRequest(gr), mUseMC(useMC) {} - ~ClustererDPL() override = default; - void init(InitContext& ic) final; - void run(ProcessingContext& pc) final; - void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; - void endOfStream(o2::framework::EndOfStreamContext& ec) final; - - private: - void updateTimeDependentParams(ProcessingContext& pc); - - int mState = 0; - bool mUseMC = true; - bool mUseClusterDictionary = true; - int mNThreads = 1; - std::unique_ptr mFile = nullptr; - std::unique_ptr mClusterer = nullptr; - std::shared_ptr mGGCCDBRequest; -}; - -/// create a processor spec -/// run ITS cluster finder -framework::DataProcessorSpec getClustererSpec(bool useMC); - -} // namespace its -} // namespace o2 - -#endif /* O2_ITS_CLUSTERERDPL */ diff --git a/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterSpec.cxx deleted file mode 100644 index 4dffbaf88893c..0000000000000 --- a/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterSpec.cxx +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file ClusterWriterSpec.cxx - -#include - -#include "ITSWorkflow/ClusterWriterSpec.h" -#include "DPLUtils/MakeRootTreeWriterSpec.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace its -{ - -template -using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; -using CompClusType = std::vector; -using PatternsType = std::vector; -using ROFrameRType = std::vector; -using LabelsType = o2::dataformats::MCTruthContainer; -using ROFRecLblT = std::vector; -using namespace o2::header; - -DataProcessorSpec getClusterWriterSpec(bool useMC) -{ - // Spectators for logging - // this is only to restore the original behavior - auto compClustersSize = std::make_shared(0); - auto compClustersSizeGetter = [compClustersSize](CompClusType const& compClusters) { - *compClustersSize = compClusters.size(); - }; - auto logger = [compClustersSize](std::vector const& rofs) { - LOG(info) << "ITSClusterWriter pulled " << *compClustersSize << " clusters, in " << rofs.size() << " RO frames"; - }; - return MakeRootTreeWriterSpec("its-cluster-writer", - "o2clus_its.root", - MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Tree with ITS clusters"}, - BranchDefinition{InputSpec{"compclus", "ITS", "COMPCLUSTERS", 0}, - "ITSClusterComp", - compClustersSizeGetter}, - BranchDefinition{InputSpec{"patterns", "ITS", "PATTERNS", 0}, - "ITSClusterPatt"}, - BranchDefinition{InputSpec{"ROframes", "ITS", "CLUSTERSROF", 0}, - "ITSClustersROF", - logger}, - BranchDefinition{InputSpec{"labels", "ITS", "CLUSTERSMCTR", 0}, - "ITSClusterMCTruth", - (useMC ? 1 : 0), // one branch if mc labels enabled - ""}, - BranchDefinition{InputSpec{"MC2ROframes", "ITS", "CLUSTERSMC2ROF", 0}, - "ITSClustersMC2ROF", - (useMC ? 1 : 0), // one branch if mc labels enabled - ""})(); -} - -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx index ca5db7acd63e1..aba468b3e9460 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx @@ -12,7 +12,7 @@ /// @file ClusterWriterWorkflow.cxx #include "ITSWorkflow/ClusterWriterWorkflow.h" -#include "ITSWorkflow/ClusterWriterSpec.h" +#include "ITSMFTWorkflow/ClusterWriterSpec.h" namespace o2 { @@ -26,7 +26,7 @@ framework::WorkflowSpec getWorkflow(bool useMC) { framework::WorkflowSpec specs; - specs.emplace_back(o2::its::getClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC)); return specs; } diff --git a/Detectors/ITSMFT/ITS/workflow/src/ClustererSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/ClustererSpec.cxx deleted file mode 100644 index d58e4f5d915c1..0000000000000 --- a/Detectors/ITSMFT/ITS/workflow/src/ClustererSpec.cxx +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file ClustererSpec.cxx - -#include - -#include "Framework/ControlService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/CCDBParamSpec.h" -#include "ITSWorkflow/ClustererSpec.h" -#include "DataFormatsITSMFT/Digit.h" -#include "ITSMFTReconstruction/ChipMappingITS.h" -#include "ITSMFTReconstruction/ClustererParam.h" -#include "DataFormatsITSMFT/TopologyDictionary.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/ConstMCTruthContainer.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include "DataFormatsParameters/GRPObject.h" -#include "ITSMFTReconstruction/DigitPixelReader.h" -#include "ITSMFTBase/DPLAlpideParam.h" -#include "CommonConstants/LHCConstants.h" -#include "DetectorsCommonDataFormats/DetectorNameConf.h" - -using namespace o2::framework; -using namespace o2::itsmft; - -namespace o2 -{ -namespace its -{ - -void ClustererDPL::init(InitContext& ic) -{ - mClusterer = std::make_unique(); - mClusterer->setNChips(o2::itsmft::ChipMappingITS::getNChips()); - mUseClusterDictionary = !ic.options().get("ignore-cluster-dictionary"); - o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); - mNThreads = std::max(1, ic.options().get("nthreads")); - LOGP(info, "Initialising ITSClusterer with {} threads", mNThreads); - mState = 1; -} - -void ClustererDPL::run(ProcessingContext& pc) -{ - updateTimeDependentParams(pc); - auto digits = pc.inputs().get>("digits"); - auto rofs = pc.inputs().get>("ROframes"); - - gsl::span mc2rofs; - gsl::span labelbuffer; - if (mUseMC) { - labelbuffer = pc.inputs().get>("labels"); - mc2rofs = pc.inputs().get>("MC2ROframes"); - } - o2::dataformats::ConstMCTruthContainerView labels(labelbuffer); - - LOG(info) << "ITSClusterer pulled " << digits.size() << " digits, in " - << rofs.size() << " RO frames"; - LOG(info) << "ITSClusterer pulled " << labels.getNElements() << " labels "; - - o2::itsmft::DigitPixelReader reader; - reader.setSquashingDepth(mClusterer->getMaxROFDepthToSquash()); - reader.setSquashingDist(mClusterer->getMaxRowColDiffToMask()); // Sharing same parameter/logic with masking - reader.setMaxBCSeparationToSquash(mClusterer->getMaxBCSeparationToSquash()); - reader.setDigits(digits); - reader.setROFRecords(rofs); - if (mUseMC) { - reader.setMC2ROFRecords(mc2rofs); - reader.setDigitsMCTruth(labels.getIndexedSize() > 0 ? &labels : nullptr); - } - reader.init(); - auto orig = o2::header::gDataOriginITS; - std::vector clusCompVec; - std::vector clusROFVec; - std::vector clusPattVec; - - std::unique_ptr> clusterLabels; - if (mUseMC) { - clusterLabels = std::make_unique>(); - } - mClusterer->process(mNThreads, reader, &clusCompVec, &clusPattVec, &clusROFVec, clusterLabels.get()); - pc.outputs().snapshot(Output{orig, "COMPCLUSTERS", 0}, clusCompVec); - pc.outputs().snapshot(Output{orig, "CLUSTERSROF", 0}, clusROFVec); - pc.outputs().snapshot(Output{orig, "PATTERNS", 0}, clusPattVec); - - if (mUseMC) { - pc.outputs().snapshot(Output{orig, "CLUSTERSMCTR", 0}, *clusterLabels.get()); // at the moment requires snapshot - std::vector clusterMC2ROframes(mc2rofs.size()); - for (int i = mc2rofs.size(); i--;) { - clusterMC2ROframes[i] = mc2rofs[i]; // Simply, replicate it from digits ? - } - pc.outputs().snapshot(Output{orig, "CLUSTERSMC2ROF", 0}, clusterMC2ROframes); - } - - // TODO: in principle, after masking "overflow" pixels the MC2ROFRecord maxROF supposed to change, nominally to minROF - // -> consider recalculationg maxROF - LOG(info) << "ITSClusterer pushed " << clusCompVec.size() << " clusters, in " << clusROFVec.size() << " RO frames"; -} - -///_______________________________________ -void ClustererDPL::updateTimeDependentParams(ProcessingContext& pc) -{ - static bool initOnceDone = false; - o2::base::GRPGeomHelper::instance().checkUpdates(pc); - if (!initOnceDone) { // this params need to be queried only once - initOnceDone = true; - pc.inputs().get("cldict"); // just to trigger the finaliseCCDB - pc.inputs().get*>("alppar"); - pc.inputs().get*>("cluspar"); - mClusterer->setContinuousReadOut(o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::ITS)); - // settings for the fired pixel overflow masking - const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); - const auto& clParams = o2::itsmft::ClustererParam::Instance(); - mClusterer->setDropHugeClusters(clParams.dropHugeClusters); - if (clParams.maxBCDiffToMaskBias > 0 && clParams.maxBCDiffToSquashBias > 0) { - LOGP(fatal, "maxBCDiffToMaskBias = {} and maxBCDiffToSquashBias = {} cannot be set at the same time. Either set masking or squashing with a BCDiff > 0", clParams.maxBCDiffToMaskBias, clParams.maxBCDiffToSquashBias); - } - auto nbc = clParams.maxBCDiffToMaskBias; - nbc += mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); - mClusterer->setMaxBCSeparationToMask(nbc); - mClusterer->setMaxRowColDiffToMask(clParams.maxRowColDiffToMask); - // Squasher - int rofBC = mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); // ROF length in BC - mClusterer->setMaxBCSeparationToSquash(rofBC + clParams.maxBCDiffToSquashBias); - int nROFsToSquash = 0; // squashing disabled if no reset due to maxSOTMUS>0. - if (clParams.maxSOTMUS > 0 && rofBC > 0) { - nROFsToSquash = 2 + int(clParams.maxSOTMUS / (rofBC * o2::constants::lhc::LHCBunchSpacingMUS)); // use squashing - } - mClusterer->setMaxROFDepthToSquash(clParams.maxBCDiffToSquashBias > 0 ? nROFsToSquash : 0); - mClusterer->print(); - } - // we may have other params which need to be queried regularly -} - -///_______________________________________ -void ClustererDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) -{ - if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { - return; - } - if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { - LOG(info) << "cluster dictionary updated" << (!mUseClusterDictionary ? " but its using is disabled" : ""); - if (mUseClusterDictionary) { - mClusterer->setDictionary((const o2::itsmft::TopologyDictionary*)obj); - } - return; - } - // Note: strictly speaking, for Configurable params we don't need finaliseCCDB check, the singletons are updated at the CCDB fetcher level - if (matcher == ConcreteDataMatcher("ITS", "ALPIDEPARAM", 0)) { - LOG(info) << "Alpide param updated"; - const auto& par = o2::itsmft::DPLAlpideParam::Instance(); - par.printKeyValues(); - return; - } - if (matcher == ConcreteDataMatcher("ITS", "CLUSPARAM", 0)) { - LOG(info) << "Cluster param updated"; - const auto& par = o2::itsmft::ClustererParam::Instance(); - par.printKeyValues(); - return; - } -} - -DataProcessorSpec getClustererSpec(bool useMC) -{ - std::vector inputs; - inputs.emplace_back("digits", "ITS", "DIGITS", 0, Lifetime::Timeframe); - inputs.emplace_back("ROframes", "ITS", "DIGITSROF", 0, Lifetime::Timeframe); - inputs.emplace_back("cldict", "ITS", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/ClusterDictionary")); - inputs.emplace_back("cluspar", "ITS", "CLUSPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/ClustererParam")); - inputs.emplace_back("alppar", "ITS", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam")); - auto ggRequest = std::make_shared(false, // orbitResetTime - true, // GRPECS=true - false, // GRPLHCIF - false, // GRPMagField - false, // askMatLUT - o2::base::GRPGeomRequest::None, // geometry - inputs, - true); - std::vector outputs; - outputs.emplace_back("ITS", "COMPCLUSTERS", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "PATTERNS", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "CLUSTERSROF", 0, Lifetime::Timeframe); - - if (useMC) { - inputs.emplace_back("labels", "ITS", "DIGITSMCTR", 0, Lifetime::Timeframe); - inputs.emplace_back("MC2ROframes", "ITS", "DIGITSMC2ROF", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); - } - - return DataProcessorSpec{ - "its-clusterer", - inputs, - outputs, - AlgorithmSpec{adaptFromTask(ggRequest, useMC)}, - Options{ - {"ignore-cluster-dictionary", VariantType::Bool, false, {"do not use cluster dictionary, always store explicit patterns"}}, - {"nthreads", VariantType::Int, 1, {"Number of clustering threads"}}}}; -} - -///_______________________________________ -void ClustererDPL::endOfStream(o2::framework::EndOfStreamContext& ec) -{ - mClusterer->print(); -} - -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx index 60e28556716f2..9f8cb6c83ef99 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx @@ -12,8 +12,8 @@ /// @file RecoWorkflow.cxx #include "ITSWorkflow/RecoWorkflow.h" -#include "ITSWorkflow/ClustererSpec.h" -#include "ITSWorkflow/ClusterWriterSpec.h" +#include "ITSMFTWorkflow/ClustererSpec.h" +#include "ITSMFTWorkflow/ClusterWriterSpec.h" #include "ITSWorkflow/TrackerSpec.h" #include "ITSWorkflow/TrackWriterSpec.h" #include "ITStracking/TrackingConfigParam.h" @@ -43,10 +43,10 @@ framework::WorkflowSpec getWorkflow(bool useMC, specs.emplace_back(o2::itsmft::getITSDigitReaderSpec(useMC, false, true, "itsdigits.root")); } if (!upstreamClusters) { - specs.emplace_back(o2::its::getClustererSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClustererSpec(useMC)); } if (!disableRootOutput) { - specs.emplace_back(o2::its::getClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC)); } if ((trmode != TrackingMode::Off) && (TrackerParamConfig::Instance().trackingMode != TrackingMode::Off)) { if (useGPUWF) { diff --git a/Detectors/ITSMFT/MFT/workflow/CMakeLists.txt b/Detectors/ITSMFT/MFT/workflow/CMakeLists.txt index acb3d0b3e835f..b83699498a6b8 100644 --- a/Detectors/ITSMFT/MFT/workflow/CMakeLists.txt +++ b/Detectors/ITSMFT/MFT/workflow/CMakeLists.txt @@ -12,8 +12,6 @@ o2_add_library(MFTWorkflow TARGETVARNAME targetName SOURCES src/RecoWorkflow.cxx - src/ClustererSpec.cxx - src/ClusterWriterSpec.cxx src/TrackerSpec.cxx src/TrackReaderSpec.cxx src/TrackWriterSpec.cxx diff --git a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClusterWriterSpec.h b/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClusterWriterSpec.h deleted file mode 100644 index 51dc5a6481eb5..0000000000000 --- a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClusterWriterSpec.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file ClusterWriterSpec.h - -#ifndef O2_MFT_CLUSTERWRITER_H_ -#define O2_MFT_CLUSTERWRITER_H_ - -#include "Framework/DataProcessorSpec.h" - -namespace o2 -{ -namespace mft -{ - -/// create a processor spec -/// write MFT clusters a root file -framework::DataProcessorSpec getClusterWriterSpec(bool useMC); - -} // namespace mft -} // namespace o2 - -#endif /* O2_MFT_CLUSTERWRITER_H */ diff --git a/Detectors/ITSMFT/MFT/workflow/src/ClusterWriterSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/ClusterWriterSpec.cxx deleted file mode 100644 index c8061310e34f6..0000000000000 --- a/Detectors/ITSMFT/MFT/workflow/src/ClusterWriterSpec.cxx +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file ClusterWriterSpec.cxx - -#include - -#include "MFTWorkflow/ClusterWriterSpec.h" -#include "DPLUtils/MakeRootTreeWriterSpec.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" -#include "DataFormatsITSMFT/ROFRecord.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace mft -{ - -template -using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; -using CompClusType = std::vector; -using PatternsType = std::vector; -using ROFrameRType = std::vector; -using LabelsType = o2::dataformats::MCTruthContainer; -using ROFRecLblT = std::vector; -using namespace o2::header; - -DataProcessorSpec getClusterWriterSpec(bool useMC) -{ - // Spectators for logging - // this is only to restore the original behavior - auto compClustersSize = std::make_shared(0); - auto compClustersSizeGetter = [compClustersSize](CompClusType const& compClusters) { - *compClustersSize = compClusters.size(); - }; - auto logger = [compClustersSize](std::vector const& rofs) { - LOG(info) << "MFTClusterWriter pulled " << *compClustersSize << " clusters, in " << rofs.size() << " RO frames"; - }; - return MakeRootTreeWriterSpec("mft-cluster-writer", - "mftclusters.root", - MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Tree with MFT clusters"}, - BranchDefinition{InputSpec{"compclus", "MFT", "COMPCLUSTERS", 0}, - "MFTClusterComp", - compClustersSizeGetter}, - BranchDefinition{InputSpec{"patterns", "MFT", "PATTERNS", 0}, - "MFTClusterPatt"}, - BranchDefinition{InputSpec{"ROframes", "MFT", "CLUSTERSROF", 0}, - "MFTClustersROF", - logger}, - BranchDefinition{InputSpec{"labels", "MFT", "CLUSTERSMCTR", 0}, - "MFTClusterMCTruth", - (useMC ? 1 : 0), // one branch if mc labels enabled - ""}, - BranchDefinition{InputSpec{"MC2ROframes", "MFT", "CLUSTERSMC2ROF", 0}, - "MFTClustersMC2ROF", - (useMC ? 1 : 0), // one branch if mc labels enabled - ""})(); -} - -} // namespace mft -} // namespace o2 diff --git a/Detectors/ITSMFT/MFT/workflow/src/ClustererSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/ClustererSpec.cxx deleted file mode 100644 index 766d7c1a0729e..0000000000000 --- a/Detectors/ITSMFT/MFT/workflow/src/ClustererSpec.cxx +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file ClustererSpec.cxx - -#include - -#include "Framework/ControlService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/CCDBParamSpec.h" -#include "MFTWorkflow/ClustererSpec.h" -#include "DataFormatsITSMFT/Digit.h" -#include "ITSMFTReconstruction/ChipMappingMFT.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "DataFormatsITSMFT/TopologyDictionary.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/ConstMCTruthContainer.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include "DataFormatsParameters/GRPObject.h" -#include "ITSMFTReconstruction/DigitPixelReader.h" -#include "DetectorsBase/GeometryManager.h" -#include "MFTBase/GeometryTGeo.h" -#include "ITSMFTBase/DPLAlpideParam.h" -#include "CommonConstants/LHCConstants.h" -#include "DetectorsCommonDataFormats/DetectorNameConf.h" -#include "ITSMFTReconstruction/ClustererParam.h" - -using namespace o2::framework; -using namespace o2::itsmft; - -namespace o2 -{ -namespace mft -{ - -void ClustererDPL::init(InitContext& ic) -{ - mClusterer = std::make_unique(); - mClusterer->setNChips(o2::itsmft::ChipMappingMFT::getNChips()); - mUseClusterDictionary = !ic.options().get("ignore-cluster-dictionary"); - o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); - mNThreads = std::max(1, ic.options().get("nthreads")); - mState = 1; -} - -void ClustererDPL::run(ProcessingContext& pc) -{ - updateTimeDependentParams(pc); - auto digits = pc.inputs().get>("digits"); - auto rofs = pc.inputs().get>("ROframes"); - - gsl::span mc2rofs; - gsl::span labelbuffer; - if (mUseMC) { - labelbuffer = pc.inputs().get>("labels"); - mc2rofs = pc.inputs().get>("MC2ROframes"); - } - const o2::dataformats::ConstMCTruthContainerView labels(labelbuffer); - - LOG(debug) << "MFTClusterer pulled " << digits.size() << " digits, in " - << rofs.size() << " RO frames"; - - o2::itsmft::DigitPixelReader reader; - reader.setSquashingDepth(mClusterer->getMaxROFDepthToSquash()); - reader.setSquashingDist(mClusterer->getMaxRowColDiffToMask()); // Sharing same parameter/logic with masking - reader.setMaxBCSeparationToSquash(mClusterer->getMaxBCSeparationToSquash()); - reader.setDigits(digits); - reader.setROFRecords(rofs); - if (mUseMC) { - reader.setMC2ROFRecords(mc2rofs); - reader.setDigitsMCTruth(labels.getIndexedSize() > 0 ? &labels : nullptr); - } - reader.init(); - auto orig = o2::header::gDataOriginMFT; - std::vector clusCompVec; - std::vector clusROFVec; - std::vector clusPattVec; - - std::unique_ptr> clusterLabels; - if (mUseMC) { - clusterLabels = std::make_unique>(); - } - mClusterer->process(mNThreads, reader, &clusCompVec, &clusPattVec, &clusROFVec, clusterLabels.get()); - pc.outputs().snapshot(Output{orig, "COMPCLUSTERS", 0}, clusCompVec); - pc.outputs().snapshot(Output{orig, "CLUSTERSROF", 0}, clusROFVec); - pc.outputs().snapshot(Output{orig, "PATTERNS", 0}, clusPattVec); - - if (mUseMC) { - pc.outputs().snapshot(Output{orig, "CLUSTERSMCTR", 0}, *clusterLabels.get()); // at the moment requires snapshot - std::vector clusterMC2ROframes(mc2rofs.size()); - for (int i = mc2rofs.size(); i--;) { - clusterMC2ROframes[i] = mc2rofs[i]; // Simply, replicate it from digits ? - } - pc.outputs().snapshot(Output{orig, "CLUSTERSMC2ROF", 0}, clusterMC2ROframes); - } - - // TODO: in principle, after masking "overflow" pixels the MC2ROFRecord maxROF supposed to change, nominally to minROF - // -> consider recalculationg maxROF - LOG(debug) << "MFTClusterer pushed " << clusCompVec.size() << " compressed clusters, in " << clusROFVec.size() << " RO frames"; -} - -///_______________________________________ -void ClustererDPL::updateTimeDependentParams(ProcessingContext& pc) -{ - o2::base::GRPGeomHelper::instance().checkUpdates(pc); - static bool initOnceDone = false; - if (!initOnceDone) { // this params need to be queried only once - initOnceDone = true; - pc.inputs().get("cldict"); // just to trigger the finaliseCCDB - pc.inputs().get*>("alppar"); - pc.inputs().get*>("cluspar"); - mClusterer->setContinuousReadOut(o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::MFT)); - // settings for the fired pixel overflow masking - const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); - const auto& clParams = o2::itsmft::ClustererParam::Instance(); - if (clParams.maxBCDiffToMaskBias > 0 && clParams.maxBCDiffToSquashBias > 0) { - LOGP(fatal, "maxBCDiffToMaskBias = {} and maxBCDiffToSquashBias = {} cannot be set at the same time. Either set masking or squashing with a BCDiff > 0", clParams.maxBCDiffToMaskBias, clParams.maxBCDiffToSquashBias); - } - mClusterer->setDropHugeClusters(clParams.dropHugeClusters); - auto nbc = clParams.maxBCDiffToMaskBias; - nbc += mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); - mClusterer->setMaxBCSeparationToMask(nbc); - mClusterer->setMaxRowColDiffToMask(clParams.maxRowColDiffToMask); - // Squasher - int rofBC = mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); // ROF length in BC - mClusterer->setMaxBCSeparationToSquash(rofBC + clParams.maxBCDiffToSquashBias); - int nROFsToSquash = 0; // squashing disabled if no reset due to maxSOTMUS>0. - if (clParams.maxSOTMUS > 0 && rofBC > 0) { - nROFsToSquash = 2 + int(clParams.maxSOTMUS / (rofBC * o2::constants::lhc::LHCBunchSpacingMUS)); // use squashing - } - mClusterer->setMaxROFDepthToSquash(nROFsToSquash); - mClusterer->print(); - } - // we may have other params which need to be queried regularly -} - -///_______________________________________ -void ClustererDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) -{ - if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { - return; - } - if (matcher == ConcreteDataMatcher("MFT", "CLUSDICT", 0)) { - LOG(info) << "cluster dictionary updated" << (!mUseClusterDictionary ? " but its using is disabled" : ""); - if (mUseClusterDictionary) { - mClusterer->setDictionary((const TopologyDictionary*)obj); - } - return; - } - // Note: strictly speaking, for Configurable params we don't need finaliseCCDB check, the singletons are updated at the CCDB fetcher level - if (matcher == ConcreteDataMatcher("MFT", "ALPIDEPARAM", 0)) { - LOG(info) << "Alpide param updated"; - const auto& par = o2::itsmft::DPLAlpideParam::Instance(); - par.printKeyValues(); - return; - } - if (matcher == ConcreteDataMatcher("MFT", "CLUSPARAM", 0)) { - LOG(info) << "Cluster param updated"; - const auto& par = o2::itsmft::ClustererParam::Instance(); - par.printKeyValues(); - return; - } -} - -DataProcessorSpec getClustererSpec(bool useMC) -{ - std::vector inputs; - inputs.emplace_back("digits", "MFT", "DIGITS", 0, Lifetime::Timeframe); - inputs.emplace_back("ROframes", "MFT", "DIGITSROF", 0, Lifetime::Timeframe); - inputs.emplace_back("cldict", "MFT", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("MFT/Calib/ClusterDictionary")); - inputs.emplace_back("cluspar", "MFT", "CLUSPARAM", 0, Lifetime::Condition, ccdbParamSpec("MFT/Config/ClustererParam")); - inputs.emplace_back("alppar", "MFT", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("MFT/Config/AlpideParam")); - auto ggRequest = std::make_shared(false, // orbitResetTime - true, // GRPECS=true - false, // GRPLHCIF - false, // GRPMagField - false, // askMatLUT - o2::base::GRPGeomRequest::None, // geometry - inputs, - true); - std::vector outputs; - outputs.emplace_back("MFT", "COMPCLUSTERS", 0, Lifetime::Timeframe); - outputs.emplace_back("MFT", "PATTERNS", 0, Lifetime::Timeframe); - outputs.emplace_back("MFT", "CLUSTERSROF", 0, Lifetime::Timeframe); - - if (useMC) { - inputs.emplace_back("labels", "MFT", "DIGITSMCTR", 0, Lifetime::Timeframe); - inputs.emplace_back("MC2ROframes", "MFT", "DIGITSMC2ROF", 0, Lifetime::Timeframe); - outputs.emplace_back("MFT", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - outputs.emplace_back("MFT", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); - } - - return DataProcessorSpec{ - "mft-clusterer", - inputs, - outputs, - AlgorithmSpec{adaptFromTask(ggRequest, useMC)}, - Options{ - {"ignore-cluster-dictionary", VariantType::Bool, false, {"do not use cluster dictionary, always store explicit patterns"}}, - {"nthreads", VariantType::Int, 1, {"Number of clustering threads"}}}}; -} - -} // namespace mft -} // namespace o2 diff --git a/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx b/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx index 615c9c1b275d4..5d85c0ef81670 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx @@ -12,9 +12,9 @@ /// @file RecoWorkflow.cxx #include +#include "ITSMFTWorkflow/ClustererSpec.h" +#include "ITSMFTWorkflow/ClusterWriterSpec.h" #include "MFTWorkflow/RecoWorkflow.h" -#include "MFTWorkflow/ClustererSpec.h" -#include "MFTWorkflow/ClusterWriterSpec.h" #include "MFTWorkflow/TrackerSpec.h" #include "MFTWorkflow/TrackWriterSpec.h" #include "ITSMFTWorkflow/DigitReaderSpec.h" @@ -52,10 +52,10 @@ framework::WorkflowSpec getWorkflow( } } if (!upstreamClusters) { - specs.emplace_back(o2::mft::getClustererSpec(useMC)); + specs.emplace_back(o2::itsmft::getMFTClustererSpec(useMC)); } if (!disableRootOutput) { - specs.emplace_back(o2::mft::getClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getMFTClusterWriterSpec(useMC)); } if (runTracking) { diff --git a/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-writer-workflow.cxx b/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-writer-workflow.cxx index f42b2e0c92a4a..b656970693808 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-writer-workflow.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-writer-workflow.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "MFTWorkflow/ClusterWriterSpec.h" +#include "ITSMFTWorkflow/ClusterWriterSpec.h" #include "Framework/ConfigParamSpec.h" #include "Framework/CompletionPolicyHelpers.h" @@ -34,6 +34,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { auto useMC = !configcontext.options().get("disable-mc"); WorkflowSpec specs; - specs.emplace_back(o2::mft::getClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getMFTClusterWriterSpec(useMC)); return specs; } diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h index c66468905d0aa..0bdbb701a9356 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h @@ -215,13 +215,15 @@ class Clusterer int getMaxRowColDiffToMask() const { return mMaxRowColDiffToMask; } void setMaxRowColDiffToMask(int v) { mMaxRowColDiffToMask = v; } - int getMaxROFDepthToSquash() const { return mSquashingDepth; } + int getMaxROFDepthToSquash(int layer = -1) const { return (layer < 0) ? mSquashingDepth : mSquashingLayerDepth[layer]; } void setMaxROFDepthToSquash(int v) { mSquashingDepth = v; } + void addMaxROFDepthToSquash(int v) { mSquashingLayerDepth.push_back(v); } - int getMaxBCSeparationToSquash() const { return mMaxBCSeparationToSquash; } + int getMaxBCSeparationToSquash(int layer = -1) const { return (layer < 0) ? mMaxBCSeparationToSquash : mMaxBCSeparationToSquashLayer[layer]; } void setMaxBCSeparationToSquash(int n) { mMaxBCSeparationToSquash = n; } + void addMaxBCSeparationToSquash(int n) { mMaxBCSeparationToSquashLayer.push_back(n); } - void print() const; + void print(bool showTiming = true) const; void clear(); void reset(); @@ -245,7 +247,7 @@ class Clusterer bool mContinuousReadout = true; ///< flag continuous readout bool mDropHugeClusters = false; ///< don't include clusters that would be split in more than one - ///< mask continuosly fired pixels in frames separated by less than this amount of BCs (fired from hit in prev. ROF) + ///< mask continuously fired pixels in frames separated by less than this amount of BCs (fired from hit in prev. ROF) int mMaxBCSeparationToMask = 6000. / o2::constants::lhc::LHCBunchSpacingNS + 10; int mMaxRowColDiffToMask = 0; ///< provide their difference in col/row is <= than this int mNHugeClus = 0; ///< number of encountered huge clusters @@ -253,6 +255,8 @@ class Clusterer ///< Squashing options int mSquashingDepth = 0; ///< squashing is applied to next N rofs int mMaxBCSeparationToSquash = 6000. / o2::constants::lhc::LHCBunchSpacingNS + 10; + std::vector mSquashingLayerDepth; + std::vector mMaxBCSeparationToSquashLayer; std::vector> mThreads; // buffers for threads std::vector mChips; // currently processed ROF's chips data @@ -288,7 +292,7 @@ void Clusterer::streamCluster(const std::vector& pixbuf, const std::a uint16_t row = bbox.rowMin, col = bbox.colMin; if (pattID == CompCluster::InvalidPatternID || pattIdConverter.isGroup(pattID)) { if (pattID != CompCluster::InvalidPatternID) { - // For groupped topologies, the reference pixel is the COG pixel + // For grouped topologies, the reference pixel is the COG pixel float xCOG = 0., zCOG = 0.; ClusterPattern::getCOG(rowSpanW, colSpanW, patt.data(), xCOG, zCOG); row += round(xCOG); diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ClustererParam.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ClustererParam.h index a71e5f3095b06..3188a4f3b0010 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ClustererParam.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ClustererParam.h @@ -29,16 +29,26 @@ template struct ClustererParam : public o2::conf::ConfigurableParamHelper> { static_assert(N == o2::detectors::DetID::ITS || N == o2::detectors::DetID::MFT, "only DetID::ITS or DetID:: MFT are allowed"); + static constexpr int getNLayers() + { + return N == o2::detectors::DetID::ITS ? 7 : 10; + } + static constexpr std::string_view getParamName() { return N == o2::detectors::DetID::ITS ? ParamName[0] : ParamName[1]; } - int maxRowColDiffToMask = DEFRowColDiffToMask(); ///< pixel may be masked as overflow if such a neighbour in prev frame was fired - int maxBCDiffToMaskBias = 10; ///< mask if 2 ROFs differ by <= StrobeLength + Bias BCs, use value <0 to disable masking - int maxBCDiffToSquashBias = -10; ///< squash if 2 ROFs differ by <= StrobeLength + Bias BCs, use value <0 to disable squashing - float maxSOTMUS = 8.; ///< max expected signal over threshold in \mus - bool dropHugeClusters = false; ///< option to drop huge clusters (mitigate beam background) + int maxRowColDiffToMask = DEFRowColDiffToMask(); ///< pixel may be masked as overflow if such a neighbour in prev frame was fired + int maxBCDiffToMaskBias = 10; ///< mask if 2 ROFs differ by <= StrobeLength + Bias BCs, use value <0 to disable masking + int maxBCDiffToSquashBias = -10; ///< squash if 2 ROFs differ by <= StrobeLength + Bias BCs, use value <0 to disable squashing + float maxSOTMUS = 8.; ///< max expected signal over threshold in \mus + bool dropHugeClusters = false; ///< option to drop huge clusters (mitigate beam background) + int maxBCDiffToSquashBiasLayer[getNLayers()] = {}; ///< squash mask per layer + int getMaxBCDiffToSquashBias(int layer) const noexcept + { + return maxBCDiffToSquashBiasLayer[layer] ? maxBCDiffToSquashBiasLayer[layer] : maxBCDiffToSquashBias; + } O2ParamDef(ClustererParam, getParamName().data()); @@ -46,7 +56,7 @@ struct ClustererParam : public o2::conf::ConfigurableParamHelper(nFired, nThreads); #ifndef WITH_OPENMP nThreads = 1; #endif @@ -173,7 +168,7 @@ void Clusterer::ClustererThread::process(uint16_t chip, uint16_t nChips, CompClu const ConstMCTruth* labelsDigPtr, MCTruth* labelsClPtr, const ROFRecord& rofPtr) { if (stats.empty() || stats.back().firstChip + stats.back().nChips != chip) { // there is a jump, register new block - stats.emplace_back(ThreadStat{chip, 0, uint32_t(compClusPtr->size()), patternsPtr ? uint32_t(patternsPtr->size()) : 0, 0, 0}); + stats.emplace_back(ThreadStat{.firstChip = chip, .nChips = 0, .firstClus = uint32_t(compClusPtr->size()), .firstPatt = patternsPtr ? uint32_t(patternsPtr->size()) : 0, .nClus = 0, .nPatt = 0}); } for (int ic = 0; ic < nChips; ic++) { auto* curChipData = parent->mFiredChipsPtr[chip + ic]; @@ -476,22 +471,31 @@ void Clusterer::clear() } //__________________________________________________ -void Clusterer::print() const +void Clusterer::print(bool showsTiming) const { // print settings - LOGP(info, "Clusterizer squashes overflow pixels separated by {} BC and <= {} in row/col seeking down to {} neighbour ROFs", mMaxBCSeparationToSquash, mMaxRowColDiffToMask, mSquashingDepth); + if (mSquashingLayerDepth.empty()) { + LOGP(info, "Clusterizer squashes overflow pixels separated by {} BC and <= {} in row/col seeking down to {} neighbour ROFs", mMaxBCSeparationToSquash, mMaxRowColDiffToMask, mSquashingDepth); + } else { + LOGP(info, "Clusterizer squashes overflow pixels <= {} in row/col", mMaxRowColDiffToMask); + for (size_t i{0}; i < mSquashingLayerDepth.size(); ++i) { + LOGP(info, "\tlay:{} separated by {} BC seeking down to {} neighbour ROFs", i, mMaxBCSeparationToSquashLayer[i], mSquashingLayerDepth[i]); + } + } LOGP(info, "Clusterizer masks overflow pixels separated by < {} BC and <= {} in row/col", mMaxBCSeparationToMask, mMaxRowColDiffToMask); LOGP(info, "Clusterizer does {} drop huge clusters", mDropHugeClusters ? "" : "not"); + if (showsTiming) { #ifdef _PERFORM_TIMING_ - auto& tmr = const_cast(mTimer); // ugly but this is what root does internally - auto& tmrm = const_cast(mTimerMerge); - LOG(info) << "Inclusive clusterization timing (w/o disk IO): Cpu: " << tmr.CpuTime() - << " Real: " << tmr.RealTime() << " s in " << tmr.Counter() << " slots"; - LOG(info) << "Threads output merging timing : Cpu: " << tmrm.CpuTime() - << " Real: " << tmrm.RealTime() << " s in " << tmrm.Counter() << " slots"; + auto& tmr = const_cast(mTimer); // ugly but this is what root does internally + auto& tmrm = const_cast(mTimerMerge); + LOG(info) << "Inclusive clusterization timing (w/o disk IO): Cpu: " << tmr.CpuTime() + << " Real: " << tmr.RealTime() << " s in " << tmr.Counter() << " slots"; + LOG(info) << "Threads output merging timing : Cpu: " << tmrm.CpuTime() + << " Real: " << tmrm.RealTime() << " s in " << tmrm.Counter() << " slots"; #endif + } } //__________________________________________________ diff --git a/Detectors/ITSMFT/common/reconstruction/src/DigitPixelReader.cxx b/Detectors/ITSMFT/common/reconstruction/src/DigitPixelReader.cxx index b8d88a6fc4223..5c1dbde074649 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/DigitPixelReader.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/DigitPixelReader.cxx @@ -330,3 +330,14 @@ void DigitPixelReader::clear() mROFRecVec = gsl::span(); mMC2ROFRecVec = gsl::span(); } + +//______________________________________________________________________________ +void DigitPixelReader::reset() +{ + clear(); + mSquashedDigitsMask.clear(); + mBookmarkNextROFs.clear(); + mIdDig = 0; + mIdROF = 0; + mIdROFLast = 0; +} diff --git a/Detectors/ITSMFT/common/workflow/CMakeLists.txt b/Detectors/ITSMFT/common/workflow/CMakeLists.txt index 63cd8d6c0bcee..ead08c4422260 100644 --- a/Detectors/ITSMFT/common/workflow/CMakeLists.txt +++ b/Detectors/ITSMFT/common/workflow/CMakeLists.txt @@ -11,6 +11,8 @@ o2_add_library(ITSMFTWorkflow SOURCES src/ClusterReaderSpec.cxx + src/ClusterWriterSpec.cxx + src/ClustererSpec.cxx src/DigitWriterSpec.cxx src/DigitReaderSpec.cxx src/STFDecoderSpec.cxx diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterReaderSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterReaderSpec.h index 99318df1cd9d9..82e3890de7475 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterReaderSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterReaderSpec.h @@ -23,46 +23,51 @@ #include "DataFormatsITSMFT/CompCluster.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" +#include "ITSMFTBase/DPLAlpideParam.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "DetectorsCommonDataFormats/DetID.h" using namespace o2::framework; -namespace o2 -{ -namespace itsmft +namespace o2::itsmft { +template class ClusterReader : public Task { public: + static constexpr o2::detectors::DetID ID{N == o2::detectors::DetID::ITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT}; + static constexpr o2::header::DataOrigin Origin{(N == o2::detectors::DetID::ITS) ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + static constexpr int NLayers{o2::itsmft::DPLAlpideParam::supportsStaggering() ? o2::itsmft::DPLAlpideParam::getNLayers() : 1}; + ClusterReader() = delete; - ClusterReader(o2::detectors::DetID id, bool useMC, bool usePatterns = true, bool triggers = true); + ClusterReader(bool useMC, bool usePatterns = true, bool triggers = true); ~ClusterReader() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; protected: void connectTree(const std::string& filename); + template + void setBranchAddress(const std::string& base, Ptr& addr, int layer); + std::string getBranchName(const std::string& base, int index) const; - std::vector mClusROFRec, *mClusROFRecPtr = &mClusROFRec; - std::vector mClusterCompArray, *mClusterCompArrayPtr = &mClusterCompArray; - std::vector mPatternsArray, *mPatternsArrayPtr = &mPatternsArray; - o2::dataformats::MCTruthContainer mClusterMCTruth, *mClusterMCTruthPtr = &mClusterMCTruth; - std::vector mClusMC2ROFs, *mClusMC2ROFsPtr = &mClusMC2ROFs; - - o2::header::DataOrigin mOrigin = o2::header::gDataOriginInvalid; + std::array*, NLayers> mClusROFRec; + std::array*, NLayers> mClusterCompArray; + std::array*, NLayers> mPatternsArray; + std::array*, NLayers> mClusterMCTruth; + std::array*, NLayers> mClusMC2ROFs; std::unique_ptr mFile; std::unique_ptr mTree; - bool mUseMC = true; // use MC truth + bool mUseMC = true; // use MC truth bool mUsePatterns = true; // send patterns bool mTriggerOut = true; // send dummy triggers vector - std::string mDetName = ""; - std::string mDetNameLC = ""; - std::string mFileName = ""; + std::string mDetName; + std::string mDetNameLC; + std::string mFileName; std::string mClusTreeName = "o2sim"; std::string mClusROFBranchName = "ClustersROF"; std::string mClusterPattBranchName = "ClusterPatt"; @@ -71,24 +76,18 @@ class ClusterReader : public Task std::string mClustMC2ROFBranchName = "ClustersMC2ROF"; }; -class ITSClusterReader : public ClusterReader +class ITSClusterReader : public ClusterReader { public: ITSClusterReader(bool useMC = true, bool usePatterns = true, bool triggerOut = true) - : ClusterReader(o2::detectors::DetID::ITS, useMC, usePatterns, triggerOut) - { - mOrigin = o2::header::gDataOriginITS; - } + : ClusterReader(useMC, usePatterns, triggerOut) {} }; -class MFTClusterReader : public ClusterReader +class MFTClusterReader : public ClusterReader { public: MFTClusterReader(bool useMC = true, bool usePatterns = true, bool triggerOut = true) - : ClusterReader(o2::detectors::DetID::MFT, useMC, usePatterns, triggerOut) - { - mOrigin = o2::header::gDataOriginMFT; - } + : ClusterReader(useMC, usePatterns, triggerOut) {} }; /// create a processor spec @@ -96,7 +95,6 @@ class MFTClusterReader : public ClusterReader framework::DataProcessorSpec getITSClusterReaderSpec(bool useMC = true, bool usePatterns = true, bool useTriggers = true); framework::DataProcessorSpec getMFTClusterReaderSpec(bool useMC = true, bool usePatterns = true, bool useTriggers = true); -} // namespace itsmft -} // namespace o2 +} // namespace o2::itsmft #endif /* O2_ITSMFT_CLUSTERREADER */ diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClusterWriterSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterWriterSpec.h similarity index 73% rename from Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClusterWriterSpec.h rename to Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterWriterSpec.h index 42b96786af27a..5ae371e7e09c4 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClusterWriterSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterWriterSpec.h @@ -11,21 +11,19 @@ /// @file ClusterWriterSpec.h -#ifndef O2_ITS_CLUSTERWRITER -#define O2_ITS_CLUSTERWRITER +#ifndef O2_ITSMFT_CLUSTERWRITER +#define O2_ITSMFT_CLUSTERWRITER #include "Framework/DataProcessorSpec.h" -namespace o2 -{ -namespace its +namespace o2::itsmft { -/// create a processor spec -/// write ITS clusters to ROOT file +template framework::DataProcessorSpec getClusterWriterSpec(bool useMC); +framework::DataProcessorSpec getITSClusterWriterSpec(bool useMC); +framework::DataProcessorSpec getMFTClusterWriterSpec(bool useMC); -} // namespace its -} // namespace o2 +} // namespace o2::itsmft #endif /* O2_ITS_CLUSTERWRITER */ diff --git a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClustererSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClustererSpec.h similarity index 64% rename from Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClustererSpec.h rename to Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClustererSpec.h index f0a763597ff74..b6ebc282c2a27 100644 --- a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClustererSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClustererSpec.h @@ -11,24 +11,27 @@ /// @file ClustererSpec.h -#ifndef O2_MFT_CLUSTERERDPL_H_ -#define O2_MFT_CLUSTERERDPL_H_ +#ifndef O2_ITSMFT_CLUSTERERDPL_H_ +#define O2_ITSMFT_CLUSTERERDPL_H_ -#include #include "DetectorsBase/GRPGeomHelper.h" #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "ITSMFTReconstruction/Clusterer.h" +#include "ITSMFTBase/DPLAlpideParam.h" using namespace o2::framework; -namespace o2 -{ -namespace mft +namespace o2::itsmft { +template class ClustererDPL : public Task { + static constexpr o2::detectors::DetID ID{N == o2::detectors::DetID::ITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT}; + static constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + static constexpr int NLayers{o2::itsmft::DPLAlpideParam::supportsStaggering() ? o2::itsmft::DPLAlpideParam::getNLayers() : 1}; + public: ClustererDPL(std::shared_ptr gr, bool useMC) : mGGCCDBRequest(gr), mUseMC(useMC) {} ~ClustererDPL() override = default; @@ -39,20 +42,19 @@ class ClustererDPL : public Task private: void updateTimeDependentParams(ProcessingContext& pc); - int mState = 0; + std::string mDetName; bool mUseMC = true; bool mUseClusterDictionary = true; int mNThreads = 1; - std::unique_ptr mFile = nullptr; std::unique_ptr mClusterer = nullptr; std::shared_ptr mGGCCDBRequest; + int mLayers{NLayers}; + std::vector mFilter; }; -/// create a processor spec -/// run MFT cluster finder -framework::DataProcessorSpec getClustererSpec(bool useMC); +framework::DataProcessorSpec getITSClustererSpec(bool useMC); +framework::DataProcessorSpec getMFTClustererSpec(bool useMC); -} // namespace mft -} // namespace o2 +} // namespace o2::itsmft #endif /* O2_MFT_CLUSTERERDPL */ diff --git a/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx index ea906056c7898..bc6418a077810 100644 --- a/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx @@ -12,15 +12,16 @@ /// @file ClusterReaderSpec.cxx #include +#include -#include "TTree.h" +#include #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/Logger.h" #include "ITSMFTWorkflow/ClusterReaderSpec.h" +#include "ITSMFTBase/DPLAlpideParam.h" #include "DataFormatsITSMFT/PhysTrigger.h" -#include #include "CommonUtils/NameConf.h" using namespace o2::framework; @@ -31,45 +32,48 @@ namespace o2 namespace itsmft { -ClusterReader::ClusterReader(o2::detectors::DetID id, bool useMC, bool usePatterns, bool triggerOut) +template +ClusterReader::ClusterReader(bool useMC, bool usePatterns, bool triggerOut) : mUseMC(useMC), mUsePatterns(usePatterns), mTriggerOut(triggerOut), mDetName(Origin.as()), mDetNameLC(mDetName) { - assert(id == o2::detectors::DetID::ITS || id == o2::detectors::DetID::MFT); - mDetNameLC = mDetName = id.getName(); - mUseMC = useMC; - mUsePatterns = usePatterns; - mTriggerOut = triggerOut; std::transform(mDetNameLC.begin(), mDetNameLC.end(), mDetNameLC.begin(), ::tolower); + + mClusROFRec.fill(nullptr); + mClusterCompArray.fill(nullptr); + mPatternsArray.fill(nullptr); + mClusterMCTruth.fill(nullptr); + mClusMC2ROFs.fill(nullptr); } -void ClusterReader::init(InitContext& ic) +template +void ClusterReader::init(InitContext& ic) { mFileName = o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(ic.options().get("input-dir")), ic.options().get((mDetNameLC + "-cluster-infile").c_str())); connectTree(mFileName); } -void ClusterReader::run(ProcessingContext& pc) +template +void ClusterReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); - LOG(info) << mDetName << "ClusterReader pushes " << mClusROFRec.size() << " ROFRecords," - << mClusterCompArray.size() << " compact clusters at entry " << ent; - - // This is a very ugly way of providing DataDescription, which anyway does not need to contain detector name. - // To be fixed once the names-definition class is ready - pc.outputs().snapshot(Output{mOrigin, "CLUSTERSROF", 0}, mClusROFRec); - pc.outputs().snapshot(Output{mOrigin, "COMPCLUSTERS", 0}, mClusterCompArray); - if (mUsePatterns) { - pc.outputs().snapshot(Output{mOrigin, "PATTERNS", 0}, mPatternsArray); - } - if (mUseMC) { - pc.outputs().snapshot(Output{mOrigin, "CLUSTERSMCTR", 0}, mClusterMCTruth); - pc.outputs().snapshot(Output{mOrigin, "CLUSTERSMC2ROF", 0}, mClusMC2ROFs); + + for (uint32_t iLayer = 0; iLayer < NLayers; ++iLayer) { + LOG(info) << mDetName << "ClusterReader:" << iLayer << " pushes " << mClusROFRec[iLayer]->size() << " ROFRecords, " << mClusterCompArray[iLayer]->size() << " compact clusters at entry " << ent; + pc.outputs().snapshot(Output{Origin, "CLUSTERSROF", iLayer}, *mClusROFRec[iLayer]); + pc.outputs().snapshot(Output{Origin, "COMPCLUSTERS", iLayer}, *mClusterCompArray[iLayer]); + if (mUsePatterns) { + pc.outputs().snapshot(Output{Origin, "PATTERNS", iLayer}, *mPatternsArray[iLayer]); + } + if (mUseMC) { + pc.outputs().snapshot(Output{Origin, "CLUSTERSMCTR", iLayer}, *mClusterMCTruth[iLayer]); + pc.outputs().snapshot(Output{Origin, "CLUSTERSMC2ROF", iLayer}, *mClusMC2ROFs[iLayer]); + } } if (mTriggerOut) { std::vector dummyTrig; - pc.outputs().snapshot(Output{mOrigin, "PHYSTRIG", 0}, dummyTrig); + pc.outputs().snapshot(Output{Origin, "PHYSTRIG", 0}, dummyTrig); } if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); @@ -77,7 +81,8 @@ void ClusterReader::run(ProcessingContext& pc) } } -void ClusterReader::connectTree(const std::string& filename) +template +void ClusterReader::connectTree(const std::string& filename) { mTree.reset(nullptr); // in case it was already loaded mFile.reset(TFile::Open(filename.c_str())); @@ -85,70 +90,89 @@ void ClusterReader::connectTree(const std::string& filename) mTree.reset((TTree*)mFile->Get(mClusTreeName.c_str())); assert(mTree); - mTree->SetBranchAddress((mDetName + mClusROFBranchName).c_str(), &mClusROFRecPtr); - mTree->SetBranchAddress((mDetName + mClusterCompBranchName).c_str(), &mClusterCompArrayPtr); - if (mUsePatterns) { - mTree->SetBranchAddress((mDetName + mClusterPattBranchName).c_str(), &mPatternsArrayPtr); - } - if (mUseMC) { - if (mTree->GetBranch((mDetName + mClustMCTruthBranchName).c_str()) && - mTree->GetBranch((mDetName + mClustMC2ROFBranchName).c_str())) { - mTree->SetBranchAddress((mDetName + mClustMCTruthBranchName).c_str(), &mClusterMCTruthPtr); - mTree->SetBranchAddress((mDetName + mClustMC2ROFBranchName).c_str(), &mClusMC2ROFsPtr); - } else { - LOG(info) << "MC-truth is missing"; - mUseMC = false; + for (uint32_t iLayer = 0; iLayer < NLayers; ++iLayer) { + setBranchAddress(mClusROFBranchName, mClusROFRec[iLayer], iLayer); + setBranchAddress(mClusterCompBranchName, mClusterCompArray[iLayer], iLayer); + if (mUsePatterns) { + setBranchAddress(mClusterPattBranchName, mPatternsArray[iLayer], iLayer); + } + if (mUseMC) { + if (mTree->GetBranch(getBranchName(mClustMCTruthBranchName, iLayer).c_str()) && + mTree->GetBranch(getBranchName(mClustMC2ROFBranchName, iLayer).c_str())) { + setBranchAddress(mClustMCTruthBranchName, mClusterMCTruth[iLayer], iLayer); + setBranchAddress(mClustMC2ROFBranchName, mClusMC2ROFs[iLayer], iLayer); + } else { + LOG(info) << "MC-truth is missing"; + mUseMC = false; + } } } LOG(info) << "Loaded tree from " << filename << " with " << mTree->GetEntries() << " entries"; } -DataProcessorSpec getITSClusterReaderSpec(bool useMC, bool usePatterns, bool triggerOut) +template +std::string ClusterReader::getBranchName(const std::string& base, int index) const +{ + if constexpr (o2::itsmft::DPLAlpideParam::supportsStaggering()) { + return mDetName + base + "_" + std::to_string(index); + } + return mDetName + base; +} + +template +template +void ClusterReader::setBranchAddress(const std::string& base, Ptr& addr, int layer) { - std::vector outputSpec; - outputSpec.emplace_back("ITS", "CLUSTERSROF", 0, Lifetime::Timeframe); - outputSpec.emplace_back("ITS", "COMPCLUSTERS", 0, Lifetime::Timeframe); - if (usePatterns) { - outputSpec.emplace_back("ITS", "PATTERNS", 0, Lifetime::Timeframe); + const auto name = getBranchName(base, layer); + if (Int_t ret = mTree->SetBranchAddress(name.c_str(), &addr); ret != 0) { + LOGP(fatal, "failed to set branch address for {} ret={}", name, ret); } - if (useMC) { - outputSpec.emplace_back("ITS", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - outputSpec.emplace_back("ITS", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); +} + +namespace +{ +template +std::vector makeOutChannels(o2::header::DataOrigin detOrig, bool mctruth, bool usePatterns, bool triggerOut) +{ + std::vector outputs; + for (uint32_t iLayer = 0; iLayer < ((o2::itsmft::DPLAlpideParam::supportsStaggering()) ? o2::itsmft::DPLAlpideParam::getNLayers() : 1); ++iLayer) { + outputs.emplace_back(detOrig, "CLUSTERSROF", iLayer, Lifetime::Timeframe); + outputs.emplace_back(detOrig, "COMPCLUSTERS", iLayer, Lifetime::Timeframe); + if (usePatterns) { + outputs.emplace_back(detOrig, "PATTERNS", iLayer, Lifetime::Timeframe); + } + if (mctruth) { + outputs.emplace_back(detOrig, "CLUSTERSMCTR", iLayer, Lifetime::Timeframe); + outputs.emplace_back(detOrig, "CLUSTERSMC2ROF", iLayer, Lifetime::Timeframe); + } } if (triggerOut) { - outputSpec.emplace_back("ITS", "PHYSTRIG", 0, Lifetime::Timeframe); + outputs.emplace_back(detOrig, "PHYSTRIG", 0, Lifetime::Timeframe); } + return outputs; +} +} // namespace + +DataProcessorSpec getITSClusterReaderSpec(bool useMC, bool usePatterns, bool triggerOut) +{ return DataProcessorSpec{ - "its-cluster-reader", - Inputs{}, - outputSpec, - AlgorithmSpec{adaptFromTask(useMC, usePatterns, triggerOut)}, - Options{ + .name = "its-cluster-reader", + .inputs = Inputs{}, + .outputs = makeOutChannels("ITS", useMC, usePatterns, triggerOut), + .algorithm = AlgorithmSpec{adaptFromTask(useMC, usePatterns, triggerOut)}, + .options = Options{ {"its-cluster-infile", VariantType::String, "o2clus_its.root", {"Name of the input cluster file"}}, {"input-dir", VariantType::String, "none", {"Input directory"}}}}; } DataProcessorSpec getMFTClusterReaderSpec(bool useMC, bool usePatterns, bool triggerOut) { - std::vector outputSpec; - outputSpec.emplace_back("MFT", "CLUSTERSROF", 0, Lifetime::Timeframe); - outputSpec.emplace_back("MFT", "COMPCLUSTERS", 0, Lifetime::Timeframe); - if (usePatterns) { - outputSpec.emplace_back("MFT", "PATTERNS", 0, Lifetime::Timeframe); - } - if (useMC) { - outputSpec.emplace_back("MFT", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - outputSpec.emplace_back("MFT", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); - } - if (triggerOut) { - outputSpec.emplace_back("MFT", "PHYSTRIG", 0, Lifetime::Timeframe); - } return DataProcessorSpec{ - "mft-cluster-reader", - Inputs{}, - outputSpec, - AlgorithmSpec{adaptFromTask(useMC, usePatterns, triggerOut)}, - Options{ + .name = "mft-cluster-reader", + .inputs = Inputs{}, + .outputs = makeOutChannels("MFT", useMC, usePatterns, triggerOut), + .algorithm = AlgorithmSpec{adaptFromTask(useMC, usePatterns, triggerOut)}, + .options = Options{ {"mft-cluster-infile", VariantType::String, "mftclusters.root", {"Name of the input cluster file"}}, {"input-dir", VariantType::String, "none", {"Input directory"}}}}; } diff --git a/Detectors/ITSMFT/common/workflow/src/ClusterWriterSpec.cxx b/Detectors/ITSMFT/common/workflow/src/ClusterWriterSpec.cxx new file mode 100644 index 0000000000000..c1900c346133b --- /dev/null +++ b/Detectors/ITSMFT/common/workflow/src/ClusterWriterSpec.cxx @@ -0,0 +1,107 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file ClusterWriterSpec.cxx + +#include +#include +#include +#include +#include + +#include "Framework/ConcreteDataMatcher.h" +#include "ITSMFTBase/DPLAlpideParam.h" +#include "ITSMFTWorkflow/ClusterWriterSpec.h" +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" + +using namespace o2::framework; + +namespace o2::itsmft +{ + +template +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; +using CompClusType = std::vector; +using PatternsType = std::vector; +using ROFrameRType = std::vector; +using LabelsType = o2::dataformats::MCTruthContainer; +using ROFRecLblT = std::vector; +using namespace o2::header; + +template +DataProcessorSpec getClusterWriterSpec(bool useMC) +{ + static constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + constexpr int NLayers = (DPLAlpideParam::supportsStaggering()) ? DPLAlpideParam::getNLayers() : 1; + const auto detName = Origin.as(); + // Spectators for logging + auto compClusterSizes = std::make_shared>(); + auto compClustersSizeGetter = [compClusterSizes](CompClusType const& compClusters, DataRef const& ref) { + auto const* dh = DataRefUtils::getHeader(ref); + (*compClusterSizes)[dh->subSpecification] = compClusters.size(); + }; + auto logger = [detName, compClusterSizes](std::vector const& rofs, DataRef const& ref) { + auto const* dh = DataRefUtils::getHeader(ref); + const auto i = dh->subSpecification; + LOG(info) << detName << "ClusterWriter:" << i << " pulled " << (*compClusterSizes)[i] << " clusters, in " << rofs.size() << " RO frames"; + }; + auto getIndex = [](DataRef const& ref) -> size_t { + auto const* dh = DataRefUtils::getHeader(ref); + return static_cast(dh->subSpecification); + }; + auto getName = [](std::string base, size_t index) -> std::string { + if constexpr (DPLAlpideParam::supportsStaggering()) { + return base += "_" + std::to_string(index); + } + return base; + }; + auto detNameLC = detName; + std::transform(detNameLC.begin(), detNameLC.end(), detNameLC.begin(), [](unsigned char c) { return std::tolower(c); }); + return MakeRootTreeWriterSpec(std::format("{}-cluster-writer", detNameLC).c_str(), + (o2::detectors::DetID::ITS == N) ? "o2clus_its.root" : "mftclusters.root", + MakeRootTreeWriterSpec::TreeAttributes{.name = "o2sim", .title = std::format("Tree with {} clusters", detName)}, + BranchDefinition{InputSpec{"compclus", ConcreteDataTypeMatcher{Origin, "COMPCLUSTERS"}}, + (detName + "ClusterComp").c_str(), "compact-cluster-branch", + NLayers, + compClustersSizeGetter, + getIndex, + getName}, + BranchDefinition{InputSpec{"patterns", ConcreteDataTypeMatcher{Origin, "PATTERNS"}}, + (detName + "ClusterPatt").c_str(), "cluster-pattern-branch", + NLayers, + getIndex, + getName}, + BranchDefinition{InputSpec{"ROframes", ConcreteDataTypeMatcher{Origin, "CLUSTERSROF"}}, + (detName + "ClustersROF").c_str(), "cluster-rof-branch", + NLayers, + logger, + getIndex, + getName}, + BranchDefinition{InputSpec{"labels", ConcreteDataTypeMatcher{Origin, "CLUSTERSMCTR"}}, + (detName + "ClusterMCTruth").c_str(), "cluster-label-branch", + (useMC ? NLayers : 0), + getIndex, + getName}, + BranchDefinition{InputSpec{"MC2ROframes", ConcreteDataTypeMatcher{Origin, "CLUSTERSMC2ROF"}}, + (detName + "ClustersMC2ROF").c_str(), "cluster-mc2rof-branch", + (useMC ? NLayers : 0), + getIndex, + getName})(); +} + +framework::DataProcessorSpec getITSClusterWriterSpec(bool useMC) { return getClusterWriterSpec(useMC); } +framework::DataProcessorSpec getMFTClusterWriterSpec(bool useMC) { return getClusterWriterSpec(useMC); } + +} // namespace o2::itsmft diff --git a/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx b/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx new file mode 100644 index 0000000000000..0b6bb44ee78c8 --- /dev/null +++ b/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx @@ -0,0 +1,325 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file ClustererSpec.cxx + +#include + +#include "ITSMFTWorkflow/ClustererSpec.h" +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/CCDBParamSpec.h" +#include "DataFormatsITSMFT/Digit.h" +#include "Framework/InputRecordWalker.h" +#include "ITSMFTReconstruction/ChipMappingMFT.h" +#include "ITSMFTReconstruction/ChipMappingITS.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/TopologyDictionary.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsParameters/GRPObject.h" +#include "ITSMFTReconstruction/DigitPixelReader.h" +#include "DetectorsBase/GeometryManager.h" +#include "ITSMFTBase/DPLAlpideParam.h" +#include "CommonConstants/LHCConstants.h" +#include "DetectorsCommonDataFormats/DetectorNameConf.h" +#include "ITSMFTReconstruction/ClustererParam.h" + +namespace o2::itsmft +{ + +template +void ClustererDPL::init(InitContext& ic) +{ + mClusterer = std::make_unique(); + mClusterer->setNChips((N == o2::detectors::DetID::ITS) ? o2::itsmft::ChipMappingITS::getNChips() : o2::itsmft::ChipMappingMFT::getNChips()); + mUseClusterDictionary = !ic.options().get("ignore-cluster-dictionary"); + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + mNThreads = std::max(1, ic.options().get("nthreads")); + mDetName = Origin.as(); + + // prepare data filter + for (int iLayer = 0; iLayer < NLayers; ++iLayer) { + mFilter.emplace_back("digits", Origin, "DIGITS", iLayer, Lifetime::Timeframe); + mFilter.emplace_back("ROframe", Origin, "DIGITSROF", iLayer, Lifetime::Timeframe); + if (mUseMC) { + mFilter.emplace_back("labels", Origin, "DIGITSMCTR", iLayer, Lifetime::Timeframe); + mFilter.emplace_back("MC2ROframes", Origin, "DIGITSMC2ROF", iLayer, Lifetime::Timeframe); + } + } +} + +template +void ClustererDPL::run(ProcessingContext& pc) +{ + updateTimeDependentParams(pc); + + // filter input and compose + std::array, NLayers> digits; + std::array, NLayers> rofs; + std::array, NLayers> labelsbuffer; + std::array, NLayers> mc2rofs; + for (const DataRef& ref : InputRecordWalker{pc.inputs(), mFilter}) { + auto const* dh = DataRefUtils::getHeader(ref); + if (DataRefUtils::match(ref, {"digits", ConcreteDataTypeMatcher{Origin, "DIGITS"}})) { + digits[dh->subSpecification] = pc.inputs().get>(ref); + } + if (DataRefUtils::match(ref, {"ROframe", ConcreteDataTypeMatcher{Origin, "DIGITSROF"}})) { + rofs[dh->subSpecification] = pc.inputs().get>(ref); + } + if (DataRefUtils::match(ref, {"labels", ConcreteDataTypeMatcher{Origin, "DIGITSMCTR"}})) { + labelsbuffer[dh->subSpecification] = pc.inputs().get>(ref); + } + if (DataRefUtils::match(ref, {"MC2ROframes", ConcreteDataTypeMatcher{Origin, "DIGITSMC2ROF"}})) { + mc2rofs[dh->subSpecification] = pc.inputs().get>(ref); + } + } + + // query the first orbit in this TF + const auto firstTForbit = pc.services().get().firstTForbit; + const o2::InteractionRecord firstIR(0, firstTForbit); + const auto& par = DPLAlpideParam::Instance(); + + // process received inputs + uint64_t nClusters{0}; + TStopwatch sw; + o2::itsmft::DigitPixelReader reader; + for (uint32_t iLayer{0}; iLayer < NLayers; ++iLayer) { + int layer = (DPLAlpideParam::supportsStaggering()) ? iLayer : -1; + sw.Start(); + LOG(info) << mDetName << "Clusterer:" << layer << " pulled " << digits[iLayer].size() << " digits, in " << rofs[iLayer].size() << " RO frames"; + + mClusterer->setMaxROFDepthToSquash(mClusterer->getMaxROFDepthToSquash(layer)); + o2::dataformats::ConstMCTruthContainerView labels(labelsbuffer[iLayer]); + reader.setSquashingDepth(mClusterer->getMaxROFDepthToSquash(layer)); + reader.setSquashingDist(mClusterer->getMaxRowColDiffToMask()); // Sharing same parameter/logic with masking + reader.setMaxBCSeparationToSquash(mClusterer->getMaxBCSeparationToSquash(layer)); + reader.setDigits(digits[iLayer]); + reader.setROFRecords(rofs[iLayer]); + if (mUseMC) { + reader.setMC2ROFRecords(mc2rofs[iLayer]); + LOG(info) << mDetName << "Clusterer:" << layer << " pulled " << labels.getNElements() << " labels "; + reader.setDigitsMCTruth(labels.getIndexedSize() > 0 ? &labels : nullptr); + } + reader.init(); + std::vector clusCompVec; + std::vector clusROFVec; + std::vector clusPattVec; + + std::unique_ptr> clusterLabels; + if (mUseMC) { + clusterLabels = std::make_unique>(); + } + mClusterer->process(mNThreads, reader, &clusCompVec, &clusPattVec, &clusROFVec, clusterLabels.get()); + + // ensure that the rof output is continuous + size_t nROFs = clusROFVec.size(); + const int nROFsPerOrbit = o2::constants::lhc::LHCMaxBunches / par.getROFLengthInBC(iLayer); + const int nROFsTF = nROFsPerOrbit * o2::base::GRPGeomHelper::getNHBFPerTF(); + if (nROFsTF != clusROFVec.size()) { + // it can happen that in the digitization rofs without contributing hits are skipped + // however downstream consumers of the clusters cannot know apriori the time structure + // the cluster rofs do not account for the bias so it will start always at BC=0 + // if we receive more cluster rofs then there supposed to be, do not throw away this data + // the clusterer should be blind to this! + const size_t nROFsLayer = std::max((size_t)nROFsTF, clusROFVec.size()); + std::vector expClusRofVec(nROFsLayer); + for (int iROF{0}; iROF < nROFsLayer; ++iROF) { + auto& rof = expClusRofVec[iROF]; + int orb = iROF * par.getROFLengthInBC(iLayer) / o2::constants::lhc::LHCMaxBunches + firstTForbit; + int bc = iROF * par.getROFLengthInBC(iLayer) % o2::constants::lhc::LHCMaxBunches; + o2::InteractionRecord ir(bc, orb); + rof.setBCData(ir); + rof.setROFrame(iROF); + rof.setNEntries(0); + rof.setFirstEntry(-1); + } + uint32_t prevEntry{0}; + for (const auto& rof : clusROFVec) { + const auto& ir = rof.getBCData(); + const auto irToFirst = ir - firstIR; + const int irROF = irToFirst.toLong() / par.getROFLengthInBC(iLayer); + auto& expROF = expClusRofVec[irROF]; + expROF.setFirstEntry(rof.getFirstEntry()); + expROF.setNEntries(rof.getNEntries()); + if (expROF.getBCData() != rof.getBCData()) { + LOGP(fatal, "detected mismatch between expected ROF:{} and received ROF:{}", expROF.asString(), rof.asString()); + } + } + int prevFirst{0}; + for (auto& rof : expClusRofVec) { + if (rof.getFirstEntry() < 0) { + rof.setFirstEntry(prevFirst); + } + prevFirst = rof.getFirstEntry(); + } + nROFs = expClusRofVec.size(); + pc.outputs().snapshot(Output{Origin, "CLUSTERSROF", iLayer}, expClusRofVec); + } else { + pc.outputs().snapshot(Output{Origin, "CLUSTERSROF", iLayer}, clusROFVec); + } + pc.outputs().snapshot(Output{Origin, "COMPCLUSTERS", iLayer}, clusCompVec); + pc.outputs().snapshot(Output{Origin, "PATTERNS", iLayer}, clusPattVec); + + nClusters += clusCompVec.size(); + + if (mUseMC) { + pc.outputs().snapshot(Output{Origin, "CLUSTERSMCTR", iLayer}, *clusterLabels); // at the moment requires snapshot + std::vector clusterMC2ROframes(mc2rofs[iLayer].size()); + for (int i = mc2rofs[iLayer].size(); i--;) { + clusterMC2ROframes[i] = mc2rofs[iLayer][i]; // Simply, replicate it from digits ? + } + pc.outputs().snapshot(Output{Origin, "CLUSTERSMC2ROF", iLayer}, clusterMC2ROframes); + } + reader.reset(); + + // TODO: in principle, after masking "overflow" pixels the MC2ROFRecord maxROF supposed to change, nominally to minROF + // -> consider recalculationg maxROF + sw.Stop(); + LOG(info) << mDetName << "Clusterer:" << layer << " pushed " << clusCompVec.size() << " clusters, in " << nROFs << " RO frames in " << sw.RealTime() << " s"; + } + + LOG(info) << mDetName << "Clusterer produced " << nClusters << " clusters"; +} + +///_______________________________________ +template +void ClustererDPL::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + static bool initOnceDone = false; + if (!initOnceDone) { // this params need to be queried only once + initOnceDone = true; + pc.inputs().get("cldict"); // just to trigger the finaliseCCDB + pc.inputs().get*>("alppar"); + pc.inputs().get*>("cluspar"); + mClusterer->setContinuousReadOut(o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(N)); + // settings for the fired pixel overflow masking + const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); + const auto& clParams = o2::itsmft::ClustererParam::Instance(); + if (clParams.maxBCDiffToMaskBias > 0 && clParams.maxBCDiffToSquashBias > 0) { + LOGP(fatal, "maxBCDiffToMaskBias = {} and maxBCDiffToSquashBias = {} cannot be set at the same time. Either set masking or squashing with a BCDiff > 0", clParams.maxBCDiffToMaskBias, clParams.maxBCDiffToSquashBias); + } + mClusterer->setDropHugeClusters(clParams.dropHugeClusters); + auto nbc = clParams.maxBCDiffToMaskBias; + nbc += mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); + mClusterer->setMaxBCSeparationToMask(nbc); + mClusterer->setMaxRowColDiffToMask(clParams.maxRowColDiffToMask); + // Squasher + int rofBC = mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); // ROF length in BC + mClusterer->setMaxBCSeparationToSquash(rofBC + clParams.maxBCDiffToSquashBias); + int nROFsToSquash = 0; // squashing disabled if no reset due to maxSOTMUS>0. + if (clParams.maxSOTMUS > 0 && rofBC > 0) { + nROFsToSquash = 2 + int(clParams.maxSOTMUS / (rofBC * o2::constants::lhc::LHCBunchSpacingMUS)); // use squashing + } + mClusterer->setMaxROFDepthToSquash(nROFsToSquash); + if constexpr (DPLAlpideParam::supportsStaggering()) { + if (mClusterer->isContinuousReadOut()) { + for (int iLayer{0}; iLayer < NLayers; ++iLayer) { + mClusterer->addMaxBCSeparationToSquash(alpParams.getROFLengthInBC(iLayer) + clParams.getMaxBCDiffToSquashBias(iLayer)); + mClusterer->addMaxROFDepthToSquash((clParams.getMaxBCDiffToSquashBias(iLayer) > 0) ? 2 + int(clParams.maxSOTMUS / (alpParams.getROFLengthInBC(iLayer) * o2::constants::lhc::LHCBunchSpacingMUS)) : 0); + } + } + } + mClusterer->print(false); + } + // we may have other params which need to be queried regularly +} + +///_______________________________________ +template +void ClustererDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + if (matcher == ConcreteDataMatcher(Origin, "CLUSDICT", 0)) { + LOG(info) << "cluster dictionary updated" << (!mUseClusterDictionary ? " but its using is disabled" : ""); + if (mUseClusterDictionary) { + mClusterer->setDictionary((const TopologyDictionary*)obj); + } + return; + } + // Note: strictly speaking, for Configurable params we don't need finaliseCCDB check, the singletons are updated at the CCDB fetcher level + if (matcher == ConcreteDataMatcher(Origin, "ALPIDEPARAM", 0)) { + LOG(info) << "Alpide param updated"; + const auto& par = o2::itsmft::DPLAlpideParam::Instance(); + par.printKeyValues(); + return; + } + if (matcher == ConcreteDataMatcher(Origin, "CLUSPARAM", 0)) { + LOG(info) << "Cluster param updated"; + const auto& par = o2::itsmft::ClustererParam::Instance(); + par.printKeyValues(); + return; + } +} + +namespace +{ +template +DataProcessorSpec getClustererSpec(bool useMC) +{ + constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + std::vector inputs; + constexpr uint32_t nLayers = (DPLAlpideParam::supportsStaggering()) ? DPLAlpideParam::getNLayers() : 1; + for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer) { + inputs.emplace_back("digits", Origin, "DIGITS", iLayer, Lifetime::Timeframe); + inputs.emplace_back("ROframes", Origin, "DIGITSROF", iLayer, Lifetime::Timeframe); + if (useMC) { + inputs.emplace_back("labels", Origin, "DIGITSMCTR", iLayer, Lifetime::Timeframe); + inputs.emplace_back("MC2ROframes", Origin, "DIGITSMC2ROF", iLayer, Lifetime::Timeframe); + } + } + inputs.emplace_back("cldict", Origin, "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec(Origin.as() + "/Calib/ClusterDictionary")); + inputs.emplace_back("cluspar", Origin, "CLUSPARAM", 0, Lifetime::Condition, ccdbParamSpec(Origin.as() + "/Config/ClustererParam")); + inputs.emplace_back("alppar", Origin, "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec(Origin.as() + "/Config/AlpideParam")); + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::None, // geometry + inputs, + true); + std::vector outputs; + for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer) { + outputs.emplace_back(Origin, "COMPCLUSTERS", iLayer, Lifetime::Timeframe); + outputs.emplace_back(Origin, "PATTERNS", iLayer, Lifetime::Timeframe); + outputs.emplace_back(Origin, "CLUSTERSROF", iLayer, Lifetime::Timeframe); + if (useMC) { + outputs.emplace_back(Origin, "CLUSTERSMCTR", iLayer, Lifetime::Timeframe); + outputs.emplace_back(Origin, "CLUSTERSMC2ROF", iLayer, Lifetime::Timeframe); + } + } + return DataProcessorSpec{ + .name = (N == o2::detectors::DetID::ITS) ? "its-clusterer" : "mft-clusterer", + .inputs = inputs, + .outputs = outputs, + .algorithm = AlgorithmSpec{adaptFromTask>(ggRequest, useMC)}, + .options = Options{ + {"ignore-cluster-dictionary", VariantType::Bool, false, {"do not use cluster dictionary, always store explicit patterns"}}, + {"nthreads", VariantType::Int, 1, {"Number of clustering threads"}}}}; +} +} // namespace + +framework::DataProcessorSpec getITSClustererSpec(bool useMC) +{ + return getClustererSpec(useMC); +} + +framework::DataProcessorSpec getMFTClustererSpec(bool useMC) +{ + return getClustererSpec(useMC); +} + +} // namespace o2::itsmft diff --git a/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx b/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx index 004c3f6097167..60fe4fabfe481 100644 --- a/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx +++ b/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx @@ -12,7 +12,7 @@ #include "ITS3Workflow/RecoWorkflow.h" #include "ITS3Workflow/ClustererSpec.h" #include "ITS3Workflow/TrackerSpec.h" -#include "ITSWorkflow/ClusterWriterSpec.h" +#include "ITSMFTWorkflow/ClusterWriterSpec.h" #include "ITSWorkflow/TrackWriterSpec.h" #include "ITS3Workflow/DigitReaderSpec.h" #include "GPUWorkflow/GPUWorkflowSpec.h" @@ -40,7 +40,7 @@ framework::WorkflowSpec getWorkflow(bool useMC, its::TrackingMode::Type trmode, } if (!disableRootOutput) { - specs.emplace_back(o2::its::getClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC)); } if (trmode != its::TrackingMode::Off) { From 4ec73c1134146fdf5648327c62d112625240f3c3 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 12 Feb 2026 13:32:13 +0100 Subject: [PATCH 095/133] DPL: introduce range based views to navigate data model (#15061) --- .../Core/include/Framework/DataModelViews.h | 239 ++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 Framework/Core/include/Framework/DataModelViews.h diff --git a/Framework/Core/include/Framework/DataModelViews.h b/Framework/Core/include/Framework/DataModelViews.h new file mode 100644 index 0000000000000..b7a334454bb6e --- /dev/null +++ b/Framework/Core/include/Framework/DataModelViews.h @@ -0,0 +1,239 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef O2_FRAMEWORK_DATASPECVIEWS_H_ +#define O2_FRAMEWORK_DATASPECVIEWS_H_ + +#include +#include +#include "DomainInfoHeader.h" +#include "SourceInfoHeader.h" +#include "Headers/DataHeader.h" +#include + +namespace o2::framework +{ + +struct count_payloads { + // ends the pipeline, returns the container + template + requires std::ranges::random_access_range && std::ranges::sized_range + friend size_t operator|(R&& r, count_payloads self) + { + size_t count = 0; + size_t mi = 0; + while (mi < r.size()) { + auto* header = o2::header::get(r[mi]->GetData()); + if (!header) { + throw std::runtime_error("Not a DataHeader"); + } + if (header->splitPayloadParts > 1 && header->splitPayloadIndex == header->splitPayloadParts) { + count += header->splitPayloadParts; + mi += header->splitPayloadParts + 1; + } else { + count += header->splitPayloadParts ? header->splitPayloadParts : 1; + mi += header->splitPayloadParts ? 2 * header->splitPayloadParts : 2; + } + } + return count; + } +}; + +struct count_parts { + // ends the pipeline, returns the number of parts + template + requires std::ranges::random_access_range && std::ranges::sized_range + friend size_t operator|(R&& r, count_parts self) + { + size_t count = 0; + size_t mi = 0; + while (mi < r.size()) { + auto* header = o2::header::get(r[mi]->GetData()); + auto* sih = o2::header::get(r[mi]->GetData()); + auto* dih = o2::header::get(r[mi]->GetData()); + if (!header && !sih && !dih) { + throw std::runtime_error("Header information not found"); + } + // We skip oldest possible timeframe / end of stream and not consider it + // as actual parts. + if (dih || sih) { + count += 1; + mi += 2; + } else if (header->splitPayloadParts > 1 && header->splitPayloadIndex == header->splitPayloadParts) { + count += 1; + mi += header->splitPayloadParts + 1; + } else { + count += header->splitPayloadParts; + mi += header->splitPayloadParts ? 2 * header->splitPayloadParts : 2; + } + } + return count; + } +}; + +struct DataRefIndices { + size_t headerIdx; + size_t payloadIdx; +}; + +struct get_pair { + size_t pairId; + template + requires std::ranges::random_access_range && std::ranges::sized_range + friend DataRefIndices operator|(R&& r, get_pair self) + { + size_t count = 0; + size_t mi = 0; + while (mi < r.size()) { + auto* header = o2::header::get(r[mi]->GetData()); + if (!header) { + throw std::runtime_error("Not a DataHeader"); + } + size_t diff = self.pairId - count; + if (header->splitPayloadParts > 1 && header->splitPayloadIndex == header->splitPayloadParts) { + count += header->splitPayloadParts; + if (self.pairId < count) { + return {mi, mi + 1 + diff}; + } + mi += header->splitPayloadParts + 1; + } else { + count += header->splitPayloadParts ? header->splitPayloadParts : 1; + if (self.pairId < count) { + return {mi, mi + 2 * diff + 1}; + } + mi += header->splitPayloadParts ? 2 * header->splitPayloadParts : 2; + } + } + throw std::runtime_error("Payload not found"); + } +}; + +struct get_dataref_indices { + size_t part; + size_t subPart; + // ends the pipeline, returns the number of parts + template + requires std::ranges::random_access_range && std::ranges::sized_range + friend DataRefIndices operator|(R&& r, get_dataref_indices self) + { + size_t count = 0; + size_t mi = 0; + while (mi < r.size()) { + auto* header = o2::header::get(r[mi]->GetData()); + if (!header) { + throw std::runtime_error("Not a DataHeader"); + } + if (header->splitPayloadParts > 1 && header->splitPayloadIndex == header->splitPayloadParts) { + if (self.part == count) { + return {mi, mi + 1 + self.subPart}; + } + count += 1; + mi += header->splitPayloadParts + 1; + } else { + if (self.part == count) { + return {mi, mi + 2 * self.subPart + 1}; + } + count += 1; + mi += header->splitPayloadParts ? 2 * header->splitPayloadParts : 2; + } + } + throw std::runtime_error("Payload not found"); + } +}; + +struct get_header { + size_t id; + // ends the pipeline, returns the number of parts + template + requires std::ranges::random_access_range && std::ranges::sized_range + friend fair::mq::MessagePtr& operator|(R&& r, get_header self) + { + return r[(r | get_dataref_indices{self.id, 0}).headerIdx]; + } +}; + +struct get_payload { + size_t part; + size_t subPart; + // ends the pipeline, returns the number of parts + template + requires std::ranges::random_access_range && std::ranges::sized_range + friend fair::mq::MessagePtr& operator|(R&& r, get_payload self) + { + return r[(r | get_dataref_indices{self.part, self.subPart}).payloadIdx]; + } +}; + +struct get_num_payloads { + size_t id; + // ends the pipeline, returns the number of parts + template + requires std::ranges::random_access_range && std::ranges::sized_range + friend size_t operator|(R&& r, get_num_payloads self) + { + size_t count = 0; + size_t mi = 0; + while (mi < r.size()) { + auto* header = o2::header::get(r[mi]->GetData()); + if (!header) { + throw std::runtime_error("Not a DataHeader"); + } + if (self.id == count) { + if (header->splitPayloadParts > 1 && (header->splitPayloadIndex == header->splitPayloadParts)) { + return header->splitPayloadParts; + } else { + return 1; + } + } + if (header->splitPayloadParts > 1 && (header->splitPayloadIndex == header->splitPayloadParts)) { + count += 1; + mi += header->splitPayloadParts + 1; + } else { + count += 1; + mi += header->splitPayloadParts ? 2 * header->splitPayloadParts : 2; + } + } + return 0; + } +}; + +struct MessageSet; + +struct MessageStore { + std::span sets; + size_t inputsPerSlot = 0; +}; + +struct inputs_for_slot { + TimesliceSlot slot; + template + requires requires(R r) { std::ranges::random_access_range; } + friend std::span operator|(R&& r, inputs_for_slot self) + { + return std::span(r.sets[self.slot.index * r.inputsPerSlot]); + } +}; + +struct messages_for_input { + size_t inputIdx; + template + requires std::ranges::random_access_range + friend std::span operator|(R&& r, messages_for_input self) + { + return r[self.inputIdx].messages; + } +}; + +// FIXME: we should use special index classes in place of size_t +// FIXME: we need something to substitute a range in the store with another + +} // namespace o2::framework + +#endif // O2_FRAMEWORK_DATASPECVIEWS_H_ From 834cbc5b6f9e0f73b4bdedbb96df73538571571f Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 12 Feb 2026 14:39:27 +0100 Subject: [PATCH 096/133] FST: Make previousOrbit configurable --- prodtests/full_system_test.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/prodtests/full_system_test.sh b/prodtests/full_system_test.sh index e2ecca590140f..82021d6c65e63 100755 --- a/prodtests/full_system_test.sh +++ b/prodtests/full_system_test.sh @@ -40,6 +40,7 @@ export LC_ALL=C BEAMTYPE=${BEAMTYPE:-PbPb} NEvents=${NEvents:-10} #550 for full TF (the number of PbPb events) NEventsQED=${NEventsQED:-1000} #35000 for full TF +OrbitsBeforeTf=${OrbitsBeforeTf:-1} NCPUS=$(getNumberOfPhysicalCPUCores) echo "Found ${NCPUS} physical CPU cores" NJOBS=${NJOBS:-"${NCPUS}"} @@ -159,7 +160,7 @@ taskwrapper collcontext.log o2-steer-colcontexttool \ --extract-per-timeframe tf:o2sim \ --with-vertices kCCDB \ --maxCollsPerTF ${NEvents} \ - --orbitsEarly 1 \ + --orbitsEarly ${OrbitsBeforeTf} \ --bcPatternFile ccdb \ ${QEDSPEC} From 23765b5bdef76fb5a3151d01df8f908936aacf94 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 13 Feb 2026 09:03:17 +0100 Subject: [PATCH 097/133] DPL: Enforce that dpl pipeline length is at least as long as number of TFs in flight (#15048) --- .../Core/include/Framework/DataRelayer.h | 3 ++- .../Core/include/Framework/DefaultsHelpers.h | 10 +++++++- Framework/Core/src/ArrowSupport.cxx | 2 +- Framework/Core/src/CommonServices.cxx | 4 +++- Framework/Core/src/DataProcessingDevice.cxx | 12 ++++++---- Framework/Core/src/DataRelayer.cxx | 17 ++++++++++++-- Framework/Core/src/DefaultsHelpers.cxx | 23 +++++++++++++++---- Framework/Core/src/runDataProcessing.cxx | 5 ++-- Framework/Core/test/benchmark_DataRelayer.cxx | 10 ++++---- Framework/Core/test/test_DataRelayer.cxx | 22 +++++++++--------- 10 files changed, 75 insertions(+), 33 deletions(-) diff --git a/Framework/Core/include/Framework/DataRelayer.h b/Framework/Core/include/Framework/DataRelayer.h index 1e010fc12f3d4..e5a2aecea1de4 100644 --- a/Framework/Core/include/Framework/DataRelayer.h +++ b/Framework/Core/include/Framework/DataRelayer.h @@ -102,7 +102,8 @@ class DataRelayer DataRelayer(CompletionPolicy const&, std::vector const& routes, TimesliceIndex&, - ServiceRegistryRef); + ServiceRegistryRef, + int); /// This invokes the appropriate `InputRoute::danglingChecker` on every /// entry in the cache and if it returns true, it creates a new diff --git a/Framework/Core/include/Framework/DefaultsHelpers.h b/Framework/Core/include/Framework/DefaultsHelpers.h index 16d41d03baa7f..68e64cc42a90e 100644 --- a/Framework/Core/include/Framework/DefaultsHelpers.h +++ b/Framework/Core/include/Framework/DefaultsHelpers.h @@ -12,16 +12,24 @@ #ifndef O2_FRAMEWORK_DEFAULTHELPERS_H_ #define O2_FRAMEWORK_DEFAULTHELPERS_H_ +namespace fair::mq +{ +class ProgOptions; +} + namespace o2::framework { enum struct DeploymentMode; +struct DeviceConfig; struct DefaultsHelpers { static DeploymentMode deploymentMode(); /// @true if running online static bool onlineDeploymentMode(); /// get max number of timeslices in the queue - static unsigned int pipelineLength(); + static unsigned int pipelineLength(unsigned int minLength); + static unsigned int pipelineLength(const fair::mq::ProgOptions& options); + static unsigned int pipelineLength(const DeviceConfig& dc); }; } // namespace o2::framework diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index 450f31f4ba7d3..c5cc021a53478 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -564,7 +564,7 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() if (dc.options.count("timeframes-rate-limit") && dc.options["timeframes-rate-limit"].defaulted() == false) { config->maxTimeframes = std::stoll(dc.options["timeframes-rate-limit"].as()); } else { - config->maxTimeframes = readers * DefaultsHelpers::pipelineLength(); + config->maxTimeframes = readers * DefaultsHelpers::pipelineLength(dc); } static bool once = false; // Until we guarantee this is called only once... diff --git a/Framework/Core/src/CommonServices.cxx b/Framework/Core/src/CommonServices.cxx index f786d99fd2c0d..6486406a06dca 100644 --- a/Framework/Core/src/CommonServices.cxx +++ b/Framework/Core/src/CommonServices.cxx @@ -414,11 +414,13 @@ o2::framework::ServiceSpec CommonServices::dataRelayer() .name = "datarelayer", .init = [](ServiceRegistryRef services, DeviceState&, fair::mq::ProgOptions& options) -> ServiceHandle { auto& spec = services.get(); + int pipelineLength = DefaultsHelpers::pipelineLength(options); return ServiceHandle{TypeIdHelpers::uniqueId(), new DataRelayer(spec.completionPolicy, spec.inputs, services.get(), - services)}; + services, + pipelineLength)}; }, .configure = noConfiguration(), .kind = ServiceKind::Serial}; diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index ccfb58db7559a..da04a23e81c0c 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -1483,7 +1483,7 @@ void DataProcessingDevice::doPrepare(ServiceRegistryRef ref) auto& infos = state.inputChannelInfos; if (context.balancingInputs) { - static int pipelineLength = DefaultsHelpers::pipelineLength(); + static int pipelineLength = DefaultsHelpers::pipelineLength(*ref.get().device()->fConfig); static uint64_t ahead = getenv("DPL_MAX_CHANNEL_AHEAD") ? std::atoll(getenv("DPL_MAX_CHANNEL_AHEAD")) : std::max(8, std::min(pipelineLength - 48, pipelineLength / 2)); auto newEnd = std::remove_if(pollOrder.begin(), pollOrder.end(), [&infos, limitNew = currentOldest.value + ahead](int a) -> bool { return infos[a].oldestForChannel.value > limitNew; @@ -2259,12 +2259,14 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v return false; } - auto postUpdateStats = [ref](DataRelayer::RecordAction const& action, InputRecord const& record, uint64_t tStart, uint64_t tStartMilli) { + int pipelineLength = DefaultsHelpers::pipelineLength(*ref.get().device()->fConfig); + + auto postUpdateStats = [ref, pipelineLength](DataRelayer::RecordAction const& action, InputRecord const& record, uint64_t tStart, uint64_t tStartMilli) { auto& stats = ref.get(); auto& states = ref.get(); std::atomic_thread_fence(std::memory_order_release); char relayerSlotState[1024]; - int written = snprintf(relayerSlotState, 1024, "%d ", DefaultsHelpers::pipelineLength()); + int written = snprintf(relayerSlotState, 1024, "%d ", pipelineLength); char* buffer = relayerSlotState + written; for (size_t ai = 0; ai != record.size(); ai++) { buffer[ai] = record.isValid(ai) ? '3' : '0'; @@ -2291,11 +2293,11 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v count++; }; - auto preUpdateStats = [ref](DataRelayer::RecordAction const& action, InputRecord const& record, uint64_t) { + auto preUpdateStats = [ref, pipelineLength](DataRelayer::RecordAction const& action, InputRecord const& record, uint64_t) { auto& states = ref.get(); std::atomic_thread_fence(std::memory_order_release); char relayerSlotState[1024]; - snprintf(relayerSlotState, 1024, "%d ", DefaultsHelpers::pipelineLength()); + snprintf(relayerSlotState, 1024, "%d ", pipelineLength); char* buffer = strchr(relayerSlotState, ' ') + 1; for (size_t ai = 0; ai != record.size(); ai++) { buffer[ai] = record.isValid(ai) ? '2' : '0'; diff --git a/Framework/Core/src/DataRelayer.cxx b/Framework/Core/src/DataRelayer.cxx index 05b64b6ed1dad..cece5b343659f 100644 --- a/Framework/Core/src/DataRelayer.cxx +++ b/Framework/Core/src/DataRelayer.cxx @@ -37,6 +37,7 @@ #include "Framework/DataProcessingStates.h" #include "Framework/DataTakingContext.h" #include "Framework/DefaultsHelpers.h" +#include "Framework/RawDeviceService.h" #include "Headers/DataHeaderHelpers.h" #include "Framework/Formatters.h" @@ -48,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -70,7 +72,8 @@ constexpr int INVALID_INPUT = -1; DataRelayer::DataRelayer(const CompletionPolicy& policy, std::vector const& routes, TimesliceIndex& index, - ServiceRegistryRef services) + ServiceRegistryRef services, + int pipelineLength) : mContext{services}, mTimesliceIndex{index}, mCompletionPolicy{policy}, @@ -81,7 +84,17 @@ DataRelayer::DataRelayer(const CompletionPolicy& policy, std::scoped_lock lock(mMutex); if (policy.configureRelayer == nullptr) { - static int pipelineLength = DefaultsHelpers::pipelineLength(); + if (pipelineLength == -1) { + auto getPipelineLengthHelper = [&services]() { + try { + return DefaultsHelpers::pipelineLength(*services.get().device()->fConfig); + } catch (...) { + return DefaultsHelpers::pipelineLength(0); + } + }; + static int detectedPipelineLength = getPipelineLengthHelper(); + pipelineLength = detectedPipelineLength; + } setPipelineLength(pipelineLength); } else { policy.configureRelayer(*this); diff --git a/Framework/Core/src/DefaultsHelpers.cxx b/Framework/Core/src/DefaultsHelpers.cxx index 4dcc734216f0c..5fd1ed29e7af6 100644 --- a/Framework/Core/src/DefaultsHelpers.cxx +++ b/Framework/Core/src/DefaultsHelpers.cxx @@ -11,6 +11,9 @@ #include "Framework/DefaultsHelpers.h" #include "Framework/DataTakingContext.h" +#include "Framework/DeviceConfig.h" +#include + #include #include #include @@ -18,23 +21,35 @@ namespace o2::framework { -unsigned int DefaultsHelpers::pipelineLength() +unsigned int DefaultsHelpers::pipelineLength(unsigned int minLength) { static bool override = getenv("DPL_DEFAULT_PIPELINE_LENGTH"); if (override) { static unsigned int retval = atoi(getenv("DPL_DEFAULT_PIPELINE_LENGTH")); - return retval; + return std::max(minLength, retval); } DeploymentMode deploymentMode = DefaultsHelpers::deploymentMode(); // just some reasonable numers // The number should really be tuned at runtime for each processor. if (deploymentMode == DeploymentMode::OnlineDDS || deploymentMode == DeploymentMode::OnlineECS || deploymentMode == DeploymentMode::FST) { - return 512; + return std::max(minLength, 512u); } else { - return 64; + return std::max(minLength, 64u); } } +unsigned int DefaultsHelpers::pipelineLength(const DeviceConfig& dc) +{ + static unsigned int minLength = dc.options.count("timeframes-rate-limit") ? std::max(0, atoi(dc.options["timeframes-rate-limit"].as().c_str())) : 0; + return pipelineLength(minLength); +} + +unsigned int DefaultsHelpers::pipelineLength(const fair::mq::ProgOptions& options) +{ + static unsigned int minLength = options.Count("timeframes-rate-limit") ? std::max(0, atoi(options.GetValue("timeframes-rate-limit").c_str())) : 0; + return pipelineLength(minLength); +} + static DeploymentMode getDeploymentMode_internal() { char* explicitMode = getenv("O2_DPL_DEPLOYMENT_MODE"); diff --git a/Framework/Core/src/runDataProcessing.cxx b/Framework/Core/src/runDataProcessing.cxx index 166f26878c363..ced884ebaa1ed 100644 --- a/Framework/Core/src/runDataProcessing.cxx +++ b/Framework/Core/src/runDataProcessing.cxx @@ -817,7 +817,8 @@ void spawnDevice(uv_loop_t* loop, .sendInitialValue = true, }); - for (size_t i = 0; i < DefaultsHelpers::pipelineLength(); ++i) { + unsigned int pipelineLength = DefaultsHelpers::pipelineLength(DeviceConfig{varmap}); + for (size_t i = 0; i < pipelineLength; ++i) { allStates.back().registerState(DataProcessingStates::StateSpec{ .name = fmt::format("matcher_variables/{}", i), .stateId = static_cast((short)(ProcessingStateId::CONTEXT_VARIABLES_BASE) + i), @@ -826,7 +827,7 @@ void spawnDevice(uv_loop_t* loop, }); } - for (size_t i = 0; i < DefaultsHelpers::pipelineLength(); ++i) { + for (size_t i = 0; i < pipelineLength; ++i) { allStates.back().registerState(DataProcessingStates::StateSpec{ .name = fmt::format("data_relayer/{}", i), .stateId = static_cast((short)(ProcessingStateId::DATA_RELAYER_BASE) + i), diff --git a/Framework/Core/test/benchmark_DataRelayer.cxx b/Framework/Core/test/benchmark_DataRelayer.cxx index dcff3930dbaad..3c3d2294fdd7e 100644 --- a/Framework/Core/test/benchmark_DataRelayer.cxx +++ b/Framework/Core/test/benchmark_DataRelayer.cxx @@ -65,7 +65,7 @@ static void BM_RelaySingleSlot(benchmark::State& state) TimesliceIndex index{1, infos}; auto policy = CompletionPolicyHelpers::consumeWhenAny(); ServiceRegistry registry; - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); // Let's create a dummy O2 Message with two headers in the stack: @@ -118,7 +118,7 @@ static void BM_RelayMultipleSlots(benchmark::State& state) auto policy = CompletionPolicyHelpers::consumeWhenAny(); ServiceRegistry registry; - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); // Let's create a dummy O2 Message with two headers in the stack: @@ -177,7 +177,7 @@ static void BM_RelayMultipleRoutes(benchmark::State& state) auto policy = CompletionPolicyHelpers::consumeWhenAny(); ServiceRegistry registry; - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); // Let's create a dummy O2 Message with two headers in the stack: @@ -254,7 +254,7 @@ static void BM_RelaySplitParts(benchmark::State& state) auto policy = CompletionPolicyHelpers::consumeWhenAny(); ServiceRegistry registry; - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); // Let's create a dummy O2 Message with two headers in the stack: @@ -314,7 +314,7 @@ static void BM_RelayMultiplePayloads(benchmark::State& state) auto policy = CompletionPolicyHelpers::consumeWhenAny(); ServiceRegistry registry; - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); // DataHeader matching the one provided in the input diff --git a/Framework/Core/test/test_DataRelayer.cxx b/Framework/Core/test/test_DataRelayer.cxx index 7d5a3ded88e16..8957e361cb8a2 100644 --- a/Framework/Core/test/test_DataRelayer.cxx +++ b/Framework/Core/test/test_DataRelayer.cxx @@ -83,7 +83,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::consumeWhenAny(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); // Let's create a dummy O2 Message with two headers in the stack: @@ -133,7 +133,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::consumeWhenAny(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); // Let's create a dummy O2 Message with two headers in the stack: @@ -195,7 +195,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::consumeWhenAll(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); @@ -276,7 +276,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::consumeWhenAll(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(3); auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); @@ -359,7 +359,7 @@ TEST_CASE("DataRelayer") std::vector infos{1}; TimesliceIndex index{1, infos}; ref.registerService(ServiceRegistryHelpers::handleForService(&index)); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); // Only two messages to fill the cache. relayer.setPipelineLength(2); @@ -437,7 +437,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::processWhenAny(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); // Only two messages to fill the cache. relayer.setPipelineLength(2); @@ -509,7 +509,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::processWhenAny(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); // Only two messages to fill the cache. relayer.setPipelineLength(3); @@ -568,7 +568,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::processWhenAny(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); // Only two messages to fill the cache. relayer.setPipelineLength(1); @@ -629,7 +629,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::processWhenAny(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); // Only two messages to fill the cache. relayer.setPipelineLength(1); @@ -698,7 +698,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::consumeWhenAny(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); DataHeader dh{"CLUSTERS", "TPC", 0}; @@ -752,7 +752,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::consumeWhenAny(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); From ab29595c9106a6f5c28bc1a12cb0402cb7f446b8 Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Fri, 13 Feb 2026 10:47:40 +0100 Subject: [PATCH 098/133] Update examples on AO2D creation from MCTracks (#15056) --- run/SimExamples/HepMC_STARlight/run_HepMCToAOD.sh | 2 +- run/SimExamples/McTracksToAOD/run_O2Kine.sh | 5 +++-- run/SimExamples/McTracksToAOD/run_Pythia8.sh | 2 +- run/SimExamples/McTracksToAOD/run_trigger.sh | 6 +++--- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/run/SimExamples/HepMC_STARlight/run_HepMCToAOD.sh b/run/SimExamples/HepMC_STARlight/run_HepMCToAOD.sh index f08de81b92d8c..7671d03b97b8f 100755 --- a/run/SimExamples/HepMC_STARlight/run_HepMCToAOD.sh +++ b/run/SimExamples/HepMC_STARlight/run_HepMCToAOD.sh @@ -18,7 +18,7 @@ set -x # PART b) ... apply vertex smearing on top of HepMC events and perform simple analysis NEV=$(grep EVENT slight.out | wc -l) -o2-sim-dpl-eventgen -b --nevents ${NEV} --generator hepmc --confKeyValues \ +o2-sim-dpl-eventgen -b --nEvents ${NEV} --generator hepmc --configKeyValues \ "GeneratorFileOrCmd.fileNames=starlight.hepmc;Diamond.position[2]=0.1;Diamond.width[2]=0.05" |\ o2-sim-mctracks-to-aod -b | o2-analysis-mctracks-to-aod-simple-task -b diff --git a/run/SimExamples/McTracksToAOD/run_O2Kine.sh b/run/SimExamples/McTracksToAOD/run_O2Kine.sh index 7506f00834fcf..9afac20cd1a0b 100755 --- a/run/SimExamples/McTracksToAOD/run_O2Kine.sh +++ b/run/SimExamples/McTracksToAOD/run_O2Kine.sh @@ -10,6 +10,7 @@ NEVENTS=1000 # launch generator process (for 10000 min bias Pythia8 events; no Geant; no geometry) # o2-sim -j 1 -g pythia8pp -n ${NEVENTS} --noGeant --vertexMode kNoVertex &> sim.log +## Add --aod-writer-keep dangling to o2-sim-mctracks-to-aod to write the AO2D file to disc (as AnalysisResults_trees.root) # Option 1) -- use o2-mckine-publisher [ -f AnalysisResults.root ] && rm AnalysisResults.root o2-sim-kine-publisher -b --kineFileName o2sim --aggregate-timeframe 10 |\ @@ -19,8 +20,8 @@ mv AnalysisResults.root AnalysisResult_1.root # Option 2) -- use o2-sim-dpl-eventgen + extkinO2 generator (this should be equivalent to Option 1) [ -f AnalysisResults.root ] && rm AnalysisResults.root -o2-sim-dpl-eventgen -b --nevents ${NEVENTS} --aggregate-timeframe 10 --generator extkinO2 \ - --confKeyValues "GeneratorFromO2Kine.fileName=o2sim_Kine.root" --vertexMode kNoVertex |\ +o2-sim-dpl-eventgen -b --nEvents ${NEVENTS} --aggregate-timeframe 10 --generator extkinO2 \ + --configKeyValues "GeneratorFromO2Kine.fileName=o2sim_Kine.root" --vertexMode kNoVertex |\ o2-sim-mctracks-to-aod -b |\ o2-analysis-mctracks-to-aod-simple-task -b &> log2 mv AnalysisResults.root AnalysisResult_2.root diff --git a/run/SimExamples/McTracksToAOD/run_Pythia8.sh b/run/SimExamples/McTracksToAOD/run_Pythia8.sh index 8bac774c5892b..93d2024b05d37 100755 --- a/run/SimExamples/McTracksToAOD/run_Pythia8.sh +++ b/run/SimExamples/McTracksToAOD/run_Pythia8.sh @@ -8,7 +8,7 @@ NEVENTS=1000 # --aggregate-timeframe 10 is used to combine 10 generated events into a timeframe that is then converted to AOD tables # note that if you need special configuration for the analysis tasks, it needs to be passed to proxy and converter as well - +## Add --aod-writer-keep dangling to o2-sim-mctracks-to-aod to write the AO2D file to disc (as AnalysisResults_trees.root) o2-sim-dpl-eventgen -b --nEvents ${NEVENTS} --aggregate-timeframe 10 --generator pythia8pp --vertexMode kNoVertex |\ o2-sim-mctracks-to-aod -b | o2-analysis-mctracks-to-aod-simple-task -b &> pythia8.log diff --git a/run/SimExamples/McTracksToAOD/run_trigger.sh b/run/SimExamples/McTracksToAOD/run_trigger.sh index ca720191cbad2..5b278c0b666e5 100755 --- a/run/SimExamples/McTracksToAOD/run_trigger.sh +++ b/run/SimExamples/McTracksToAOD/run_trigger.sh @@ -5,9 +5,9 @@ set -x NEVENTS=1000 - +## Add --aod-writer-keep dangling to o2-sim-mctracks-to-aod to write the AO2D file to disc (as AnalysisResults_trees.root) CONFKEY="TriggerExternal.fileName=trigger.macro;TriggerExternal.funcName=trigger()" -o2-sim-dpl-eventgen -b --nevents ${NEVENTS} --aggregate-timeframe 10 --generator pythia8pp --trigger external \ - --vertexMode kDiamondParam --confKeyValues "${CONFKEY}" |\ +o2-sim-dpl-eventgen -b --nEvents ${NEVENTS} --aggregate-timeframe 10 --generator pythia8pp --trigger external \ + --vertexMode kDiamondParam --configKeyValues "${CONFKEY}" |\ o2-sim-mctracks-to-aod -b | o2-analysis-mctracks-to-aod-simple-task -b From 7c503090305ab42c607db29f49917e32dfd28023 Mon Sep 17 00:00:00 2001 From: Stefano Cannito <143754257+scannito@users.noreply.github.com> Date: Fri, 13 Feb 2026 17:44:23 +0100 Subject: [PATCH 099/133] [A3 TRK] Fix kCylinder option + services crossing (#15067) * Fix for kCylinder * Fix services crossing --- .../ALICE3/TRK/simulation/src/TRKLayer.cxx | 32 +++++++++---------- .../ALICE3/TRK/simulation/src/TRKServices.cxx | 4 +-- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx index 8d30cf9759e40..82b6fbd40af59 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx @@ -122,17 +122,12 @@ TGeoVolume* TRKLayer::createChip(std::string type) chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); sensVol = createSensor("cylinder"); - metalVol = createMetalStack("cylinder"); - - TGeoCombiTrans* transSens = new TGeoCombiTrans(); - transSens->SetTranslation(0, -(mChipThickness - mSensorThickness) / 2, 0); // TO BE CHECKED !!! LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); - chipVol->AddNode(sensVol, 1, transSens); + chipVol->AddNode(sensVol, 1, nullptr); - TGeoCombiTrans* transMetal = new TGeoCombiTrans(); - transMetal->SetTranslation(0, mSensorThickness / 2, 0); // TO BE CHECKED !!! + metalVol = createMetalStack("cylinder"); LOGP(debug, "Inserting {} in {} ", metalVol->GetName(), chipVol->GetName()); - chipVol->AddNode(metalVol, 1, transMetal); + chipVol->AddNode(metalVol, 1, nullptr); // deadVol = createDeadzone("cylinder"); } else if (type == "flat") { @@ -175,7 +170,9 @@ TGeoVolume* TRKLayer::createModule(std::string type) TGeoVolume* moduleVol; if (type == "cylinder") { - module = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, (constants::moduleMLOT::length * mNumberOfModules) / 2); + double moduleLength = constants::moduleMLOT::length * mNumberOfModules; + + module = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, moduleLength / 2); moduleVol = new TGeoVolume(moduleName.c_str(), module, medSi); TGeoVolume* chipVol = createChip("cylinder"); @@ -229,8 +226,10 @@ TGeoVolume* TRKLayer::createHalfStave(std::string type) TGeoShape* halfStave; TGeoVolume* halfStaveVol; + double halfStaveLength = constants::moduleMLOT::length * mNumberOfModules; + if (type == "cylinder") { - halfStave = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, mChipLength / 2); + halfStave = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, halfStaveLength / 2); halfStaveVol = new TGeoVolume(halfStaveName.c_str(), halfStave, medSi); TGeoVolume* moduleVol = createModule("cylinder"); @@ -239,7 +238,6 @@ TGeoVolume* TRKLayer::createHalfStave(std::string type) } else if (type == "flat") { double moduleLength = constants::moduleMLOT::length; double halfStaveWidth = constants::OT::halfstave::width; - double halfStaveLength = constants::moduleMLOT::length * mNumberOfModules; halfStave = new TGeoBBox(halfStaveWidth / 2, mChipThickness / 2, halfStaveLength / 2); halfStaveVol = new TGeoVolume(halfStaveName.c_str(), halfStave, medSi); @@ -271,8 +269,10 @@ TGeoVolume* TRKLayer::createStave(std::string type) TGeoShape* stave; TGeoVolume* staveVol; + double staveLength = constants::moduleMLOT::length * mNumberOfModules; + if (type == "cylinder") { - stave = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, (constants::moduleMLOT::length * mNumberOfModules) / 2); + stave = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, staveLength / 2); staveVol = new TGeoVolume(staveName.c_str(), stave, medAir); TGeoVolume* moduleVol = createModule("cylinder"); @@ -281,7 +281,6 @@ TGeoVolume* TRKLayer::createStave(std::string type) } else if (type == "flat") { double moduleLength = constants::moduleMLOT::length; double staveWidth = constants::ML::width; - double staveLength = constants::moduleMLOT::length * mNumberOfModules; stave = new TGeoBBox(staveWidth / 2, mChipThickness / 2, staveLength / 2); staveVol = new TGeoVolume(staveName.c_str(), stave, medAir); @@ -304,7 +303,6 @@ TGeoVolume* TRKLayer::createStave(std::string type) double halfstaveWidth = constants::OT::halfstave::width; double staveWidth = constants::OT::width - overlap; - double staveLength = constants::moduleMLOT::length * mNumberOfModules; stave = new TGeoBBox(staveWidth / 2, mLogicalVolumeThickness / 2, staveLength / 2); staveVol = new TGeoVolume(staveName.c_str(), stave, medAir); @@ -343,15 +341,16 @@ void TRKLayer::createLayer(TGeoVolume* motherVolume) TGeoTube* layer; TGeoVolume* layerVol; + double layerLength = constants::moduleMLOT::length * mNumberOfModules; + if (mLayout == eLayout::kCylinder) { - layer = new TGeoTube(mInnerRadius - 0.333 * layerThickness, mInnerRadius + 0.667 * layerThickness, (constants::moduleMLOT::length * mNumberOfModules) / 2); + layer = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, layerLength / 2); layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); TGeoVolume* staveVol = createStave("cylinder"); LOGP(debug, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); layerVol->AddNode(staveVol, 1, nullptr); } else if (mLayout == eLayout::kTurboStaves) { - double layerLength = constants::moduleMLOT::length * mNumberOfModules; double staveWidth = constants::ML::width; // Each stave has two modules (based on the LOI design) if (mInnerRadius > 25) { @@ -390,7 +389,6 @@ void TRKLayer::createLayer(TGeoVolume* motherVolume) } else if (mLayout == kStaggered) { double overlapInStave = constants::moduleMLOT::gaps::outerEdgeLongSide + constants::moduleMLOT::chip::passiveEdgeReadOut + 0.1; // 1.5mm outer-edge + 1mm deadzone + 1mm (true)overlap - double layerLength = constants::moduleMLOT::length * mNumberOfModules; double staveWidth = constants::OT::width - overlapInStave; layer = new TGeoTube(mInnerRadius - 0.333 * layerThickness, mInnerRadius + 0.667 * layerThickness, layerLength / 2); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx index 51eea905c436a..cbe00e8fc9e89 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx @@ -264,7 +264,7 @@ void TRKServices::createMiddleServices(TGeoVolume* motherVolume) // Carbon Fiber Cylinder support for the middle tracker float rMinMiddleCarbonSupport = 34.8f; // Arbitrary value float rMaxMiddleCarbonSupport = 35.f; // 2 mm of carbon fiber - const float zLengthMiddleCarbon = 62.f; + const float zLengthMiddleCarbon = 64.2f; TGeoTube* middleBarrelCarbonSupport = new TGeoTube("TRK_MID_CARBONSUPPORTsh", rMinMiddleCarbonSupport, rMaxMiddleCarbonSupport, zLengthMiddleCarbon); TGeoVolume* middleBarrelCarbonSupportVolume = new TGeoVolume("TRK_MID_CARBONSUPPORT", middleBarrelCarbonSupport, medCFiber); middleBarrelCarbonSupportVolume->SetLineColor(kGray); @@ -318,7 +318,7 @@ void TRKServices::createMiddleServices(TGeoVolume* motherVolume) // Middle barrel connection disks const float rMinMiddleBarrelDisk = 5.68f; const float rMaxMiddleBarrelDisk = 35.f; - const float zLengthMiddleBarrel = 62.f; + const float zLengthMiddleBarrel = 64.2f; for (auto& orientation : {Orientation::kASide, Orientation::kCSide}) { TGeoTube* middleBarrelConnDiskSIO2 = new TGeoTube(Form("TRK_MIDBARCONN_DISK_SIO2sh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, siO2FiberThick); TGeoTube* middleBarrelConnDiskPE = new TGeoTube(Form("TRK_MIDBARCONN_DISK_PEsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, peFiberThick); From 48c0b5433f9d4f7af17e8ad34ea6e3363fce5963 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Sun, 8 Feb 2026 19:17:34 +0100 Subject: [PATCH 100/133] Support to plug-and-play external (CAD) geometry This commit adds support for - conversion from CAD geometries (STEP) file to (meshed) TGeo geometry via the tool O2_CADtoTGeo.py - ability to setup and include simulations modules from external geometries via the ExternalModule mechanism - ability to navigate complex TGeoTessellated solids efficiently (will stay here until fully integrated in the official ROOT repo) It includes all the (basic) functionality to pick a detector layout from a CAD design and actually simulate it with o2-sim. Instructions are provided in a dedicated documentation markdown. The commit is related to epic https://its.cern.ch/jira/browse/O2-6616 --- Detectors/Base/CMakeLists.txt | 7 +- .../include/DetectorsBase/O2Tessellated.h | 142 ++ .../include/DetectorsBase/TGeoGeometryUtils.h | 38 + Detectors/Base/src/DetectorsBaseLinkDef.h | 2 + Detectors/Base/src/O2Tessellated.cxx | 1509 +++++++++++++++++ Detectors/Base/src/TGeoGeometryUtils.cxx | 144 ++ Detectors/Base/src/bvh2_extra_kernels.h | 79 + Detectors/Base/src/bvh2_third_party.h | 49 + Detectors/Passive/CMakeLists.txt | 2 + .../include/DetectorsPassive/ExternalModule.h | 64 + Detectors/Passive/src/ExternalModule.cxx | 175 ++ Steer/include/Steer/O2MCApplicationBase.h | 2 + Steer/src/O2MCApplication.cxx | 60 + macro/build_geometry.C | 14 + scripts/geometry/O2_CADtoTGeo.py | 602 +++++++ scripts/geometry/README.md | 27 + scripts/geometry/simulating_CAD_modules.md | 72 + 17 files changed, 2987 insertions(+), 1 deletion(-) create mode 100644 Detectors/Base/include/DetectorsBase/O2Tessellated.h create mode 100644 Detectors/Base/include/DetectorsBase/TGeoGeometryUtils.h create mode 100644 Detectors/Base/src/O2Tessellated.cxx create mode 100644 Detectors/Base/src/TGeoGeometryUtils.cxx create mode 100644 Detectors/Base/src/bvh2_extra_kernels.h create mode 100644 Detectors/Base/src/bvh2_third_party.h create mode 100644 Detectors/Passive/include/DetectorsPassive/ExternalModule.h create mode 100644 Detectors/Passive/src/ExternalModule.cxx create mode 100644 scripts/geometry/O2_CADtoTGeo.py create mode 100644 scripts/geometry/README.md create mode 100644 scripts/geometry/simulating_CAD_modules.md diff --git a/Detectors/Base/CMakeLists.txt b/Detectors/Base/CMakeLists.txt index 30ab4c4fe8a40..83a9193274e4f 100644 --- a/Detectors/Base/CMakeLists.txt +++ b/Detectors/Base/CMakeLists.txt @@ -29,6 +29,8 @@ o2_add_library(DetectorsBase src/Stack.cxx src/VMCSeederService.cxx src/GlobalParams.cxx + src/O2Tessellated.cxx + src/TGeoGeometryUtils.cxx PUBLIC_LINK_LIBRARIES FairRoot::Base O2::CommonUtils O2::DetectorsCommonDataFormats @@ -46,6 +48,7 @@ o2_add_library(DetectorsBase O2::GPUDataTypes MC::VMC TBB::tbb + ROOT::Gdml ) o2_target_root_dictionary(DetectorsBase @@ -62,7 +65,9 @@ o2_target_root_dictionary(DetectorsBase include/DetectorsBase/Aligner.h include/DetectorsBase/Stack.h include/DetectorsBase/SimFieldUtils.h - include/DetectorsBase/GlobalParams.h) + include/DetectorsBase/GlobalParams.h + include/DetectorsBase/O2Tessellated.h + ) if(BUILD_SIMULATION) if (NOT APPLE) diff --git a/Detectors/Base/include/DetectorsBase/O2Tessellated.h b/Detectors/Base/include/DetectorsBase/O2Tessellated.h new file mode 100644 index 0000000000000..0a1cee8b3e01f --- /dev/null +++ b/Detectors/Base/include/DetectorsBase/O2Tessellated.h @@ -0,0 +1,142 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_BASE_O2TESSELLATED_ +#define ALICEO2_BASE_O2TESSELLATED_ + +#include "TGeoShape.h" +#include "TGeoBBox.h" +#include "TGeoVector3.h" +#include "TGeoTypedefs.h" +#include "TGeoTessellated.h" + +namespace o2 +{ +namespace base +{ + +class O2Tessellated : public TGeoBBox +{ + + public: + using Vertex_t = Tessellated::Vertex_t; + + private: + int fNfacets = 0; // Number of facets + int fNvert = 0; // Number of vertices + int fNseg = 0; // Number of segments + bool fDefined = false; //! Shape fully defined + bool fClosedBody = false; // The faces are making a closed body + + // for now separate vectors but might be better to group per face + std::vector fVertices; // List of vertices + std::vector fFacets; // List of facets + std::vector fOutwardNormals; // Vector of outward-facing normals (to be streamed !) + + std::multimap fVerticesMap; //! Temporary map used to deduplicate vertices + bool fIsClosed = false; //! to know if shape still needs closure/initialization + void* fBVH = nullptr; //! BVH acceleration structure for safety and navigation + + O2Tessellated(const O2Tessellated&) = delete; + O2Tessellated& operator=(const O2Tessellated&) = delete; + + // bvh helper functions + void BuildBVH(); + void CalculateNormals(); + + public: + // constructors + O2Tessellated() {} + O2Tessellated(const char* name, int nfacets = 0); + O2Tessellated(const char* name, const std::vector& vertices); + // from a TGeoTessellated + O2Tessellated(TGeoTessellated const&, bool check = false); + + // destructor + ~O2Tessellated() override {} + + void ComputeBBox() override; + void CloseShape(bool check = true, bool fixFlipped = true, bool verbose = true); + + bool AddFacet(const Vertex_t& pt0, const Vertex_t& pt1, const Vertex_t& pt2); + bool AddFacet(const Vertex_t& pt0, const Vertex_t& pt1, const Vertex_t& pt2, const Vertex_t& pt3); + bool AddFacet(int i1, int i2, int i3); + bool AddFacet(int i1, int i2, int i3, int i4); + int AddVertex(const Vertex_t& vert); + + bool FacetCheck(int ifacet) const; + Vertex_t FacetComputeNormal(int ifacet, bool& degenerated) const; + + int GetNfacets() const { return fFacets.size(); } + int GetNsegments() const { return fNseg; } + int GetNvertices() const { return fNvert; } + bool IsClosedBody() const { return fClosedBody; } + bool IsDefined() const { return fDefined; } + + const TGeoFacet& GetFacet(int i) const { return fFacets[i]; } + const Vertex_t& GetVertex(int i) const { return fVertices[i]; } + + int DistancetoPrimitive(int, int) override { return 99999; } + const TBuffer3D& GetBuffer3D(int reqSections, Bool_t localFrame) const override; + void GetMeshNumbers(int& nvert, int& nsegs, int& npols) const override; + int GetNmeshVertices() const override { return fNvert; } + void InspectShape() const override {} + TBuffer3D* MakeBuffer3D() const override; + void Print(Option_t* option = "") const override; + void SavePrimitive(std::ostream&, Option_t*) override {} + void SetPoints(double* points) const override; + void SetPoints(Float_t* points) const override; + void SetSegsAndPols(TBuffer3D& buff) const override; + void Sizeof3D() const override {} + + /// Resize and center the shape in a box of size maxsize + void ResizeCenter(double maxsize); + + /// Flip all facets + void FlipFacets() + { + for (auto facet : fFacets) + facet.Flip(); + } + + bool CheckClosure(bool fixFlipped = true, bool verbose = true); + + /// Reader from .obj format + static O2Tessellated* ImportFromObjFormat(const char* objfile, bool check = false, bool verbose = false); + + // navigation functions used by TGeoNavigator (attention: only the iact == 3 cases implemented for now) + Double_t DistFromOutside(const Double_t* point, const Double_t* dir, Int_t iact = 1, + Double_t step = TGeoShape::Big(), Double_t* safe = nullptr) const override; + Double_t DistFromInside(const Double_t* point, const Double_t* dir, Int_t iact = 1, Double_t step = TGeoShape::Big(), + Double_t* safe = nullptr) const override; + bool Contains(const Double_t* point) const override; + Double_t Safety(const Double_t* point, Bool_t in = kTRUE) const override; + void ComputeNormal(const Double_t* point, const Double_t* dir, Double_t* norm) const override; + + // these are trivial implementations, just for debugging + Double_t DistFromInside_Loop(const Double_t* point, const Double_t* dir) const; + Double_t DistFromOutside_Loop(const Double_t* point, const Double_t* dir) const; + bool Contains_Loop(const Double_t* point) const; + + Double_t Capacity() const override; + + private: + // a safety kernel used in multiple implementations + template + Double_t SafetyKernel(const Double_t* point, bool in, int* closest_facet_id = nullptr) const; + + ClassDefOverride(O2Tessellated, 1) // tessellated shape class +}; + +} // namespace base +} // namespace o2 + +#endif diff --git a/Detectors/Base/include/DetectorsBase/TGeoGeometryUtils.h b/Detectors/Base/include/DetectorsBase/TGeoGeometryUtils.h new file mode 100644 index 0000000000000..5ec85f1c14702 --- /dev/null +++ b/Detectors/Base/include/DetectorsBase/TGeoGeometryUtils.h @@ -0,0 +1,38 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TGeoGeometryUtils.h +/// \author Sandro Wenzel (CERN) +/// \brief Collection of utility functions for TGeo + +#ifndef ALICEO2_BASE_TGEOGEOMETRYUTILS_H_ +#define ALICEO2_BASE_TGEOGEOMETRYUTILS_H_ + +class TGeoShape; +class TGeoTessellated; + +namespace o2 +{ +namespace base +{ + +/// A few utility functions to operate on TGeo geometries (transformations, printing, ...) +class TGeoGeometryUtils +{ + public: + ///< Transform any (primitive) TGeoShape to a tessellated representation + static TGeoTessellated* TGeoShapeToTGeoTessellated(TGeoShape const*); +}; + +} // namespace base +} // namespace o2 + +#endif diff --git a/Detectors/Base/src/DetectorsBaseLinkDef.h b/Detectors/Base/src/DetectorsBaseLinkDef.h index bd76e9bfbe2e4..8255c143ebb4a 100644 --- a/Detectors/Base/src/DetectorsBaseLinkDef.h +++ b/Detectors/Base/src/DetectorsBaseLinkDef.h @@ -42,4 +42,6 @@ #pragma link C++ class o2::data::Stack + ; +#pragma link C++ class o2::base::O2Tessellated - ; + #endif diff --git a/Detectors/Base/src/O2Tessellated.cxx b/Detectors/Base/src/O2Tessellated.cxx new file mode 100644 index 0000000000000..256a70e5a697a --- /dev/null +++ b/Detectors/Base/src/O2Tessellated.cxx @@ -0,0 +1,1509 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Sandro Wenzel 2026 + +// An implementation of TGeoTessellated augmented with efficient navigation functions. +// Asked for integration into ROOT here https://github.com/root-project/root/pull/21045 +// Will be deleted once we get this from ROOT. + +#include +#include + +#include "TGeoManager.h" +#include "TGeoMatrix.h" +#include "TGeoVolume.h" +#include "TVirtualGeoPainter.h" +#include "DetectorsBase/O2Tessellated.h" +#include "TBuffer3D.h" +#include "TBuffer3DTypes.h" +#include "TMath.h" +#include "TBuffer.h" + +#include +#include + +// THIS IS THIRD PARTY CODE (TO BE PUT IN ROOT) WHICH DOES NOT NEED TO ADHERE TO OUR LINTING +// NOLINTBEGIN + +// include the Third-party BVH headers +#include "bvh2_third_party.h" +// some kernels on top of BVH +#include "bvh2_extra_kernels.h" + +#include +#include + +using namespace o2::base; +ClassImp(O2Tessellated); + +using Vertex_t = Tessellated::Vertex_t; + +//////////////////////////////////////////////////////////////////////////////// +/// Compact consecutive equal vertices + +int TGeoFacet::CompactFacet(Vertex_t* vert, int nvertices) +{ + // Compact the common vertices and return new facet + if (nvertices < 2) + return nvertices; + int nvert = nvertices; + int i = 0; + while (i < nvert) { + if (vert[(i + 1) % nvert] == vert[i]) { + // shift last vertices left by one element + for (int j = i + 2; j < nvert; ++j) + vert[j - 1] = vert[j]; + nvert--; + } + i++; + } + return nvert; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Check if a connected neighbour facet has compatible normal + +bool TGeoFacet::IsNeighbour(const TGeoFacet& other, bool& flip) const +{ + + // Find a connecting segment + bool neighbour = false; + int line1[2], line2[2]; + int npoints = 0; + for (int i = 0; i < fNvert; ++i) { + auto ivert = fIvert[i]; + // Check if the other facet has the same vertex + for (int j = 0; j < other.GetNvert(); ++j) { + if (ivert == other[j]) { + line1[npoints] = i; + line2[npoints] = j; + if (++npoints == 2) { + neighbour = true; + bool order1 = line1[1] == line1[0] + 1; + bool order2 = line2[1] == (line2[0] + 1) % other.GetNvert(); + flip = (order1 == order2); + return neighbour; + } + } + } + } + return neighbour; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Constructor. In case nfacets is zero, it is user's responsibility to +/// call CloseShape once all faces are defined. + +O2Tessellated::O2Tessellated(const char* name, int nfacets) : TGeoBBox(name, 0, 0, 0) +{ + fNfacets = nfacets; + if (nfacets) + fFacets.reserve(nfacets); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Constructor providing directly the array of vertices. Facets have to be added +/// providing vertex indices rather than coordinates. + +O2Tessellated::O2Tessellated(const char* name, const std::vector& vertices) : TGeoBBox(name, 0, 0, 0) +{ + fVertices = vertices; + fNvert = fVertices.size(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Construct from TGeoTessellated + +O2Tessellated::O2Tessellated(TGeoTessellated const& tsl, bool check) : TGeoBBox(tsl.GetName(), 0, 0, 0) +{ + fNfacets = tsl.GetNfacets(); + fNvert = tsl.GetNvertices(); + fNseg = tsl.GetNsegments(); + + // copy facet and vertex done + fVertices.reserve(fNvert); + fFacets.reserve(fNfacets); + for (int i = 0; i < fNfacets; ++i) { + fFacets.push_back(tsl.GetFacet(i)); + } + for (int i = 0; i < fNvert; ++i) { + fVertices.push_back(tsl.GetVertex(i)); + } + // finish remaining structures + CloseShape(check); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Add a vertex checking for duplicates, returning the vertex index + +int O2Tessellated::AddVertex(Vertex_t const& vert) +{ + constexpr double tolerance = 1.e-10; + auto vertexHash = [&](Vertex_t const& vertex) { + // Compute hash for the vertex + long hash = 0; + // helper function to generate hash from integer numbers + auto hash_combine = [](long seed, const long value) { + return seed ^ (std::hash{}(value) + 0x9e3779b9 + (seed << 6) + (seed >> 2)); + }; + for (int i = 0; i < 3; i++) { + // use tolerance to generate int with the desired precision from a real number for hashing + hash = hash_combine(hash, std::roundl(vertex[i] / tolerance)); + } + return hash; + }; + + auto hash = vertexHash(vert); + bool isAdded = false; + int ivert = -1; + // Get the compatible vertices + auto range = fVerticesMap.equal_range(hash); + for (auto it = range.first; it != range.second; ++it) { + ivert = it->second; + if (fVertices[ivert] == vert) { + isAdded = true; + break; + } + } + if (!isAdded) { + ivert = fVertices.size(); + fVertices.push_back(vert); + fVerticesMap.insert(std::make_pair(hash, ivert)); + } + return ivert; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Adding a triangular facet from vertex positions in absolute coordinates + +bool O2Tessellated::AddFacet(const Vertex_t& pt0, const Vertex_t& pt1, const Vertex_t& pt2) +{ + if (fDefined) { + Error("AddFacet", "Shape %s already fully defined. Not adding", GetName()); + return false; + } + + Vertex_t vert[3]; + vert[0] = pt0; + vert[1] = pt1; + vert[2] = pt2; + int nvert = TGeoFacet::CompactFacet(vert, 3); + if (nvert < 3) { + Error("AddFacet", "Triangular facet at index %d degenerated. Not adding.", GetNfacets()); + return false; + } + int ind[3]; + for (auto i = 0; i < 3; ++i) + ind[i] = AddVertex(vert[i]); + fNseg += 3; + fFacets.emplace_back(ind[0], ind[1], ind[2]); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Adding a triangular facet from indices of vertices + +bool O2Tessellated::AddFacet(int i0, int i1, int i2) +{ + if (fDefined) { + Error("AddFacet", "Shape %s already fully defined. Not adding", GetName()); + return false; + } + if (fVertices.empty()) { + Error("AddFacet", "Shape %s Cannot add facets by indices without vertices. Not adding", GetName()); + return false; + } + + fNseg += 3; + fFacets.emplace_back(i0, i1, i2); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Adding a quadrilateral facet from vertex positions in absolute coordinates + +bool O2Tessellated::AddFacet(const Vertex_t& pt0, const Vertex_t& pt1, const Vertex_t& pt2, const Vertex_t& pt3) +{ + if (fDefined) { + Error("AddFacet", "Shape %s already fully defined. Not adding", GetName()); + return false; + } + Vertex_t vert[4]; + vert[0] = pt0; + vert[1] = pt1; + vert[2] = pt2; + vert[3] = pt3; + int nvert = TGeoFacet::CompactFacet(vert, 4); + if (nvert < 3) { + Error("AddFacet", "Quadrilateral facet at index %d degenerated. Not adding.", GetNfacets()); + return false; + } + + int ind[4]; + for (auto i = 0; i < nvert; ++i) + ind[i] = AddVertex(vert[i]); + fNseg += nvert; + if (nvert == 3) + fFacets.emplace_back(ind[0], ind[1], ind[2]); + else + fFacets.emplace_back(ind[0], ind[1], ind[2], ind[3]); + + if (fNfacets > 0 && GetNfacets() == fNfacets) + CloseShape(false); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Adding a quadrilateral facet from indices of vertices + +bool O2Tessellated::AddFacet(int i0, int i1, int i2, int i3) +{ + if (fDefined) { + Error("AddFacet", "Shape %s already fully defined. Not adding", GetName()); + return false; + } + if (fVertices.empty()) { + Error("AddFacet", "Shape %s Cannot add facets by indices without vertices. Not adding", GetName()); + return false; + } + + fNseg += 4; + fFacets.emplace_back(i0, i1, i2, i3); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Compute normal for a given facet + +Vertex_t O2Tessellated::FacetComputeNormal(int ifacet, bool& degenerated) const +{ + // Compute normal using non-zero segments + constexpr double kTolerance = 1.e-20; + auto const& facet = fFacets[ifacet]; + int nvert = facet.GetNvert(); + degenerated = true; + Vertex_t normal; + for (int i = 0; i < nvert - 1; ++i) { + Vertex_t e1 = fVertices[facet[i + 1]] - fVertices[facet[i]]; + if (e1.Mag2() < kTolerance) + continue; + for (int j = i + 1; j < nvert; ++j) { + Vertex_t e2 = fVertices[facet[(j + 1) % nvert]] - fVertices[facet[j]]; + if (e2.Mag2() < kTolerance) + continue; + normal = Vertex_t::Cross(e1, e2); + // e1 and e2 may be colinear + if (normal.Mag2() < kTolerance) + continue; + normal.Normalize(); + degenerated = false; + break; + } + if (!degenerated) + break; + } + return normal; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Check validity of facet + +bool O2Tessellated::FacetCheck(int ifacet) const +{ + constexpr double kTolerance = 1.e-10; + auto const& facet = fFacets[ifacet]; + int nvert = facet.GetNvert(); + bool degenerated = true; + FacetComputeNormal(ifacet, degenerated); + if (degenerated) { + std::cout << "Facet: " << ifacet << " is degenerated\n"; + return false; + } + + // Compute surface area + double surfaceArea = 0.; + for (int i = 1; i < nvert - 1; ++i) { + Vertex_t e1 = fVertices[facet[i]] - fVertices[facet[0]]; + Vertex_t e2 = fVertices[facet[i + 1]] - fVertices[facet[0]]; + surfaceArea += 0.5 * Vertex_t::Cross(e1, e2).Mag(); + } + if (surfaceArea < kTolerance) { + std::cout << "Facet: " << ifacet << " has zero surface area\n"; + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Close the shape: calculate bounding box and compact vertices + +void O2Tessellated::CloseShape(bool check, bool fixFlipped, bool verbose) +{ + if (fIsClosed && fBVH) { + return; + } + // Compute bounding box + fDefined = true; + fNvert = fVertices.size(); + fNfacets = fFacets.size(); + ComputeBBox(); + + BuildBVH(); + if (fOutwardNormals.size() == 0) { + CalculateNormals(); + } else { + // short check if the normal container is of correct size + if (fOutwardNormals.size() != fFacets.size()) { + std::cerr << "Inconsistency in normal container"; + } + } + fIsClosed = true; + + // Cleanup the vertex map + std::multimap().swap(fVerticesMap); + + if (fVertices.size() > 0) { + if (!check) + return; + + // Check facets + for (auto i = 0; i < fNfacets; ++i) + FacetCheck(i); + + fClosedBody = CheckClosure(fixFlipped, verbose); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Check closure of the solid and check/fix flipped normals + +bool O2Tessellated::CheckClosure(bool fixFlipped, bool verbose) +{ + int* nn = new int[fNfacets]; + bool* flipped = new bool[fNfacets]; + bool hasorphans = false; + bool hasflipped = false; + for (int i = 0; i < fNfacets; ++i) { + nn[i] = 0; + flipped[i] = false; + } + + for (int icrt = 0; icrt < fNfacets; ++icrt) { + // all neighbours checked? + if (nn[icrt] >= fFacets[icrt].GetNvert()) + continue; + for (int i = icrt + 1; i < fNfacets; ++i) { + bool isneighbour = fFacets[icrt].IsNeighbour(fFacets[i], flipped[i]); + if (isneighbour) { + if (flipped[icrt]) + flipped[i] = !flipped[i]; + if (flipped[i]) + hasflipped = true; + nn[icrt]++; + nn[i]++; + if (nn[icrt] == fFacets[icrt].GetNvert()) + break; + } + } + if (nn[icrt] < fFacets[icrt].GetNvert()) + hasorphans = true; + } + + if (hasorphans && verbose) { + Error("Check", "Tessellated solid %s has following not fully connected facets:", GetName()); + for (int icrt = 0; icrt < fNfacets; ++icrt) { + if (nn[icrt] < fFacets[icrt].GetNvert()) + std::cout << icrt << " (" << fFacets[icrt].GetNvert() << " edges, " << nn[icrt] << " neighbours)\n"; + } + } + fClosedBody = !hasorphans; + int nfixed = 0; + if (hasflipped) { + if (verbose) + Warning("Check", "Tessellated solid %s has following facets with flipped normals:", GetName()); + for (int icrt = 0; icrt < fNfacets; ++icrt) { + if (flipped[icrt]) { + if (verbose) + std::cout << icrt << "\n"; + if (fixFlipped) { + fFacets[icrt].Flip(); + nfixed++; + } + } + } + if (nfixed && verbose) + Info("Check", "Automatically flipped %d facets to match first defined facet", nfixed); + } + delete[] nn; + delete[] flipped; + + return !hasorphans; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Compute bounding box + +void O2Tessellated::ComputeBBox() +{ + const double kBig = TGeoShape::Big(); + double vmin[3] = {kBig, kBig, kBig}; + double vmax[3] = {-kBig, -kBig, -kBig}; + for (const auto& facet : fFacets) { + for (int i = 0; i < facet.GetNvert(); ++i) { + for (int j = 0; j < 3; ++j) { + vmin[j] = TMath::Min(vmin[j], fVertices[facet[i]].operator[](j)); + vmax[j] = TMath::Max(vmax[j], fVertices[facet[i]].operator[](j)); + } + } + } + fDX = 0.5 * (vmax[0] - vmin[0]); + fDY = 0.5 * (vmax[1] - vmin[1]); + fDZ = 0.5 * (vmax[2] - vmin[2]); + for (int i = 0; i < 3; ++i) + fOrigin[i] = 0.5 * (vmax[i] + vmin[i]); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Returns numbers of vertices, segments and polygons composing the shape mesh. + +void O2Tessellated::GetMeshNumbers(int& nvert, int& nsegs, int& npols) const +{ + nvert = fNvert; + nsegs = fNseg; + npols = GetNfacets(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Creates a TBuffer3D describing *this* shape. +/// Coordinates are in local reference frame. + +TBuffer3D* O2Tessellated::MakeBuffer3D() const +{ + const int nvert = fNvert; + const int nsegs = fNseg; + const int npols = GetNfacets(); + auto buff = new TBuffer3D(TBuffer3DTypes::kGeneric, nvert, 3 * nvert, nsegs, 3 * nsegs, npols, 6 * npols); + if (buff) { + SetPoints(buff->fPnts); + SetSegsAndPols(*buff); + } + return buff; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Prints basic info + +void O2Tessellated::Print(Option_t*) const +{ + std::cout << "=== Tessellated shape " << GetName() << " having " << GetNvertices() << " vertices and " + << GetNfacets() << " facets\n"; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Fills TBuffer3D structure for segments and polygons. + +void O2Tessellated::SetSegsAndPols(TBuffer3D& buff) const +{ + const int c = GetBasicColor(); + int* segs = buff.fSegs; + int* pols = buff.fPols; + + int indseg = 0; // segment internal data index + int indpol = 0; // polygon internal data index + int sind = 0; // segment index + for (const auto& facet : fFacets) { + auto nvert = facet.GetNvert(); + pols[indpol++] = c; + pols[indpol++] = nvert; + for (auto j = 0; j < nvert; ++j) { + int k = (j + 1) % nvert; + // segment made by next consecutive points + segs[indseg++] = c; + segs[indseg++] = facet[j]; + segs[indseg++] = facet[k]; + // add segment to current polygon and increment segment index + pols[indpol + nvert - j - 1] = sind++; + } + indpol += nvert; + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Fill tessellated points to an array. + +void O2Tessellated::SetPoints(double* points) const +{ + int ind = 0; + for (const auto& vertex : fVertices) { + vertex.CopyTo(&points[ind]); + ind += 3; + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Fill tessellated points in float. + +void O2Tessellated::SetPoints(Float_t* points) const +{ + int ind = 0; + for (const auto& vertex : fVertices) { + points[ind++] = vertex.x(); + points[ind++] = vertex.y(); + points[ind++] = vertex.z(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Resize the shape by scaling vertices within maxsize and center to origin + +void O2Tessellated::ResizeCenter(double maxsize) +{ + using Vector3_t = Vertex_t; + + if (!fDefined) { + Error("ResizeCenter", "Not all faces are defined"); + return; + } + Vector3_t origin(fOrigin[0], fOrigin[1], fOrigin[2]); + double maxedge = TMath::Max(TMath::Max(fDX, fDY), fDZ); + double scale = maxsize / maxedge; + for (size_t i = 0; i < fVertices.size(); ++i) { + fVertices[i] = scale * (fVertices[i] - origin); + } + fOrigin[0] = fOrigin[1] = fOrigin[2] = 0; + fDX *= scale; + fDY *= scale; + fDZ *= scale; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Fills a static 3D buffer and returns a reference. + +const TBuffer3D& O2Tessellated::GetBuffer3D(int reqSections, Bool_t localFrame) const +{ + static TBuffer3D buffer(TBuffer3DTypes::kGeneric); + + FillBuffer3D(buffer, reqSections, localFrame); + + const int nvert = fNvert; + const int nsegs = fNseg; + const int npols = GetNfacets(); + + if (reqSections & TBuffer3D::kRawSizes) { + if (buffer.SetRawSizes(nvert, 3 * nvert, nsegs, 3 * nsegs, npols, 6 * npols)) { + buffer.SetSectionsValid(TBuffer3D::kRawSizes); + } + } + if ((reqSections & TBuffer3D::kRaw) && buffer.SectionsValid(TBuffer3D::kRawSizes)) { + SetPoints(buffer.fPnts); + if (!buffer.fLocalFrame) { + TransformPoints(buffer.fPnts, buffer.NbPnts()); + } + + SetSegsAndPols(buffer); + buffer.SetSectionsValid(TBuffer3D::kRaw); + } + + return buffer; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Reads a single tessellated solid from an .obj file. + +O2Tessellated* O2Tessellated::ImportFromObjFormat(const char* objfile, bool check, bool verbose) +{ + using std::vector, std::string, std::ifstream, std::stringstream, std::endl; + + vector vertices; + vector sfacets; + + struct FacetInd_t { + int i0 = -1; + int i1 = -1; + int i2 = -1; + int i3 = -1; + int nvert = 0; + FacetInd_t(int a, int b, int c) + { + i0 = a; + i1 = b; + i2 = c; + nvert = 3; + }; + FacetInd_t(int a, int b, int c, int d) + { + i0 = a; + i1 = b; + i2 = c; + i3 = d; + nvert = 4; + }; + }; + + vector facets; + // List of geometric vertices, with (x, y, z [,w]) coordinates, w is optional and defaults to 1.0. + // struct vtx_t { double x = 0; double y = 0; double z = 0; double w = 1; }; + + // Texture coordinates in u, [,v ,w]) coordinates, these will vary between 0 and 1. v, w are optional and default to + // 0. + // struct tex_t { double u; double v; double w; }; + + // List of vertex normals in (x,y,z) form; normals might not be unit vectors. + // struct vn_t { double x; double y; double z; }; + + // Parameter space vertices in ( u [,v] [,w] ) form; free form geometry statement + // struct vp_t { double u; double v; double w; }; + + // Faces are defined using lists of vertex, texture and normal indices which start at 1. + // Polygons such as quadrilaterals can be defined by using more than three vertex/texture/normal indices. + // f v1//vn1 v2//vn2 v3//vn3 ... + + // Records starting with the letter "l" specify the order of the vertices which build a polyline. + // l v1 v2 v3 v4 v5 v6 ... + + string line; + int ind[4] = {0}; + ifstream file(objfile); + if (!file.is_open()) { + ::Error("O2Tessellated::ImportFromObjFormat", "Unable to open %s", objfile); + return nullptr; + } + + while (getline(file, line)) { + stringstream ss(line); + string tag; + + // We ignore everything which is not a vertex or a face + if (line.rfind('v', 0) == 0 && line.rfind("vt", 0) != 0 && line.rfind("vn", 0) != 0 && line.rfind("vn", 0) != 0) { + // Decode the vertex + double pos[4] = {0, 0, 0, 1}; + ss >> tag >> pos[0] >> pos[1] >> pos[2] >> pos[3]; + vertices.emplace_back(pos[0] * pos[3], pos[1] * pos[3], pos[2] * pos[3]); + } + + else if (line.rfind('f', 0) == 0) { + // Decode the face + ss >> tag; + string word; + sfacets.clear(); + while (ss >> word) + sfacets.push_back(word); + if (sfacets.size() > 4 || sfacets.size() < 3) { + ::Error("O2Tessellated::ImportFromObjFormat", "Detected face having unsupported %zu vertices", + sfacets.size()); + return nullptr; + } + int nvert = 0; + for (auto& sword : sfacets) { + stringstream ssword(sword); + string token; + getline(ssword, token, '/'); // just need the vertex index, which is the first token + // Convert string token to integer + + ind[nvert++] = stoi(token) - 1; + if (ind[nvert - 1] < 0) { + ::Error("O2Tessellated::ImportFromObjFormat", "Unsupported relative vertex index definition in %s", + objfile); + return nullptr; + } + } + if (nvert == 3) + facets.emplace_back(ind[0], ind[1], ind[2]); + else + facets.emplace_back(ind[0], ind[1], ind[2], ind[3]); + } + } + + int nvertices = (int)vertices.size(); + int nfacets = (int)facets.size(); + if (nfacets < 3) { + ::Error("O2Tessellated::ImportFromObjFormat", "Not enough faces detected in %s", objfile); + return nullptr; + } + + string sobjfile(objfile); + if (verbose) + std::cout << "Read " << nvertices << " vertices and " << nfacets << " facets from " << sobjfile << endl; + + auto tsl = new O2Tessellated(sobjfile.erase(sobjfile.find_last_of('.')).c_str(), vertices); + + for (int i = 0; i < nfacets; ++i) { + auto facet = facets[i]; + if (facet.nvert == 3) + tsl->AddFacet(facet.i0, facet.i1, facet.i2); + else + tsl->AddFacet(facet.i0, facet.i1, facet.i2, facet.i3); + } + tsl->CloseShape(check, true, verbose); + tsl->Print(); + return tsl; +} + +// implementation of some geometry helper functions in anonymous namespace +namespace +{ + +using Vertex_t = Tessellated::Vertex_t; +// The classic Moeller-Trumbore ray triangle-intersection kernel: +// - Compute triangle edges e1, e2 +// - Compute determinant det +// - Reject parallel rays +// - Compute barycentric coordinates u, v +// - Compute ray parameter t +double rayTriangle(const Vertex_t& orig, const Vertex_t& dir, const Vertex_t& v0, const Vertex_t& v1, + const Vertex_t& v2, double rayEPS = 1e-8) +{ + constexpr double EPS = 1e-8; + const double INF = std::numeric_limits::infinity(); + Vertex_t e1{v1[0] - v0[0], v1[1] - v0[1], v1[2] - v0[2]}; + Vertex_t e2{v2[0] - v0[0], v2[1] - v0[1], v2[2] - v0[2]}; + auto p = Vertex_t::Cross(dir, e2); + auto det = e1.Dot(p); + if (std::abs(det) <= EPS) { + return INF; + } + + Vertex_t tvec{orig[0] - v0[0], orig[1] - v0[1], orig[2] - v0[2]}; + auto invDet = 1.0 / det; + auto u = tvec.Dot(p) * invDet; + if (u < 0.0 || u > 1.0) { + return INF; + } + auto q = Vertex_t::Cross(tvec, e1); + auto v = dir.Dot(q) * invDet; + if (v < 0.0 || u + v > 1.0) { + return INF; + } + auto t = e2.Dot(q) * invDet; + return (t > rayEPS) ? t : INF; +} + +template +struct Vec3f { + T x, y, z; +}; + +template +inline Vec3f operator-(const Vec3f& a, const Vec3f& b) +{ + return {a.x - b.x, a.y - b.y, a.z - b.z}; +} + +template +inline Vec3f cross(const Vec3f& a, const Vec3f& b) +{ + return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x}; +} + +template +inline T dot(const Vec3f& a, const Vec3f& b) +{ + return a.x * b.x + a.y * b.y + a.z * b.z; +} + +// Kernel to get closest/shortest distance between a point and a triangl (a,b,c). +// Performed by default in float since Safety can be approximate. +// Project point onto triangle plane +// If projection lies inside → distance to plane +// Otherwise compute min distance to the three edges +// Return squared distance +template +T pointTriangleDistSq(const Vec3f& p, const Vec3f& a, const Vec3f& b, const Vec3f& c) +{ + // Edges + Vec3f ab = b - a; + Vec3f ac = c - a; + Vec3f ap = p - a; + + auto d1 = dot(ab, ap); + auto d2 = dot(ac, ap); + if (d1 <= T(0.0) && d2 <= T(0.0)) { + return dot(ap, ap); // barycentric (1,0,0) + } + + Vec3f bp = p - b; + auto d3 = dot(ab, bp); + auto d4 = dot(ac, bp); + if (d3 >= T(0.0) && d4 <= d3) { + return dot(bp, bp); // (0,1,0) + } + + T vc = d1 * d4 - d3 * d2; + if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f) { + T v = d1 / (d1 - d3); + Vec3f proj = {a.x + v * ab.x, a.y + v * ab.y, a.z + v * ab.z}; + Vec3f d = p - proj; + return dot(d, d); // edge AB + } + + Vec3f cp = p - c; + T d5 = dot(ab, cp); + T d6 = dot(ac, cp); + if (d6 >= T(0.0f) && d5 <= d6) { + return dot(cp, cp); // (0,0,1) + } + + T vb = d5 * d2 - d1 * d6; + if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f) { + T w = d2 / (d2 - d6); + Vec3f proj = {a.x + w * ac.x, a.y + w * ac.y, a.z + w * ac.z}; + Vec3f d = p - proj; + return dot(d, d); // edge AC + } + + T va = d3 * d6 - d5 * d4; + if (va <= 0.0f && (d4 - d3) >= 0.0f && (d5 - d6) >= 0.0f) { + T w = (d4 - d3) / ((d4 - d3) + (d5 - d6)); + Vec3f proj = {b.x + w * (c.x - b.x), b.y + w * (c.y - b.y), b.z + w * (c.z - b.z)}; + Vec3f d = p - proj; + return dot(d, d); // edge BC + } + + // Inside face region + T denom = T(1.0f) / (va + vb + vc); + T v = vb * denom; + T w = vc * denom; + + Vec3f proj = {a.x + ab.x * v + ac.x * w, a.y + ab.y * v + ac.y * w, a.z + ab.z * v + ac.z * w}; + + Vec3f d = p - proj; + return dot(d, d); +} + +template +inline Vec3f normalize(const Vec3f& v) +{ + T len2 = dot(v, v); + if (len2 == T(0.0f)) { + std::cerr << "Degnerate triangle. Cannot determine normal"; + return {0, 0, 0}; + } + T invLen = T(1.0f) / std::sqrt(len2); + return {v.x * invLen, v.y * invLen, v.z * invLen}; +} + +template +inline Vec3f triangleNormal(const Vec3f& a, const Vec3f& b, const Vec3f& c) +{ + const Vec3f e1 = b - a; + const Vec3f e2 = c - a; + return normalize(cross(e1, e2)); +} + +} // end anonymous namespace + +//////////////////////////////////////////////////////////////////////////////// +/// DistFromOutside + +Double_t O2Tessellated::DistFromOutside(const Double_t* point, const Double_t* dir, Int_t /*iact*/, Double_t stepmax, + Double_t* /*safe*/) const +{ + // use the BVH intersector in combination with leaf ray-triangle testing + double local_step = Big(); // we need this otherwise the lambda get's confused + + using Scalar = float; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + using Ray = bvh::v2::Ray; + + // let's fetch the bvh + auto mybvh = (Bvh*)fBVH; + if (!mybvh) { + assert(false); + return -1.; + } + + auto truncate_roundup = [](double orig) { + float epsilon = std::numeric_limits::epsilon() * std::fabs(orig); + // Add the bias to x before assigning it to y + return static_cast(orig + epsilon); + }; + + // let's do very quick checks against the top node + const auto topnode_bbox = mybvh->get_root().get_bbox(); + if ((-point[0] + topnode_bbox.min[0]) > stepmax) { + return Big(); + } + if ((-point[1] + topnode_bbox.min[1]) > stepmax) { + return Big(); + } + if ((-point[2] + topnode_bbox.min[2]) > stepmax) { + return Big(); + } + if ((point[0] - topnode_bbox.max[0]) > stepmax) { + return Big(); + } + if ((point[1] - topnode_bbox.max[1]) > stepmax) { + return Big(); + } + if ((point[2] - topnode_bbox.max[2]) > stepmax) { + return Big(); + } + + // the ray used for bvh interaction + Ray ray(Vec3(point[0], point[1], point[2]), // origin + Vec3(dir[0], dir[1], dir[2]), // direction + 0.0f, // minimum distance (could give stepmax ?) + truncate_roundup(local_step)); + + static constexpr bool use_robust_traversal = true; + + Vertex_t dir_v{dir[0], dir[1], dir[2]}; + // Traverse the BVH and apply concrete object intersection in BVH leafs + bvh::v2::GrowingStack stack; + mybvh->intersect(ray, mybvh->get_root().index, stack, [&](size_t begin, size_t end) { + for (size_t prim_id = begin; prim_id < end; ++prim_id) { + auto objectid = mybvh->prim_ids[prim_id]; + const auto& facet = fFacets[objectid]; + const auto& n = fOutwardNormals[objectid]; + + // quick normal test. Coming from outside, the dot product must be negative + if (n.Dot(dir_v) > 0.) { + continue; + } + + auto thisdist = rayTriangle(Vertex_t(point[0], point[1], point[2]), dir_v, + fVertices[facet[0]], fVertices[facet[1]], fVertices[facet[2]], 0.); + + if (thisdist < local_step) { + local_step = thisdist; + } + } + return false; // go on after this + }); + + return local_step; +} + +//////////////////////////////////////////////////////////////////////////////// +/// DistFromOutside + +Double_t O2Tessellated::DistFromInside(const Double_t* point, const Double_t* dir, Int_t /*iact*/, Double_t /*stepmax*/, + Double_t* /*safe*/) const +{ + // use the BVH intersector in combination with leaf ray-triangle testing + double local_step = Big(); // we need this otherwise the lambda get's confused + + using Scalar = float; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + using Ray = bvh::v2::Ray; + + // let's fetch the bvh + auto mybvh = (Bvh*)fBVH; + if (!mybvh) { + assert(false); + return -1.; + } + + auto truncate_roundup = [](double orig) { + float epsilon = std::numeric_limits::epsilon() * std::fabs(orig); + // Add the bias to x before assigning it to y + return static_cast(orig + epsilon); + }; + + // the ray used for bvh interaction + Ray ray(Vec3(point[0], point[1], point[2]), // origin + Vec3(dir[0], dir[1], dir[2]), // direction + 0., // minimum distance (could give stepmax ?) + truncate_roundup(local_step)); + + static constexpr bool use_robust_traversal = true; + + Vertex_t dir_v{dir[0], dir[1], dir[2]}; + // Traverse the BVH and apply concrete object intersection in BVH leafs + bvh::v2::GrowingStack stack; + mybvh->intersect(ray, mybvh->get_root().index, stack, [&](size_t begin, size_t end) { + for (size_t prim_id = begin; prim_id < end; ++prim_id) { + auto objectid = mybvh->prim_ids[prim_id]; + auto facet = fFacets[objectid]; + const auto& n = fOutwardNormals[objectid]; + + // Only exiting surfaces are relevant (from inside--> dot product must be positive) + if (n.Dot(dir_v) <= 0.) { + continue; + } + + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = + rayTriangle(Vertex_t{point[0], point[1], point[2]}, dir_v, v0, v1, v2, 0.); + if (t < local_step) { + local_step = t; + } + } + return false; // go on after this + }); + + return local_step; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Capacity + +Double_t O2Tessellated::Capacity() const +{ + // For explanation of the following algorithm see: + // https://en.wikipedia.org/wiki/Polyhedron#Volume + // http://wwwf.imperial.ac.uk/~rn/centroid.pdf + + double vol = 0.0; + for (size_t i = 0; i < fFacets.size(); ++i) { + auto& facet = fFacets[i]; + auto a = fVertices[facet[0]]; + auto b = fVertices[facet[1]]; + auto c = fVertices[facet[2]]; + vol += + a[0] * (b[1] * c[2] - b[2] * c[1]) + b[0] * (c[1] * a[2] - c[2] * a[1]) + c[0] * (a[1] * b[2] - a[2] * b[1]); + } + return vol / 6.0; +} + +//////////////////////////////////////////////////////////////////////////////// +/// BuildBVH + +void O2Tessellated::BuildBVH() +{ + using Scalar = float; + using BBox = bvh::v2::BBox; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + + // helper determining axis aligned bounding box from a facet; + auto GetBoundingBox = [this](TGeoFacet const& facet) { +#ifndef NDEBUG + const auto nvertices = facet.GetNvert(); + assert(nvertices == 3); // for now only triangles +#endif + const auto& v1 = fVertices[facet[0]]; + const auto& v2 = fVertices[facet[1]]; + const auto& v3 = fVertices[facet[2]]; + BBox bbox; + bbox.min[0] = std::min(std::min(v1[0], v2[0]), v3[0]) - 0.001f; + bbox.min[1] = std::min(std::min(v1[1], v2[1]), v3[1]) - 0.001f; + bbox.min[2] = std::min(std::min(v1[2], v2[2]), v3[2]) - 0.001f; + bbox.max[0] = std::max(std::max(v1[0], v2[0]), v3[0]) + 0.001f; + bbox.max[1] = std::max(std::max(v1[1], v2[1]), v3[1]) + 0.001f; + bbox.max[2] = std::max(std::max(v1[2], v2[2]), v3[2]) + 0.001f; + return bbox; + }; + + // we need bounding boxes enclosing the primitives and centers of primitives + // (replaced here by centers of bounding boxes) to build the bvh + std::vector bboxes; + std::vector centers; + + // loop over all the triangles/Facets; + int nd = fFacets.size(); + for (int i = 0; i < nd; ++i) { + auto& facet = fFacets[i]; + + // fetch the bounding box of this node and add to the vector of bounding boxes + (bboxes).push_back(GetBoundingBox(facet)); + centers.emplace_back((bboxes).back().get_center()); + } + + // check if some previous object is registered and delete if necessary + if (fBVH) { + delete (Bvh*)fBVH; + fBVH = nullptr; + } + + // create the bvh + typename bvh::v2::DefaultBuilder::Config config; + config.quality = bvh::v2::DefaultBuilder::Quality::High; + auto bvh = bvh::v2::DefaultBuilder::build(bboxes, centers, config); + auto bvhptr = new Bvh; + *bvhptr = std::move(bvh); // copy structure + fBVH = (void*)(bvhptr); + + return; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Contains + +bool O2Tessellated::Contains(Double_t const* point) const +{ + // we do the parity test + using Scalar = float; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + using Ray = bvh::v2::Ray; + + // let's fetch the bvh + auto mybvh = (Bvh*)fBVH; + if (!mybvh) { + assert(false); + return false; + } + + auto truncate_roundup = [](double orig) { + float epsilon = std::numeric_limits::epsilon() * std::fabs(orig); + // Add the bias to x before assigning it to y + return static_cast(orig + epsilon); + }; + + // let's do very quick checks against the top node + if (!TGeoBBox::Contains(point)) { + return false; + } + + // An arbitrary test direction. + // Doesn't need to be normalized and probes all normals. Also ensuring to be skewed somewhat + // without evident symmetries. + Vertex_t test_dir{1.0, 1.41421356237, 1.73205080757}; + + double local_step = Big(); + // the ray used for bvh interaction + Ray ray(Vec3(point[0], point[1], point[2]), // origin + Vec3(test_dir[0], test_dir[1], test_dir[2]), // direction + 0.0f, // minimum distance (could give stepmax ?) + truncate_roundup(local_step)); + + static constexpr bool use_robust_traversal = true; + + // Traverse the BVH and apply concrete object intersection in BVH leafs + bvh::v2::GrowingStack stack; + size_t crossings = 0; + mybvh->intersect(ray, mybvh->get_root().index, stack, [&](size_t begin, size_t end) { + for (size_t prim_id = begin; prim_id < end; ++prim_id) { + auto objectid = mybvh->prim_ids[prim_id]; + auto& facet = fFacets[objectid]; + + // for the parity test, we probe all crossing surfaces + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = rayTriangle(Vertex_t(point[0], point[1], point[2]), + test_dir, v0, v1, v2, 0.); + + if (t != std::numeric_limits::infinity()) { + ++crossings; + } + } + return false; + }); + + return crossings & 1; +} + +namespace +{ + +// Helper classes/structs used for priority queue - BVH traversal +// structure keeping cost (value) for a BVH index +struct BVHPrioElement { + size_t bvh_node_id; + float value; +}; + +// A priority queue for BVHPrioElement with an additional clear method +// for quick reset. We intentionally derive from std::priority_queue here to expose a +// clear() convenience method via access to the protected container `c`. +// This is internal, non-polymorphic code and relies on standard-library +// implementation details that are stable across supported platforms. +template +class BVHPrioQueue : public std::priority_queue, Comparator> +{ + public: + using std::priority_queue, + Comparator>::priority_queue; // constructor inclusion + + // convenience method to quickly clear/reset the queue (instead of having to pop one by one) + void clear() { this->c.clear(); } +}; + +} // namespace + +/// a reusable safety kernel, which optionally returns the closest face +template +inline Double_t O2Tessellated::SafetyKernel(const Double_t* point, bool in, int* closest_facet_id) const +{ + // This is the classic traversal/pruning of a BVH based on priority queue search + + float smallest_safety_sq = TGeoShape::Big(); + + using Scalar = float; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + + // let's fetch the bvh + auto mybvh = (Bvh*)fBVH; + + // testpoint object in float for quick BVH interaction + Vec3 testpoint(point[0], point[1], point[2]); + + auto currnode = mybvh->nodes[0]; // we start from the top BVH node + // we do a quick check on the top node (in case we are outside shape) + bool outside_top = false; + if (!in) { + outside_top = !bvh::v2::extra::contains(currnode.get_bbox(), testpoint); + if (outside_top) { + const auto safety_sq_to_top = bvh::v2::extra::SafetySqToNode(currnode.get_bbox(), testpoint); + // we simply return safety to the outer bounding box as an estimate + return std::sqrt(safety_sq_to_top); + } + } + + // comparator bringing out "smallest" value on top + auto cmp = [](BVHPrioElement a, BVHPrioElement b) { return a.value > b.value; }; + static thread_local BVHPrioQueue queue(cmp); + queue.clear(); + + // algorithm is based on standard iterative tree traversal with priority queues + float current_safety_to_node_sq = 0.f; + + if (returnFace) { + *closest_facet_id = -1; + } + + do { + if (currnode.is_leaf()) { + // we are in a leaf node and actually talk to a face/triangular primitive + const auto begin_prim_id = currnode.index.first_id(); + const auto end_prim_id = begin_prim_id + currnode.index.prim_count(); + + for (auto p_id = begin_prim_id; p_id < end_prim_id; p_id++) { + const auto object_id = mybvh->prim_ids[p_id]; + + const auto& facet = fFacets[object_id]; + const auto& v1 = fVertices[facet[0]]; + const auto& v2 = fVertices[facet[1]]; + const auto& v3 = fVertices[facet[2]]; + + auto thissafetySQ = pointTriangleDistSq(Vec3f{point[0], point[1], point[2]}, Vec3f{v1[0], v1[1], v1[2]}, + Vec3f{v2[0], v2[1], v2[2]}, Vec3f{v3[0], v3[1], v3[2]}); + + if (thissafetySQ < smallest_safety_sq) { + smallest_safety_sq = thissafetySQ; + if (returnFace) { + *closest_facet_id = object_id; + } + } + } + } else { + // not a leave node ... for further traversal, + // we inject the children into priority queue based on distance to it's bounding box + const auto leftchild_id = currnode.index.first_id(); + const auto rightchild_id = leftchild_id + 1; + + for (size_t childid : {leftchild_id, rightchild_id}) { + if (childid >= mybvh->nodes.size()) { + continue; + } + + const auto& node = mybvh->nodes[childid]; + const auto inside = bvh::v2::extra::contains(node.get_bbox(), testpoint); + + if (inside) { + // this must be further considered because we are inside the bounding box + queue.push(BVHPrioElement{childid, -1.}); + } else { + auto safety_to_node_square = bvh::v2::extra::SafetySqToNode(node.get_bbox(), testpoint); + if (safety_to_node_square <= smallest_safety_sq) { + // this should be further considered + queue.push(BVHPrioElement{childid, safety_to_node_square}); + } + } + } + } + + if (queue.size() > 0) { + auto currElement = queue.top(); + currnode = mybvh->nodes[currElement.bvh_node_id]; + current_safety_to_node_sq = currElement.value; + queue.pop(); + } else { + break; + } + } while (current_safety_to_node_sq <= smallest_safety_sq); + + return std::nextafter(std::sqrt(smallest_safety_sq), 0.0f); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Safety + +Double_t O2Tessellated::Safety(const Double_t* point, Bool_t in) const +{ + // we could use some caching here (in future) since queries to the solid will likely + // be made with some locality + + // fall-back to precise safety kernel + return SafetyKernel(point, in); +} + +//////////////////////////////////////////////////////////////////////////////// +/// ComputeNormal interface + +void O2Tessellated::ComputeNormal(const Double_t* point, const Double_t* dir, Double_t* norm) const +{ + // We take the approach to identify closest facet to the point via safety + // and returning the normal from this face. + + // TODO: Before doing that we could check for cached points from other queries + + // use safety kernel + int closest_face_id = -1; + SafetyKernel(point, true, &closest_face_id); + + if (closest_face_id < 0) { + norm[0] = 1.; + norm[1] = 0.; + norm[2] = 0.; + return; + } + + const auto& n = fOutwardNormals[closest_face_id]; + norm[0] = n[0]; + norm[1] = n[1]; + norm[2] = n[2]; + + // change sign depending on dir + if (norm[0] * dir[0] + norm[1] * dir[1] + norm[2] * dir[2] < 0) { + norm[0] = -norm[0]; + norm[1] = -norm[1]; + norm[2] = -norm[2]; + } + return; +} + +//////////////////////////////////////////////////////////////////////////////// +/// trivial (non-BVH) DistFromInside function + +Double_t O2Tessellated::DistFromInside_Loop(const Double_t* point, const Double_t* dir) const +{ + Vertex_t p(point[0], point[1], point[2]); + Vertex_t d(dir[0], dir[1], dir[2]); + + double dist = Big(); + for (size_t i = 0; i < fFacets.size(); ++i) { + const auto& facet = fFacets[i]; + const auto& n = fOutwardNormals[i]; + + // Only exiting surfaces are relevant (from inside--> dot product must be positive) + if (n.Dot(d) <= 0.0) { + continue; + } + + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = rayTriangle(p, d, v0, v1, v2, 0.); + + if (t < dist) { + dist = t; + } + } + return dist; +} + +//////////////////////////////////////////////////////////////////////////////// +/// trivial (non-BVH) DistFromOutside function + +Double_t O2Tessellated::DistFromOutside_Loop(const Double_t* point, const Double_t* dir) const +{ + Vertex_t p(point[0], point[1], point[2]); + Vertex_t d(dir[0], dir[1], dir[2]); + + double dist = Big(); + for (size_t i = 0; i < fFacets.size(); ++i) { + const auto& facet = fFacets[i]; + const auto& n = fOutwardNormals[i]; + + // Only exiting surfaces are relevant (from outside, the dot product must be negative) + if (n.Dot(d) > 0.0) { + continue; + } + + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = rayTriangle(p, d, v0, v1, v2, 0.); + + if (t < dist) { + dist = t; + } + } + return dist; +} + +//////////////////////////////////////////////////////////////////////////////// +/// trivial (non-BVH) Contains + +bool O2Tessellated::Contains_Loop(const Double_t* point) const +{ + // Fixed ray direction + const Vertex_t test_dir{1.0, 1.41421356237, 1.73205080757}; + + Vertex_t p(point[0], point[1], point[2]); + + int crossings = 0; + for (size_t i = 0; i < fFacets.size(); ++i) { + const auto& facet = fFacets[i]; + + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = rayTriangle(p, test_dir, v0, v1, v2, 0.); + if (t != std::numeric_limits::infinity()) { + ++crossings; + } + } + return (crossings & 1); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Custom streamer which performs Closing on read. +/// Recalculation of BVH and normals is fast + +void O2Tessellated::Streamer(TBuffer& b) +{ + if (b.IsReading()) { + b.ReadClassBuffer(O2Tessellated::Class(), this); + CloseShape(false); // close shape but do not re-perform checks + } else { + b.WriteClassBuffer(O2Tessellated::Class(), this); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Calculate the normals + +void O2Tessellated::CalculateNormals() +{ + fOutwardNormals.clear(); + for (auto& facet : fFacets) { + auto& v1 = fVertices[facet[0]]; + auto& v2 = fVertices[facet[1]]; + auto& v3 = fVertices[facet[2]]; + using Vec3d = Vec3f; + auto norm = triangleNormal(Vec3d{v1[0], v1[1], v1[2]}, Vec3d{v2[0], v2[1], v2[2]}, Vec3d{v3[0], v3[1], v3[2]}); + fOutwardNormals.emplace_back(Vertex_t{norm.x, norm.y, norm.z}); + } +} + +// NOLINTEND \ No newline at end of file diff --git a/Detectors/Base/src/TGeoGeometryUtils.cxx b/Detectors/Base/src/TGeoGeometryUtils.cxx new file mode 100644 index 0000000000000..6f06eff17a6d7 --- /dev/null +++ b/Detectors/Base/src/TGeoGeometryUtils.cxx @@ -0,0 +1,144 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TGeoGeometryUtils.cxx +/// \author Sandro Wenzel (CERN) +/// \brief Collection of utility functions for TGeo + +#include +#include +#include +#include +#include + +namespace o2 +{ +namespace base +{ + +namespace +{ +// some helpers to interpret TGeo TBuffer3D output +// and convert it to surface triangles (reengineered from TGeo code) + +std::vector BuildVertexLoop(const TBuffer3D& buf, + const std::vector& segs) +{ + // adjacency list + std::unordered_map> adj; + + for (int s : segs) { + int a = buf.fSegs[3 * s + 1]; + int b = buf.fSegs[3 * s + 2]; + adj[a].push_back(b); + adj[b].push_back(a); + } + + // start from any vertex + int start = adj.begin()->first; + int prev = -1; + int curr = start; + + std::vector loop; + + while (true) { + loop.push_back(curr); + + const auto& nbrs = adj[curr]; + int next = -1; + + for (int n : nbrs) { + if (n != prev) { + next = n; + break; + } + } + + if (next == -1 || next == start) { + break; + } + + prev = curr; + curr = next; + } + return loop; +} + +std::vector> ExtractPolygons(const TBuffer3D& buf) +{ + std::vector> polys; + Int_t idx = 0; + + for (Int_t ip = 0; ip < buf.NbPols(); ++ip) { + + idx++; // color + Int_t nseg = buf.fPols[idx++]; + + std::vector segs(nseg); + for (Int_t i = 0; i < nseg; ++i) { + segs[i] = buf.fPols[idx++]; + } + + auto verts = BuildVertexLoop(buf, segs); + if (verts.size() >= 3) { + polys.push_back(std::move(verts)); + } + } + + return polys; +} + +std::vector> + Triangulate(const std::vector>& polys) +{ + std::vector> tris; + for (const auto& poly : polys) { + int nv = poly.size(); + if (nv < 3) { + continue; + } + + int v0 = poly[0]; + for (int i = 1; i < nv - 1; ++i) { + tris.push_back({{v0, poly[i], poly[i + 1]}}); + } + } + return tris; +} + +TGeoTessellated* MakeTessellated(const TBuffer3D& buf) +{ + auto polys = ExtractPolygons(buf); + auto tris = Triangulate(polys); + int i = 0; + auto* tess = new TGeoTessellated("tess"); + const Double_t* p = buf.fPnts; + for (auto& t : tris) { + tess->AddFacet( + TGeoTessellated::Vertex_t{p[3 * t[0]], p[3 * t[0] + 1], p[3 * t[0] + 2]}, + TGeoTessellated::Vertex_t{p[3 * t[1]], p[3 * t[1] + 1], p[3 * t[1] + 2]}, + TGeoTessellated::Vertex_t{p[3 * t[2]], p[3 * t[2] + 1], p[3 * t[2] + 2]}); + } + tess->CloseShape(); + return tess; +} +} // end anonymous namespace + +///< Transform any (primitive) TGeoShape to a TGeoTessellated +TGeoTessellated* TGeoGeometryUtils::TGeoShapeToTGeoTessellated(TGeoShape const* shape) +{ + auto& buf = shape->GetBuffer3D(TBuffer3D::kRawSizes | TBuffer3D::kRaw | TBuffer3D::kCore, false); + auto tes = MakeTessellated(buf); + return tes; +} + +} // namespace base +} // namespace o2 diff --git a/Detectors/Base/src/bvh2_extra_kernels.h b/Detectors/Base/src/bvh2_extra_kernels.h new file mode 100644 index 0000000000000..70e43202a53c4 --- /dev/null +++ b/Detectors/Base/src/bvh2_extra_kernels.h @@ -0,0 +1,79 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Sandro Wenzel 2026 + +#ifndef ROOT_GEOM_BVH2_EXTRA + +namespace bvh::v2::extra +{ + +// reusable geometry kernels used in multiple places +// for interaction with BVH2 structures + +// determines if a point is inside the bounding box +template +bool contains(bvh::v2::BBox const& box, bvh::v2::Vec const& p) +{ + auto min = box.min; + auto max = box.max; + return (p[0] >= min[0] && p[0] <= max[0]) && (p[1] >= min[1] && p[1] <= max[1]) && + (p[2] >= min[2] && p[2] <= max[2]); +} + +// determines the largest squared distance of point to any of the bounding box corners +template +auto RmaxSqToNode(bvh::v2::BBox const& box, bvh::v2::Vec const& p) +{ + // construct the 8 corners to get the maximal distance + const auto minCorner = box.min; + const auto maxCorner = box.max; + using Vec3 = bvh::v2::Vec; + // these are the corners of the bounding box + const std::array, 8> corners{ + Vec3{minCorner[0], minCorner[1], minCorner[2]}, Vec3{minCorner[0], minCorner[1], maxCorner[2]}, + Vec3{minCorner[0], maxCorner[1], minCorner[2]}, Vec3{minCorner[0], maxCorner[1], maxCorner[2]}, + Vec3{maxCorner[0], minCorner[1], minCorner[2]}, Vec3{maxCorner[0], minCorner[1], maxCorner[2]}, + Vec3{maxCorner[0], maxCorner[1], minCorner[2]}, Vec3{maxCorner[0], maxCorner[1], maxCorner[2]}}; + + T Rmax_sq{0}; + for (const auto& corner : corners) { + float R_sq = 0.; + const auto dx = corner[0] - p[0]; + R_sq += dx * dx; + const auto dy = corner[1] - p[1]; + R_sq += dy * dy; + const auto dz = corner[2] - p[2]; + R_sq += dz * dz; + Rmax_sq = std::max(Rmax_sq, R_sq); + } + return Rmax_sq; +}; + +// determines the minimum squared distance of point to a bounding box ("safey square") +template +auto SafetySqToNode(bvh::v2::BBox const& box, bvh::v2::Vec const& p) +{ + T sqDist{0.0}; + for (int i = 0; i < 3; i++) { + T v = p[i]; + if (v < box.min[i]) { + sqDist += (box.min[i] - v) * (box.min[i] - v); + } else if (v > box.max[i]) { + sqDist += (v - box.max[i]) * (v - box.max[i]); + } + } + return sqDist; +}; + +} // namespace bvh::v2::extra + +#endif \ No newline at end of file diff --git a/Detectors/Base/src/bvh2_third_party.h b/Detectors/Base/src/bvh2_third_party.h new file mode 100644 index 0000000000000..5cf7772269642 --- /dev/null +++ b/Detectors/Base/src/bvh2_third_party.h @@ -0,0 +1,49 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Sandro Wenzel 2026 + +#ifndef ROOT_GEOM_BVH2_THIRD_PARTY + +// A single entry header into third-party BVH2 installed in ROOT +// Good place to manage compiler warnings etc. + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wshadow" +#pragma clang diagnostic ignored "-Wpsabi" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +#pragma GCC diagnostic ignored "-Wpsabi" +#pragma GCC diagnostic ignored "-Wall" +#pragma GCC diagnostic ignored "-Wshadow" +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma GCC diagnostic ignored "-Wattributes" +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 5051) +#endif + +#include +#include +#include +#include +#include +#include + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + +#endif \ No newline at end of file diff --git a/Detectors/Passive/CMakeLists.txt b/Detectors/Passive/CMakeLists.txt index 0976530bc6571..a24954ad10539 100644 --- a/Detectors/Passive/CMakeLists.txt +++ b/Detectors/Passive/CMakeLists.txt @@ -23,6 +23,7 @@ o2_add_library(DetectorsPassive src/Hall.cxx src/HallSimParam.cxx src/PassiveBase.cxx + src/ExternalModule.cxx PUBLIC_LINK_LIBRARIES O2::Field O2::DetectorsBase O2::SimConfig) o2_target_root_dictionary(DetectorsPassive @@ -39,6 +40,7 @@ o2_target_root_dictionary(DetectorsPassive include/DetectorsPassive/Hall.h include/DetectorsPassive/HallSimParam.h include/DetectorsPassive/PassiveBase.h + include/DetectorsPassive/ExternalModule.h LINKDEF src/PassiveLinkDef.h) # FIXME: if PutFrameInTop really depends on TRD, then the following can not work diff --git a/Detectors/Passive/include/DetectorsPassive/ExternalModule.h b/Detectors/Passive/include/DetectorsPassive/ExternalModule.h new file mode 100644 index 0000000000000..155870ae42a6d --- /dev/null +++ b/Detectors/Passive/include/DetectorsPassive/ExternalModule.h @@ -0,0 +1,64 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_PASSIVE_EXTERNALMODULE_H +#define ALICEO2_PASSIVE_EXTERNALMODULE_H + +#include "DetectorsPassive/PassiveBase.h" // base class of passive modules +#include "Rtypes.h" // for Pipe::Class, ClassDef, Pipe::Streamer + +class TGeoVolume; +class TGeoTransformation; + +namespace o2 +{ +namespace passive +{ + +// options used to configure a generic plug and play external module +struct ExternalModuleOptions { + std::string root_macro_file; // the file where to lookup the ROOT geometry building macro + std::string top_volume; // the volume to be added + std::string anchor_volume; // the volume into which the detector will be hooked + TGeoMatrix const* placement = nullptr; // how to place the module inside anchor_volume +}; + +// a module (passive material) defined externally (ROOT macro / GDML / TGeo geometry) +class ExternalModule : public PassiveBase +{ + public: + ExternalModule(const char* name, const char* long_title, ExternalModuleOptions options); + ExternalModule() = default; // default constructor + + ~ExternalModule() override = default; + void ConstructGeometry() override; + + /// Clone this object (used in MT mode only) + FairModule* CloneModule() const override { return nullptr; } + + typedef std::function GeomBuilderFcn; // function hook for external geometry builder + + private: + // void createMaterials(); + ExternalModule(const ExternalModule& orig); + ExternalModule& operator=(const ExternalModule&); + + GeomBuilderFcn mGeomHook; + ExternalModuleOptions mOptions; + + bool initGeomBuilderHook(); // function to load/JIT Geometry builder hook + void remapMedia(TGeoVolume* vol); // performs a remapping of materials/media IDs after registration with VMC + + // ClassDefOverride(ExternalModule, 0); +}; +} // namespace passive +} // namespace o2 +#endif diff --git a/Detectors/Passive/src/ExternalModule.cxx b/Detectors/Passive/src/ExternalModule.cxx new file mode 100644 index 0000000000000..fc6bd6953b82d --- /dev/null +++ b/Detectors/Passive/src/ExternalModule.cxx @@ -0,0 +1,175 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Sandro Wenzel (CERN), 2026 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// ClassImp(o2::passive::ExternalModule) + +namespace o2::passive +{ + +ExternalModule::ExternalModule(const char* name, const char* long_title, ExternalModuleOptions options) : PassiveBase(name, long_title), mOptions(options) +{ +} + +void ExternalModule::remapMedia(TGeoVolume* top_volume) +{ + std::unordered_map medium_ptr_mapping; + std::unordered_set volumes_already_treated; + int counter = 1; + + auto modulename = GetName(); + + // The transformer function + auto transform_media = [&](TGeoVolume* vol_) { + if (volumes_already_treated.find(vol_) != volumes_already_treated.end()) { + // this volume was already transformed + return; + } + volumes_already_treated.insert(vol_); + + if (dynamic_cast(vol_)) { + // do nothing for assemblies (they don't have a medium) + return; + } + + auto medium = vol_->GetMedium(); + if (!medium) { + return; + } + + auto iter = medium_ptr_mapping.find(medium); + if (iter != medium_ptr_mapping.end()) { + // This medium has already been transformed, so + // we just update the volume + vol_->SetMedium(iter->second); + return; + } else { + std::cout << "Transforming media with name " << medium->GetName() << " for volume " << vol_->GetName() << "\n"; + + // we found a medium, not yet treated + auto curr_mat = medium->GetMaterial(); + auto& matmgr = o2::base::MaterialManager::Instance(); + + matmgr.Material(modulename, counter, curr_mat->GetName(), curr_mat->GetA(), curr_mat->GetZ(), curr_mat->GetDensity(), curr_mat->GetRadLen(), curr_mat->GetIntLen()); + // TGeo medium params are stored in a flat array with the following convention + // fParams[0] = isvol; + // fParams[1] = ifield; + // fParams[2] = fieldm; + // fParams[3] = tmaxfd; + // fParams[4] = stemax; + // fParams[5] = deemax; + // fParams[6] = epsil; + // fParams[7] = stmin; + const auto isvol = medium->GetParam(0); + const auto isxfld = medium->GetParam(1); + const auto sxmgmx = medium->GetParam(2); + const auto tmaxfd = medium->GetParam(3); + const auto stemax = medium->GetParam(4); + const auto deemax = medium->GetParam(5); + const auto epsil = medium->GetParam(6); + const auto stmin = medium->GetParam(7); + + matmgr.Medium(modulename, counter, medium->GetName(), counter, isvol, isxfld, sxmgmx, tmaxfd, stemax, deemax, epsil, stmin); + + // there will be new Material and Medium objects; fetch them + auto new_med = matmgr.getTGeoMedium(modulename, counter); + + // insert into cache + medium_ptr_mapping[medium] = new_med; + vol_->SetMedium(new_med); + counter++; + } + }; // end transformer lambda + + // a generic volume walker + std::function visit_volume; + visit_volume = [&](TGeoVolume* vol) -> void { + if (!vol) { + return; + } + + // call the transformer + transform_media(vol); + + // Recurse into daughters + const int nd = vol->GetNdaughters(); + for (int i = 0; i < nd; ++i) { + TGeoNode* node = vol->GetNode(i); + if (!node) { + continue; + } + TGeoVolume* child = node->GetVolume(); + if (!child) { + continue; + } + + visit_volume(child); + } + }; + + visit_volume(top_volume); +} + +void ExternalModule::ConstructGeometry() +{ + // JIT the geom builder hook + if (!initGeomBuilderHook()) { + LOG(error) << " Could not load geometry builder hook"; + return; + } + + // otherwise execute it and obtain pointer to top most module volume + auto module_top = mGeomHook(); + if (!module_top) { + LOG(error) << "No module found\n"; + return; + } + + remapMedia(const_cast(module_top)); + + // place it into the provided anchor volume (needs to exist) + auto anchor = gGeoManager->FindVolumeFast(mOptions.anchor_volume.c_str()); + if (!anchor) { + LOG(error) << "Anchor volume " << mOptions.anchor_volume << " not found. Aborting"; + return; + } + anchor->AddNode(const_cast(module_top), 1, const_cast(mOptions.placement)); +} + +bool ExternalModule::initGeomBuilderHook() +{ + if (mOptions.root_macro_file.size() > 0) { + LOG(info) << "Initializing the hook for geometry module building"; + auto expandedHookFileName = o2::utils::expandShellVarsInFileName(mOptions.root_macro_file); + if (std::filesystem::exists(expandedHookFileName)) { + // if this file exists we will compile the hook on the fly (the last one is an identifier --> maybe make it dependent on this class) + mGeomHook = o2::conf::GetFromMacro(mOptions.root_macro_file, "get_builder_hook_unchecked()", "function", "o2_passive_extmodule_builder"); + LOG(info) << "Hook initialized from file " << expandedHookFileName; + return true; + } + } + return false; +} + +} // namespace o2::passive \ No newline at end of file diff --git a/Steer/include/Steer/O2MCApplicationBase.h b/Steer/include/Steer/O2MCApplicationBase.h index 36966be9bde62..d61199baba0ae 100644 --- a/Steer/include/Steer/O2MCApplicationBase.h +++ b/Steer/include/Steer/O2MCApplicationBase.h @@ -58,6 +58,8 @@ class O2MCApplicationBase : public FairMCApplication typedef std::function TrackRefFcn; + void fixTGeoRuntimeShapes(); + protected: o2::conf::SimCutParams const& mCutParams; // reference to parameter system unsigned long long mStepCounter{0}; diff --git a/Steer/src/O2MCApplication.cxx b/Steer/src/O2MCApplication.cxx index 584598d350581..f832ab70ab121 100644 --- a/Steer/src/O2MCApplication.cxx +++ b/Steer/src/O2MCApplication.cxx @@ -37,6 +37,11 @@ #include "SimConfig/GlobalProcessCutSimParam.h" #include "DetectorsBase/GeometryManagerParam.h" #include +#include +#include +#include +#include +#include namespace o2 { @@ -209,10 +214,65 @@ bool O2MCApplicationBase::MisalignGeometry() gGeoManager->SetUseParallelWorldNav(true); } + // performs possible optimizations (shape replacements on the runtime geometry) + fixTGeoRuntimeShapes(); + // return original return value of misalignment procedure return true; } +void O2MCApplicationBase::fixTGeoRuntimeShapes() +{ + // Replace TGeo shapes by other ones for performance or other reasons. + // Should only affect runtime of simulation. + + // TODO: make this configurable via external JSON rules/macro + + // Also delete original shapes for memory reasons + + // We follow a visitor pattern on a geom hierarchy + // for now replace a TGeoTessellate by our own implementation + std::unordered_set volumes_visited; + std::unordered_set old_shape_pointers; + + std::function visit; + visit = [&](TGeoNode* node) -> void { + if (!node) { + return; + } + auto vol = node->GetVolume(); + if (volumes_visited.find(vol) != volumes_visited.end()) { + return; + } + volumes_visited.insert(vol); + + // transform the shape of this volume + auto shape = vol->GetShape(); + if (shape->IsA() == TGeoTessellated::Class()) { + auto tsl = static_cast(shape); + + // make a new O2Tessellated until ROOT has proper support for navigation in TGeoTessellated + std::cout << "Converting to O2Tessellated for vol " << vol->GetName() << "\n"; + auto replacement_shape = new o2::base::O2Tessellated(*tsl, false); + vol->SetShape(replacement_shape); + old_shape_pointers.insert(shape); + } + // other cases could come here + + for (int i = 0; i < vol->GetNdaughters(); ++i) { + auto child_node = vol->GetNode(i); + visit(child_node); + } + }; + + visit(gGeoManager->GetTopNode()); + + for (auto ptr : old_shape_pointers) { + delete ptr; + ptr = nullptr; + } +} + void O2MCApplicationBase::finishEventCommon() { LOG(info) << "This event/chunk did " << mStepCounter << " steps"; diff --git a/macro/build_geometry.C b/macro/build_geometry.C index 6b13f2eac2766..ccc3b13fe728d 100644 --- a/macro/build_geometry.C +++ b/macro/build_geometry.C @@ -63,6 +63,8 @@ #include #endif +#include + using Return = o2::base::Detector*; void finalize_geometry(FairRunSim* run); @@ -182,6 +184,18 @@ void build_geometry(FairRunSim* run = nullptr) } #endif + if (isActivated("EXT")) { + // EXAMPLE!! how to pick geometry generated from external (CAD) module via `O2_CADtoTGeo.py` + o2::passive::ExternalModuleOptions options; + options.root_macro_file = "PATH_TO_EXTERNAL_GEOM_MODULE/geom.C"; + options.anchor_volume = "barrel"; // hook this into barrel + auto rot = new TGeoCombiTrans(); + rot->RotateX(90); + rot->SetDy(30); // we need to compensate for a shift of barrel with respect to zero + options.placement = rot; + run->AddModule(new o2::passive::ExternalModule("FOO", "BAR", options)); + } + // the absorber if (isActivated("ABSO")) { // the frame structure to support other detectors diff --git a/scripts/geometry/O2_CADtoTGeo.py b/scripts/geometry/O2_CADtoTGeo.py new file mode 100644 index 0000000000000..d564cdc6124a8 --- /dev/null +++ b/scripts/geometry/O2_CADtoTGeo.py @@ -0,0 +1,602 @@ +#!/usr/bin/env python3 +""" +A Python script, doing a deep STEP/XCAF -> ROOT TGeo conversion. +For now, all CAD solids are simply meshed. The ROOT geometry is build as a C++ ROOT macro +and facet data is stored in binary form to keep disc space minimal. + +Generates (into --output-folder): + - geom.C (small ROOT macro) + - facets__.bin for each leaf logical volume (float32 triangles) + +Facet file format (little-endian): + uint32 nTriangles + then nTriangles * 9 * float32: + ax ay az bx by bz cx cy cz + +VOLNAME is a filename-safe version of the XCAF label name when available (e.g. "nut"), +and LID is the XCAF label entry (e.g. "0:1:1:7" -> "0_1_1_7") to keep filenames unique. + +Naming: + - C++ variable names stay based on XCAF label entry (e.g. 0:1:1:7) for uniqueness. + - ROOT object names (TGeoVolume / TGeoTessellated / TGeoVolumeAssembly) use the label's + human name when available (e.g. "nut", "rod-assembly"), falling back to the entry. + +Units: + - By default, the script tries to detect the STEP LENGTH unit by scanning the STEP file + header/contents (common patterns like .MILLI. / .CENTI. / .METRE. / INCH / FOOT). + - You can override with --step-unit {auto,mm,cm,m,in,ft}. TGeo expects cm. + +Author: + - Sandro Wenzel, CERN (02/2026) +""" + +import warnings +warnings.filterwarnings("ignore", message=".*all to deprecated function.*", category=DeprecationWarning) + +import argparse +import re +import struct +from pathlib import Path as _Path + +from OCC.Core.Bnd import Bnd_Box +from OCC.Core.BRepBndLib import brepbndlib +from OCC.Core.BRepMesh import BRepMesh_IncrementalMesh +from OCC.Core.BRep import BRep_Tool +from OCC.Core.TopLoc import TopLoc_Location +from OCC.Core.TopAbs import TopAbs_REVERSED +from OCC.Extend.TopologyUtils import TopologyExplorer + +from OCC.Core.STEPCAFControl import STEPCAFControl_Reader +from OCC.Core.TDocStd import TDocStd_Document +from OCC.Core.XCAFDoc import XCAFDoc_DocumentTool +from OCC.Core.IFSelect import IFSelect_RetDone + +from OCC.Core.TDF import TDF_Label, TDF_LabelSequence, TDF_Tool +from OCC.Core.TCollection import TCollection_AsciiString +from OCC.Core.gp import gp_Trsf + + +# ------------------------------- +# STEP/XCAF loading +# ------------------------------- + +def load_step_with_xcaf(path: str): + doc = TDocStd_Document("pythonocc-doc") + reader = STEPCAFControl_Reader() + reader.SetColorMode(True) + reader.SetNameMode(True) + reader.SetLayerMode(True) + + status = reader.ReadFile(path) + if status != IFSelect_RetDone: + raise RuntimeError(f"STEP read failed for: {path}") + + reader.Transfer(doc) + shape_tool = XCAFDoc_DocumentTool.ShapeTool(doc.Main()) + return doc, shape_tool + + +def label_id(label: TDF_Label) -> str: + s = TCollection_AsciiString() + TDF_Tool.Entry(label, s) + return s.ToCString() + + +def label_name(label: TDF_Label) -> str: + # Uses the XCAF/STEP name when present; can be empty. + try: + n = label.GetLabelName() + if n: + return str(n) + except Exception: + pass + return "" + + +# ------------------------------- +# Units +# ------------------------------- + +def step_unit_scale_to_cm(step_unit: str) -> float: + step_unit = (step_unit or "auto").lower() + if step_unit == "mm": + return 0.1 + if step_unit == "cm": + return 1.0 + if step_unit == "m": + return 100.0 + if step_unit == "in": + return 2.54 + if step_unit == "ft": + return 30.48 + raise ValueError(f"Unknown --step-unit {step_unit} (use auto, mm, cm, m, in, ft)") + + +def detect_step_length_unit(step_path: str) -> str: + """ + Heuristic unit detection by scanning STEP file text for common unit tokens. + This avoids relying on OCCT APIs that can vary across pythonOCC builds. + + Returns one of: mm, cm, m, in, ft. Defaults to mm if uncertain. + """ + p = _Path(step_path) + # STEP can be huge: read only the first few MB; units are near the header. + max_bytes = 4 * 1024 * 1024 + data = p.open("rb").read(max_bytes).decode("latin-1", errors="ignore").upper() + + if ".MILLI." in data: + return "mm" + if ".CENTI." in data: + return "cm" + if ".METRE." in data or ".METER." in data: + return "m" + if "INCH" in data: + return "in" + if "FOOT" in data or "FEET" in data: + return "ft" + + # Conservative default for mechanical CAD STEP is mm + return "mm" + + +# ------------------------------- +# Triangulation helpers +# ------------------------------- + +def _scale_triangles(triangles, s: float): + if s == 1.0: + return triangles + out = [] + for (a, b, c) in triangles: + out.append(( + (a[0] * s, a[1] * s, a[2] * s), + (b[0] * s, b[1] * s, b[2] * s), + (c[0] * s, c[1] * s, c[2] * s), + )) + return out + + +def triangulate_asbbox(shape, scale_to_cm: float = 1.0): + box = Bnd_Box() + brepbndlib.Add(shape, box) + xmin, ymin, zmin, xmax, ymax, zmax = box.Get() + + p000 = (xmin, ymin, zmin) + p001 = (xmin, ymin, zmax) + p010 = (xmin, ymax, zmin) + p011 = (xmin, ymax, zmax) + p100 = (xmax, ymin, zmin) + p101 = (xmax, ymin, zmax) + p110 = (xmax, ymax, zmin) + p111 = (xmax, ymax, zmax) + + triangles = [ + (p000, p100, p110), (p000, p110, p010), + (p001, p111, p101), (p001, p011, p111), + (p000, p101, p100), (p000, p001, p101), + (p010, p110, p111), (p010, p111, p011), + (p000, p010, p011), (p000, p011, p001), + (p100, p101, p111), (p100, p111, p110), + ] + return _scale_triangles(triangles, scale_to_cm) + + +def triangulate_CAD_solid(my_solid, meshparam, scale_to_cm: float = 1.0): + lin_defl = float(meshparam.get("lin_defl", 0.1)) + ang_defl = float(meshparam.get("ang_defl", 0.1)) + + parallel = True + try: + BRepMesh_IncrementalMesh(my_solid, lin_defl, False, ang_defl, bool(parallel)) + except TypeError: + BRepMesh_IncrementalMesh(my_solid, lin_defl, False, ang_defl) + + triangles = [] + for face in TopologyExplorer(my_solid).faces(): + loc = TopLoc_Location() + triangulation = BRep_Tool.Triangulation(face, loc) + if triangulation is None: + continue + + trsf = loc.Transformation() + reverse = (face.Orientation() == TopAbs_REVERSED) + + for i in range(1, triangulation.NbTriangles() + 1): + tri = triangulation.Triangle(i) + n1, n2, n3 = tri.Get() + + p1 = triangulation.Node(n1).Transformed(trsf) + p2 = triangulation.Node(n2).Transformed(trsf) + p3 = triangulation.Node(n3).Transformed(trsf) + + if reverse: + p2, p3 = p3, p2 + + triangles.append(( + (p1.X(), p1.Y(), p1.Z()), + (p2.X(), p2.Y(), p2.Z()), + (p3.X(), p3.Y(), p3.Z()), + )) + + return _scale_triangles(triangles, scale_to_cm) + + +# ------------------------------- +# Naming helpers +# ------------------------------- + +def sanitize_cpp_name(s: str) -> str: + safe = re.sub(r"[^0-9a-zA-Z]", "_", s) + if not safe: + safe = "x" + if not (safe[0].isalpha() or safe[0] == "_"): + safe = "_" + safe + return safe + + +def sanitize_filename(s: str) -> str: + safe = re.sub(r"[^0-9a-zA-Z]", "_", s) + return safe or "x" + + +# ------------------------------- +# Binary facet IO +# ------------------------------- + +def write_facets_bin(path: _Path, triangles): + path.parent.mkdir(parents=True, exist_ok=True) + with open(path, "wb") as f: + f.write(struct.pack(" str: + m = trsf.GetRotation().GetMatrix() + t = trsf.TranslationPart() + return f""" + Double_t {name}_m[9] = {{ + {m.Value(1,1)}, {m.Value(1,2)}, {m.Value(1,3)}, + {m.Value(2,1)}, {m.Value(2,2)}, {m.Value(2,3)}, + {m.Value(3,1)}, {m.Value(3,2)}, {m.Value(3,3)} + }}; + TGeoRotation *{name}_rot = new TGeoRotation(); + {name}_rot->SetMatrix({name}_m); + TGeoCombiTrans *{name} = new TGeoCombiTrans({t.X()*scale_to_cm}, {t.Y()*scale_to_cm}, {t.Z()*scale_to_cm}, {name}_rot); +""" + + +def emit_cpp_prelude() -> str: + return """#include +#include +#include +#include +#include +#include + +static void LoadFacets(const std::string& file, TGeoTessellated* solid, bool check=false) +{ + std::ifstream in(file, std::ios::binary); + if (!in) throw std::runtime_error("Cannot open facet file: " + file); + + uint32_t nTri = 0; + in.read(reinterpret_cast(&nTri), sizeof(nTri)); + if (!in) throw std::runtime_error("Bad facet header in: " + file); + + for (uint32_t i=0;i(v), sizeof(v)); + if (!in) throw std::runtime_error("Unexpected EOF in: " + file); + + solid->AddFacet(TGeoTessellated::Vertex_t(v[0],v[1],v[2]), + TGeoTessellated::Vertex_t(v[3],v[4],v[5]), + TGeoTessellated::Vertex_t(v[6],v[7],v[8])); + } + solid->CloseShape(check, true); +} +""" + + +def emit_materials_cpp() -> str: + return """ // Default material/medium (placeholder; can be replaced later) + TGeoMaterial *mat_Default = new TGeoMaterial("Default", 0., 0., 0.); + TGeoMedium *med_Default = new TGeoMedium("Default", 1, mat_Default); +""" + + +def emit_tessellated_cpp(lid: str, vol_display_name: str, facet_abspath: str, ntriangles: int) -> str: + safe = sanitize_cpp_name(lid) + shape_name = vol_display_name if vol_display_name else lid + + if ntriangles <= 0: + out = [] + out.append(f' TGeoBBox *solid_{safe} = new TGeoBBox("{shape_name}", 0.001, 0.001, 0.001);') + out.append(f' TGeoVolume *vol_{safe} = new TGeoVolume("{shape_name}", solid_{safe}, med_Default);') + return "\n".join(out) + + out = [] + out.append(f' TGeoTessellated *solid_{safe} = new TGeoTessellated("{shape_name}", {ntriangles});') + out.append(f' LoadFacets("{facet_abspath}", solid_{safe}, check);') + out.append(f' TGeoVolume *vol_{safe} = new TGeoVolume("{shape_name}", solid_{safe}, med_Default);') + return "\n".join(out) + + +def emit_assembly_cpp(lid: str, asm_display_name: str) -> str: + safe = sanitize_cpp_name(lid) + name = asm_display_name if asm_display_name else lid + return f' TGeoVolumeAssembly *asm_{safe} = new TGeoVolumeAssembly("{name}");' + + +# ------------------------------- +# Definition graph extraction +# ------------------------------- + +logical_volumes = {} # def_lid -> triangles +def_names = {} # def_lid -> human display name (may be "") +assemblies = set() # def_lid +placements = [] # (parent_def_lid, child_def_lid, gp_Trsf local) +top_defs = set() # top definition lids +visited_defs = set() # expanded defs + + +def cpp_var_for_def(lid: str) -> str: + safe = sanitize_cpp_name(lid) + return f"asm_{safe}" if lid in assemblies else f"vol_{safe}" + + +def expand_definition(def_label: TDF_Label, shape_tool, meshparam=None, scale_to_cm: float = 1.0): + def_lid = label_id(def_label) + if def_lid in visited_defs: + return + visited_defs.add(def_lid) + + nm = label_name(def_label) + if nm and def_lid not in def_names: + def_names[def_lid] = nm + elif def_lid not in def_names: + def_names[def_lid] = "" + + children = TDF_LabelSequence() + shape_tool.GetComponents(def_label, children) + has_children = children.Length() > 0 + + if has_children or shape_tool.IsAssembly(def_label): + assemblies.add(def_lid) + + for i in range(children.Length()): + child = children.Value(i + 1) + if shape_tool.IsReference(child): + referred = TDF_Label() + shape_tool.GetReferredShape(child, referred) + child_def_lid = label_id(referred) + + loc = shape_tool.GetLocation(child) + trsf = loc.Transformation() + placements.append((def_lid, child_def_lid, trsf)) + + expand_definition(referred, shape_tool, meshparam=meshparam, scale_to_cm=scale_to_cm) + else: + child_def_lid = label_id(child) + placements.append((def_lid, child_def_lid, gp_Trsf())) + expand_definition(child, shape_tool, meshparam=meshparam, scale_to_cm=scale_to_cm) + return + + if shape_tool.IsSimpleShape(def_label): + if def_lid not in logical_volumes: + shape = shape_tool.GetShape(def_label) + do_meshing = (meshparam is not None) and meshparam.get("do_meshing", None) is True + logical_volumes[def_lid] = triangulate_CAD_solid(shape, meshparam=meshparam, scale_to_cm=scale_to_cm) if do_meshing else triangulate_asbbox(shape, scale_to_cm=scale_to_cm) + return + + assemblies.add(def_lid) + + +def extract_graph(step_path: str, meshparam=None, scale_to_cm: float = 1.0): + global logical_volumes, def_names, assemblies, placements, top_defs, visited_defs + logical_volumes = {} + def_names = {} + assemblies = set() + placements = [] + top_defs = set() + visited_defs = set() + + doc, shape_tool = load_step_with_xcaf(step_path) + + roots = TDF_LabelSequence() + shape_tool.GetFreeShapes(roots) + + for i in range(roots.Length()): + root = roots.Value(i + 1) + if shape_tool.IsReference(root): + ref = TDF_Label() + shape_tool.GetReferredShape(root, ref) + top_defs.add(label_id(ref)) + expand_definition(ref, shape_tool, meshparam=meshparam, scale_to_cm=scale_to_cm) + else: + top_defs.add(label_id(root)) + expand_definition(root, shape_tool, meshparam=meshparam, scale_to_cm=scale_to_cm) + + return doc, shape_tool + + +# ------------------------------- +# ROOT macro emission +# ------------------------------- + +def emit_placement_cpp(parent_def: str, child_def: str, trsf: gp_Trsf, copy_no: int, scale_to_cm: float) -> str: + parent_cpp = cpp_var_for_def(parent_def) + child_cpp = cpp_var_for_def(child_def) + tr_name = f"tr_{sanitize_cpp_name(parent_def)}_{sanitize_cpp_name(child_def)}_{copy_no}" + return trsf_to_tgeo(trsf, tr_name, scale_to_cm) + f" {parent_cpp}->AddNode({child_cpp}, {copy_no}, {tr_name});\n" + + +def emit_root_macro(step_path: str, out_folder: _Path, meshparam=None, step_unit: str = "auto"): + if (step_unit or "auto").lower() == "auto": + detected = detect_step_length_unit(step_path) + scale_to_cm = step_unit_scale_to_cm(detected) + print(f"Detected STEP length unit: {detected} (scale to cm = {scale_to_cm})") + else: + scale_to_cm = step_unit_scale_to_cm(step_unit) + print(f"Using overridden STEP length unit: {step_unit} (scale to cm = {scale_to_cm})") + + extract_graph(step_path, meshparam=meshparam, scale_to_cm=scale_to_cm) + + out_folder = out_folder.expanduser().resolve() + out_folder.mkdir(parents=True, exist_ok=True) + + facet_files = {} # def_lid -> absolute path string + for lid, tris in logical_volumes.items(): + disp = def_names.get(lid, "") + volname = sanitize_filename(disp) if disp else "vol" + lidname = sanitize_filename(lid) + fname = f"facets_{volname}_{lidname}.bin" + fpath = (out_folder / fname).resolve() + write_facets_bin(fpath, tris) + facet_files[lid] = str(fpath).replace("\\", "\\\\") # C++ string literal safety + + cpp = [] + cpp.append(emit_cpp_prelude()) + + cpp.append("TGeoVolume* build(bool check=true) {") + cpp.append(' if (!gGeoManager) { throw std::runtime_error("gGeoManager is null. Call build_and_export() or create a TGeoManager first."); }') + cpp.append(emit_materials_cpp()) + + for lid in logical_volumes.keys(): + ntriangles = len(logical_volumes[lid]) + cpp.append(emit_tessellated_cpp(lid, def_names.get(lid, ""), facet_files[lid], ntriangles)) + + for lid in sorted(assemblies): + cpp.append(emit_assembly_cpp(lid, def_names.get(lid, ""))) + + for idx, (parent, child, trsf) in enumerate(placements, start=1): + cpp.append(emit_placement_cpp(parent, child, trsf, idx, scale_to_cm)) + + if len(top_defs) == 1: + top = next(iter(top_defs)) + cpp.append(f" return {cpp_var_for_def(top)};") + else: + cpp.append(' TGeoVolumeAssembly *asm_WORLD = new TGeoVolumeAssembly("WORLD");') + for i, node in enumerate(sorted(top_defs), start=1): + cpp.append(f" asm_WORLD->AddNode({cpp_var_for_def(node)}, {i});") + cpp.append(" return asm_WORLD;") + + cpp.append("}") + + # exports a function allowing to export the geometry to TGeo file + cpp.append('void build_and_export(const char* out_root = "geom.root", bool check=true) {') + cpp.append(' if (!gGeoManager) { new TGeoManager("geom","geom"); }') + cpp.append(' TGeoVolume* top = build(check);') + cpp.append(' gGeoManager->SetTopVolume(top);') + cpp.append(' gGeoManager->CloseGeometry();') + cpp.append(' gGeoManager->CheckOverlaps();') + cpp.append(' gGeoManager->Export(out_root);') + cpp.append('}') + + # exports a function to get get hold of the builder function in ALICE O2 + cpp.append('std::function get_builder_hook_checked() {') + cpp.append(' return []() { return build(true); };') + cpp.append('}') + # exports a function to get get hold of the builder function in ALICE O2 + cpp.append('std::function get_builder_hook_unchecked() {') + cpp.append(' return []() { return build(false); };') + cpp.append('}') + + return "\n".join(cpp) + + +# ------------------------------- +# Geometry Tree printing (debug) +# ------------------------------- + +def label_entry(label): + s = TCollection_AsciiString() + TDF_Tool.Entry(label, s) + return s.ToCString() + + +def traverse_print(label, shape_tool, depth=0): + indent = " " * depth + name = label.GetLabelName() + entry = label_entry(label) + print(f"{indent}- {name} =>[{entry}]") + + if shape_tool.IsReference(label): + ref_label = TDF_Label() + shape_tool.GetReferredShape(label, ref_label) + traverse_print(ref_label, shape_tool, depth + 1) + return + + children = TDF_LabelSequence() + shape_tool.GetComponents(label, children) + if children.Length() > 0 or shape_tool.IsAssembly(label): + for i in range(children.Length()): + traverse_print(children.Value(i + 1), shape_tool, depth + 1) + return + + if shape_tool.IsSimpleShape(label): + shape = shape_tool.GetShape(label) + print(f"{indent} [LogicalShape id={id(shape)}]") + + +def print_geom(step_file): + print(f"Printing GEOM hierarchy for {step_file}") + doc, shape_tool = load_step_with_xcaf(step_file) + roots = TDF_LabelSequence() + shape_tool.GetFreeShapes(roots) + for i in range(roots.Length()): + traverse_print(roots.Value(i + 1), shape_tool) + + +# ------------------------------- +# CLI +# ------------------------------- + +def main(): + ap = argparse.ArgumentParser(description="Convert STEP/XCAF to ROOT TGeo macro, facets in per-volume binary files.") + ap.add_argument("step", help="Input STEP file") + ap.add_argument("-o", "--out", default="geom.C", help="Output ROOT macro file name (default: geom.C)") + ap.add_argument("--output-folder", default="./", help="Output folder for macro + facet files") + ap.add_argument("--out-path", default=None, help="(deprecated) Alias for --output-folder") + ap.add_argument("--mesh", action="store_true", help="Use full BRepMesh triangulation instead of bounding boxes") + ap.add_argument("--print-tree", action="store_true", help="Just prints the geometry tree") + ap.add_argument("--mesh-prec", default=0.1, help="meshing precision. lower --> slower") + ap.add_argument("--step-unit", default="auto", choices=["auto", "mm", "cm", "m", "in", "ft"], help="STEP length unit override (default: auto-detect)") + + args = ap.parse_args() + + step_path = str(_Path(args.step).expanduser().resolve()) + if args.print_tree: + print_geom(step_path) + return + + out_folder = _Path(args.output_folder) + if args.out_path is not None: + out_folder = _Path(args.out_path) + + meshparam = {"do_meshing": args.mesh, "lin_defl": args.mesh_prec, "ang_defl": args.mesh_prec} + + out_folder = out_folder.expanduser().resolve() + out_folder.mkdir(parents=True, exist_ok=True) + + out_macro = (out_folder / _Path(args.out).name).resolve() + code = emit_root_macro(step_path, out_folder, meshparam=meshparam, step_unit=args.step_unit) + out_macro.write_text(code) + + print(f"Wrote ROOT macro: {out_macro}") + print(f"Wrote facet files into: {out_folder}") + print("In ROOT you can do:") + print(f" root -l {out_macro}") + print(' build_and_export("geom.root");') + + +if __name__ == "__main__": + main() diff --git a/scripts/geometry/README.md b/scripts/geometry/README.md new file mode 100644 index 0000000000000..4fb2d1ec610d4 --- /dev/null +++ b/scripts/geometry/README.md @@ -0,0 +1,27 @@ +This is the tool O2_CADtoTGeo.py which translates from geometries in STEP format (CAD export) to +TGeo. + +To use the tool, setup a conda environment with python-occ core installed. +The following should work on standard linux x86: + +``` +# -) download miniconda into $HOME/miniconda (if not already done) +if [ ! -d $HOME/miniconda ]; then + curl -fsSL https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-aarch64.sh -o miniconda.sh + bash miniconda.sh -b -p $HOME/miniconda +fi + +# -) source conda into the environment (in every shell you want to use this) +source $HOME/miniconda/etc/profile.d/conda.sh + +# -) Create an OCC environment (for OpenCacade) +conda create -n occ python=3.10 -y +conda activate occ + +# 3) Install OpenCascade Python bindings +conda install -c conda-forge pythonocc-core -y + +# 4) Run the tool, e.g. +conda activate occ +python PATH_TO_ALICEO2_SOURCES/scripts/geometry/O2_CADtoTGeo.py --help +``` \ No newline at end of file diff --git a/scripts/geometry/simulating_CAD_modules.md b/scripts/geometry/simulating_CAD_modules.md new file mode 100644 index 0000000000000..ccd59a3523781 --- /dev/null +++ b/scripts/geometry/simulating_CAD_modules.md @@ -0,0 +1,72 @@ +# ALICE-O2 GEANT Simulation of CAD Geometries + +These are a few notes related to the inclusion of external (CAD-described) detector modules into the O2 simulation framework. + +## Description of the Workflow + +In principle, such integration is now possible and requires the following steps: + +1. The CAD geometry needs to be exported to STEP format and must contain only the final geometry (no artificial eta-cut elements). Ideally, the geometry should be fully hierarchical with proper solid reuse. The solids should retain their proper surface representation for detailed analysis. + +2. A tool `O2-CADtoTGeo.py` is provided to convert the STEP geometry into TGeo format. The tool is part of AliceO2 and is based on Python bindings (OCC) for OpenCascade. The tool can be used as follows: + + ```bash + python O2-CADtoTGeo.py STEP_FILE --output-folder my_detector -o geom.C --mesh \ + --mesh-prec 0.2 + ``` + + This will create a ROOT macro file `geom.C` containing the geometry description in ROOT format, as well as several binary files describing the TGeo solids. The `geom.C` file can either be used directly in ROOT to inspect the geometry or be provided to ALICE-O2 for inclusion in the geometry. + +3. Introduction of materials/media in the file `geom.C`. Currently, the file `geom.C` needs to be patched or edited to properly include `TGeoMaterial`/`TGeoMedium` definitions and connect them to the relevant `TGeoVolume` objects. At present, every solid has the same dummy material attached, which is not realistic. It may be a good idea to create a new file `geom_withMaterials.C`, which differs from `geom.C` by the addition of these material definitions. + +4. Once the conversion is complete, the module can be inserted into the O2 geometry via the `ExternalModule` class. To do so, follow this pattern in `build_geometry.C`: + + ```cpp + if (isActivated("EXT")) { + o2::passive::ExternalModuleOptions options; + options.root_macro_file = "PATH_TO_MY_DETECTOR/my_detector/geom_withMaterials.C"; + options.anchor_volume = "barrel"; // hook this into barrel + auto rot = new TGeoCombiTrans(); + rot->RotateX(90); + rot->SetDy(30); // compensate for a shift of the barrel with respect to zero + options.placement = rot; + run->AddModule(new o2::passive::ExternalModule("A3VTX", "ALICE3 beam pipe", options)); + } + ``` + +5. Create a custom detector geometry list file `my_det.json` in JSON format that includes the external detector (and any other required components, such as the L3 magnet in this example): + + ```json + { + "MY_DET": [ + "EXT", + "MAG" + ] + } + ``` + +6. Run the Geant simulation with: + + ```bash + o2-sim --detectorList MY_DET:my_det.json -g pythia8pp .... + ``` + +## Known Limitations + +- The `O2-CADtoTGeo.py` tool currently converts geometries only into TGeoTessellated solids. This may be suboptimal for primitive shapes or only an approximation for shapes with exact second-order surfaces (e.g., tubes). The precision (and therefore the number of surface triangles) can be controlled with the `--mesh-prec` parameter. The smaller the value, the more precise the mesh. + +- Meshed solids created by the tool may have issues, such as topological errors or non-watertight surfaces. It is planned to include "healing" steps via additional processing with well-known geometry kernels (e.g., CGAL). + +- The tool does not currently export materials or TGeoMedia. These must be inserted or edited manually. It is planned to make this process more automatic and user-friendly. + +- The Python tool requires the OCC Python module, which is currently not part of our software distribution. We have found it most practical to run the tool in a separate conda environment (fully decoupled from the ALICE software stack). + +- The tool currently generates a `geom.C` macro file. In the future, it may be possible to directly create an in-memory TGeo representation for deeper integration. + +- Currently, only passive modules can be integrated. Treatment of sensitive volumes or parts will be addressed in a future step. + +## Software Installation + +- The simulation must be run in the standard O2 environment built with alibuild. + +- The CAD conversion tool must currently be run in a dedicated conda environment, as described in scripts/geometry/README.md in the AliceO2 source code. \ No newline at end of file From 17d865e646a90d7e19ae43424d2404dd163885ec Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Fri, 13 Feb 2026 16:34:50 +0100 Subject: [PATCH 101/133] Fix Header info forwarding --- Generators/src/GeneratorHybrid.cxx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Generators/src/GeneratorHybrid.cxx b/Generators/src/GeneratorHybrid.cxx index 2a13f9876e717..f853b772e3cd3 100644 --- a/Generators/src/GeneratorHybrid.cxx +++ b/Generators/src/GeneratorHybrid.cxx @@ -420,6 +420,7 @@ bool GeneratorHybrid::importParticles() mMCEventHeader.clearInfo(); if (mCocktailMode) { // in cocktail mode we need to merge the particles from the different generators + bool baseGen = true; // first generator of the cocktail is used as reference to update the event header information for (auto subIndex : subGenIndex) { LOG(info) << "Importing particles for task " << subIndex; auto subParticles = gens[subIndex]->getParticles(); @@ -441,8 +442,10 @@ bool GeneratorHybrid::importParticles() } mParticles.insert(mParticles.end(), subParticles.begin(), subParticles.end()); - // fetch the event Header information from the underlying generator - gens[subIndex]->updateHeader(&mMCEventHeader); + if (baseGen) { + gens[subIndex]->updateHeader(&mMCEventHeader); + baseGen = false; + } mInputTaskQueue.push(subIndex); mTasksStarted++; } @@ -481,7 +484,9 @@ bool GeneratorHybrid::importParticles() void GeneratorHybrid::updateHeader(o2::dataformats::MCEventHeader* eventHeader) { if (eventHeader) { - // we forward the original header information if any + // Forward the base class fields from FairMCEventHeader + static_cast(*eventHeader) = static_cast(mMCEventHeader); + // Copy the key-value store info eventHeader->copyInfoFrom(mMCEventHeader); // put additional information about From 8361c429fd87b9d0cb215b1ac83f5ee1fd162269 Mon Sep 17 00:00:00 2001 From: ddobrigk Date: Sat, 14 Feb 2026 09:47:17 +0100 Subject: [PATCH 102/133] Add option to compress out non-dEdx info in TrackQA table (#15045) * Add compress-out option for non-dEdx info in TrackQA * Add compress-out option for non-dEdx info in TrackQA * Retain also TPC-only tracks used by svtx, strangeness tracking * Please consider the following formatting changes --------- Co-authored-by: ALICE Action Bot --- .../AODProducerWorkflowSpec.h | 1 + Detectors/AOD/src/AODProducerWorkflowSpec.cxx | 34 ++++++++++--------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h index 2d16f343dc1eb..2c58db42ed856 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h @@ -238,6 +238,7 @@ class AODProducerWorkflowDPL : public Task bool mPropTracks{false}; bool mPropMuons{false}; float mTrackQCKeepGlobalTracks{false}; + float mTrackQCRetainOnlydEdx{false}; float mTrackQCFraction{0.00}; int64_t mTrackQCNTrCut{4}; float mTrackQCDCAxy{3.}; diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index be169ad4be19d..852419a9895eb 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -363,10 +363,10 @@ void AODProducerWorkflowDPL::addToTracksQATable(TracksQACursorType& tracksQACurs { tracksQACursor( trackQAInfoHolder.trackID, - truncateFloatFraction(trackQAInfoHolder.tpcTime0, mTPCTime0), + mTrackQCRetainOnlydEdx ? 0.0f : truncateFloatFraction(trackQAInfoHolder.tpcTime0, mTPCTime0), truncateFloatFraction(trackQAInfoHolder.tpcdEdxNorm, mTrackSignal), - trackQAInfoHolder.tpcdcaR, - trackQAInfoHolder.tpcdcaZ, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.tpcdcaR, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.tpcdcaZ, trackQAInfoHolder.tpcClusterByteMask, trackQAInfoHolder.tpcdEdxMax0R, trackQAInfoHolder.tpcdEdxMax1R, @@ -376,18 +376,18 @@ void AODProducerWorkflowDPL::addToTracksQATable(TracksQACursorType& tracksQACurs trackQAInfoHolder.tpcdEdxTot1R, trackQAInfoHolder.tpcdEdxTot2R, trackQAInfoHolder.tpcdEdxTot3R, - trackQAInfoHolder.dRefContY, - trackQAInfoHolder.dRefContZ, - trackQAInfoHolder.dRefContSnp, - trackQAInfoHolder.dRefContTgl, - trackQAInfoHolder.dRefContQ2Pt, - trackQAInfoHolder.dRefGloY, - trackQAInfoHolder.dRefGloZ, - trackQAInfoHolder.dRefGloSnp, - trackQAInfoHolder.dRefGloTgl, - trackQAInfoHolder.dRefGloQ2Pt, - trackQAInfoHolder.dTofdX, - trackQAInfoHolder.dTofdZ); + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefContY, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefContZ, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefContSnp, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefContTgl, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefContQ2Pt, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefGloY, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefGloZ, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefGloSnp, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefGloTgl, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefGloQ2Pt, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dTofdX, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dTofdZ); } template @@ -499,7 +499,7 @@ void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, float weight = 0; static std::uniform_real_distribution<> distr(0., 1.); - bool writeQAData = o2::math_utils::Tsallis::downsampleTsallisCharged(data.getTrackParam(trackIndex).getPt(), mTrackQCFraction, mSqrtS, weight, distr(mGenerator)) || (src != GIndex::TPC && mTrackQCKeepGlobalTracks); + bool writeQAData = o2::math_utils::Tsallis::downsampleTsallisCharged(data.getTrackParam(trackIndex).getPt(), mTrackQCFraction, mSqrtS, weight, distr(mGenerator)) || ((src != GIndex::TPC || mGIDUsedBySVtx.find(trackIndex) != mGIDUsedBySVtx.end() || mGIDUsedByStr.find(trackIndex) != mGIDUsedByStr.end()) && mTrackQCKeepGlobalTracks); auto extraInfoHolder = processBarrelTrack(collisionID, collisionBC, trackIndex, data, bcsMap); if (writeQAData) { @@ -1720,6 +1720,7 @@ void AODProducerWorkflowDPL::init(InitContext& ic) } } mTrackQCKeepGlobalTracks = ic.options().get("trackqc-keepglobaltracks"); + mTrackQCRetainOnlydEdx = ic.options().get("trackqc-retainonlydedx"); mTrackQCFraction = ic.options().get("trackqc-fraction"); mTrackQCNTrCut = ic.options().get("trackqc-NTrCut"); mTrackQCDCAxy = ic.options().get("trackqc-tpc-dca"); @@ -3356,6 +3357,7 @@ DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, boo ConfigParamSpec{"propagate-muons", VariantType::Bool, false, {"Propagate muons to IP"}}, ConfigParamSpec{"thin-tracks", VariantType::Bool, false, {"Produce thinned track tables"}}, ConfigParamSpec{"trackqc-keepglobaltracks", VariantType::Bool, false, {"Always keep TrackQA for global tracks"}}, + ConfigParamSpec{"trackqc-retainonlydedx", VariantType::Bool, false, {"Keep only dEdx information, zero out everything else"}}, ConfigParamSpec{"trackqc-fraction", VariantType::Float, float(0.1), {"Fraction of tracks to QC"}}, ConfigParamSpec{"trackqc-NTrCut", VariantType::Int64, 4L, {"Minimal length of the track - in amount of tracklets"}}, ConfigParamSpec{"trackqc-tpc-dca", VariantType::Float, 3.f, {"Keep TPC standalone track with this DCAxy to the PV"}}, From b87ed891b1f785c042f7cc9f2d490d3f5a6dd3f1 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 15 Feb 2026 14:29:48 +0100 Subject: [PATCH 103/133] DPL Examples: use the new completion policy for parallel processing (#15059) This demonstrates how the new policy can be used in conjunction with wildcards in order to simplify parallelism based on the subSpecification. --- .../Framework/CompletionPolicyHelpers.h | 1 - .../TestWorkflows/src/o2ParallelWorkflow.cxx | 77 ++++++++++++------- 2 files changed, 48 insertions(+), 30 deletions(-) diff --git a/Framework/Core/include/Framework/CompletionPolicyHelpers.h b/Framework/Core/include/Framework/CompletionPolicyHelpers.h index 9fce626854e5b..09ea8b7ea6b61 100644 --- a/Framework/Core/include/Framework/CompletionPolicyHelpers.h +++ b/Framework/Core/include/Framework/CompletionPolicyHelpers.h @@ -11,7 +11,6 @@ #ifndef O2_FRAMEWORK_COMPLETIONPOLICYHELPERS_H_ #define O2_FRAMEWORK_COMPLETIONPOLICYHELPERS_H_ -#include "Framework/ChannelSpec.h" #include "Framework/CompletionPolicy.h" #include "Headers/DataHeader.h" diff --git a/Framework/TestWorkflows/src/o2ParallelWorkflow.cxx b/Framework/TestWorkflows/src/o2ParallelWorkflow.cxx index 841f4a8f2b9bd..bdc08ad45ea24 100644 --- a/Framework/TestWorkflows/src/o2ParallelWorkflow.cxx +++ b/Framework/TestWorkflows/src/o2ParallelWorkflow.cxx @@ -9,7 +9,12 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include "Framework/ConcreteDataMatcher.h" #include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "Framework/InputRecordWalker.h" +#include "Framework/Logger.h" #include #include @@ -29,13 +34,16 @@ void customize(std::vector& workflowOptions) ConfigParamSpec{"3-layer-pipelining", VariantType::Int, 1, {timeHelp}}); } +void customize(std::vector& policies) +{ + policies = { + CompletionPolicyHelpers::consumeWhenPastOldestPossibleTimeframe("merger-policy", [](auto const&) -> bool { return true; })}; +} + #include "Framework/runDataProcessing.h" #include "Framework/DataProcessorSpec.h" #include "Framework/DataSpecUtils.h" #include "Framework/ParallelContext.h" -#include "Framework/ControlService.h" - -#include "Framework/Logger.h" #include @@ -43,22 +51,24 @@ using DataHeader = o2::header::DataHeader; DataProcessorSpec templateProcessor() { - return DataProcessorSpec{"some-processor", { - InputSpec{"x", "TST", "A", 0, Lifetime::Timeframe}, - }, - { + return DataProcessorSpec{.name = "some-processor", + .inputs = { + InputSpec{"x", "TST", "A", 0, Lifetime::Timeframe}, + }, + .outputs = { OutputSpec{"TST", "P", 0, Lifetime::Timeframe}, }, // The producer is stateful, we use a static for the state in this // particular case, but a Singleton or a captured new object would // work as well. - AlgorithmSpec{[](InitContext& setup) { + .algorithm = AlgorithmSpec{[](InitContext& setup) { srand(setup.services().get().index1D()); return [](ProcessingContext& ctx) { // Create a single output. size_t index = ctx.services().get().index1D(); - auto& aData = ctx.outputs().make( + auto& i = ctx.outputs().make( Output{"TST", "P", static_cast(index)}, 1); + i[0] = index; std::this_thread::sleep_for(std::chrono::seconds(rand() % 5)); }; }}}; @@ -86,34 +96,43 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) outputSpecs.emplace_back("TST", "A", ssi); } - workflow.push_back(DataProcessorSpec{"reader", {}, outputSpecs, AlgorithmSpec{[jobs](InitContext& initCtx) { - return [jobs](ProcessingContext& ctx) { - for (size_t ji = 0; ji < jobs; ++ji) { - ctx.outputs().make(Output{"TST", "A", static_cast(ji)}, - 1); - } - }; - }}}); + workflow.push_back(DataProcessorSpec{ + .name = "reader", + .outputs = outputSpecs, + .algorithm = AlgorithmSpec{[jobs](InitContext& initCtx) { + return [jobs](ProcessingContext& ctx) { + static int count = 0; + for (size_t ji = 0; ji < jobs; ++ji) { + int& i = ctx.outputs().make(Output{"TST", "A", static_cast(ji)}); + i = count * 100 + ji; + } + count++; + }; + }}}); workflow.push_back(timePipeline(DataProcessorSpec{ - "merger", - mergeInputs(InputSpec{"x", "TST", "P"}, - jobs, - [](InputSpec& input, size_t index) { - DataSpecUtils::updateMatchingSubspec(input, index); - }), - {OutputSpec{{"out"}, "TST", "M"}}, - AlgorithmSpec{[](InitContext& setup) { + .name = "merger", + .inputs = {InputSpec{"all", ConcreteDataTypeMatcher{"TST", "P"}}}, + .outputs = {OutputSpec{{"out"}, "TST", "M"}}, + .algorithm = AlgorithmSpec{[](InitContext& setup) { return [](ProcessingContext& ctx) { + LOGP(info, "Run"); + for (const auto& input : o2::framework::InputRecordWalker(ctx.inputs())) { + if (input.header == nullptr) { + LOGP(error, "Missing header"); + continue; + } + int record = *(int*)input.payload; + LOGP(info, "Record {}", record); + } ctx.outputs().make(OutputRef("out", 0), 1); }; }}}, stages)); workflow.push_back(DataProcessorSpec{ - "writer", - {InputSpec{"x", "TST", "M"}}, - {}, - AlgorithmSpec{[](InitContext& setup) { + .name = "writer", + .inputs = {InputSpec{"x", "TST", "M"}}, + .algorithm = AlgorithmSpec{[](InitContext& setup) { return [](ProcessingContext& ctx) { }; }}}); From cbace3965ebdcb915f78a5535d447571ae056a68 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sun, 15 Feb 2026 21:48:06 +0100 Subject: [PATCH 104/133] GPU TPC: Assume more sector track hits for low field data --- GPU/GPUTracking/DataTypes/GPUMemorySizeScalers.h | 6 ++++-- GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/GPU/GPUTracking/DataTypes/GPUMemorySizeScalers.h b/GPU/GPUTracking/DataTypes/GPUMemorySizeScalers.h index ff8abdc1a491e..067a11817d7ac 100644 --- a/GPU/GPUTracking/DataTypes/GPUMemorySizeScalers.h +++ b/GPU/GPUTracking/DataTypes/GPUMemorySizeScalers.h @@ -43,7 +43,9 @@ struct GPUMemorySizeScalers { double tpcClustersPerPeak = 0.9; double tpcStartHitsPerHit = 0.08; double tpcTrackletsPerStartHit = 0.8; + double tpcTrackletsPerStartHitLowField = 0.85; double tpcTrackletHitsPerHit = 5; + double tpcTrackletHitsPerHitLowField = 7; double tpcSectorTracksPerHit = 0.02; double tpcSectorTrackHitsPerHit = 0.8; double tpcSectorTrackHitsPerHitWithRejection = 1.0; @@ -80,8 +82,8 @@ struct GPUMemorySizeScalers { inline size_t NTPCClusters(size_t tpcDigits, bool perSector = false) { return getValue(perSector ? tpcMaxSectorClusters : tpcMaxClusters, (conservative ? 1.0 : tpcClustersPerPeak) * NTPCPeaks(tpcDigits, perSector)); } inline size_t NTPCStartHits(size_t tpcHits) { return getValue(tpcMaxStartHits, tpcHits * tpcStartHitsPerHit); } inline size_t NTPCRowStartHits(size_t tpcHits) { return getValue(tpcMaxRowStartHits, std::max(NTPCStartHits(tpcHits) * (tpcHits < 30000000 ? 20 : 12) / GPUCA_ROW_COUNT, tpcMinRowStartHits)); } - inline size_t NTPCTracklets(size_t tpcHits) { return getValue(tpcMaxTracklets, NTPCStartHits(tpcHits) * tpcTrackletsPerStartHit); } - inline size_t NTPCTrackletHits(size_t tpcHits) { return getValue(tpcMaxTrackletHits, hitOffset + tpcHits * tpcTrackletHitsPerHit); } + inline size_t NTPCTracklets(size_t tpcHits, bool lowField) { return getValue(tpcMaxTracklets, NTPCStartHits(tpcHits) * (lowField ? tpcTrackletsPerStartHitLowField : tpcTrackletsPerStartHit)); } + inline size_t NTPCTrackletHits(size_t tpcHits, bool lowField) { return getValue(tpcMaxTrackletHits, hitOffset + tpcHits * (lowField ? tpcTrackletHitsPerHitLowField : tpcTrackletHitsPerHit)); } inline size_t NTPCSectorTracks(size_t tpcHits) { return getValue(tpcMaxSectorTracks, tpcHits * tpcSectorTracksPerHit); } inline size_t NTPCSectorTrackHits(size_t tpcHits, uint8_t withRejection = 0) { return getValue(tpcMaxSectorTrackHits, tpcHits * (withRejection ? tpcSectorTrackHitsPerHitWithRejection : tpcSectorTrackHitsPerHit)); } inline size_t NTPCMergedTracks(size_t tpcSectorTracks) { return getValue(tpcMaxMergedTracks, tpcSectorTracks * (conservative ? 1.0 : tpcMergedTrackPerSectorTrack)); } diff --git a/GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx b/GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx index 03931f73a4a12..506f90c55abf3 100644 --- a/GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx +++ b/GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx @@ -144,8 +144,9 @@ void GPUTPCTracker::SetMaxData(const GPUTrackingInOutPointers& io) } else { mNMaxRowStartHits = mRec->MemoryScalers()->NTPCRowStartHits(mData.NumberOfHits()); } - mNMaxTracklets = mRec->MemoryScalers()->NTPCTracklets(mData.NumberOfHits()); - mNMaxRowHits = mRec->MemoryScalers()->NTPCTrackletHits(mData.NumberOfHits()); + bool lowField = CAMath::Abs(Param().bzkG) < 4; + mNMaxTracklets = mRec->MemoryScalers()->NTPCTracklets(mData.NumberOfHits(), lowField); + mNMaxRowHits = mRec->MemoryScalers()->NTPCTrackletHits(mData.NumberOfHits(), lowField); mNMaxTracks = mRec->MemoryScalers()->NTPCSectorTracks(mData.NumberOfHits()); if (io.clustersNative) { uint32_t sectorOffset = mISector >= GPUCA_NSECTORS / 2 ? GPUCA_NSECTORS / 2 : 0; From 23a36dfe2517df697788634b9a64088e86c0822b Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Mon, 16 Feb 2026 14:10:23 +0100 Subject: [PATCH 105/133] Add Kine publisher test (#15058) --- prodtests/full_system_test.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/prodtests/full_system_test.sh b/prodtests/full_system_test.sh index 82021d6c65e63..07ccdf01d4566 100755 --- a/prodtests/full_system_test.sh +++ b/prodtests/full_system_test.sh @@ -168,6 +168,13 @@ taskwrapper collcontext.log o2-steer-colcontexttool \ SIMOPTKEY+="GenTPCLoopers.colsys=${BEAMTYPE};" taskwrapper sim.log o2-sim ${FST_BFIELD+--field=}${FST_BFIELD} --vertexMode kCollContext --seed $O2SIMSEED -n $NEvents --configKeyValues "\"$SIMOPTKEY\"" -g ${FST_GENERATOR} -e ${FST_MC_ENGINE} -j $NJOBS --run ${RUNNUMBER} -o o2sim --fromCollContext collisioncontext.root:o2sim +# Test MCTracks to AO2D conversion tool +taskwrapper kine2aod.log "o2-sim-kine-publisher -b --kineFileName o2sim --aggregate-timeframe $NEvents | o2-sim-mctracks-to-aod -b --aod-writer-keep dangling | o2-analysis-mctracks-to-aod-simple-task -b" +if [[ ! -s AnalysisResults_trees.root ]] || [[ ! -s AnalysisResults.root ]]; then + echo "Error: AnalysisResults_trees.root (AO2D from Kine file) or AnalysisResults.root (simple analysis task output) missing or empty" + exit 1 +fi + if [[ $DO_EMBEDDING == 1 ]]; then taskwrapper embed.log o2-sim ${FST_BFIELD+--field=}${FST_BFIELD} -j $NJOBS --run ${RUNNUMBER} -n $NEvents -g pythia8pp -e ${FST_MC_ENGINE} -o sig --configKeyValues ${FST_EMBEDDING_CONFIG} --embedIntoFile o2sim_MCHeader.root fi From 75a357d3e2fcb58cadca886055691d53eac62004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiktor=20Piero=C5=BCak?= <94726725+wpierozak@users.noreply.github.com> Date: Mon, 16 Feb 2026 14:14:28 +0100 Subject: [PATCH 106/133] Afit 124 (#14985) * FV0: included dead channel map in reconstruction * FV0: fixed fetch of dead channel map in reco * FV0:Added debug log when data from dead channel is discard * FV0: changed handling of dead channel in reco * Implementation DeadChannelMap in FDD/FT0 * Updaed FDD reco * FIT:Fixed application of DeadChannelMap in FV0 and FDD reconstruction task * Fixed missing constructor arguments for FDD RecoSepc * Please consider the following formatting changes --------- Co-authored-by: wpierozak Co-authored-by: wpierozak Co-authored-by: ALICE Action Bot --- .../include/FDDReconstruction/Reconstructor.h | 9 ++++++- .../FDD/reconstruction/src/Reconstructor.cxx | 5 ++++ .../include/FDDWorkflow/RecoWorkflow.h | 2 +- .../include/FDDWorkflow/ReconstructorSpec.h | 10 +++++-- .../FIT/FDD/workflow/src/RecoWorkflow.cxx | 4 +-- .../FDD/workflow/src/ReconstructorSpec.cxx | 23 ++++++++++++++-- .../FDD/workflow/src/fdd-reco-workflow.cxx | 4 ++- .../FT0Reconstruction/CollisionTimeRecoTask.h | 7 +++++ .../src/CollisionTimeRecoTask.cxx | 4 +++ .../include/FT0Workflow/RecoWorkflow.h | 2 +- .../include/FT0Workflow/ReconstructionSpec.h | 6 +++-- .../FIT/FT0/workflow/src/RecoWorkflow.cxx | 4 +-- .../FT0/workflow/src/ReconstructionSpec.cxx | 22 +++++++++++++-- .../FT0/workflow/src/ft0-reco-workflow.cxx | 6 +++-- .../include/FV0Reconstruction/BaseRecoTask.h | 6 +++-- .../FV0/reconstruction/src/BaseRecoTask.cxx | 27 +++++++++++-------- .../include/FV0Workflow/RecoWorkflow.h | 2 +- .../include/FV0Workflow/ReconstructionSpec.h | 6 +++-- .../FIT/FV0/workflow/src/RecoWorkflow.cxx | 5 ++-- .../FV0/workflow/src/ReconstructionSpec.cxx | 21 ++++++++++----- .../FV0/workflow/src/fv0-reco-workflow.cxx | 4 ++- 21 files changed, 135 insertions(+), 44 deletions(-) diff --git a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/Reconstructor.h b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/Reconstructor.h index 161b800a2c3ca..8881605b652ac 100644 --- a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/Reconstructor.h +++ b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/Reconstructor.h @@ -17,6 +17,7 @@ #include #include "DataFormatsFDD/Digit.h" #include "DataFormatsFDD/RecPoint.h" +#include "DataFormatsFIT/DeadChannelMap.h" namespace o2 { namespace fdd @@ -30,10 +31,16 @@ class Reconstructor gsl::span inChData, std::vector& RecPoints, std::vector& outChData); - void finish(); + void setDeadChannelMap(o2::fit::DeadChannelMap const* deadChannelMap) + { + LOG(info) << "Updated dead channel map"; + mDeadChannelMap = deadChannelMap; + } + private: + o2::fit::DeadChannelMap const* mDeadChannelMap = nullptr; ClassDefNV(Reconstructor, 3); }; } // namespace fdd diff --git a/Detectors/FIT/FDD/reconstruction/src/Reconstructor.cxx b/Detectors/FIT/FDD/reconstruction/src/Reconstructor.cxx index 3a87a11046a77..7d133e30df08e 100644 --- a/Detectors/FIT/FDD/reconstruction/src/Reconstructor.cxx +++ b/Detectors/FIT/FDD/reconstruction/src/Reconstructor.cxx @@ -33,7 +33,12 @@ void Reconstructor::process(o2::fdd::Digit const& digitBC, gsl::spanisChannelAlive(inChData[ich].mPMNumber)) { + LOG(debug) << "Channel " << ich << " is dead - discarding data"; + continue; + } bool inTime = inChData[ich].getFlag(ChannelData::EEventDataBit::kIsEventInTVDC); bool inAdcGate = inChData[ich].getFlag(ChannelData::EEventDataBit::kIsCFDinADCgate); if (inAdcGate) { diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RecoWorkflow.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RecoWorkflow.h index 2dbd854e34eee..0d5d308216bb0 100644 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RecoWorkflow.h +++ b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RecoWorkflow.h @@ -20,7 +20,7 @@ namespace o2 { namespace fdd { -framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut); +framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut, bool useDeadChannelMap); } // namespace fdd } // namespace o2 #endif diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/ReconstructorSpec.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/ReconstructorSpec.h index 7dcb5d9aaba40..8f20ff1513ab4 100644 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/ReconstructorSpec.h +++ b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/ReconstructorSpec.h @@ -18,6 +18,8 @@ #include "Framework/Task.h" #include "FDDReconstruction/Reconstructor.h" #include "DataFormatsFDD/RecPoint.h" +#include "DataFormatsFIT/DeadChannelMap.h" +#include "Framework/ConcreteDataMatcher.h" using namespace o2::framework; @@ -29,21 +31,25 @@ namespace fdd class FDDReconstructorDPL : public Task { public: - FDDReconstructorDPL(bool useMC) : mUseMC(useMC) {} + FDDReconstructorDPL(bool useMC, bool useDeadChannelMap) : mUseMC(useMC), mUseDeadChannelMap(useDeadChannelMap) {} ~FDDReconstructorDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; private: bool mUseMC = true; + bool mUseDeadChannelMap = true; + bool mUpdateDeadChannelMap = true; std::vector mRecPoints; std::vector mRecChData; + o2::fit::DeadChannelMap const* mDeadChannelMap; o2::fdd::Reconstructor mReco; o2::header::DataOrigin mOrigin = o2::header::gDataOriginFDD; }; /// create a processor spec -framework::DataProcessorSpec getFDDReconstructorSpec(bool useMC = true); +framework::DataProcessorSpec getFDDReconstructorSpec(bool useMC = true, bool useDeadChannelMap = true); } // namespace fdd } // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/RecoWorkflow.cxx b/Detectors/FIT/FDD/workflow/src/RecoWorkflow.cxx index a7d4c15af81bb..b464e689f7a75 100644 --- a/Detectors/FIT/FDD/workflow/src/RecoWorkflow.cxx +++ b/Detectors/FIT/FDD/workflow/src/RecoWorkflow.cxx @@ -22,14 +22,14 @@ namespace o2 namespace fdd { -framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut) +framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut, bool useDeadChannelMap) { framework::WorkflowSpec specs; if (!disableRootInp) { specs.emplace_back(o2::fdd::getFDDDigitReaderSpec(useMC)); } - specs.emplace_back(o2::fdd::getFDDReconstructorSpec(useMC)); + specs.emplace_back(o2::fdd::getFDDReconstructorSpec(useMC, useDeadChannelMap)); if (!disableRootOut) { specs.emplace_back(o2::fdd::getFDDRecPointWriterSpec(useMC)); } diff --git a/Detectors/FIT/FDD/workflow/src/ReconstructorSpec.cxx b/Detectors/FIT/FDD/workflow/src/ReconstructorSpec.cxx index b7a0b9876a2ee..1d5d599b5ee31 100644 --- a/Detectors/FIT/FDD/workflow/src/ReconstructorSpec.cxx +++ b/Detectors/FIT/FDD/workflow/src/ReconstructorSpec.cxx @@ -18,6 +18,7 @@ #include "FDDWorkflow/ReconstructorSpec.h" #include "DataFormatsFDD/Digit.h" #include "DataFormatsFDD/MCLabel.h" +#include "Framework/CCDBParamSpec.h" using namespace o2::framework; @@ -44,6 +45,11 @@ void FDDReconstructorDPL::run(ProcessingContext& pc) // lblPtr = labels.get(); LOG(info) << "Ignoring MC info"; } + if (mUseDeadChannelMap && mUpdateDeadChannelMap) { + LOG(info) << "Populating reconsturctor object with Dead Channel Map object"; + auto deadChannelMap = pc.inputs().get("deadChannelMap"); + mReco.setDeadChannelMap(deadChannelMap.get()); + } int nDig = digitsBC.size(); mRecPoints.reserve(nDig); mRecChData.reserve(digitsCh.size()); @@ -58,16 +64,29 @@ void FDDReconstructorDPL::run(ProcessingContext& pc) pc.outputs().snapshot(Output{mOrigin, "RECCHDATA", 0}, mRecChData); } -DataProcessorSpec getFDDReconstructorSpec(bool useMC) +void FDDReconstructorDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (matcher == ConcreteDataMatcher("FDD", "DeadChannelMap", 0)) { + mUpdateDeadChannelMap = false; + return; + } +} + +DataProcessorSpec getFDDReconstructorSpec(bool useMC, bool useDeadChannelMap) { std::vector inputSpec; std::vector outputSpec; inputSpec.emplace_back("digitsBC", o2::header::gDataOriginFDD, "DIGITSBC", 0, Lifetime::Timeframe); inputSpec.emplace_back("digitsCh", o2::header::gDataOriginFDD, "DIGITSCH", 0, Lifetime::Timeframe); + if (useMC) { LOG(info) << "Currently FDDReconstructor does not consume and provide MC truth"; // inputSpec.emplace_back("labels", o2::header::gDataOriginFDD, "DIGITSMCTR", 0, Lifetime::Timeframe); } + if (useDeadChannelMap) { + LOG(info) << "Dead channel map will be applied during reconstruction"; + inputSpec.emplace_back("deadChannelMap", o2::header::gDataOriginFDD, "DeadChannelMap", 0, Lifetime::Condition, ccdbParamSpec("FDD/Calib/DeadChannelMap")); + } outputSpec.emplace_back(o2::header::gDataOriginFDD, "RECPOINTS", 0, Lifetime::Timeframe); outputSpec.emplace_back(o2::header::gDataOriginFDD, "RECCHDATA", 0, Lifetime::Timeframe); @@ -75,7 +94,7 @@ DataProcessorSpec getFDDReconstructorSpec(bool useMC) "fdd-reconstructor", inputSpec, outputSpec, - AlgorithmSpec{adaptFromTask(useMC)}, + AlgorithmSpec{adaptFromTask(useMC, useDeadChannelMap)}, Options{}}; } diff --git a/Detectors/FIT/FDD/workflow/src/fdd-reco-workflow.cxx b/Detectors/FIT/FDD/workflow/src/fdd-reco-workflow.cxx index 652ddb8bd2a29..888792425909b 100644 --- a/Detectors/FIT/FDD/workflow/src/fdd-reco-workflow.cxx +++ b/Detectors/FIT/FDD/workflow/src/fdd-reco-workflow.cxx @@ -38,6 +38,7 @@ void customize(std::vector& workflowOptions) {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}, {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input readers"}}, {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writers"}}, + {"disable-dead-channel-map", o2::framework::VariantType::Bool, false, {"disable dead channel map"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); @@ -57,8 +58,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto useMC = !configcontext.options().get("disable-mc"); auto disableRootInp = configcontext.options().get("disable-root-input"); auto disableRootOut = configcontext.options().get("disable-root-output"); + bool useDeadChannelMap = !configcontext.options().get("disable-dead-channel-map"); - auto wf = o2::fdd::getRecoWorkflow(useMC, disableRootInp, disableRootOut); + auto wf = o2::fdd::getRecoWorkflow(useMC, disableRootInp, disableRootOut, useDeadChannelMap); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, wf); diff --git a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CollisionTimeRecoTask.h b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CollisionTimeRecoTask.h index ff3f8384f488d..9f6cd500b9e74 100644 --- a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CollisionTimeRecoTask.h +++ b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CollisionTimeRecoTask.h @@ -21,6 +21,7 @@ #include "DataFormatsFT0/FT0ChannelTimeCalibrationObject.h" #include "DataFormatsFT0/SpectraInfoObject.h" #include "DataFormatsFT0/SlewingCoef.h" +#include "DataFormatsFIT/DeadChannelMap.h" #include #include #include @@ -57,10 +58,16 @@ class CollisionTimeRecoTask LOG(info) << "Init for slewing calib object"; mCalibSlew = calibSlew->makeSlewingPlots(); }; + void SetDeadChannelMap(const o2::fit::DeadChannelMap* deadChannelMap) + { + LOG(info) << "Updated dead channel map for CollisionTimeRecoTask"; + mDeadChannelMap = deadChannelMap; + } float getTimeInPS(const o2::ft0::ChannelData& channelData); private: o2::ft0::TimeSpectraInfoObject const* mTimeCalibObject = nullptr; + const o2::fit::DeadChannelMap* mDeadChannelMap = nullptr; typename o2::ft0::SlewingCoef::SlewingPlots_t mCalibSlew{}; }; } // namespace ft0 diff --git a/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx b/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx index 7363cef57cf31..3e3ffe52671e9 100644 --- a/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx +++ b/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx @@ -67,6 +67,10 @@ RP CollisionTimeRecoTask::processDigit(const o2::ft0::Digit& digit, // Reference channels shouldn't participate in reco at all! continue; } + if (mDeadChannelMap && !mDeadChannelMap->isChannelAlive(channelData.ChId)) { + LOG(debug) << "Channel " << channelData.ChId << " is dead - discarding data"; + continue; + } const float timeInPS = getTimeInPS(channelData); if (ChannelFilterParam::Instance().checkAll(channelData)) { outChData.emplace_back(channelData.ChId, timeInPS, (float)channelData.QTCAmpl, channelData.ChainQTC); diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecoWorkflow.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecoWorkflow.h index 3c6e4599a250c..6de23a1c66bfd 100644 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecoWorkflow.h +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecoWorkflow.h @@ -20,7 +20,7 @@ namespace o2 { namespace ft0 { -framework::WorkflowSpec getRecoWorkflow(bool useMC, std::string ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib, bool disableRootInp, bool disableRootOut); +framework::WorkflowSpec getRecoWorkflow(bool useMC, std::string ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib, bool disableRootInp, bool disableRootOut, bool useDeadChannelMap = true); } // namespace ft0 } // namespace o2 #endif diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/ReconstructionSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/ReconstructionSpec.h index 1c671352e6ba7..307b2109fe35f 100644 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/ReconstructionSpec.h +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/ReconstructionSpec.h @@ -34,7 +34,7 @@ class ReconstructionDPL : public Task static constexpr int NCHANNELS = o2::ft0::Geometry::Nchannels; public: - ReconstructionDPL(bool useMC, const std::string& ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib) : mUseMC(useMC), mCCDBpath(ccdbpath), mUseTimeOffsetCalib(useTimeOffsetCalib), mUseSlewingCalib(useSlewingCalib) {} + ReconstructionDPL(bool useMC, const std::string& ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib, bool useDeadChannelMap) : mUseMC(useMC), mCCDBpath(ccdbpath), mUseTimeOffsetCalib(useTimeOffsetCalib), mUseSlewingCalib(useSlewingCalib), mUseDeadChannelMap(useDeadChannelMap) {} ~ReconstructionDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -46,6 +46,8 @@ class ReconstructionDPL : public Task bool mUpdateCCDB = true; bool mUseTimeOffsetCalib = true; bool mUseSlewingCalib = true; + bool mUseDeadChannelMap = true; + bool mUpdateDeadChannelMap = true; const std::string mCCDBpath = o2::base::NameConf::getCCDBServer(); std::vector mRecPoints; std::vector mRecChData; @@ -55,7 +57,7 @@ class ReconstructionDPL : public Task }; /// create a processor spec -framework::DataProcessorSpec getReconstructionSpec(bool useMC = false, const std::string ccdbpath = "http://alice-ccdb.cern.ch", bool useTimeOffsetCalib = true, bool useSlewingCalib = true); +framework::DataProcessorSpec getReconstructionSpec(bool useMC = false, const std::string ccdbpath = "http://alice-ccdb.cern.ch", bool useTimeOffsetCalib = true, bool useSlewingCalib = true, bool useDeadChannelMap = true); } // namespace ft0 } // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/RecoWorkflow.cxx b/Detectors/FIT/FT0/workflow/src/RecoWorkflow.cxx index 247158164ac3b..2231011febd7f 100644 --- a/Detectors/FIT/FT0/workflow/src/RecoWorkflow.cxx +++ b/Detectors/FIT/FT0/workflow/src/RecoWorkflow.cxx @@ -22,13 +22,13 @@ namespace o2 namespace ft0 { -framework::WorkflowSpec getRecoWorkflow(bool useMC, std::string ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib, bool disableRootInp, bool disableRootOut) +framework::WorkflowSpec getRecoWorkflow(bool useMC, std::string ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib, bool disableRootInp, bool disableRootOut, bool useDeadChannelMap) { framework::WorkflowSpec specs; if (!disableRootInp) { specs.emplace_back(o2::ft0::getDigitReaderSpec(useMC)); } - specs.emplace_back(o2::ft0::getReconstructionSpec(useMC, ccdbpath, useTimeOffsetCalib, useSlewingCalib)); + specs.emplace_back(o2::ft0::getReconstructionSpec(useMC, ccdbpath, useTimeOffsetCalib, useSlewingCalib, useDeadChannelMap)); if (!disableRootOut) { specs.emplace_back(o2::ft0::getRecPointWriterSpec(useMC)); } diff --git a/Detectors/FIT/FT0/workflow/src/ReconstructionSpec.cxx b/Detectors/FIT/FT0/workflow/src/ReconstructionSpec.cxx index 40bc96ebca58e..bc5217c8d7471 100644 --- a/Detectors/FIT/FT0/workflow/src/ReconstructionSpec.cxx +++ b/Detectors/FIT/FT0/workflow/src/ReconstructionSpec.cxx @@ -44,6 +44,7 @@ void ReconstructionDPL::init(InitContext& ic) LOG(info) << "FT0 param mMinRMS: " << CalibParam::Instance().mMinRMS; LOG(info) << "FT0 param mMaxSigma: " << CalibParam::Instance().mMaxSigma; LOG(info) << "FT0 param mMaxDiffMean: " << CalibParam::Instance().mMaxDiffMean; + LOG(info) << "FT0 dead channel map will be applied " << mUseDeadChannelMap; } void ReconstructionDPL::run(ProcessingContext& pc) @@ -69,6 +70,12 @@ void ReconstructionDPL::run(ProcessingContext& pc) mReco.SetSlewingCalibObject(slewingCalibObject.get()); } + if (mUseDeadChannelMap && mUpdateDeadChannelMap) { + LOG(debug) << "Applying dead channel map"; + auto deadChannelMap = pc.inputs().get("deadChannelMap"); + mReco.SetDeadChannelMap(deadChannelMap.get()); + } + mRecPoints.reserve(digits.size()); mRecChData.reserve(channels.size()); mReco.processTF(digits, channels, mRecPoints, mRecChData); @@ -91,6 +98,11 @@ void ReconstructionDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) mUseSlewingCalib = false; // upload only once, slewing should be stable during the run return; } + if (matcher == ConcreteDataMatcher("FT0", "DeadChannelMap", 0)) { + LOG(debug) << "New DeadChannelMap is uploaded"; + mUpdateDeadChannelMap = false; + return; + } } void ReconstructionDPL::endOfStream(EndOfStreamContext& ec) @@ -99,12 +111,13 @@ void ReconstructionDPL::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib) +DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib, bool useDeadChannelMap) { std::vector inputSpec; std::vector outputSpec; inputSpec.emplace_back("digits", o2::header::gDataOriginFT0, "DIGITSBC", 0, Lifetime::Timeframe); inputSpec.emplace_back("digch", o2::header::gDataOriginFT0, "DIGITSCH", 0, Lifetime::Timeframe); + if (useMC) { LOG(info) << "Currently Reconstruction does not consume and provide MC truth"; inputSpec.emplace_back("labels", o2::header::gDataOriginFT0, "DIGITSMCTR", 0, Lifetime::Timeframe); @@ -121,6 +134,11 @@ DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath, ccdbParamSpec("FT0/Calib/SlewingCoef")); } + if (useDeadChannelMap) { + LOG(info) << "Dead channel map will be applied during reconstruction"; + inputSpec.emplace_back("deadChannelMap", o2::header::gDataOriginFT0, "DeadChannelMap", 0, Lifetime::Condition, ccdbParamSpec("FT0/Calib/DeadChannelMap")); + } + outputSpec.emplace_back(o2::header::gDataOriginFT0, "RECPOINTS", 0, Lifetime::Timeframe); outputSpec.emplace_back(o2::header::gDataOriginFT0, "RECCHDATA", 0, Lifetime::Timeframe); @@ -128,7 +146,7 @@ DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath, "ft0-reconstructor", inputSpec, outputSpec, - AlgorithmSpec{adaptFromTask(useMC, ccdbpath, useTimeOffsetCalib, useSlewingCalib)}, + AlgorithmSpec{adaptFromTask(useMC, ccdbpath, useTimeOffsetCalib, useSlewingCalib, useDeadChannelMap)}, Options{}}; } diff --git a/Detectors/FIT/FT0/workflow/src/ft0-reco-workflow.cxx b/Detectors/FIT/FT0/workflow/src/ft0-reco-workflow.cxx index 3e6a6bf5da090..ab39068aedb38 100644 --- a/Detectors/FIT/FT0/workflow/src/ft0-reco-workflow.cxx +++ b/Detectors/FIT/FT0/workflow/src/ft0-reco-workflow.cxx @@ -41,7 +41,8 @@ void customize(std::vector& workflowOptions) {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writers"}}, {"disable-time-offset-calib", o2::framework::VariantType::Bool, false, {"disable timeoffset calibration"}}, {"disable-slewing-calib", o2::framework::VariantType::Bool, false, {"disable slewing calibration"}}, - {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + {"disable-dead-channel-map", VariantType::Bool, false, {"disable dead channel map"}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -64,9 +65,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto disableRootOut = configcontext.options().get("disable-root-output"); const auto useTimeOffsetCalib = !configcontext.options().get("disable-time-offset-calib"); const auto useSlewingCalib = !configcontext.options().get("disable-slewing-calib"); + const auto useDeadChannelMap = !configcontext.options().get("disable-dead-channel-map"); LOG(info) << "WorkflowSpec getRecoWorkflow useMC " << useMC << " CCDB " << ccdbpath; - auto wf = o2::ft0::getRecoWorkflow(useMC, ccdbpath, useTimeOffsetCalib, useSlewingCalib, disableRootInp, disableRootOut); + auto wf = o2::ft0::getRecoWorkflow(useMC, ccdbpath, useTimeOffsetCalib, useSlewingCalib, disableRootInp, disableRootOut, useDeadChannelMap); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, wf); diff --git a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/BaseRecoTask.h b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/BaseRecoTask.h index 12d89b82a13cc..c5cb5b0da6d05 100644 --- a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/BaseRecoTask.h +++ b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/BaseRecoTask.h @@ -18,6 +18,7 @@ #include "DataFormatsFV0/ChannelData.h" #include "DataFormatsFV0/RecPoints.h" #include "DataFormatsFV0/FV0ChannelTimeCalibrationObject.h" +#include "DataFormatsFIT/DeadChannelMap.h" #include namespace o2 @@ -33,14 +34,15 @@ class BaseRecoTask ~BaseRecoTask() = default; o2::fv0::RecPoints process(o2::fv0::Digit const& bcd, gsl::span inChData, - gsl::span outChData); + std::vector& outChData); void FinishTask(); void SetChannelOffset(o2::fv0::FV0ChannelTimeCalibrationObject const* caliboffsets) { mCalibOffset = caliboffsets; }; + void SetDeadChannelMap(o2::fit::DeadChannelMap const* deadChannelMap) { mDeadChannelMap = deadChannelMap; } int getOffset(int channel); private: o2::fv0::FV0ChannelTimeCalibrationObject const* mCalibOffset = nullptr; - + o2::fit::DeadChannelMap const* mDeadChannelMap = nullptr; ClassDefNV(BaseRecoTask, 3); }; } // namespace fv0 diff --git a/Detectors/FIT/FV0/reconstruction/src/BaseRecoTask.cxx b/Detectors/FIT/FV0/reconstruction/src/BaseRecoTask.cxx index 8a217232592df..8032220f8996d 100644 --- a/Detectors/FIT/FV0/reconstruction/src/BaseRecoTask.cxx +++ b/Detectors/FIT/FV0/reconstruction/src/BaseRecoTask.cxx @@ -27,7 +27,7 @@ using RP = o2::fv0::RecPoints; RP BaseRecoTask::process(o2::fv0::Digit const& bcd, gsl::span inChData, - gsl::span outChData) + std::vector& outChData) { LOG(debug) << "Running reconstruction on new event"; @@ -44,22 +44,27 @@ RP BaseRecoTask::process(o2::fv0::Digit const& bcd, int nch = inChData.size(); for (int ich = 0; ich < nch; ich++) { LOG(debug) << " channel " << ich << " / " << nch; + if (mDeadChannelMap && !mDeadChannelMap->isChannelAlive(inChData[ich].ChId)) { + LOG(debug) << "Channel " << ich << " is dead - discarding data"; + continue; + } int offsetChannel = getOffset(int(inChData[ich].ChId)); - outChData[ich] = o2::fv0::ChannelDataFloat{inChData[ich].ChId, - (inChData[ich].CFDTime - offsetChannel) * DigitizationConstant::TIME_PER_TDCCHANNEL, - (float)inChData[ich].QTCAmpl, - inChData[ich].ChainQTC}; + outChData.emplace_back(o2::fv0::ChannelDataFloat{inChData[ich].ChId, + (inChData[ich].CFDTime - offsetChannel) * DigitizationConstant::TIME_PER_TDCCHANNEL, + (float)inChData[ich].QTCAmpl, + inChData[ich].ChainQTC}); + const auto& currentOutCh = outChData.back(); // Conditions for reconstructing collision time (3 variants: first, average-relaxed and average-tight) - if (outChData[ich].charge > FV0DigParam::Instance().chargeThrForMeanTime) { - sideAtimeFirst = std::min(static_cast(sideAtimeFirst), outChData[ich].time); + if (currentOutCh.charge > FV0DigParam::Instance().chargeThrForMeanTime) { + sideAtimeFirst = std::min(static_cast(sideAtimeFirst), currentOutCh.time); if (inChData[ich].areAllFlagsGood()) { - if (std::abs(outChData[ich].time) < FV0DigParam::Instance().mTimeThresholdForReco) { - sideAtimeAvg += outChData[ich].time; + if (std::abs(currentOutCh.time) < FV0DigParam::Instance().mTimeThresholdForReco) { + sideAtimeAvg += currentOutCh.time; ndigitsA++; } - if (outChData[ich].charge > FV0DigParam::Instance().mAmpThresholdForReco && std::abs(outChData[ich].time) < FV0DigParam::Instance().mTimeThresholdForReco) { - sideAtimeAvgSelected += outChData[ich].time; + if (currentOutCh.charge > FV0DigParam::Instance().mAmpThresholdForReco && std::abs(currentOutCh.time) < FV0DigParam::Instance().mTimeThresholdForReco) { + sideAtimeAvgSelected += currentOutCh.time; ndigitsASelected++; } } diff --git a/Detectors/FIT/FV0/workflow/include/FV0Workflow/RecoWorkflow.h b/Detectors/FIT/FV0/workflow/include/FV0Workflow/RecoWorkflow.h index 015870d9178e2..f035b2406e5ba 100644 --- a/Detectors/FIT/FV0/workflow/include/FV0Workflow/RecoWorkflow.h +++ b/Detectors/FIT/FV0/workflow/include/FV0Workflow/RecoWorkflow.h @@ -20,7 +20,7 @@ namespace o2 { namespace fv0 { -framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut); +framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut, bool useDeadChannelMap); } // namespace fv0 } // namespace o2 #endif diff --git a/Detectors/FIT/FV0/workflow/include/FV0Workflow/ReconstructionSpec.h b/Detectors/FIT/FV0/workflow/include/FV0Workflow/ReconstructionSpec.h index d71e154280e3d..934ce4d2c4a66 100644 --- a/Detectors/FIT/FV0/workflow/include/FV0Workflow/ReconstructionSpec.h +++ b/Detectors/FIT/FV0/workflow/include/FV0Workflow/ReconstructionSpec.h @@ -34,7 +34,7 @@ class ReconstructionDPL : public Task static constexpr int NCHANNELS = o2::fv0::Constants::nFv0Channels; public: - ReconstructionDPL(bool useMC, const std::string ccdbpath) : mUseMC(useMC), mCCDBpath(ccdbpath) {} + ReconstructionDPL(bool useMC, bool useDeadChannelMap, const std::string ccdbpath) : mUseMC(useMC), mUseDeadChannelMap(useDeadChannelMap), mCCDBpath(ccdbpath) {} ~ReconstructionDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -44,6 +44,8 @@ class ReconstructionDPL : public Task private: bool mUseMC = false; bool mUpdateCCDB = true; + bool mUseDeadChannelMap = true; + bool mUpdateDeadChannelMap = true; const std::string mCCDBpath = o2::base::NameConf::getCCDBServer(); std::vector mRecPoints; std::vector mRecChData; @@ -53,7 +55,7 @@ class ReconstructionDPL : public Task }; /// create a processor spec -framework::DataProcessorSpec getReconstructionSpec(bool useMC = false, const std::string ccdbpath = "http://alice-ccdb.cern.ch"); +framework::DataProcessorSpec getReconstructionSpec(bool useMC = false, bool useDeadChannelMap = true, const std::string ccdbpath = "http://alice-ccdb.cern.ch"); } // namespace fv0 } // namespace o2 diff --git a/Detectors/FIT/FV0/workflow/src/RecoWorkflow.cxx b/Detectors/FIT/FV0/workflow/src/RecoWorkflow.cxx index 6bfc5479303d1..a0ef71b75765a 100644 --- a/Detectors/FIT/FV0/workflow/src/RecoWorkflow.cxx +++ b/Detectors/FIT/FV0/workflow/src/RecoWorkflow.cxx @@ -22,14 +22,13 @@ namespace o2 namespace fv0 { -framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut) +framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut, bool useDeadChannelMap) { framework::WorkflowSpec specs; if (!disableRootInp) { specs.emplace_back(o2::fv0::getDigitReaderSpec(useMC)); } - - specs.emplace_back(o2::fv0::getReconstructionSpec(useMC)); + specs.emplace_back(o2::fv0::getReconstructionSpec(useMC, useDeadChannelMap)); if (!disableRootOut) { specs.emplace_back(o2::fv0::getRecPointWriterSpec(useMC)); } diff --git a/Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx b/Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx index 520ac4dbaa563..b97186bbf81a8 100644 --- a/Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx +++ b/Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx @@ -21,6 +21,7 @@ #include "DataFormatsFV0/ChannelData.h" #include "DataFormatsFV0/MCLabel.h" #include "DataFormatsFV0/FV0ChannelTimeCalibrationObject.h" +#include "DataFormatsFIT/DeadChannelMap.h" #include "Framework/CCDBParamSpec.h" using namespace o2::framework; @@ -53,18 +54,19 @@ void ReconstructionDPL::run(ProcessingContext& pc) auto caliboffsets = pc.inputs().get("fv0offsets"); mReco.SetChannelOffset(caliboffsets.get()); } + if (mUseDeadChannelMap && mUpdateDeadChannelMap) { + auto deadChannelMap = pc.inputs().get("deadChannelMap"); + mReco.SetDeadChannelMap(deadChannelMap.get()); + } int nDig = digits.size(); LOG(debug) << " nDig " << nDig << " | ndigch " << digch.size(); mRecPoints.reserve(nDig); - mRecChData.resize(digch.size()); for (int id = 0; id < nDig; id++) { const auto& digit = digits[id]; LOG(debug) << " ndig " << id << " bc " << digit.getBC() << " orbit " << digit.getOrbit(); auto channels = digit.getBunchChannelData(digch); - gsl::span out_ch(mRecChData); - out_ch = out_ch.subspan(digit.ref.getFirstEntry(), digit.ref.getEntries()); - mRecPoints.emplace_back(mReco.process(digit, channels, out_ch)); + mRecPoints.emplace_back(mReco.process(digit, channels, mRecChData)); } LOG(debug) << "FV0 reconstruction pushes " << mRecPoints.size() << " RecPoints"; @@ -80,6 +82,9 @@ void ReconstructionDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) mUpdateCCDB = false; return; } + if (matcher == ConcreteDataMatcher(o2::header::gDataOriginFV0, "DeadChannelMap", 0)) { + mUpdateDeadChannelMap = false; + } } void ReconstructionDPL::endOfStream(EndOfStreamContext& ec) @@ -88,7 +93,7 @@ void ReconstructionDPL::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath) +DataProcessorSpec getReconstructionSpec(bool useMC, bool useDeadChannelMap, const std::string ccdbpath) { std::vector inputSpec; std::vector outputSpec; @@ -98,6 +103,10 @@ DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath) LOG(info) << "Currently Reconstruction does not consume and provide MC truth"; inputSpec.emplace_back("labels", o2::header::gDataOriginFV0, "DIGITSMCTR", 0, Lifetime::Timeframe); } + if (useDeadChannelMap) { + LOG(info) << "Dead channel map will be applied during reconstruction"; + inputSpec.emplace_back("deadChannelMap", o2::header::gDataOriginFV0, "DeadChannelMap", 0, Lifetime::Condition, ccdbParamSpec("FV0/Calib/DeadChannelMap")); + } inputSpec.emplace_back("fv0offsets", "FV0", "TimeOffset", 0, Lifetime::Condition, ccdbParamSpec("FV0/Calib/ChannelTimeOffset")); @@ -109,7 +118,7 @@ DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath) "fv0-reconstructor", inputSpec, outputSpec, - AlgorithmSpec{adaptFromTask(useMC, ccdbpath)}, + AlgorithmSpec{adaptFromTask(useMC, useDeadChannelMap, ccdbpath)}, Options{}}; } diff --git a/Detectors/FIT/FV0/workflow/src/fv0-reco-workflow.cxx b/Detectors/FIT/FV0/workflow/src/fv0-reco-workflow.cxx index 16d1383c7e8c4..309560e2d6b36 100644 --- a/Detectors/FIT/FV0/workflow/src/fv0-reco-workflow.cxx +++ b/Detectors/FIT/FV0/workflow/src/fv0-reco-workflow.cxx @@ -39,6 +39,7 @@ void customize(std::vector& workflowOptions) {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}, {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input readers"}}, {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writers"}}, + {"disable-dead-channel-map", o2::framework::VariantType::Bool, false, {"disable dead channel map"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); @@ -59,9 +60,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto useMC = !configcontext.options().get("disable-mc"); auto disableRootInp = configcontext.options().get("disable-root-input"); auto disableRootOut = configcontext.options().get("disable-root-output"); + bool useDeadChannelMap = !configcontext.options().get("disable-dead-channel-map"); LOG(info) << "WorkflowSpec getRecoWorkflow useMC " << useMC; - auto wf = o2::fv0::getRecoWorkflow(useMC, disableRootInp, disableRootOut); + auto wf = o2::fv0::getRecoWorkflow(useMC, disableRootInp, disableRootOut, useDeadChannelMap); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, wf); From 3e6876861dffa4a73c9c7fd8c858d6f7cb3f9554 Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Mon, 16 Feb 2026 16:03:27 +0100 Subject: [PATCH 107/133] First version of the hit based CA tracker for ALICE3 IT/OT (#15066) * ITSTracking: change visibility of methods Change TimeFrame prepareClusters method visibility to protected Add computeTracksMClabels method to Tracker class * Hit based CA for ALICE3 tracker --- .../tracking/include/ITStracking/TimeFrame.h | 2 +- .../tracking/include/ITStracking/Tracker.h | 2 +- Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt | 3 +- .../ALICE3/TRK/macros/test/CMakeLists.txt | 10 + .../ALICE3/TRK/macros/test/CheckTracksCA.C | 345 ++++++++++++++++++ .../ALICE3/TRK/reconstruction/CMakeLists.txt | 34 ++ .../include/TRKReconstruction/TimeFrame.h | 73 ++++ .../src/TRKReconstructionLinkDef.h | 20 + .../TRK/reconstruction/src/TimeFrame.cxx | 189 ++++++++++ .../ALICE3/TRK/workflow/CMakeLists.txt | 10 +- .../Upgrades/ALICE3/TRK/workflow/README.md | 130 +++++++ .../include/TRKWorkflow/RecoWorkflow.h | 2 + .../include/TRKWorkflow/TrackWriterSpec.h | 31 ++ .../include/TRKWorkflow/TrackerSpec.h | 12 +- .../ALICE3/TRK/workflow/src/RecoWorkflow.cxx | 11 +- .../TRK/workflow/src/TrackWriterSpec.cxx | 57 +++ .../ALICE3/TRK/workflow/src/TrackerSpec.cxx | 336 ++++++++++++++++- .../TRK/workflow/src/trk-reco-workflow.cxx | 4 +- 18 files changed, 1251 insertions(+), 20 deletions(-) create mode 100644 Detectors/Upgrades/ALICE3/TRK/macros/test/CheckTracksCA.C create mode 100644 Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt create mode 100644 Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h create mode 100644 Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h create mode 100644 Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx create mode 100644 Detectors/Upgrades/ALICE3/TRK/workflow/README.md create mode 100644 Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackWriterSpec.h create mode 100644 Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackWriterSpec.cxx diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h index 4dbb9f09f6192..acc884ea68b8b 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h @@ -299,7 +299,7 @@ struct TimeFrame { virtual bool isGPU() const noexcept { return false; } virtual const char* getName() const noexcept { return "CPU"; } - private: + protected: void prepareClusters(const TrackingParameters& trkParam, const int maxLayers = nLayers); float mBz = 5.; unsigned int mNTotalLowPtVertices = 0; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h index 4c903ed1f3ca1..3ea382c626fed 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h @@ -72,6 +72,7 @@ class Tracker bool isMatLUT() const { return mTraits->isMatLUT(); } void setNThreads(int n, std::shared_ptr& arena) { mTraits->setNThreads(n, arena); } void printSummary() const; + void computeTracksMClabels(); private: void initialiseTimeFrame(int iteration) { mTraits->initialiseTimeFrame(iteration); } @@ -84,7 +85,6 @@ class Tracker // MC interaction void computeRoadsMClabels(); - void computeTracksMClabels(); void rectifyClusterIndices(); template diff --git a/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt index e623239122658..6e3437c9d841b 100644 --- a/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt @@ -12,4 +12,5 @@ add_subdirectory(base) add_subdirectory(macros) add_subdirectory(simulation) -add_subdirectory(workflow) \ No newline at end of file +add_subdirectory(reconstruction) +add_subdirectory(workflow) diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt index 379207eb07481..d9908bbfeb1e5 100644 --- a/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt @@ -18,4 +18,14 @@ o2_add_test_root_macro(CheckDigits.C O2::SimulationDataFormat O2::DetectorsBase O2::Steer + LABELS trk COMPILE_ONLY) + +o2_add_test_root_macro(CheckTracksCA.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsITS + O2::ITStracking + O2::SimulationDataFormat + O2::DetectorsBase + O2::TRKBase + O2::TRKSimulation + O2::Steer LABELS trk COMPILE_ONLY) \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckTracksCA.C b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckTracksCA.C new file mode 100644 index 0000000000000..ae75616b7719c --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckTracksCA.C @@ -0,0 +1,345 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CheckTracksCA.C +/// \brief Quality assurance macro for TRK tracking + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DataFormatsITS/TrackITS.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTrack.h" +#include "Steer/MCKinematicsReader.h" +#include "TRKSimulation/Hit.h" +#include "TRKBase/GeometryTGeo.h" +#include "DetectorsBase/GeometryManager.h" + +#endif + +using namespace std; +using namespace o2; + +/// Structure to track particle hit information +struct ParticleHitInfo { + std::bitset<11> layerHits; ///< Which layers have hits (11 layers for TRK) + int nHits = 0; ///< Total number of hits + float pt = 0.0f; ///< Particle pT + + void addHit(int layer) + { + if (!layerHits[layer]) { + layerHits[layer] = true; + nHits++; + } + } + + bool hasConsecutiveLayers(int nConsecutive) const + { + for (int startLayer = 0; startLayer <= 11 - nConsecutive; ++startLayer) { + bool allSet = true; + for (int i = 0; i < nConsecutive; ++i) { + if (!layerHits[startLayer + i]) { + allSet = false; + break; + } + } + if (allSet) { + return true; + } + } + return false; + } +}; + +void CheckTracksCA(std::string tracfile = "o2trac_trk.root", + std::string kinefile = "o2sim_Kine.root", + std::string hitsfile = "o2sim_HitsTRK.root", + std::string outputfile = "trk_qa_output.root") +{ + gStyle->SetOptStat(0); + + std::cout << "=== Starting TRK Track Quality Assurance ===" << std::endl; + std::cout << "Input files:" << std::endl; + std::cout << " Tracks: " << tracfile << std::endl; + std::cout << " Kinematics: " << kinefile << std::endl; + std::cout << " Hits: " << hitsfile << std::endl; + std::cout << " Output: " << outputfile << std::endl; + std::cout << std::endl; + + // MC kinematics reader + o2::steer::MCKinematicsReader kineReader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + const int nEvents = kineReader.getNEvents(0); + std::cout << "Number of MC events: " << nEvents << std::endl; + + // Open hits file to count hits per particle per layer + TFile* hitsFile = TFile::Open(hitsfile.c_str(), "READ"); + if (!hitsFile || hitsFile->IsZombie()) { + std::cerr << "ERROR: Cannot open hits file: " << hitsfile << std::endl; + return; + } + TTree* hitsTree = hitsFile->Get("o2sim"); + if (!hitsTree) { + std::cerr << "ERROR: Cannot find o2sim tree in hits file" << std::endl; + return; + } + + // Open reconstructed tracks file + TFile* tracFile = TFile::Open(tracfile.c_str(), "READ"); + if (!tracFile || tracFile->IsZombie()) { + std::cerr << "ERROR: Cannot open tracks file: " << tracfile << std::endl; + return; + } + TTree* recTree = tracFile->Get("o2sim"); + if (!recTree) { + std::cerr << "ERROR: Cannot find o2sim tree in tracks file" << std::endl; + return; + } + + // Reconstructed tracks and labels + std::vector* recTracks = nullptr; + std::vector* trkLabels = nullptr; + recTree->SetBranchAddress("TRKTrack", &recTracks); + recTree->SetBranchAddress("TRKTrackMCTruth", &trkLabels); + + std::cout << "Reading tracks from tree..." << std::endl; + + // Analyze hits tree to count hits per particle per layer + std::cout << "Analyzing hits from tree..." << std::endl; + std::unordered_map particleHitMap; + + // Load geometry for layer determination + o2::base::GeometryManager::loadGeometry(); + auto* gman = o2::trk::GeometryTGeo::Instance(); + + // Array to map detector to starting layer + constexpr std::array startLayer{0, 3}; + + std::vector* trkHit = nullptr; + hitsTree->SetBranchAddress("TRKHit", &trkHit); + + Long64_t nHitsEntries = hitsTree->GetEntries(); + std::cout << "Processing " << nHitsEntries << " hit entries..." << std::endl; + + for (Long64_t iEntry = 0; iEntry < nHitsEntries; ++iEntry) { + hitsTree->GetEntry(iEntry); + + for (const auto& hit : *trkHit) { + // Skip disk hits (only barrel) + if (gman->getDisk(hit.GetDetectorID()) != -1) { + continue; + } + + // Determine layer + int subDetID = gman->getSubDetID(hit.GetDetectorID()); + const int layer = startLayer[subDetID] + gman->getLayer(hit.GetDetectorID()); + + // Create label for this particle + o2::MCCompLabel label(hit.GetTrackID(), static_cast(iEntry), 0); + + // Add hit to particle's hit map + particleHitMap[label].addHit(layer); + } + } + + std::cout << "Found " << particleHitMap.size() << " unique particles with hits" << std::endl; + + // Store particle info and fill generated histograms + std::unordered_map particlePtMap; + + // Create histograms + constexpr int nLayers = 11; + constexpr int nb = 100; + double xbins[nb + 1], ptcutl = 0.05, ptcuth = 10.; + double a = std::log(ptcuth / ptcutl) / nb; + for (int i = 0; i <= nb; i++) + xbins[i] = ptcutl * std::exp(i * a); + + TH1D genParticlePtHist("genParticlePt", "Generated Particle p_{T} (All Layers); #it{p}_{T} (GeV/#it{c}); Counts", nb, xbins); + TH1D genParticlePt7LayersHist("genParticlePt7Layers", "Generated Particle p_{T} with hits in at least 7 consecutive layers; #it{p}_{T} (GeV/#it{c}); Counts", nb, xbins); + TH1D goodTracks("goodTracks", "Good Tracks; p_{T} (GeV/c); Counts", nb, xbins); + TH1D fakeTracks("fakeTracks", "Fake Tracks; p_{T} (GeV/c); Counts", nb, xbins); + + std::array goodTracksMatching, fakeTracksMatching; + for (int i = 0; i < 5; ++i) { + goodTracksMatching[i] = TH1D(Form("goodTracksMatching_%dLayers", i + 7), + Form("Good Tracks with %d layer hits; p_{T} (GeV/c); Counts", i + 7), + nb, xbins); + fakeTracksMatching[i] = TH1D(Form("fakeTracksMatching_%dLayers", i + 7), + Form("Fake Tracks with %d layer hits; p_{T} (GeV/c); Counts", i + 7), + nb, xbins); + } + + TH1D numberOfClustersPerTrack("numberOfClustersPerTrack", + "Number of clusters per track; N_{clusters}; Counts", + 12, -0.5, 11.5); + + // First pass: identify particles with full hit coverage from kinematics + std::cout << "Analyzing MC particles..." << std::endl; + for (int iEvent = 0; iEvent < nEvents; ++iEvent) { + const auto& mcTracks = kineReader.getTracks(iEvent); + for (size_t iTrack = 0; iTrack < mcTracks.size(); ++iTrack) { + const auto& mcTrack = mcTracks[iTrack]; + if (!mcTrack.isPrimary()) { + continue; + } + + // Create label for this particle + o2::MCCompLabel label(iTrack, iEvent, 0); + float pt = mcTrack.GetPt(); + + // Store particle info + particlePtMap[label] = pt; + + // Check if this particle has hits + auto hitIt = particleHitMap.find(label); + if (hitIt != particleHitMap.end()) { + // Store pT in hit info + hitIt->second.pt = pt; + + // Fill histogram for particles with hits in all 11 layers + if (hitIt->second.nHits == 11) { + genParticlePtHist.Fill(pt); + } + + // Fill histogram for particles with at least 7 consecutive layer hits + if (hitIt->second.hasConsecutiveLayers(7)) { + genParticlePt7LayersHist.Fill(pt); + } + } + } + } + + std::cout << "Generated particles with 11 hits: " << genParticlePtHist.GetEntries() << std::endl; + std::cout << "Generated particles with 7+ consecutive hits: " << genParticlePt7LayersHist.GetEntries() << std::endl; + + // Second pass: analyze reconstructed tracks + std::cout << "Analyzing reconstructed tracks..." << std::endl; + int nROFs = recTree->GetEntries(); + int totalTracks = 0; + int goodTracksCount = 0; + int fakeTracksCount = 0; + + for (int iROF = 0; iROF < nROFs; ++iROF) { + recTree->GetEntry(iROF); + + if (!recTracks || !trkLabels) { + continue; + } + + totalTracks += recTracks->size(); + + for (size_t iTrack = 0; iTrack < recTracks->size(); ++iTrack) { + const auto& track = recTracks->at(iTrack); + const auto& label = trkLabels->at(iTrack); + + if (!label.isSet() || !label.isValid()) { + continue; + } + + int eventID = label.getEventID(); + int trackID = label.getTrackID(); + int nClusters = track.getNumberOfClusters(); + + // Get MC track info + if (eventID < 0 || eventID >= nEvents) { + continue; + } + + const auto& mcTracks = kineReader.getTracks(eventID); + if (trackID < 0 || trackID >= (int)mcTracks.size()) { + continue; + } + + float pt = mcTracks[trackID].GetPt(); + + // Fill histograms + numberOfClustersPerTrack.Fill(nClusters); + + auto key = o2::MCCompLabel(trackID, eventID, 0); + if (particleHitMap.find(key) != particleHitMap.end() && particleHitMap[key].hasConsecutiveLayers(11)) { + if (label.isFake()) { + fakeTracks.Fill(pt); + fakeTracksCount++; + if (nClusters >= 7 && nClusters <= 11) { + fakeTracksMatching[nClusters - 7].Fill(pt); + } + } else { + goodTracks.Fill(pt); + goodTracksCount++; + if (nClusters >= 7 && nClusters <= 11) { + goodTracksMatching[nClusters - 7].Fill(pt); + } + } + } + } + } + + // Create efficiency histograms + std::cout << "Computing efficiencies..." << std::endl; + + std::array efficiencyHistograms; + THStack* efficiencyStack = new THStack("efficiencyStack", + "Tracking Efficiency; #it{p}_{T} (GeV/#it{c}); Efficiency"); + + int colors[5] = {kRed, kBlue, kGreen + 2, kMagenta, kOrange}; + for (int i = 0; i < 5; ++i) { + int nClusters = i + 7; + efficiencyHistograms[i] = TH1D(Form("efficiency_%dClusters", nClusters), + Form("Efficiency for %d cluster tracks; #it{p}_{T} (GeV/#it{c}); Efficiency", nClusters), + nb, xbins); + + efficiencyHistograms[i].Divide(&goodTracksMatching[i], &genParticlePtHist, 1, 1, "B"); + + efficiencyHistograms[i].SetLineColor(colors[i]); + efficiencyHistograms[i].SetFillColor(colors[i]); + efficiencyHistograms[i].SetLineWidth(2); + efficiencyHistograms[i].SetMarkerColor(colors[i]); + efficiencyHistograms[i].SetMarkerStyle(20 + i); + efficiencyStack->Add(&efficiencyHistograms[i]); + } + + // Write output + std::cout << "Writing output to " << outputfile << std::endl; + TFile outFile(outputfile.c_str(), "RECREATE"); + genParticlePtHist.Write(); + goodTracks.Write(); + fakeTracks.Write(); + for (int i = 0; i < 5; ++i) { + goodTracksMatching[i].Write(); + fakeTracksMatching[i].Write(); + efficiencyHistograms[i].Write(); + } + efficiencyStack->Write(); + genParticlePt7LayersHist.Write(); + numberOfClustersPerTrack.Write(); + outFile.Close(); + + // Clean up + hitsFile->Close(); + tracFile->Close(); + delete efficiencyStack; + delete hitsFile; + delete tracFile; +} diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt new file mode 100644 index 0000000000000..01ddc783d192b --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt @@ -0,0 +1,34 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_library(TRKReconstruction + TARGETVARNAME targetName + SOURCES src/TimeFrame.cxx + PUBLIC_LINK_LIBRARIES + O2::ITStracking + O2::GPUCommon + Microsoft.GSL::GSL + O2::CommonConstants + O2::DataFormatsITSMFT + O2::SimulationDataFormat + O2::ITSBase + O2::ITSReconstruction + O2::ITSMFTReconstruction + O2::DataFormatsITS + O2::TRKSimulation + nlohmann_json::nlohmann_json + PRIVATE_LINK_LIBRARIES + O2::Steer + TBB::tbb) + +o2_target_root_dictionary(TRKReconstruction + HEADERS include/TRKReconstruction/TimeFrame.h + LINKDEF src/TRKReconstructionLinkDef.h) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h new file mode 100644 index 0000000000000..d2ca6fba132e1 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h @@ -0,0 +1,73 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file TimeFrame.h +/// \brief TRK TimeFrame class derived from ITS TimeFrame +/// + +#ifndef ALICEO2_TRK_TIMEFRAME_H +#define ALICEO2_TRK_TIMEFRAME_H + +#include "ITStracking/TimeFrame.h" +#include "ITStracking/Constants.h" +#include "ITStracking/Configuration.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include +#include +#include +#include + +#include + +class TTree; + +namespace o2 +{ +namespace trk +{ +class Hit; +class GeometryTGeo; + +/// TRK TimeFrame class that extends ITS TimeFrame functionality +/// This allows for customization of tracking algorithms specific to the TRK detector +template +class TimeFrame : public o2::its::TimeFrame +{ + public: + TimeFrame() = default; + ~TimeFrame() override = default; + + /// Override methods if needed for TRK-specific behavior + /// For now, we inherit all functionality from ITS TimeFrame + + /// Process hits from TTree to initialize ROFs + /// \param hitsTree Tree containing TRK hits + /// \param mcHeaderTree Tree containing MC event headers + /// \param nEvents Number of events to process + /// \param gman TRK geometry manager instance + /// \param config Configuration parameters for hit reconstruction + int loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, const nlohmann::json& config); + + /// Add primary vertices from MC headers for each ROF + /// \param mcHeaderTree Tree containing MC event headers + /// \param nRofs Number of ROFs (Read-Out Frames) + /// \param nEvents Number of events to process + /// \param inROFpileup Number of events per ROF + void getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup); + + private: + ClassDefNV(TimeFrame, 1); +}; + +} // namespace trk +} // namespace o2 + +#endif // ALICEO2_TRK_TIMEFRAME_H diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h new file mode 100644 index 0000000000000..09ab598ec626c --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h @@ -0,0 +1,20 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::trk::TimeFrame < 11> + ; + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx new file mode 100644 index 0000000000000..686270826049b --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx @@ -0,0 +1,189 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file TimeFrame.cxx +/// \brief TRK TimeFrame implementation +/// + +#include "TRKReconstruction/TimeFrame.h" +#include "TRKSimulation/Hit.h" +#include "TRKBase/GeometryTGeo.h" +#include "Framework/Logger.h" +#include "SimulationDataFormat/MCEventHeader.h" +#include +#include +#include +#include + +namespace o2::trk +{ + +template +int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, const nlohmann::json& config) +{ + constexpr std::array startLayer{0, 3}; + const Long64_t nEvents = hitsTree->GetEntries(); + + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L) | o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); + + std::vector* trkHit = nullptr; + hitsTree->SetBranchAddress("TRKHit", &trkHit); + + const int inROFpileup{config.contains("inROFpileup") ? config["inROFpileup"].get() : 1}; + + // Calculate number of ROFs and initialize data structures + this->mNrof = (nEvents + inROFpileup - 1) / inROFpileup; + + // Reset and prepare ROF data structures + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + this->mMinR[iLayer] = std::numeric_limits::max(); + this->mMaxR[iLayer] = std::numeric_limits::lowest(); + this->mROFramesClusters[iLayer].clear(); + this->mROFramesClusters[iLayer].resize(this->mNrof + 1, 0); + this->mUnsortedClusters[iLayer].clear(); + this->mTrackingFrameInfo[iLayer].clear(); + this->mClusterExternalIndices[iLayer].clear(); + } + + // Pre-count hits to reserve memory efficiently + int totalNHits{0}; + std::array clusterCountPerLayer{}; + for (Long64_t iEvent = 0; iEvent < nEvents; ++iEvent) { + hitsTree->GetEntry(iEvent); + for (const auto& hit : *trkHit) { + if (gman->getDisk(hit.GetDetectorID()) != -1) { + continue; // skip non-barrel hits + } + int subDetID = gman->getSubDetID(hit.GetDetectorID()); + const int layer = startLayer[subDetID] + gman->getLayer(hit.GetDetectorID()); + ++clusterCountPerLayer[layer]; + totalNHits++; + } + trkHit->clear(); + } + + // Reserve memory for all layers + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + this->mUnsortedClusters[iLayer].reserve(clusterCountPerLayer[iLayer]); + this->mTrackingFrameInfo[iLayer].reserve(clusterCountPerLayer[iLayer]); + this->mClusterExternalIndices[iLayer].reserve(clusterCountPerLayer[iLayer]); + } + clearResizeBoundedVector(this->mClusterSize, totalNHits, this->mMemoryPool.get()); + + std::array resolution{0.001, 0.001, 0.001, 0.001, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004}; + if (config["geometry"]["pitch"].size() == nLayers) { + for (int iLayer{0}; iLayer < config["geometry"]["pitch"].size(); ++iLayer) { + LOGP(info, "Setting resolution for layer {} from config", iLayer); + LOGP(info, "Layer {} pitch {} cm", iLayer, config["geometry"]["pitch"][iLayer].get()); + resolution[iLayer] = config["geometry"]["pitch"][iLayer].get() / std::sqrt(12.f); + } + } + LOGP(info, "Number of active parts in VD: {}", gman->getNumberOfActivePartsVD()); + + int hitCounter{0}; + auto labels = new dataformats::MCTruthContainer(); + + int iRof{0}; // Current ROF index + for (Long64_t iEvent = 0; iEvent < nEvents; ++iEvent) { + hitsTree->GetEntry(iEvent); + + for (auto& hit : *trkHit) { + if (gman->getDisk(hit.GetDetectorID()) != -1) { + continue; // skip non-barrel hits for this test + } + int subDetID = gman->getSubDetID(hit.GetDetectorID()); + const int layer = startLayer[subDetID] + gman->getLayer(hit.GetDetectorID()); + + float alpha{0.f}; + o2::math_utils::Point3D gloXYZ; + o2::math_utils::Point3D trkXYZ; + float r{0.f}; + if (layer >= 3) { + int chipID = hit.GetDetectorID(); + alpha = gman->getSensorRefAlphaMLOT(chipID); + const o2::math_utils::Transform3D& l2g = gman->getMatrixL2G(chipID); + auto locXYZ = l2g ^ (hit.GetPos()); + locXYZ.SetX(locXYZ.X() + gRandom->Gaus(0.0, resolution[layer])); + locXYZ.SetZ(locXYZ.Z() + gRandom->Gaus(0.0, resolution[layer])); + gloXYZ = gman->getMatrixL2G(chipID) * locXYZ; + trkXYZ = gman->getMatrixT2L(chipID - gman->getNumberOfActivePartsVD()) ^ locXYZ; + r = std::hypot(gloXYZ.X(), gloXYZ.Y()); + } else { + const auto& hitPos = hit.GetPos(); + r = std::hypot(hitPos.X(), hitPos.Y()); + alpha = std::atan2(hitPos.Y(), hitPos.X()) + gRandom->Gaus(0.0, resolution[layer] / r); + o2::math_utils::bringTo02Pi(alpha); + gloXYZ.SetX(r * std::cos(alpha)); + gloXYZ.SetY(r * std::sin(alpha)); + gloXYZ.SetZ(hitPos.Z() + gRandom->Gaus(0.0, resolution[layer])); + trkXYZ.SetX(r); + trkXYZ.SetY(0.f); + trkXYZ.SetZ(gloXYZ.Z()); + } + this->mMinR[layer] = std::min(this->mMinR[layer], r); + this->mMaxR[layer] = std::max(this->mMaxR[layer], r); + this->addTrackingFrameInfoToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), trkXYZ.x(), alpha, + std::array{trkXYZ.y(), trkXYZ.z()}, + std::array{resolution[layer] * resolution[layer], 0., resolution[layer] * resolution[layer]}); + /// Rotate to the global frame + this->addClusterToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), this->mUnsortedClusters[layer].size()); + this->addClusterExternalIndexToLayer(layer, hitCounter); + MCCompLabel label{hit.GetTrackID(), static_cast(iEvent), 0}; + labels->addElement(hitCounter, label); + this->mClusterSize[hitCounter] = 1; // For compatibility with cluster-based tracking, set cluster size to 1 for hits + hitCounter++; + } + trkHit->clear(); + + // Update ROF structure when we complete an ROF or reach the last event + if ((iEvent + 1) % inROFpileup == 0 || iEvent == nEvents - 1) { + iRof++; + for (unsigned int iLayer{0}; iLayer < this->mUnsortedClusters.size(); ++iLayer) { + this->mROFramesClusters[iLayer][iRof] = this->mUnsortedClusters[iLayer].size(); // effectively calculating an exclusive sum + } + // Update primary vertices ROF structure + } + this->mClusterLabels = labels; + } + return this->mNrof; +} + +template +void TimeFrame::getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup) +{ + auto mcheader = new o2::dataformats::MCEventHeader; + mcHeaderTree->SetBranchAddress("MCEventHeader.", &mcheader); + + this->mROFramesPV.clear(); + this->mROFramesPV.resize(nRofs + 1, 0); + this->mPrimaryVertices.clear(); + + int iRof{0}; + for (Long64_t iEvent = 0; iEvent < nEvents; ++iEvent) { + mcHeaderTree->GetEntry(iEvent); + o2::its::Vertex vertex; + vertex.setXYZ(mcheader->GetX(), mcheader->GetY(), mcheader->GetZ()); + vertex.setNContributors(30); + vertex.setChi2(0.f); + LOGP(debug, "ROF {}: Added primary vertex at ({}, {}, {})", iRof, mcheader->GetX(), mcheader->GetY(), mcheader->GetZ()); + this->mPrimaryVertices.push_back(vertex); + if ((iEvent + 1) % inROFpileup == 0 || iEvent == nEvents - 1) { + iRof++; + this->mROFramesPV[iRof] = this->mPrimaryVertices.size(); // effectively calculating an exclusive sum + } + } + this->mMultiplicityCutMask.resize(nRofs, true); /// all ROFs are valid with MC primary vertices. +} + +// Explicit template instantiation for TRK with 11 layers +template class TimeFrame<11>; + +} // namespace o2::trk diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt index e86ed7982c85b..d6c8ea85c2bbd 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt @@ -14,17 +14,23 @@ o2_add_library(TRKWorkflow SOURCES src/DigitReaderSpec.cxx src/DigitWriterSpec.cxx src/TrackerSpec.cxx + src/TrackWriterSpec.cxx src/RecoWorkflow.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::GPUWorkflow O2::SimConfig O2::DataFormatsITSMFT O2::SimulationDataFormat - O2::DPLUtils) + O2::DPLUtils + O2::TRKBase + O2::TRKSimulation + O2::TRKReconstruction + nlohmann_json::nlohmann_json) o2_add_executable(reco-workflow SOURCES src/trk-reco-workflow.cxx COMPONENT_NAME alice3-trk PUBLIC_LINK_LIBRARIES O2::TRKWorkflow O2::TRKSimulation - O2::ITStracking) \ No newline at end of file + O2::TRKReconstruction + O2::ITStracking) diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/README.md b/Detectors/Upgrades/ALICE3/TRK/workflow/README.md new file mode 100644 index 0000000000000..afb30ed6dbdd3 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/README.md @@ -0,0 +1,130 @@ +# TRK Reconstruction Workflow + +This document describes how to run the TRK (ALICE 3 Tracker) reconstruction workflow and provides examples of configuration files. + +## Overview + +The TRK reconstruction workflow performs track reconstruction from simulated hits, producing reconstructed tracks with MC truth labels. The workflow currently supports the track reconstruction from hits using the Cellular Automaton (CA) algorithm. The ouput is stored to a ROOT file for offline analysis (example of QA macro provided in `macros/test/CheckTracksCA.C`). + +## Quick Start + +### Basic Command + +```bash +o2-alice3-trk-reco-workflow --tracking-from-hits-config config_tracker.json -b +``` + +### Command Line Options + +- `--tracking-from-hits-config `: Path to tracking configuration JSON file (required) +- `-b`: Batch mode (no GUI) +- `--disable-root-output`: Skip writing tracks to ROOT file +- `--help`: Show all available options + +## Configuration File + +The tracking configuration is provided via a JSON file that specifies: +1. Input file paths +2. Geometry parameters (magnetic field, detector pitch) +3. Tracking algorithm parameters (can specify multiple iterations) + +### Example Configuration (`config_tracker.json`) + +```json +{ + "inputfiles": { + "hits": "o2sim_HitsTRK.root", + "geometry": "o2sim_geometry.root", + "mcHeader": "o2sim_MCHeader.root", + "kinematics": "o2sim_Kine.root" + }, + "geometry": { + "bz": 5.0, + "pitch": [0.001, 0.001, 0.001, 0.001, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004] + }, + "trackingparams": [{ + "NLayers": 11, + "DeltaROF": 0, + "LayerZ": [25.1, 25.1, 25.1, 64.2, 64.2, 64.2, 64.2, 64.2, 128.5, 128.5, 128.5], + "LayerRadii": [0.5, 1.2, 2.5, 7.05, 9.05, 12.05, 20.05, 30.05, 45.05, 60.5, 80.05], + "LayerxX0": [0.001, 0.001, 0.001, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01], + "LayerResolution": [0.0003, 0.0003, 0.0003, 0.0003, 0.0012, 0.0012, 0.0012, 0.0012, 0.0012, 0.0012, 0.0012], + "SystErrorY2": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + "SystErrorZ2": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + "ZBins": 256, + "PhiBins": 128, + "nROFsPerIterations": -1, + "UseDiamond": false, + "Diamond": [0.0, 0.0, 0.0], + "AllowSharingFirstCluster": false, + "ClusterSharing": 0, + "MinTrackLength": 7, + "NSigmaCut": 10, + "PVres": 0.01, + "TrackletMinPt": 0.1, + "TrackletsPerClusterLimit": 2.0, + "CellDeltaTanLambdaSigma": 0.007, + "CellsPerClusterLimit": 2.0, + "MaxChi2ClusterAttachment": 60.0, + "MaxChi2NDF": 30.0, + "ReseedIfShorter": 6, + "MinPt": [0.0, 0.0, 0.0, 0.0], + "StartLayerMask": 4095, + "RepeatRefitOut": false, + "ShiftRefToCluster": true, + "FindShortTracks": false, + "PerPrimaryVertexProcessing": false, + "SaveTimeBenchmarks": false, + "DoUPCIteration": false, + "FataliseUponFailure": true, + "UseTrackFollower": true, + "UseTrackFollowerTop": false, + "UseTrackFollowerBot": false, + "UseTrackFollowerMix": true, + "TrackFollowerNSigmaCutZ": 1.0, + "TrackFollowerNSigmaCutPhi": 1.0, + "createArtefactLabels": false, + "PrintMemory": false, + "DropTFUponFailure": false + }] +} +``` +Note that the `trackingparams` field can contain multiple sets of parameters for different iterations of the tracking algorithm. The example above shows a single iteration with 11 layers and it is **not** optimized. + +## Complete Workflow Example + +### 1. Run Simulation + +First, generate simulation data: + +```bash +o2-sim-serial-run5 -n 200 -g pythia8hi -m TRK --configKeyValues "Diamond.width[0]=0.01;Diamond.width[1]=0.01;Diamond.width[2]=5;TRKBase.layoutML=kTurboStaves;TRKBase.layoutOL=kStaggered;" +``` + +This produces, among other files: +- `o2sim_HitsTRK.root` +- `o2sim_geometry.root` +- `o2sim_MCHeader.root` +- `o2sim_Kine.root` +That will be used by the reconstruction as currently we do not have clusters. + +### 2. Run Reconstruction + +Execute the tracking workflow: + +```bash +o2-alice3-trk-reco-workflow --tracking-from-hits-config config_tracker.json -b +``` + +This produces: +- `o2trac_trk.root`: Reconstructed tracks with MC labels + +### 3. Run Quality Assurance + +Analyze the tracking performance: + +```bash +root -l +.L CheckTracksCA.C+ +CheckTracksCA("o2trac_trk.root", "o2sim_Kine.root", "o2sim_HitsTRK.root", "trk_qa_output.root") +``` diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h index 98d4154f11dd8..7046955a20c2e 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h @@ -14,6 +14,7 @@ #include "Framework/WorkflowSpec.h" #include "GPUDataTypesConfig.h" +#include namespace o2::trk { @@ -21,6 +22,7 @@ namespace reco_workflow { o2::framework::WorkflowSpec getWorkflow(bool useMC, + const std::string& hitRecoConfig, bool upstreamDigits = false, bool upstreamClusters = false, bool disableRootOutput = false, diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackWriterSpec.h b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackWriterSpec.h new file mode 100644 index 0000000000000..105504e7c9fe6 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackWriterSpec.h @@ -0,0 +1,31 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file TrackWriterSpec.h + +#ifndef O2_TRK_TRACKWRITER +#define O2_TRK_TRACKWRITER + +#include "Framework/DataProcessorSpec.h" + +namespace o2 +{ +namespace trk +{ + +/// create a processor spec +/// write TRK tracks to ROOT file +o2::framework::DataProcessorSpec getTrackWriterSpec(bool useMC); + +} // namespace trk +} // namespace o2 + +#endif /* O2_TRK_TRACKWRITER */ diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h index dac1826e21cf6..33b25737bbc29 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h @@ -19,6 +19,9 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" +#include + +#include "ITStracking/BoundedAllocator.h" #include "ITStracking/TrackingInterface.h" #include "GPUDataTypesConfig.h" @@ -26,6 +29,8 @@ #include "TStopwatch.h" +#include + namespace o2::trk { class TrackerDPL : public framework::Task @@ -33,6 +38,7 @@ class TrackerDPL : public framework::Task public: TrackerDPL(std::shared_ptr gr, bool isMC, + const std::string& hitRecoConfig, gpu::gpudatatypes::DeviceType dType = gpu::gpudatatypes::DeviceType::CPU); ~TrackerDPL() override = default; void init(framework::InitContext& ic) final; @@ -43,14 +49,18 @@ class TrackerDPL : public framework::Task private: void updateTimeDependentParams(framework::ProcessingContext& pc); + std::vector createTrackingParamsFromConfig(); // std::unique_ptr mRecChain = nullptr; // std::unique_ptr mChainITS = nullptr; // std::shared_ptr mGGCCDBRequest; // ITSTrackingInterface mITSTrackingInterface; + std::shared_ptr mMemoryPool; + std::shared_ptr mTaskArena; + nlohmann::json mHitRecoConfig; TStopwatch mTimer; }; -framework::DataProcessorSpec getTrackerSpec(bool useMC, gpu::gpudatatypes::DeviceType dType = gpu::gpudatatypes::DeviceType::CPU); +framework::DataProcessorSpec getTrackerSpec(bool useMC, const std::string& hitRecoConfig, gpu::gpudatatypes::DeviceType dType = gpu::gpudatatypes::DeviceType::CPU); } // namespace o2::trk #endif /* O2_TRK_TRACKERDPL */ diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx index 09d447a576e48..5f6cbe2f96b04 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx @@ -11,12 +11,16 @@ #include "TRKWorkflow/RecoWorkflow.h" #include "TRKWorkflow/TrackerSpec.h" +#include "TRKWorkflow/TrackWriterSpec.h" #include "Framework/CCDBParamSpec.h" +#include + namespace o2::trk::reco_workflow { framework::WorkflowSpec getWorkflow(bool useMC, + const std::string& hitRecoConfig, bool upstreamDigits, bool upstreamClusters, bool disableRootOutput, @@ -24,7 +28,12 @@ framework::WorkflowSpec getWorkflow(bool useMC, o2::gpu::gpudatatypes::DeviceType dtype) { framework::WorkflowSpec specs; - specs.emplace_back(o2::trk::getTrackerSpec(useMC, dtype)); + specs.emplace_back(o2::trk::getTrackerSpec(useMC, hitRecoConfig, dtype)); + + if (!disableRootOutput) { + specs.emplace_back(o2::trk::getTrackWriterSpec(useMC)); + } + return specs; } diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackWriterSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackWriterSpec.cxx new file mode 100644 index 0000000000000..1606c32a0ea78 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackWriterSpec.cxx @@ -0,0 +1,57 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file TrackWriterSpec.cxx + +#include + +#include "TRKWorkflow/TrackWriterSpec.h" +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "DataFormatsITS/TrackITS.h" +#include "SimulationDataFormat/MCCompLabel.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace trk +{ + +template +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; +using LabelsType = std::vector; +using namespace o2::header; + +DataProcessorSpec getTrackWriterSpec(bool useMC) +{ + // Spectators for logging + auto tracksSize = std::make_shared(0); + auto tracksSizeGetter = [tracksSize](std::vector const& tracks) { + *tracksSize = tracks.size(); + }; + auto logger = [tracksSize]() { + LOG(info) << "TRKTrackWriter pulled " << *tracksSize << " tracks"; + }; + + return MakeRootTreeWriterSpec("trk-track-writer", + "o2trac_trk.root", + MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Tree with TRK tracks"}, + BranchDefinition>{InputSpec{"tracks", "TRK", "TRACKS", 0}, + "TRKTrack", + tracksSizeGetter}, + BranchDefinition{InputSpec{"labels", "TRK", "TRACKSMCTR", 0}, + "TRKTrackMCTruth", + (useMC ? 1 : 0), // one branch if mc labels enabled + ""})(); +} + +} // namespace trk +} // namespace o2 diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx index 868a8acc0fc6e..8f26478f4496e 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx @@ -11,10 +11,24 @@ #include +#include "DetectorsBase/GeometryManager.h" +#include "ITStracking/TimeFrame.h" +#include "ITStracking/Configuration.h" +#include "Field/MagneticField.h" #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" +#include "SimulationDataFormat/MCEventHeader.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "TRKBase/GeometryTGeo.h" +#include "TRKBase/SegmentationChip.h" +#include "TRKSimulation/Hit.h" +#include "TRKReconstruction/TimeFrame.h" #include "TRKWorkflow/TrackerSpec.h" +#include + +#include +#include namespace o2 { @@ -25,8 +39,14 @@ using Vertex = o2::dataformats::Vertex>; TrackerDPL::TrackerDPL(std::shared_ptr gr, bool isMC, + const std::string& hitRecoConfigFileName, o2::gpu::gpudatatypes::DeviceType dType) { + if (!hitRecoConfigFileName.empty()) { + std::ifstream configFile(hitRecoConfigFileName); + mHitRecoConfig = nlohmann::json::parse(configFile); + } + // mITSTrackingInterface.setTrackingMode(trMode); } @@ -46,13 +66,288 @@ void TrackerDPL::stop() LOGF(info, "CPU Reconstruction total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } +std::vector TrackerDPL::createTrackingParamsFromConfig() +{ + std::vector trackingParams; + + if (!mHitRecoConfig.contains("trackingparams") || !mHitRecoConfig["trackingparams"].is_array()) { + LOGP(fatal, "No trackingparams field found in configuration or it is not an array. Returning empty vector."); + return trackingParams; + } + + for (const auto& paramConfig : mHitRecoConfig["trackingparams"]) { + o2::its::TrackingParameters params; + + // Parse integer parameters + if (paramConfig.contains("NLayers")) { + params.NLayers = paramConfig["NLayers"].get(); + } + if (paramConfig.contains("DeltaROF")) { + params.DeltaROF = paramConfig["DeltaROF"].get(); + } + if (paramConfig.contains("ZBins")) { + params.ZBins = paramConfig["ZBins"].get(); + } + if (paramConfig.contains("PhiBins")) { + params.PhiBins = paramConfig["PhiBins"].get(); + } + if (paramConfig.contains("nROFsPerIterations")) { + params.nROFsPerIterations = paramConfig["nROFsPerIterations"].get(); + } + if (paramConfig.contains("ClusterSharing")) { + params.ClusterSharing = paramConfig["ClusterSharing"].get(); + } + if (paramConfig.contains("MinTrackLength")) { + params.MinTrackLength = paramConfig["MinTrackLength"].get(); + } + if (paramConfig.contains("ReseedIfShorter")) { + params.ReseedIfShorter = paramConfig["ReseedIfShorter"].get(); + } + if (paramConfig.contains("StartLayerMask")) { + params.StartLayerMask = paramConfig["StartLayerMask"].get(); + } + + // Parse float parameters + if (paramConfig.contains("NSigmaCut")) { + params.NSigmaCut = paramConfig["NSigmaCut"].get(); + } + if (paramConfig.contains("PVres")) { + params.PVres = paramConfig["PVres"].get(); + } + if (paramConfig.contains("TrackletMinPt")) { + params.TrackletMinPt = paramConfig["TrackletMinPt"].get(); + } + if (paramConfig.contains("TrackletsPerClusterLimit")) { + params.TrackletsPerClusterLimit = paramConfig["TrackletsPerClusterLimit"].get(); + } + if (paramConfig.contains("CellDeltaTanLambdaSigma")) { + params.CellDeltaTanLambdaSigma = paramConfig["CellDeltaTanLambdaSigma"].get(); + } + if (paramConfig.contains("CellsPerClusterLimit")) { + params.CellsPerClusterLimit = paramConfig["CellsPerClusterLimit"].get(); + } + if (paramConfig.contains("MaxChi2ClusterAttachment")) { + params.MaxChi2ClusterAttachment = paramConfig["MaxChi2ClusterAttachment"].get(); + } + if (paramConfig.contains("MaxChi2NDF")) { + params.MaxChi2NDF = paramConfig["MaxChi2NDF"].get(); + } + if (paramConfig.contains("TrackFollowerNSigmaCutZ")) { + params.TrackFollowerNSigmaCutZ = paramConfig["TrackFollowerNSigmaCutZ"].get(); + } + if (paramConfig.contains("TrackFollowerNSigmaCutPhi")) { + params.TrackFollowerNSigmaCutPhi = paramConfig["TrackFollowerNSigmaCutPhi"].get(); + } + + // Parse boolean parameters + if (paramConfig.contains("UseDiamond")) { + params.UseDiamond = paramConfig["UseDiamond"].get(); + } + if (paramConfig.contains("AllowSharingFirstCluster")) { + params.AllowSharingFirstCluster = paramConfig["AllowSharingFirstCluster"].get(); + } + if (paramConfig.contains("RepeatRefitOut")) { + params.RepeatRefitOut = paramConfig["RepeatRefitOut"].get(); + } + if (paramConfig.contains("ShiftRefToCluster")) { + params.ShiftRefToCluster = paramConfig["ShiftRefToCluster"].get(); + } + if (paramConfig.contains("FindShortTracks")) { + params.FindShortTracks = paramConfig["FindShortTracks"].get(); + } + if (paramConfig.contains("PerPrimaryVertexProcessing")) { + params.PerPrimaryVertexProcessing = paramConfig["PerPrimaryVertexProcessing"].get(); + } + if (paramConfig.contains("SaveTimeBenchmarks")) { + params.SaveTimeBenchmarks = paramConfig["SaveTimeBenchmarks"].get(); + } + if (paramConfig.contains("DoUPCIteration")) { + params.DoUPCIteration = paramConfig["DoUPCIteration"].get(); + } + if (paramConfig.contains("FataliseUponFailure")) { + params.FataliseUponFailure = paramConfig["FataliseUponFailure"].get(); + } + if (paramConfig.contains("UseTrackFollower")) { + params.UseTrackFollower = paramConfig["UseTrackFollower"].get(); + } + if (paramConfig.contains("UseTrackFollowerTop")) { + params.UseTrackFollowerTop = paramConfig["UseTrackFollowerTop"].get(); + } + if (paramConfig.contains("UseTrackFollowerBot")) { + params.UseTrackFollowerBot = paramConfig["UseTrackFollowerBot"].get(); + } + if (paramConfig.contains("UseTrackFollowerMix")) { + params.UseTrackFollowerMix = paramConfig["UseTrackFollowerMix"].get(); + } + if (paramConfig.contains("createArtefactLabels")) { + params.createArtefactLabels = paramConfig["createArtefactLabels"].get(); + } + if (paramConfig.contains("PrintMemory")) { + params.PrintMemory = paramConfig["PrintMemory"].get(); + } + if (paramConfig.contains("DropTFUponFailure")) { + params.DropTFUponFailure = paramConfig["DropTFUponFailure"].get(); + } + + // Parse vector parameters + if (paramConfig.contains("LayerZ")) { + params.LayerZ = paramConfig["LayerZ"].get>(); + } + if (paramConfig.contains("LayerRadii")) { + params.LayerRadii = paramConfig["LayerRadii"].get>(); + } + if (paramConfig.contains("LayerxX0")) { + params.LayerxX0 = paramConfig["LayerxX0"].get>(); + } + if (paramConfig.contains("LayerResolution")) { + params.LayerResolution = paramConfig["LayerResolution"].get>(); + } + if (paramConfig.contains("SystErrorY2")) { + params.SystErrorY2 = paramConfig["SystErrorY2"].get>(); + } + if (paramConfig.contains("SystErrorZ2")) { + params.SystErrorZ2 = paramConfig["SystErrorZ2"].get>(); + } + if (paramConfig.contains("MinPt")) { + params.MinPt = paramConfig["MinPt"].get>(); + } + + // Parse Diamond array + if (paramConfig.contains("Diamond") && paramConfig["Diamond"].is_array() && paramConfig["Diamond"].size() == 3) { + params.Diamond[0] = paramConfig["Diamond"][0].get(); + params.Diamond[1] = paramConfig["Diamond"][1].get(); + params.Diamond[2] = paramConfig["Diamond"][2].get(); + } + + // Parse size_t parameter + if (paramConfig.contains("MaxMemory")) { + params.MaxMemory = paramConfig["MaxMemory"].get(); + } + + // Parse CorrType enum + if (paramConfig.contains("CorrType")) { + int corrTypeInt = paramConfig["CorrType"].get(); + params.CorrType = static_cast::MatCorrType>(corrTypeInt); + } + + trackingParams.push_back(params); + } + + LOGP(info, "Loaded {} tracking parameter sets from configuration", trackingParams.size()); + return trackingParams; +} + void TrackerDPL::run(ProcessingContext& pc) { auto cput = mTimer.CpuTime(); auto realt = mTimer.RealTime(); mTimer.Start(false); - // mITSTrackingInterface.updateTimeDependentParams(pc); - // mITSTrackingInterface.run(pc); + + if (!mHitRecoConfig.empty()) { + TFile hitsFile(mHitRecoConfig["inputfiles"]["hits"].get().c_str(), "READ"); + TFile mcHeaderFile(mHitRecoConfig["inputfiles"]["mcHeader"].get().c_str(), "READ"); + TTree* hitsTree = hitsFile.Get("o2sim"); + std::vector* trkHit = nullptr; + hitsTree->SetBranchAddress("TRKHit", &trkHit); + + TTree* mcHeaderTree = mcHeaderFile.Get("o2sim"); + auto mcheader = new o2::dataformats::MCEventHeader; + mcHeaderTree->SetBranchAddress("MCEventHeader.", &mcheader); + + o2::base::GeometryManager::loadGeometry(mHitRecoConfig["inputfiles"]["geometry"].get().c_str(), false, true); + auto* gman = o2::trk::GeometryTGeo::Instance(); + + const Long64_t nEvents{hitsTree->GetEntries()}; + LOGP(info, "Starting reconstruction from hits for {} events", nEvents); + + if (mMemoryPool.get() == nullptr) { + mMemoryPool = std::make_shared(); + } + if (mTaskArena.get() == nullptr) { + mTaskArena = std::make_shared(1); /// TODO: make it configurable + } + + o2::trk::TimeFrame<11> timeFrame; + o2::its::TrackerTraits<11> itsTrackerTraits; + o2::its::Tracker<11> itsTracker(&itsTrackerTraits); + timeFrame.setMemoryPool(mMemoryPool); + itsTrackerTraits.setMemoryPool(mMemoryPool); + itsTrackerTraits.setNThreads(mTaskArena->max_concurrency(), mTaskArena); + itsTrackerTraits.adoptTimeFrame(static_cast*>(&timeFrame)); + itsTracker.adoptTimeFrame(timeFrame); + itsTrackerTraits.setBz(mHitRecoConfig["geometry"]["bz"].get()); + auto field = o2::field::MagneticField::createNominalField(std::round(mHitRecoConfig["geometry"]["bz"].get()), true); + TGeoGlobalMagField::Instance()->SetField(field); + TGeoGlobalMagField::Instance()->Lock(); + + int nRofs = timeFrame.loadROFsFromHitTree(hitsTree, gman, mHitRecoConfig); + + const int inROFpileup{mHitRecoConfig.contains("inROFpileup") ? mHitRecoConfig["inROFpileup"].get() : 1}; + + // Add primary vertices from MC headers for each ROF + timeFrame.getPrimaryVerticesFromMC(mcHeaderTree, nRofs, nEvents, inROFpileup); + // Create tracking parameters from config and set them in the time frame + auto trackingParams = createTrackingParamsFromConfig(); + + itsTrackerTraits.updateTrackingParameters(trackingParams); + + for (size_t iter{0}; iter < trackingParams.size(); ++iter) { + LOGP(info, "{}", trackingParams[iter].asString()); + timeFrame.initialise(iter, trackingParams[iter], 11, false); + itsTrackerTraits.computeLayerTracklets(iter, -1, -1); + LOGP(info, "Number of tracklets in iteration {}: {}", iter, timeFrame.getNumberOfTracklets()); + itsTrackerTraits.computeLayerCells(iter); + LOGP(info, "Number of cells in iteration {}: {}", iter, timeFrame.getNumberOfCells()); + itsTrackerTraits.findCellsNeighbours(iter); + LOGP(info, "Number of cell neighbours in iteration {}: {}", iter, timeFrame.getNumberOfNeighbours()); + itsTrackerTraits.findRoads(iter); + LOGP(info, "Number of roads in iteration {}: {}", iter, timeFrame.getNumberOfTracks()); + itsTrackerTraits.extendTracks(iter); + } + + itsTracker.computeTracksMClabels(); + + // Stream tracks and their MC labels to the output + // Collect all tracks and labels from all ROFs + std::vector allTracks; + std::vector allLabels; + + int totalTracks = 0; + int goodTracks = 0; + int fakeTracks = 0; + + for (int iRof = 0; iRof < nRofs; ++iRof) { + const auto& rofTracks = timeFrame.getTracks(iRof); + const auto& rofLabels = timeFrame.getTracksLabel(iRof); + + allTracks.insert(allTracks.end(), rofTracks.begin(), rofTracks.end()); + allLabels.insert(allLabels.end(), rofLabels.begin(), rofLabels.end()); + + totalTracks += rofTracks.size(); + for (const auto& label : rofLabels) { + if (label.isFake()) { + fakeTracks++; + } else { + goodTracks++; + } + } + } + + LOGP(info, "=== Tracking Summary ==="); + LOGP(info, "Total tracks reconstructed: {}", totalTracks); + LOGP(info, "Good tracks: {} ({:.1f}%)", goodTracks, totalTracks > 0 ? 100.0 * goodTracks / totalTracks : 0); + LOGP(info, "Fake tracks: {} ({:.1f}%)", fakeTracks, totalTracks > 0 ? 100.0 * fakeTracks / totalTracks : 0); + + // Stream tracks and labels to DPL output + pc.outputs().snapshot(o2::framework::Output{"TRK", "TRACKS", 0}, allTracks); + pc.outputs().snapshot(o2::framework::Output{"TRK", "TRACKSMCTR", 0}, allLabels); + + LOGP(info, "Tracks and MC labels streamed to output"); + + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(framework::QuitRequest::Me); + } + mTimer.Stop(); LOGP(info, "CPU Reconstruction time for this TF {} s (cpu), {} s (wall)", mTimer.CpuTime() - cput, mTimer.RealTime() - realt); } @@ -67,16 +362,11 @@ void TrackerDPL::endOfStream(EndOfStreamContext& ec) LOGF(info, "TRK CA-Tracker total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getTrackerSpec(bool useMC, o2::gpu::gpudatatypes::DeviceType dType) +DataProcessorSpec getTrackerSpec(bool useMC, const std::string& hitRecoConfig, o2::gpu::gpudatatypes::DeviceType dType) { std::vector inputs; - - // inputs.emplace_back("compClusters", "TRK", "COMPCLUSTERS", 0, Lifetime::Timeframe); - // inputs.emplace_back("patterns", "TRK", "PATTERNS", 0, Lifetime::Timeframe); - // inputs.emplace_back("ROframes", "TRK", "CLUSTERSROF", 0, Lifetime::Timeframe); - - // inputs.emplace_back("itscldict", "TRK", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/ClusterDictionary")); - // inputs.emplace_back("itsalppar", "TRK", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam")); + std::vector outputs; + outputs.emplace_back("TRK", "TRACKS", 0, Lifetime::Timeframe); auto ggRequest = std::make_shared(false, // orbitResetTime false, // GRPECS=true false, // GRPLHCIF @@ -85,8 +375,29 @@ DataProcessorSpec getTrackerSpec(bool useMC, o2::gpu::gpudatatypes::DeviceType d o2::base::GRPGeomRequest::None, // geometry, but ignored until it will be put in the CCDB inputs, true); - std::vector outputs; - outputs.emplace_back("TRK", "TRACKS", 0, Lifetime::Timeframe); + + if (!hitRecoConfig.empty()) { + outputs.emplace_back("TRK", "TRACKSMCTR", 0, Lifetime::Timeframe); + return DataProcessorSpec{ + "trk-hits-tracker", + {}, + outputs, + AlgorithmSpec{adaptFromTask(ggRequest, + useMC, + hitRecoConfig, + dType)}, + Options{ConfigParamSpec{"max-loops", VariantType::Int, 1, {"max number of loops"}}}}; + } + + inputs.emplace_back("dummy", "TRK", "DUMMY", 0, Lifetime::Timeframe); + + // inputs.emplace_back("compClusters", "TRK", "COMPCLUSTERS", 0, Lifetime::Timeframe); + // inputs.emplace_back("patterns", "TRK", "PATTERNS", 0, Lifetime::Timeframe); + // inputs.emplace_back("ROframes", "TRK", "CLUSTERSROF", 0, Lifetime::Timeframe); + + // inputs.emplace_back("itscldict", "TRK", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/ClusterDictionary")); + // inputs.emplace_back("itsalppar", "TRK", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam")); + // outputs.emplace_back("TRK", "TRACKCLSID", 0, Lifetime::Timeframe); // outputs.emplace_back("TRK", "TRKTrackROF", 0, Lifetime::Timeframe); // outputs.emplace_back("TRK", "VERTICES", 0, Lifetime::Timeframe); @@ -108,6 +419,7 @@ DataProcessorSpec getTrackerSpec(bool useMC, o2::gpu::gpudatatypes::DeviceType d outputs, AlgorithmSpec{adaptFromTask(ggRequest, useMC, + hitRecoConfig, dType)}, Options{}}; } diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/trk-reco-workflow.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/trk-reco-workflow.cxx index 8f44b01da1c9c..166e6f65b4b2b 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/trk-reco-workflow.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/trk-reco-workflow.cxx @@ -52,6 +52,7 @@ void customize(std::vector& workflowOptions) {"clusters-from-upstream", VariantType::Bool, false, {"clusters will be provided from upstream, skip clusterizer"}}, {"disable-root-output", VariantType::Bool, false, {"do not write output root files"}}, {"disable-mc", VariantType::Bool, false, {"disable MC propagation even if available"}}, + {"tracking-from-hits-config", VariantType::String, "", {"JSON file with tracking from hits configuration"}}, {"disable-tracking", VariantType::Bool, false, {"disable tracking step"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, {"use-gpu-workflow", VariantType::Bool, false, {"use GPU workflow (default: false)"}}, @@ -66,6 +67,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { // Update the (declared) parameters if changed from the command line auto useMC = !configcontext.options().get("disable-mc"); + auto hitRecoConfig = configcontext.options().get("tracking-from-hits-config"); auto useGpuWF = configcontext.options().get("use-gpu-workflow"); auto gpuDevice = static_cast(configcontext.options().get("gpu-device")); auto extDigits = configcontext.options().get("digits-from-upstream"); @@ -76,5 +78,5 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // write the configuration used for the reco workflow o2::conf::ConfigurableParam::writeINI("o2itsrecoflow_configuration.ini"); - return o2::trk::reco_workflow::getWorkflow(useMC, extDigits, extClusters, disableRootOutput, useGpuWF, gpuDevice); + return o2::trk::reco_workflow::getWorkflow(useMC, hitRecoConfig, extDigits, extClusters, disableRootOutput, useGpuWF, gpuDevice); } From 712170392332bf82ef808d71c26d6a805b8e675f Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Tue, 17 Feb 2026 14:47:47 +0100 Subject: [PATCH 108/133] DPL Analysis: cleanup AnalysisTask.h and ASoA.h (#15008) --- Framework/Core/include/Framework/ASoA.h | 62 +++---- .../Core/include/Framework/AnalysisManagers.h | 6 - .../Core/include/Framework/AnalysisTask.h | 163 +++++++++--------- .../Core/include/Framework/Configurable.h | 15 +- Framework/Core/test/test_Concepts.cxx | 1 + .../include/Framework/StructToTuple.h | 46 ++--- 6 files changed, 136 insertions(+), 157 deletions(-) diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index 4fd35e0dc5065..7586d6a6d3c63 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -405,15 +405,15 @@ class Table; /// Type-checking index column binding struct Binding { void const* ptr = nullptr; - size_t hash = 0; - std::span refs; + uint32_t hash = 0; + // std::span refs; template void bind(T const* table) { ptr = table; hash = o2::framework::TypeIdHelpers::uniqueId(); - refs = std::span{T::originals}; + // refs = std::span{T::originals}; } template @@ -1293,6 +1293,9 @@ struct ArrowHelpers { template concept is_iterator = framework::base_of_template || framework::specialization_of_template; +template +concept is_table_or_iterator = is_table || is_iterator; + template concept with_originals = requires { T::originals.size(); @@ -2724,7 +2727,7 @@ consteval auto getIndexTargets() return !(*mColumnIterator).empty(); \ } \ \ - template \ + template \ auto _Getter_##_as() const \ { \ if (O2_BUILTIN_UNLIKELY(mBinding.ptr == nullptr)) { \ @@ -2734,10 +2737,15 @@ consteval auto getIndexTargets() if (O2_BUILTIN_UNLIKELY(t == nullptr)) { \ o2::soa::dereferenceWithWrongType(#_Getter_, #_Table_); \ } \ - return getIterators(); \ + auto result = std::vector(); \ + result.reserve((*mColumnIterator).size()); \ + for (auto& i : *mColumnIterator) { \ + result.emplace_back(t->rawIteratorAt(i)); \ + } \ + return result; \ } \ \ - template \ + template \ auto filtered_##_Getter_##_as() const \ { \ if (O2_BUILTIN_UNLIKELY(mBinding.ptr == nullptr)) { \ @@ -2747,35 +2755,15 @@ consteval auto getIndexTargets() if (O2_BUILTIN_UNLIKELY(t == nullptr)) { \ o2::soa::dereferenceWithWrongType(#_Getter_, #_Table_); \ } \ - return getFilteredIterators(); \ - } \ - \ - template \ - auto getIterators() const \ - { \ - auto result = std::vector(); \ - for (auto& i : *mColumnIterator) { \ - result.push_back(mBinding.get()->rawIteratorAt(i)); \ - } \ - return result; \ - } \ - \ - template \ - std::vector getFilteredIterators() const \ - { \ - if constexpr (o2::soa::is_filtered_table) { \ - auto result = std::vector(); \ - for (auto const& i : *mColumnIterator) { \ - auto pos = mBinding.get()->isInSelectedRows(i); \ - if (pos > 0) { \ - result.emplace_back(mBinding.get()->iteratorAt(pos)); \ - } \ + auto result = std::vector(); \ + result.reserve((*mColumnIterator).size()); \ + for (auto const& i : *mColumnIterator) { \ + auto pos = t->isInSelectedRows(i); \ + if (pos > 0) { \ + result.emplace_back(t->iteratorAt(pos)); \ } \ - return result; \ - } else { \ - static_assert(o2::framework::always_static_assert_v, "T is not a Filtered type"); \ } \ - return {}; \ + return result; \ } \ \ auto _Getter_() const \ @@ -3090,15 +3078,9 @@ consteval auto getIndexTargets() if (O2_BUILTIN_UNLIKELY(t == nullptr)) { \ o2::soa::dereferenceWithWrongType(#_Getter_, "self"); \ } \ - return getIterators(); \ - } \ - \ - template \ - auto getIterators() const \ - { \ auto result = std::vector(); \ for (auto& i : *mColumnIterator) { \ - result.push_back(mBinding.get()->rawIteratorAt(i)); \ + result.push_back(t->rawIteratorAt(i)); \ } \ return result; \ } \ diff --git a/Framework/Core/include/Framework/AnalysisManagers.h b/Framework/Core/include/Framework/AnalysisManagers.h index fd41a079c6570..121ce7f4b4a77 100644 --- a/Framework/Core/include/Framework/AnalysisManagers.h +++ b/Framework/Core/include/Framework/AnalysisManagers.h @@ -534,12 +534,6 @@ void bindExternalIndicesPartition(P& partition, T*... tables) } /// Cache handling -template -bool preInitializeCache(InitContext&, T&) -{ - return false; -} - template bool initializeCache(ProcessingContext&, T&) { diff --git a/Framework/Core/include/Framework/AnalysisTask.h b/Framework/Core/include/Framework/AnalysisTask.h index eb98d55cc24b2..fbd523c7b0c37 100644 --- a/Framework/Core/include/Framework/AnalysisTask.h +++ b/Framework/Core/include/Framework/AnalysisTask.h @@ -22,7 +22,6 @@ #include "Framework/EndOfStreamContext.h" #include "Framework/GroupSlicer.h" #include "Framework/StructToTuple.h" -#include "Framework/Traits.h" #include "Framework/TypeIdHelpers.h" #include "Framework/ArrowTableSlicingCache.h" #include "Framework/AnalysisDataModel.h" @@ -66,17 +65,20 @@ static constexpr bool is_enumeration_v> = true; template concept is_enumeration = is_enumeration_v>; +template +concept is_table_iterator_or_enumeration = soa::is_table_or_iterator || is_enumeration; + // Helper struct which builds a DataProcessorSpec from // the contents of an AnalysisTask... namespace { struct AnalysisDataProcessorBuilder { - template + template static void addGroupingCandidates(Cache& bk, Cache& bku, bool enabled) { - [&bk, &bku, enabled](framework::pack) mutable { + [](framework::pack, Cache& bk, Cache& bku, bool enabled) { auto key = std::string{"fIndex"} + o2::framework::cutString(soa::getLabelFromType>()); - ([&bk, &bku, &key, enabled]() mutable { + ([](Cache& bk, Cache& bku, bool enabled, std::string const& key) { if constexpr (soa::relatedByIndex, std::decay_t>()) { Entry e{soa::getLabelFromTypeForKey>(key), soa::getMatcherFromTypeForKey>(key), key, enabled}; if constexpr (o2::soa::is_smallgroups>) { @@ -85,9 +87,9 @@ struct AnalysisDataProcessorBuilder { framework::updatePairList(bk, e); } } - }(), + }(bk, bku, enabled, key), ...); - }(framework::pack{}); + }(framework::pack{}, bk, bku, enabled); } template @@ -171,8 +173,8 @@ struct AnalysisDataProcessorBuilder { return true; } /// 1. enumeration (must be the only argument) - template - static void inputsFromArgs(R (C::*)(A), const char* /*name*/, bool /*value*/, std::vector& inputs, std::vector&) //, Cache&, Cache&) + template + static void inputsFromArgs(void (C::*)(A), const char* /*name*/, bool /*value*/, std::vector& inputs, std::vector&) //, Cache&, Cache&) { std::vector inputMetadata; // FIXME: for the moment we do not support begin, end and step. @@ -180,37 +182,37 @@ struct AnalysisDataProcessorBuilder { } /// 2. 1st argument is an iterator - template - static void inputsFromArgs(R (C::*)(A, Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos) //, Cache& bk, Cache& bku) + template + static void inputsFromArgs(void (C::*)(A, Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos) //, Cache& bk, Cache& bku) requires(std::is_lvalue_reference_v && (std::is_lvalue_reference_v && ...)) { - constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); + constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); addInputsAndExpressions::parent_t, Args...>(hash, name, value, inputs, eInfos); } /// 3. generic case - template - static void inputsFromArgs(R (C::*)(Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos) //, Cache&, Cache&) + template + static void inputsFromArgs(void (C::*)(Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos) //, Cache&, Cache&) requires(std::is_lvalue_reference_v && ...) { - constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); + constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); addInputsAndExpressions(hash, name, value, inputs, eInfos); } /// 1. enumeration (no grouping) - template - static void cacheFromArgs(R (C::*)(A), bool, Cache&, Cache&) + template + static void cacheFromArgs(void (C::*)(A), bool, Cache&, Cache&) { } /// 2. iterator (the only grouping case) - template - static void cacheFromArgs(R (C::*)(A, Args...), bool value, Cache& bk, Cache& bku) + template + static void cacheFromArgs(void (C::*)(A, Args...), bool value, Cache& bk, Cache& bku) { addGroupingCandidates(bk, bku, value); } /// 3. generic case (no grouping) - template - static void cacheFromArgs(R (C::*)(A, Args...), bool, Cache&, Cache&) + template + static void cacheFromArgs(void (C::*)(A, Args...), bool, Cache&, Cache&) { } @@ -285,51 +287,53 @@ struct AnalysisDataProcessorBuilder { } } - template - static auto bindGroupingTable(InputRecord& record, R (C::*)(Grouping, Args...), std::vector& infos) + template + static auto bindGroupingTable(InputRecord& record, void (C::*)(Grouping, Args...), std::vector& infos) requires(!std::same_as) { - constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); + constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); return extract, 0>(record, infos, hash); } - template - static auto bindAssociatedTables(InputRecord& record, R (C::*)(Grouping, Args...), std::vector& infos) + template + static auto bindAssociatedTables(InputRecord& record, void (C::*)(Grouping, Args...), std::vector& infos) requires(!std::same_as && sizeof...(Args) > 0) { constexpr auto p = pack{}; - constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); + constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); return std::make_tuple(extract, has_type_at_v(p) + 1>(record, infos, hash)...); } - template + template static void overwriteInternalIndices(std::tuple& dest, std::tuple const& src) { (std::get(dest).bindInternalIndicesTo(&std::get(src)), ...); } - template - static void invokeProcess(Task& task, InputRecord& inputs, R (C::*processingFunction)(Grouping, Associated...), std::vector& infos, ArrowTableSlicingCache& slices) + template + static void invokeProcess(Task& task, InputRecord& inputs, void (Task::*processingFunction)(Grouping, Associated...), std::vector& infos, ArrowTableSlicingCache& slices) { using G = std::decay_t; auto groupingTable = AnalysisDataProcessorBuilder::bindGroupingTable(inputs, processingFunction, infos); + constexpr const int numElements = nested_brace_constructible_size>() / 10; + // set filtered tables for partitions with grouping - homogeneous_apply_refs([&groupingTable](auto& element) { + homogeneous_apply_refs_sized([&groupingTable](auto& element) { analysis_task_parsers::setPartition(element, groupingTable); analysis_task_parsers::bindInternalIndicesPartition(element, &groupingTable); return true; }, - task); + task); if constexpr (sizeof...(Associated) == 0) { // single argument to process - homogeneous_apply_refs([&groupingTable](auto& element) { + homogeneous_apply_refs_sized([&groupingTable](auto& element) { analysis_task_parsers::bindExternalIndicesPartition(element, &groupingTable); analysis_task_parsers::setGroupedCombination(element, groupingTable); return true; }, - task); + task); if constexpr (soa::is_iterator) { for (auto& element : groupingTable) { std::invoke(processingFunction, task, *element); @@ -347,7 +351,7 @@ struct AnalysisDataProcessorBuilder { // pre-bind self indices std::apply( [&task](auto&... t) mutable { - (homogeneous_apply_refs( + (homogeneous_apply_refs_sized( [&t](auto& p) { analysis_task_parsers::bindInternalIndicesPartition(p, &t); return true; @@ -359,12 +363,12 @@ struct AnalysisDataProcessorBuilder { auto binder = [&task, &groupingTable, &associatedTables](auto& x) mutable { x.bindExternalIndices(&groupingTable, &std::get>(associatedTables)...); - homogeneous_apply_refs([&x](auto& t) mutable { + homogeneous_apply_refs_sized([&x](auto& t) mutable { analysis_task_parsers::setPartition(t, x); analysis_task_parsers::bindExternalIndicesPartition(t, &x); return true; }, - task); + task); }; groupingTable.bindExternalIndices(&std::get>(associatedTables)...); @@ -376,11 +380,11 @@ struct AnalysisDataProcessorBuilder { associatedTables); // GroupedCombinations bound separately, as they should be set once for all associated tables - homogeneous_apply_refs([&groupingTable, &associatedTables](auto& t) { + homogeneous_apply_refs_sized([&groupingTable, &associatedTables](auto& t) { analysis_task_parsers::setGroupedCombination(t, groupingTable, associatedTables); return true; }, - task); + task); overwriteInternalIndices(associatedTables, associatedTables); if constexpr (soa::is_iterator>) { auto slicer = GroupSlicer(groupingTable, associatedTables, slices); @@ -394,28 +398,28 @@ struct AnalysisDataProcessorBuilder { associatedSlices); // bind partitions and grouping table - homogeneous_apply_refs([&groupingTable](auto& x) { + homogeneous_apply_refs_sized([&groupingTable](auto& x) { analysis_task_parsers::bindExternalIndicesPartition(x, &groupingTable); return true; }, - task); + task); invokeProcessWithArgs(task, processingFunction, slice.groupingElement(), associatedSlices); } } else { // bind partitions and grouping table - homogeneous_apply_refs([&groupingTable](auto& x) { + homogeneous_apply_refs_sized([&groupingTable](auto& x) { analysis_task_parsers::bindExternalIndicesPartition(x, &groupingTable); return true; }, - task); + task); invokeProcessWithArgs(task, processingFunction, groupingTable, associatedTables); } } } - template + template static void invokeProcessWithArgs(C& task, T processingFunction, G g, std::tuple& at) { std::invoke(processingFunction, task, g, std::get(at)...); @@ -523,16 +527,18 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) std::vector options; std::vector expressionInfos; + constexpr const int numElements = nested_brace_constructible_size>() / 10; + /// make sure options and configurables are set before expression infos are created - homogeneous_apply_refs([&options](auto& element) { return analysis_task_parsers::appendOption(options, element); }, *task.get()); + homogeneous_apply_refs_sized([&options](auto& element) { return analysis_task_parsers::appendOption(options, element); }, *task.get()); /// extract conditions and append them as inputs - homogeneous_apply_refs([&inputs](auto& element) { return analysis_task_parsers::appendCondition(inputs, element); }, *task.get()); + homogeneous_apply_refs_sized([&inputs](auto& element) { return analysis_task_parsers::appendCondition(inputs, element); }, *task.get()); /// parse process functions defined by corresponding configurables if constexpr (requires { &T::process; }) { AnalysisDataProcessorBuilder::inputsFromArgs(&T::process, "default", true, inputs, expressionInfos); } - homogeneous_apply_refs( + homogeneous_apply_refs_sized( [name = name_str, &expressionInfos, &inputs](auto& x) mutable { // this pushes (argumentIndex, processHash, schemaPtr, nullptr) into expressionInfos for arguments that are Filtered/filtered_iterators return AnalysisDataProcessorBuilder::requestInputsFromArgs(x, name, inputs, expressionInfos); @@ -541,39 +547,39 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) // request base tables for spawnable extended tables and indices to be built // this checks for duplications - homogeneous_apply_refs([&inputs](auto& element) { + homogeneous_apply_refs_sized([&inputs](auto& element) { return analysis_task_parsers::requestInputs(inputs, element); }, - *task.get()); + *task.get()); // no static way to check if the task defines any processing, we can only make sure it subscribes to at least something if (inputs.empty() == true) { LOG(warn) << "Task " << name_str << " has no inputs"; } - homogeneous_apply_refs([&outputs, &hash](auto& element) { return analysis_task_parsers::appendOutput(outputs, element, hash); }, *task.get()); + homogeneous_apply_refs_sized([&outputs, &hash](auto& element) { return analysis_task_parsers::appendOutput(outputs, element, hash); }, *task.get()); auto requiredServices = CommonServices::defaultServices(); auto arrowServices = CommonServices::arrowServices(); requiredServices.insert(requiredServices.end(), arrowServices.begin(), arrowServices.end()); - homogeneous_apply_refs([&requiredServices](auto& element) { return analysis_task_parsers::addService(requiredServices, element); }, *task.get()); + homogeneous_apply_refs_sized([&requiredServices](auto& element) { return analysis_task_parsers::addService(requiredServices, element); }, *task.get()); auto algo = AlgorithmSpec::InitCallback{[task = task, expressionInfos](InitContext& ic) mutable { Cache bindingsKeys; Cache bindingsKeysUnsorted; // add preslice declarations to slicing cache definition - homogeneous_apply_refs([&bindingsKeys, &bindingsKeysUnsorted](auto& element) { return analysis_task_parsers::registerCache(element, bindingsKeys, bindingsKeysUnsorted); }, *task.get()); + homogeneous_apply_refs_sized([&bindingsKeys, &bindingsKeysUnsorted](auto& element) { return analysis_task_parsers::registerCache(element, bindingsKeys, bindingsKeysUnsorted); }, *task.get()); - homogeneous_apply_refs([&ic](auto&& element) { return analysis_task_parsers::prepareOption(ic, element); }, *task.get()); - homogeneous_apply_refs([&ic](auto&& element) { return analysis_task_parsers::prepareService(ic, element); }, *task.get()); + homogeneous_apply_refs_sized([&ic](auto&& element) { return analysis_task_parsers::prepareOption(ic, element); }, *task.get()); + homogeneous_apply_refs_sized([&ic](auto&& element) { return analysis_task_parsers::prepareService(ic, element); }, *task.get()); auto& callbacks = ic.services().get(); auto eoscb = [task](EndOfStreamContext& eosContext) { - homogeneous_apply_refs([&eosContext](auto& element) { + homogeneous_apply_refs_sized([&eosContext](auto& element) { analysis_task_parsers::postRunService(eosContext, element); analysis_task_parsers::postRunOutput(eosContext, element); return true; }, - *task.get()); + *task.get()); eosContext.services().get().readyToQuit(QuitRequest::Me); }; @@ -585,84 +591,75 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) } /// update configurables in filters and partitions - homogeneous_apply_refs( + homogeneous_apply_refs_sized( [&ic](auto& element) -> bool { return analysis_task_parsers::updatePlaceholders(ic, element); }, *task.get()); /// create expression trees for filters gandiva trees matched to schemas and store the pointers into expressionInfos - homogeneous_apply_refs([&expressionInfos](auto& element) { + homogeneous_apply_refs_sized([&expressionInfos](auto& element) { return analysis_task_parsers::createExpressionTrees(expressionInfos, element); }, - *task.get()); + *task.get()); /// parse process functions to enable requested grouping caches - note that at this state process configurables have their final values if constexpr (requires { &T::process; }) { AnalysisDataProcessorBuilder::cacheFromArgs(&T::process, true, bindingsKeys, bindingsKeysUnsorted); } - homogeneous_apply_refs( - [&bindingsKeys, &bindingsKeysUnsorted](auto& x) mutable { + homogeneous_apply_refs_sized( + [&bindingsKeys, &bindingsKeysUnsorted](auto& x) { return AnalysisDataProcessorBuilder::requestCacheFromArgs(x, bindingsKeys, bindingsKeysUnsorted); }, *task.get()); ic.services().get().setCaches(std::move(bindingsKeys)); ic.services().get().setCachesUnsorted(std::move(bindingsKeysUnsorted)); - // initialize global caches - homogeneous_apply_refs([&ic](auto& element) { - return analysis_task_parsers::preInitializeCache(ic, element); - }, - *(task.get())); return [task, expressionInfos](ProcessingContext& pc) mutable { // load the ccdb object from their cache - homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::newDataframeCondition(pc.inputs(), element); }, *task.get()); + homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::newDataframeCondition(pc.inputs(), element); }, *task.get()); // reset partitions once per dataframe - homogeneous_apply_refs([](auto& element) { return analysis_task_parsers::newDataframePartition(element); }, *task.get()); + homogeneous_apply_refs_sized([](auto& element) { return analysis_task_parsers::newDataframePartition(element); }, *task.get()); // reset selections for the next dataframe - for (auto& info : expressionInfos) { - info.resetSelection = true; - } + std::ranges::for_each(expressionInfos, [](auto& info) { info.resetSelection = true; }); // reset pre-slice for the next dataframe auto slices = pc.services().get(); - homogeneous_apply_refs([&slices](auto& element) { + homogeneous_apply_refs_sized([&slices](auto& element) { return analysis_task_parsers::updateSliceInfo(element, slices); }, - *(task.get())); + *(task.get())); // initialize local caches - homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::initializeCache(pc, element); }, *(task.get())); + homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::initializeCache(pc, element); }, *(task.get())); // prepare outputs - homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::prepareOutput(pc, element); }, *task.get()); + homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::prepareOutput(pc, element); }, *task.get()); // execute run() if constexpr (requires { task->run(pc); }) { task->run(pc); } // execute process() - if constexpr (requires { AnalysisDataProcessorBuilder::invokeProcess(*(task.get()), pc.inputs(), &T::process, expressionInfos, slices); }) { + if constexpr (requires { &T::process; }) { AnalysisDataProcessorBuilder::invokeProcess(*(task.get()), pc.inputs(), &T::process, expressionInfos, slices); } // execute optional process() - homogeneous_apply_refs( - [&pc, &expressionInfos, &task, &slices](auto& x) mutable { - if constexpr (base_of_template>) { + homogeneous_apply_refs_sized( + [&pc, &expressionInfos, &task, &slices](auto& x) { + if constexpr (is_process_configurable) { if (x.value == true) { AnalysisDataProcessorBuilder::invokeProcess(*task.get(), pc.inputs(), x.process, expressionInfos, slices); return true; } + return false; } return false; }, *task.get()); // prepare delayed outputs - homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::prepareDelayedOutput(pc, element); }, *task.get()); + homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::prepareDelayedOutput(pc, element); }, *task.get()); // finalize outputs - homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::finalizeOutput(pc, element); }, *task.get()); + homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::finalizeOutput(pc, element); }, *task.get()); }; }}; return { name, - // FIXME: For the moment we hardcode this. We could build - // this list from the list of methods actually implemented in the - // task itself. inputs, outputs, algo, diff --git a/Framework/Core/include/Framework/Configurable.h b/Framework/Core/include/Framework/Configurable.h index 3ef90a0eee166..0931884da1ff7 100644 --- a/Framework/Core/include/Framework/Configurable.h +++ b/Framework/Core/include/Framework/Configurable.h @@ -84,10 +84,10 @@ template using MutableConfigurable = Configurable>; template -concept is_configurable = requires(T& t) { - typename T::type; +concept is_configurable = requires(T t) { requires std::same_as; - &T::operator typename T::type; + requires std::same_as; + requires std::same_as::type, decltype(t.value)>; }; using ConfigurableAxis = Configurable, ConfigParamKind::kAxisSpec, ConfigurablePolicyConst, ConfigParamKind::kAxisSpec>>; @@ -99,19 +99,18 @@ concept is_configurable_axis = is_configurable&& T::kind == ConfigParamKind::kAxisSpec; }; -template +template struct ProcessConfigurable : Configurable { - ProcessConfigurable(R (T::*process_)(As...), std::string const& name_, bool&& value_, std::string const& help_) + ProcessConfigurable(void (T::*process_)(As...), std::string const& name_, bool&& value_, std::string const& help_) : process{process_}, Configurable(name_, std::forward(value_), help_) { } - R(T::*process) - (As...); + void (T::*process)(As...); }; template -concept is_process_configurable = is_configurable && requires(T& t) { t.process; }; +concept is_process_configurable = is_configurable && requires(T t) { t.process; }; #define PROCESS_SWITCH(_Class_, _Name_, _Help_, _Default_) \ decltype(o2::framework::ProcessConfigurable{&_Class_ ::_Name_, #_Name_, _Default_, _Help_}) do##_Name_ = o2::framework::ProcessConfigurable{&_Class_ ::_Name_, #_Name_, _Default_, _Help_}; diff --git a/Framework/Core/test/test_Concepts.cxx b/Framework/Core/test/test_Concepts.cxx index ea94c4dfffe5a..982c748e701e4 100644 --- a/Framework/Core/test/test_Concepts.cxx +++ b/Framework/Core/test/test_Concepts.cxx @@ -174,6 +174,7 @@ TEST_CASE("IdentificationConcepts") REQUIRE(is_configurable_axis); REQUIRE(is_process_configurable); + REQUIRE(is_process_configurable); struct : ConfigurableGroup { Configurable c{"", 1, ""}; diff --git a/Framework/Foundation/include/Framework/StructToTuple.h b/Framework/Foundation/include/Framework/StructToTuple.h index 5748329f6a50d..1c7aa62260bd3 100644 --- a/Framework/Foundation/include/Framework/StructToTuple.h +++ b/Framework/Foundation/include/Framework/StructToTuple.h @@ -174,9 +174,9 @@ consteval int nested_brace_constructible_size() return brace_constructible_size() - nesting; } -template () / 10> +template () / 10, typename L> requires(D == 9) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -194,9 +194,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 8) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -214,9 +214,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 7) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -234,9 +234,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 6) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -254,9 +254,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 5) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -274,9 +274,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 4) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -294,9 +294,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 3) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -314,9 +314,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 2) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -334,9 +334,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 1) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -354,9 +354,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 0) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -373,6 +373,12 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } +template +constexpr auto homogeneous_apply_refs_sized(L l, T&& object) +{ + return homogeneous_apply_refs(l, object); +} + } // namespace o2::framework #endif // O2_FRAMEWORK_STRUCTTOTUPLE_H_ From 0355d19f2aad1726cdd020029b5eef7b9bb4625b Mon Sep 17 00:00:00 2001 From: Marco van Leeuwen Date: Tue, 17 Feb 2026 19:12:33 +0100 Subject: [PATCH 109/133] [ALICE3] Fix geometry overlaps in tracker (ML/OT) (#15072) --- .../ALICE3/FT3/simulation/src/FT3Module.cxx | 2 +- .../ALICE3/TRK/simulation/src/TRKServices.cxx | 42 +++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx index efcad74bc2cb9..9e24247958c06 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx @@ -108,7 +108,7 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double double carbonFiberThickness = 0.01; - double foamSpacingThickness = 0.5; + double foamSpacingThickness = 1.0; int dist_offset = 0; diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx index cbe00e8fc9e89..25c59b3c8fd4a 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx @@ -320,26 +320,26 @@ void TRKServices::createMiddleServices(TGeoVolume* motherVolume) const float rMaxMiddleBarrelDisk = 35.f; const float zLengthMiddleBarrel = 64.2f; for (auto& orientation : {Orientation::kASide, Orientation::kCSide}) { - TGeoTube* middleBarrelConnDiskSIO2 = new TGeoTube(Form("TRK_MIDBARCONN_DISK_SIO2sh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, siO2FiberThick); - TGeoTube* middleBarrelConnDiskPE = new TGeoTube(Form("TRK_MIDBARCONN_DISK_PEsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, peFiberThick); + TGeoTube* middleBarrelConnDiskSIO2 = new TGeoTube(Form("TRK_MIDBARCONN_DISK_SIO2sh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, siO2FiberThick / 2.); + TGeoTube* middleBarrelConnDiskPE = new TGeoTube(Form("TRK_MIDBARCONN_DISK_PEsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, peFiberThick / 2.); TGeoVolume* middleBarrelConnDiskSIO2Volume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_SIO2_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskSIO2, medSiO2); TGeoVolume* middleBarrelConnDiskPEVolume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_PE_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskPE, medPE); middleBarrelConnDiskSIO2Volume->SetLineColor(kGray); middleBarrelConnDiskPEVolume->SetLineColor(kGray); auto* rot = new TGeoRotation("", 0, 0, 180); - auto* combiTransSIO2 = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick / 2 + zLengthMiddleBarrel), rot); - auto* combiTransPE = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick / 2 + zLengthMiddleBarrel), rot); + auto* combiTransSIO2 = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick / 2. + zLengthMiddleBarrel), rot); + auto* combiTransPE = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick / 2. + zLengthMiddleBarrel), rot); motherVolume->AddNode(middleBarrelConnDiskSIO2Volume, 1, combiTransSIO2); motherVolume->AddNode(middleBarrelConnDiskPEVolume, 1, combiTransPE); - TGeoTube* middleBarrelConnDiskCu = new TGeoTube(Form("TRK_MIDBARCONN_DISK_CUsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, cuPowerThick); - TGeoTube* middleBarrelConnDiskPEPower = new TGeoTube(Form("TRK_MIDBARCONN_DISK_PEsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, pePowerThick); + TGeoTube* middleBarrelConnDiskCu = new TGeoTube(Form("TRK_MIDBARCONN_DISK_CUsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, cuPowerThick / 2.); + TGeoTube* middleBarrelConnDiskPEPower = new TGeoTube(Form("TRK_MIDBARCONN_DISK_PEsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, pePowerThick / 2.); TGeoVolume* middleBarrelConnDiskCuVolume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_CU_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskCu, medCu); TGeoVolume* middleBarrelConnDiskPEPowerVolume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_PE_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskPEPower, medPE); middleBarrelConnDiskCuVolume->SetLineColor(kGray); middleBarrelConnDiskPEPowerVolume->SetLineColor(kGray); - auto* combiTransCu = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick / 2 + zLengthMiddleBarrel), rot); - auto* combiTransPEPower = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick / 2 + zLengthMiddleBarrel), rot); + auto* combiTransCu = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick / 2. + zLengthMiddleBarrel), rot); + auto* combiTransPEPower = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick / 2. + zLengthMiddleBarrel), rot); motherVolume->AddNode(middleBarrelConnDiskCuVolume, 1, combiTransCu); motherVolume->AddNode(middleBarrelConnDiskPEPowerVolume, 1, combiTransPEPower); @@ -357,39 +357,39 @@ void TRKServices::createMiddleServices(TGeoVolume* motherVolume) float rMaxMiddleServicesBarFwd = 74.5f + siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick + puCoolingThick + h2oCoolingThick; for (auto& orientation : {Orientation::kASide, Orientation::kCSide}) { // Create fibers: 3.07mm, 50% SiO2, 50% PE - TGeoTube* middleBarFwdFiberSIO2 = new TGeoTube("TRK_MIDBARFWD_FIBER_SIO2sh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, siO2FiberThick); - TGeoTube* middleBarFwdFiberPE = new TGeoTube("TRK_MIDBARFWD_FIBER_PEsh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, peFiberThick); + TGeoTube* middleBarFwdFiberSIO2 = new TGeoTube("TRK_MIDBARFWD_FIBER_SIO2sh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, siO2FiberThick / 2.); + TGeoTube* middleBarFwdFiberPE = new TGeoTube("TRK_MIDBARFWD_FIBER_PEsh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, peFiberThick / 2.); TGeoVolume* middleBarFwdFiberSIO2Volume = new TGeoVolume("TRK_MIDBARFWD_FIBER_SIO2", middleBarFwdFiberSIO2, medSiO2); TGeoVolume* middleBarFwdFiberPEVolume = new TGeoVolume("TRK_MIDBARFWD_FIBER_PE", middleBarFwdFiberPE, medPE); middleBarFwdFiberSIO2Volume->SetLineColor(kGray); middleBarFwdFiberPEVolume->SetLineColor(kGray); auto* rot = new TGeoRotation("", 0, 0, 180); - auto* combiTransSIO2 = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick / 2 + zLengthMiddleServices), rot); - auto* combiTransPE = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick / 2 + zLengthMiddleServices), rot); + auto* combiTransSIO2 = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick / 2. + zLengthMiddleServices), rot); + auto* combiTransPE = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick / 2. + zLengthMiddleServices), rot); motherVolume->AddNode(middleBarFwdFiberSIO2Volume, 1, combiTransSIO2); motherVolume->AddNode(middleBarFwdFiberPEVolume, 1, combiTransPE); // Create powerlines: 10.9mm, 9% Cu, 91% PE - TGeoTube* middleBarFwdPowerCu = new TGeoTube("TRK_MIDBARFWD_POWER_CUsh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, cuPowerThick); - TGeoTube* middleBarFwdPowerPE = new TGeoTube("TRK_MIDBARFWD_POWER_PEsh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, pePowerThick); + TGeoTube* middleBarFwdPowerCu = new TGeoTube("TRK_MIDBARFWD_POWER_CUsh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, cuPowerThick / 2.); + TGeoTube* middleBarFwdPowerPE = new TGeoTube("TRK_MIDBARFWD_POWER_PEsh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, pePowerThick / 2.); TGeoVolume* middleBarFwdPowerCuVolume = new TGeoVolume("TRK_MIDBARFWD_POWER_CU", middleBarFwdPowerCu, medCu); TGeoVolume* middleBarFwdPowerPEVolume = new TGeoVolume("TRK_MIDBARFWD_POWER_PE", middleBarFwdPowerPE, medPE); middleBarFwdPowerCuVolume->SetLineColor(kGray); middleBarFwdPowerPEVolume->SetLineColor(kGray); - auto* combiTransCu = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick / 2 + zLengthMiddleServices), rot); - auto* combiTransPEPower = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick / 2 + zLengthMiddleServices), rot); + auto* combiTransCu = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick / 2. + zLengthMiddleServices), rot); + auto* combiTransPEPower = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick / 2. + zLengthMiddleServices), rot); motherVolume->AddNode(middleBarFwdPowerCuVolume, 1, combiTransCu); motherVolume->AddNode(middleBarFwdPowerPEVolume, 1, combiTransPEPower); // Create cooling pipes: 4.74mm, 56% PU, 44% H2O - TGeoTube* middleBarFwdCoolingPU = new TGeoTube("TRK_MIDBARFWD_COOLING_PUsh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, puCoolingThick); - TGeoTube* middleBarFwdCoolingH2O = new TGeoTube("TRK_MIDBARFWD_COOLING_H2Osh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, h2oCoolingThick); + TGeoTube* middleBarFwdCoolingPU = new TGeoTube("TRK_MIDBARFWD_COOLING_PUsh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, puCoolingThick / 2.); + TGeoTube* middleBarFwdCoolingH2O = new TGeoTube("TRK_MIDBARFWD_COOLING_H2Osh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, h2oCoolingThick / 2.); TGeoVolume* middleBarFwdCoolingPUVolume = new TGeoVolume("TRK_MIDBARFWD_COOLING_PU", middleBarFwdCoolingPU, medPU); TGeoVolume* middleBarFwdCoolingH2OVolume = new TGeoVolume("TRK_MIDBARFWD_COOLING_H2O", middleBarFwdCoolingH2O, medH2O); middleBarFwdCoolingPUVolume->SetLineColor(kGray); middleBarFwdCoolingH2OVolume->SetLineColor(kGray); - auto* combiTransCoolingPU = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick + puCoolingThick / 2 + zLengthMiddleServices), rot); - auto* combiTransCoolingH2O = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick + puCoolingThick + h2oCoolingThick / 2 + zLengthMiddleServices), rot); + auto* combiTransCoolingPU = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick + puCoolingThick / 2. + zLengthMiddleServices), rot); + auto* combiTransCoolingH2O = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick + puCoolingThick + h2oCoolingThick / 2. + zLengthMiddleServices), rot); motherVolume->AddNode(middleBarFwdCoolingPUVolume, 1, combiTransCoolingPU); motherVolume->AddNode(middleBarFwdCoolingH2OVolume, 1, combiTransCoolingH2O); } @@ -501,4 +501,4 @@ void TRKServices::createOuterBarrelServices(TGeoVolume* motherVolume) motherVolume->AddNode(outerBarrelCoolingH2OVolume, 1, nullptr); } } // namespace trk -} // namespace o2 \ No newline at end of file +} // namespace o2 From e57a6edf96c71b344ede9fe78b4b0e3c69335069 Mon Sep 17 00:00:00 2001 From: Pavel Larionov Date: Tue, 17 Feb 2026 19:15:56 +0100 Subject: [PATCH 110/133] Configurable VD design, set def to IRIS 4, remove IRIS disks (#15055) --- .../TRK/base/include/TRKBase/TRKBaseParam.h | 9 ++++++ .../ALICE3/TRK/simulation/src/Detector.cxx | 28 ++++++++++++++++--- .../TRK/simulation/src/VDGeometryBuilder.cxx | 2 +- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h index 7f2f7f32b79d9..d5e11313c0f0c 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h @@ -31,6 +31,13 @@ enum eLayout { kStaggered, }; +enum eVDLayout { + kIRIS4 = 0, + kIRISFullCyl, + kIRIS5, + kIRIS4a, +}; + struct TRKBaseParam : public o2::conf::ConfigurableParamHelper { std::string configFile = ""; float serviceTubeX0 = 0.02f; // X0 Al2O3 @@ -40,9 +47,11 @@ struct TRKBaseParam : public o2::conf::ConfigurableParamHelper { eLayout layoutML = kTurboStaves; // Type of segmentation for the middle layers eLayout layoutOL = kStaggered; // Type of segmentation for the outer layers + eVDLayout layoutVD = kIRIS4; // VD detector layout design eLayout getLayoutML() const { return layoutML; } eLayout getLayoutOL() const { return layoutOL; } + eVDLayout getLayoutVD() const { return layoutVD; } O2ParamDef(TRKBaseParam, "TRKBase"); }; diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index 06fd2d9670b67..556b016f22553 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -261,12 +261,32 @@ void Detector::createGeometry() mServices.createServices(vTRK); // Build the VD using the petal builder - // Choose the VD design (here: IRIS4 by default). - // You can wire this to a parameter in TRKBaseParam if desired. - // Alternatives: createIRIS5Geometry(vTRK); createIRIS4aGeometry(vTRK); + // Choose the VD design based on TRKBaseParam.layoutVD + auto& trkPars = TRKBaseParam::Instance(); o2::trk::clearVDSensorRegistry(); - o2::trk::createIRISGeometryFullCyl(vTRK); + + switch (trkPars.layoutVD) { + case kIRIS4: + LOG(info) << "Building VD with IRIS4 layout"; + o2::trk::createIRIS4Geometry(vTRK); + break; + case kIRISFullCyl: + LOG(info) << "Building VD with IRIS fully cylindrical layout"; + o2::trk::createIRISGeometryFullCyl(vTRK); + break; + case kIRIS5: + LOG(info) << "Building VD with IRIS5 layout"; + o2::trk::createIRIS5Geometry(vTRK); + break; + case kIRIS4a: + LOG(info) << "Building VD with IRIS4a layout"; + o2::trk::createIRIS4aGeometry(vTRK); + break; + default: + LOG(fatal) << "Unknown VD layout option: " << static_cast(trkPars.layoutVD); + break; + } // Fill sensor names from registry right after geometry creation const auto& regs = o2::trk::vdSensorRegistry(); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx index 6ce04bb8443ef..b06faa38211bb 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx @@ -749,7 +749,7 @@ static TGeoVolume* buildPetalAssembly(int nPetals, /*fullCylindricalRadialWalls=*/fullCylinders); addBarrelLayers(petalAsm, nPetals, petalID, rectangularL0, fullCylinders); - addDisks(petalAsm, nPetals, petalID, fullCylinders); + // addDisks(petalAsm, nPetals, petalID, fullCylinders); // disks removed according to the v3b layout addColdPlate(petalAsm, nPetals, petalID, /*fullCylinders=*/false); addIRISServiceModulesSegmented(petalAsm, nPetals); From e5768cde630297876b47f1c24b5da3489f56228a Mon Sep 17 00:00:00 2001 From: Andrea Sofia Triolo Date: Tue, 17 Feb 2026 19:16:53 +0100 Subject: [PATCH 111/133] ALICE3-TRK: adapt ordering key for digits to the large number of columns in the VD (#15070) --- .../simulation/include/TRKSimulation/ChipDigitsContainer.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/ChipDigitsContainer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/ChipDigitsContainer.h index 658fb823bb596..73c95b04c45e3 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/ChipDigitsContainer.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/ChipDigitsContainer.h @@ -29,6 +29,12 @@ class ChipDigitsContainer : public o2::itsmft::ChipDigitsContainer using Segmentation = SegmentationChip; + /// Get global ordering key made of readout frame, column and row + static ULong64_t getOrderingKey(UInt_t roframe, UShort_t row, UShort_t col) + { + return (static_cast(roframe) << (8 * sizeof(UInt_t))) + (static_cast(col) << (8 * sizeof(Short_t))) + row; + } + ClassDefNV(ChipDigitsContainer, 1); }; From 06150434b8a30967fb5449016758b0441748f0b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Tue, 17 Feb 2026 21:54:34 +0100 Subject: [PATCH 112/133] A3: Add geometries for IOTOF (#15073) - fix enabling and disabling of backward TOF - add v3b versions of IOTOF --- .../base/include/IOTOFBase/IOTOFBaseParam.h | 1 + .../include/IOTOFSimulation/Detector.h | 2 +- .../ALICE3/IOTOF/simulation/src/Detector.cxx | 73 ++++++++++++++----- 3 files changed, 56 insertions(+), 20 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h index 10d8c5ced94dd..bf605797cbfe5 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h @@ -25,6 +25,7 @@ struct IOTOFBaseParam : public o2::conf::ConfigurableParamHelper bool enableOuterTOF = true; bool enableForwardTOF = true; bool enableBackwardTOF = true; + std::string detectorPattern = ""; O2ParamDef(IOTOFBaseParam, "IOTOFBase"); }; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Detector.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Detector.h index 1f3b2f4fe9fac..f39a43733ccab 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Detector.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Detector.h @@ -60,7 +60,7 @@ class Detector : public o2::base::DetImpl return nullptr; } - void configLayers(bool itof = true, bool otof = true, bool ftof = true, bool btof = true); + void configLayers(bool itof = true, bool otof = true, bool ftof = true, bool btof = true, std::string pattern = ""); void configServices(); void createMaterials(); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx index a2bba7cc5fe35..3a971e81a610d 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx @@ -20,8 +20,6 @@ #include "IOTOFSimulation/Detector.h" #include "IOTOFBase/IOTOFBaseParam.h" -using o2::itsmft::Hit; - namespace o2 { namespace iotof @@ -40,7 +38,9 @@ Detector::Detector(bool active) mHits(o2::utils::createSimVector()) { auto& iotofPars = IOTOFBaseParam::Instance(); - configLayers(iotofPars.enableInnerTOF, iotofPars.enableOuterTOF, iotofPars.enableForwardTOF); + configLayers(iotofPars.enableInnerTOF, iotofPars.enableOuterTOF, + iotofPars.enableForwardTOF, iotofPars.enableBackwardTOF, + iotofPars.detectorPattern); } Detector::~Detector() @@ -56,19 +56,54 @@ void Detector::ConstructGeometry() createGeometry(); } -void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof) +void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::string pattern) { + + float radiusInnerTof = 19.f; + float radiusOuterTof = 85.f; + float lengthInnerTof = 124.f; + float lengthOuterTof = 680.f; + std::pair radiusRangeDiskTof = {15.f, 100.f}; + float zForwardTof = 370.f; + if (pattern == "") { + } else if (pattern == "v3b") { + LOG(info) << "Configuring IOTOF layers with v3b pattern"; + ftof = false; + btof = false; + } else if (pattern == "v3b1a") { + lengthOuterTof = 500.f; + zForwardTof = 270.f; + radiusRangeDiskTof = {30.f, 100.f}; + } else if (pattern == "v3b1b") { + lengthOuterTof = 500.f; + zForwardTof = 200.f; + radiusRangeDiskTof = {20.f, 68.f}; + } else if (pattern == "v3b2a") { + lengthOuterTof = 440.f; + zForwardTof = 270.f; + radiusRangeDiskTof = {30.f, 120.f}; + } else if (pattern == "v3b2b") { + lengthOuterTof = 440.f; + zForwardTof = 200.f; + radiusRangeDiskTof = {20.f, 68.f}; + } else if (pattern == "v3b3") { + lengthOuterTof = 580.f; + zForwardTof = 200.f; + radiusRangeDiskTof = {20.f, 68.f}; + } else { + LOG(fatal) << "IOTOF layer pattern " << pattern << " not recognized, exiting"; + } if (itof) { - mITOFLayer = ITOFLayer(std::string{GeometryTGeo::getITOFLayerPattern()}, 19.f, 0.f, 124.f, 0.f, 0.02f, true); // iTOF + mITOFLayer = ITOFLayer(std::string{GeometryTGeo::getITOFLayerPattern()}, radiusInnerTof, 0.f, lengthInnerTof, 0.f, 0.02f, true); // iTOF } if (otof) { - mOTOFLayer = OTOFLayer(std::string{GeometryTGeo::getOTOFLayerPattern()}, 85.f, 0.f, 680.f, 0.f, 0.02f, true); // oTOF + mOTOFLayer = OTOFLayer(std::string{GeometryTGeo::getOTOFLayerPattern()}, radiusOuterTof, 0.f, lengthOuterTof, 0.f, 0.02f, true); // oTOF } if (ftof) { - mFTOFLayer = FTOFLayer(std::string{GeometryTGeo::getFTOFLayerPattern()}, 15.f, 100.f, 0.f, 370.f, 0.02f, false); // fTOF + mFTOFLayer = FTOFLayer(std::string{GeometryTGeo::getFTOFLayerPattern()}, radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, zForwardTof, 0.02f, false); // fTOF } if (btof) { - mBTOFLayer = BTOFLayer(std::string{GeometryTGeo::getBTOFLayerPattern()}, 15.f, 100.f, 0.f, -370.f, 0.02f, false); // bTOF + mBTOFLayer = BTOFLayer(std::string{GeometryTGeo::getBTOFLayerPattern()}, radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, -zForwardTof, 0.02f, false); // bTOF } } @@ -214,28 +249,28 @@ bool Detector::ProcessHits(FairVolume* vol) bool startHit = false, stopHit = false; unsigned char status = 0; if (fMC->IsTrackEntering()) { - status |= Hit::kTrackEntering; + status |= o2::itsmft::Hit::kTrackEntering; } if (fMC->IsTrackInside()) { - status |= Hit::kTrackInside; + status |= o2::itsmft::Hit::kTrackInside; } if (fMC->IsTrackExiting()) { - status |= Hit::kTrackExiting; + status |= o2::itsmft::Hit::kTrackExiting; } if (fMC->IsTrackOut()) { - status |= Hit::kTrackOut; + status |= o2::itsmft::Hit::kTrackOut; } if (fMC->IsTrackStop()) { - status |= Hit::kTrackStopped; + status |= o2::itsmft::Hit::kTrackStopped; } if (fMC->IsTrackAlive()) { - status |= Hit::kTrackAlive; + status |= o2::itsmft::Hit::kTrackAlive; } // track is entering or created in the volume - if ((status & Hit::kTrackEntering) || (status & Hit::kTrackInside && !mTrackData.mHitStarted)) { + if ((status & o2::itsmft::Hit::kTrackEntering) || (status & o2::itsmft::Hit::kTrackInside && !mTrackData.mHitStarted)) { startHit = true; - } else if ((status & (Hit::kTrackExiting | Hit::kTrackOut | Hit::kTrackStopped))) { + } else if ((status & (o2::itsmft::Hit::kTrackExiting | o2::itsmft::Hit::kTrackOut | o2::itsmft::Hit::kTrackStopped))) { stopHit = true; } @@ -264,9 +299,9 @@ bool Detector::ProcessHits(FairVolume* vol) fMC->CurrentVolOffID(3, halfstave); fMC->CurrentVolOffID(4, stave); - Hit* p = addHit(stack->GetCurrentTrackNumber(), lay, mTrackData.mPositionStart.Vect(), positionStop.Vect(), - mTrackData.mMomentumStart.Vect(), mTrackData.mMomentumStart.E(), positionStop.T(), - mTrackData.mEnergyLoss, mTrackData.mTrkStatusStart, status); + o2::itsmft::Hit* p = addHit(stack->GetCurrentTrackNumber(), lay, mTrackData.mPositionStart.Vect(), positionStop.Vect(), + mTrackData.mMomentumStart.Vect(), mTrackData.mMomentumStart.E(), positionStop.T(), + mTrackData.mEnergyLoss, mTrackData.mTrkStatusStart, status); // RS: not sure this is needed // Increment number of Detector det points in TParticle From 41d9be4b011d3c9cc04742b271e814661002379d Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Tue, 17 Feb 2026 22:08:43 +0100 Subject: [PATCH 113/133] [ALICE3] Adapt CA for 2T simulations (#15075) --- .../reconstruction/include/TRKReconstruction/TimeFrame.h | 3 --- .../Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx | 7 ++++++- Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx | 7 ++++++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h index d2ca6fba132e1..f42a1c897efb6 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h @@ -62,9 +62,6 @@ class TimeFrame : public o2::its::TimeFrame /// \param nEvents Number of events to process /// \param inROFpileup Number of events per ROF void getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup); - - private: - ClassDefNV(TimeFrame, 1); }; } // namespace trk diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx index 686270826049b..610a08450d5ee 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx @@ -64,10 +64,12 @@ int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, } int subDetID = gman->getSubDetID(hit.GetDetectorID()); const int layer = startLayer[subDetID] + gman->getLayer(hit.GetDetectorID()); + if (layer >= nLayers) { + continue; + } ++clusterCountPerLayer[layer]; totalNHits++; } - trkHit->clear(); } // Reserve memory for all layers @@ -106,6 +108,9 @@ int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, o2::math_utils::Point3D gloXYZ; o2::math_utils::Point3D trkXYZ; float r{0.f}; + if (layer >= nLayers) { + continue; + } if (layer >= 3) { int chipID = hit.GetDetectorID(); alpha = gman->getSensorRefAlphaMLOT(chipID); diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx index 8f26478f4496e..8fc67f0fa5567 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx @@ -10,11 +10,13 @@ // or submit itself to any jurisdiction. #include +#include #include "DetectorsBase/GeometryManager.h" #include "ITStracking/TimeFrame.h" #include "ITStracking/Configuration.h" #include "Field/MagneticField.h" +#include "Field/MagFieldParam.h" #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" @@ -276,7 +278,7 @@ void TrackerDPL::run(ProcessingContext& pc) itsTrackerTraits.adoptTimeFrame(static_cast*>(&timeFrame)); itsTracker.adoptTimeFrame(timeFrame); itsTrackerTraits.setBz(mHitRecoConfig["geometry"]["bz"].get()); - auto field = o2::field::MagneticField::createNominalField(std::round(mHitRecoConfig["geometry"]["bz"].get()), true); + auto field = new field::MagneticField("ALICE3Mag", "ALICE 3 Magnetic Field", mHitRecoConfig["geometry"]["bz"].get() / 5.f, 0.0, o2::field::MagFieldParam::k5kGUniform); TGeoGlobalMagField::Instance()->SetField(field); TGeoGlobalMagField::Instance()->Lock(); @@ -291,6 +293,7 @@ void TrackerDPL::run(ProcessingContext& pc) itsTrackerTraits.updateTrackingParameters(trackingParams); + const auto trackingLoopStart = std::chrono::steady_clock::now(); for (size_t iter{0}; iter < trackingParams.size(); ++iter) { LOGP(info, "{}", trackingParams[iter].asString()); timeFrame.initialise(iter, trackingParams[iter], 11, false); @@ -304,6 +307,8 @@ void TrackerDPL::run(ProcessingContext& pc) LOGP(info, "Number of roads in iteration {}: {}", iter, timeFrame.getNumberOfTracks()); itsTrackerTraits.extendTracks(iter); } + const auto trackingLoopElapsedMs = std::chrono::duration_cast(std::chrono::steady_clock::now() - trackingLoopStart).count(); + LOGP(info, "Tracking iterations block took {} ms", trackingLoopElapsedMs); itsTracker.computeTracksMClabels(); From d95be4db7b5fe479e7adbe8b45094f628d6d97ea Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Tue, 17 Feb 2026 19:47:42 +0100 Subject: [PATCH 114/133] o2-sim: Possibility to switch between TGeo and Geant4 navigation --- Common/SimConfig/include/SimConfig/G4Params.h | 9 +++++++++ Common/SimConfig/src/SimConfigLinkDef.h | 1 + Detectors/gconfig/g4Config.C | 11 ++++++++++- Steer/src/O2MCApplication.cxx | 6 ++++++ 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/Common/SimConfig/include/SimConfig/G4Params.h b/Common/SimConfig/include/SimConfig/G4Params.h index fd36ae046d520..aa8aa05263c0a 100644 --- a/Common/SimConfig/include/SimConfig/G4Params.h +++ b/Common/SimConfig/include/SimConfig/G4Params.h @@ -33,6 +33,13 @@ enum class EG4Physics { kUSER = 8 /* allows to give own string combination */ }; +// enumerating possible geometry navigation modes +// (understanding that geometry description is always done with TGeo) +enum class EG4Nav { + kTGeo = 0, /* navigate with TGeo */ + kG4 = 1 /* navigate with G4 native geometry */ +}; + // parameters to influence the G4 engine struct G4Params : public o2::conf::ConfigurableParamHelper { EG4Physics physicsmode = EG4Physics::kFTFP_BERT_EMV_optical; // default physics mode with which to configure G4 @@ -40,6 +47,8 @@ struct G4Params : public o2::conf::ConfigurableParamHelper { std::string configMacroFile = ""; // a user provided g4Config.in file (otherwise standard one fill be taken) std::string userPhysicsList = ""; // possibility to directly give physics list as string + EG4Nav navmode = EG4Nav::kTGeo; // geometry navigation mode (default TGeo) + std::string const& getPhysicsConfigString() const; O2ParamDef(G4Params, "G4"); diff --git a/Common/SimConfig/src/SimConfigLinkDef.h b/Common/SimConfig/src/SimConfigLinkDef.h index 9c27536be5eb8..a1315e24ffedd 100644 --- a/Common/SimConfig/src/SimConfigLinkDef.h +++ b/Common/SimConfig/src/SimConfigLinkDef.h @@ -29,6 +29,7 @@ #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::conf::DigiParams> + ; #pragma link C++ enum o2::conf::EG4Physics; +#pragma link C++ enum o2::conf::EG4Nav; #pragma link C++ enum o2::conf::SimFieldMode; #pragma link C++ struct o2::conf::G4Params + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::conf::G4Params> + ; diff --git a/Detectors/gconfig/g4Config.C b/Detectors/gconfig/g4Config.C index 8f74c0105dbf5..c2b1fbd433e4b 100644 --- a/Detectors/gconfig/g4Config.C +++ b/Detectors/gconfig/g4Config.C @@ -100,7 +100,16 @@ void Config() auto& g4Params = ::o2::conf::G4Params::Instance(); auto& physicsSetup = g4Params.getPhysicsConfigString(); std::cout << "PhysicsSetup wanted " << physicsSetup << "\n"; - auto runConfiguration = new TG4RunConfiguration("geomRoot", physicsSetup, "stepLimiter+specialCuts", + std::string geomNavStr; + if (g4Params.navmode == o2::conf::EG4Nav::kTGeo) { + geomNavStr = "geomRoot"; + } else if (g4Params.navmode == o2::conf::EG4Nav::kG4) { + geomNavStr = "geomVMC+RootToGeant4"; + } else { + LOG(fatal) << "Unsupported geometry navigation mode"; + } + + auto runConfiguration = new TG4RunConfiguration(geomNavStr, physicsSetup, "stepLimiter+specialCuts", specialStacking, mtMode); /// avoid the use of G4BACKTRACE (it seems to inferfere with process logic in o2-sim) setenv("G4BACKTRACE", "none", 1); diff --git a/Steer/src/O2MCApplication.cxx b/Steer/src/O2MCApplication.cxx index f832ab70ab121..1e3f925042d01 100644 --- a/Steer/src/O2MCApplication.cxx +++ b/Steer/src/O2MCApplication.cxx @@ -42,6 +42,7 @@ #include #include #include +#include "SimConfig/G4Params.h" namespace o2 { @@ -223,6 +224,11 @@ bool O2MCApplicationBase::MisalignGeometry() void O2MCApplicationBase::fixTGeoRuntimeShapes() { + auto& g4Params = o2::conf::G4Params::Instance(); + if (g4Params.navmode != o2::conf::EG4Nav::kTGeo) { + return; + } + // Replace TGeo shapes by other ones for performance or other reasons. // Should only affect runtime of simulation. From d384645a99a311b75d221ca280d924a6bdfbb787 Mon Sep 17 00:00:00 2001 From: Andrea Sofia Triolo Date: Wed, 18 Feb 2026 15:11:43 +0100 Subject: [PATCH 115/133] ALICE3-TRK: fix detector ID assignment to hits (#15074) --- .../Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx index b32c89164f18a..059a35520c1a0 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx @@ -416,15 +416,15 @@ TString GeometryTGeo::getMatrixPath(int index) const // build the path if (subDetID == 0) { // VD if (disk >= 0) { - path += Form("%s_%d_%d/", getTRKPetalAssemblyPattern(), petalcase, petalcase + 1); // PETAL_n - path += Form("%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalDiskPattern(), disk); // PETALCASEx_DISKy_1 - // path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalDiskPattern(), disk, getTRKChipPattern(), disk); // PETALCASEx_DISKy_TRKChipy_1 + path += Form("%s_%d_%d/", getTRKPetalAssemblyPattern(), petalcase, petalcase + 1); // PETAL_n + path += Form("%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalDiskPattern(), disk); // PETALCASEx_DISKy_1 + path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalDiskPattern(), disk, getTRKChipPattern(), disk); // PETALCASEx_DISKy_TRKChipy_1 path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalDiskPattern(), disk, getTRKSensorPattern(), disk); // PETALCASEx_DISKy_TRKSensory_1 } else if (layer >= 0) { path += Form("%s_%d_%d/", getTRKPetalAssemblyPattern(), petalcase, petalcase + 1); // PETAL_n path += Form("%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalLayerPattern(), layer); // PETALCASEx_LAYERy_1 // path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalLayerPattern(), layer, getTRKStavePattern(), layer); // PETALCASEx_LAYERy_TRKStavey_1 - // path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalLayerPattern(), layer, getTRKChipPattern(), layer); // PETALCASEx_LAYERy_TRKChipy_1 + path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalLayerPattern(), layer, getTRKChipPattern(), layer); // PETALCASEx_LAYERy_TRKChipy_1 path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalLayerPattern(), layer, getTRKSensorPattern(), layer); // PETALCASEx_LAYERy_TRKSensory_1 } } else if (subDetID == 1) { // MLOT @@ -962,9 +962,9 @@ int GeometryTGeo::extractNumberOfChipsPerPetalVD() const for (int i = 0; i < subNodes->GetEntriesFast(); i++) { auto* subNode = dynamic_cast(subNodes->At(i)); - if (strstr(subNode->GetName(), getTRKSensorPattern()) != nullptr) { + if (strstr(subNode->GetName(), getTRKChipPattern()) != nullptr) { numberOfChips++; - LOGP(debug, "Found sensor in {}: {}", nodeName, subNode->GetName()); + LOGP(debug, "Found chip in {}: {}", nodeName, subNode->GetName()); } } } From 0b483951ab025a97601f15d861ba4df8de3c396e Mon Sep 17 00:00:00 2001 From: altsybee Date: Thu, 19 Feb 2026 07:54:50 +0100 Subject: [PATCH 116/133] [ALICE3] Change to upper-case 'S' in "FT3sensor_*" strings (#15078) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Change to upper-case 'S' in "FT3sensor_*" * Change to upper-case 'S' in "FT3sensor_*" in FT3Module.cxx * Update comments in exportLayout method Clarified comments in exportLayout function. --------- Co-authored-by: Nicolò Jacazio --- Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx | 7 ++++--- .../Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx index 9303979ada930..4b139272834f1 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx @@ -129,7 +129,8 @@ void Detector::buildFT3FromFile(std::string configFileName) //_________________________________________________________________________________________________ void Detector::exportLayout() { - // Export FT3 Layout description to file. One line per disk + // Export FT3 Layout description to file. + // One line per disk: // z_layer r_in r_out Layerx2X0 std::string configFileName = "FT3_layout.cfg"; @@ -795,8 +796,8 @@ void Detector::defineSensitiveVolumes() AddSensitiveVolume(v); } else { // OT disks for (int sensor_count = 0; sensor_count < MAX_SENSORS; ++sensor_count) { - std::string sensor_name_front = "FT3sensor_front_" + std::to_string(iLayer) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); - std::string sensor_name_back = "FT3sensor_back_" + std::to_string(iLayer) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + std::string sensor_name_front = "FT3Sensor_front_" + std::to_string(iLayer) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + std::string sensor_name_back = "FT3Sensor_back_" + std::to_string(iLayer) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); v = geoManager->GetVolume(sensor_name_front.c_str()); if (v) { AddSensitiveVolume(v); diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx index 9e24247958c06..9318554837706 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx @@ -481,7 +481,7 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double if (sensor_width == 2.5) { // silicon - std::string sensor_name = "FT3sensor_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + std::string sensor_name = "FT3Sensor_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); sensor = geoManager->MakeBox(sensor_name.c_str(), siliconMed, active_width / 2, active_height / 2, silicon_thickness / 2); sensor->SetLineColor(SiColor); sensor->SetFillColorAlpha(SiColor, 0.4); @@ -495,7 +495,7 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double } else { - std::string sensor_name = "FT3sensor_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + std::string sensor_name = "FT3Sensor_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); sensor = geoManager->MakeBox(sensor_name.c_str(), siliconMed, active_width / 2, sensor_height / 2, silicon_thickness / 2); sensor->SetLineColor(SiColor); sensor->SetFillColorAlpha(SiColor, 0.4); @@ -652,7 +652,7 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double if (sensor_width == 2.5) { - std::string sensor_name = "FT3sensor_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + std::string sensor_name = "FT3Sensor_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); sensor = geoManager->MakeBox(sensor_name.c_str(), siliconMed, active_width / 2, active_height / 2, silicon_thickness / 2); sensor->SetLineColor(SiColor); sensor->SetFillColorAlpha(SiColor, 0.4); @@ -666,7 +666,7 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double } else { // active (4.6 cm centered) - std::string sensor_name = "FT3sensor_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + std::string sensor_name = "FT3Sensor_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); sensor = geoManager->MakeBox(sensor_name.c_str(), siliconMed, active_width / 2, sensor_height / 2, silicon_thickness / 2); sensor->SetLineColor(SiColor); sensor->SetFillColorAlpha(SiColor, 0.4); From db8db2f046de80b4b70c4de41d0b05e07568aefd Mon Sep 17 00:00:00 2001 From: Roman Lietava Date: Thu, 19 Feb 2026 10:17:05 +0100 Subject: [PATCH 117/133] Ctpdev: getting list of unmasked inputs (#15082) * dev:CTpCfg list of used inputs * clang --- .../include/DataFormatsCTP/Configuration.h | 3 ++- .../Detectors/CTP/src/Configuration.cxx | 19 +++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/DataFormats/Detectors/CTP/include/DataFormatsCTP/Configuration.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Configuration.h index e9464089d71fc..ff1462084d53d 100644 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/Configuration.h +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Configuration.h @@ -214,7 +214,8 @@ struct CtpCfg { uint32_t orbitShift = 0; uint32_t irInputs_1_24 = 0; uint32_t irInputs_25_48 = 0; - ClassDefNV(CtpCfg, 1) + std::vector listOfUsedInputs(); + ClassDefNV(CtpCfg, 2) }; } // namespace ctp } // namespace o2 diff --git a/DataFormats/Detectors/CTP/src/Configuration.cxx b/DataFormats/Detectors/CTP/src/Configuration.cxx index 61e51bcb20d91..98458ef06d1d3 100644 --- a/DataFormats/Detectors/CTP/src/Configuration.cxx +++ b/DataFormats/Detectors/CTP/src/Configuration.cxx @@ -1227,9 +1227,24 @@ int CtpCfg::readAndSave(std::string& path) } return 0; } - +std::vector CtpCfg::listOfUsedInputs() +{ + std::cout << std::hex << "0x" << irInputs_1_24 << " " << irInputs_25_48 << std::dec << std::endl; + std::vector inputList; + for (int i = 0; i < 24; i++) { + if ((1ul << i) & irInputs_1_24) { + inputList.push_back(i); + } + } + for (int i = 0; i < 24; i++) { + if ((1ul << i) & irInputs_25_48) { + inputList.push_back(i + 24); + } + } + return inputList; +} std::ostream& o2::ctp::operator<<(std::ostream& in, const o2::ctp::CTPConfiguration& conf) { conf.printStream(in); return in; -} +} \ No newline at end of file From efa08980b26cf9ee523abf6d427b7e41dee1ed0d Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Thu, 19 Feb 2026 10:18:54 +0100 Subject: [PATCH 118/133] [ITS] Protect ultra low pt selections at the tracklet level (#15079) Checked on Pb-Pb simulation that this does not change the number of reconstructed tracks --- Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx index 70f4e3d1d3fc7..29fb4ac4c69b5 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx @@ -344,7 +344,6 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter mMSangles.resize(trkParam.NLayers); mPhiCuts.resize(mClusters.size() - 1, 0.f); - float oneOverR{0.001f * 0.3f * std::abs(mBz) / trkParam.TrackletMinPt}; for (unsigned int iLayer{0}; iLayer < nLayers; ++iLayer) { mMSangles[iLayer] = math_utils::MSangle(0.14f, trkParam.TrackletMinPt, trkParam.LayerxX0[iLayer]); @@ -352,12 +351,14 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter if (iLayer < mClusters.size() - 1) { const float& r1 = trkParam.LayerRadii[iLayer]; const float& r2 = trkParam.LayerRadii[iLayer + 1]; + oneOverR = (0.5 * oneOverR >= 1.f / r2) ? 2.f / r2 - o2::constants::math::Almost0 : oneOverR; const float res1 = o2::gpu::CAMath::Hypot(trkParam.PVres, mPositionResolution[iLayer]); const float res2 = o2::gpu::CAMath::Hypot(trkParam.PVres, mPositionResolution[iLayer + 1]); const float cosTheta1half = o2::gpu::CAMath::Sqrt(1.f - math_utils::Sq(0.5f * r1 * oneOverR)); const float cosTheta2half = o2::gpu::CAMath::Sqrt(1.f - math_utils::Sq(0.5f * r2 * oneOverR)); float x = r2 * cosTheta1half - r1 * cosTheta2half; float delta = o2::gpu::CAMath::Sqrt(1.f / (1.f - 0.25f * math_utils::Sq(x * oneOverR)) * (math_utils::Sq(0.25f * r1 * r2 * math_utils::Sq(oneOverR) / cosTheta2half + cosTheta1half) * math_utils::Sq(res1) + math_utils::Sq(0.25f * r1 * r2 * math_utils::Sq(oneOverR) / cosTheta1half + cosTheta2half) * math_utils::Sq(res2))); + /// the expression std::asin(0.5f * x * oneOverR) is equivalent to std::aCos(0.5f * r1 * oneOverR) - std::acos(0.5 * r2 * oneOverR) mPhiCuts[iLayer] = std::min(o2::gpu::CAMath::ASin(0.5f * x * oneOverR) + 2.f * mMSangles[iLayer] + delta, o2::constants::math::PI * 0.5f); } } From 98820e9b681677c61f1acac3e5f768cb806eb319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Thu, 19 Feb 2026 11:53:32 +0100 Subject: [PATCH 119/133] EMCAL: Delete unused files (#15026) --- .../DataFormatsEMCAL/EMCALChannelData.h | 55 ------ .../Detectors/EMCAL/src/EMCALChannelData.cxx | 19 -- .../reconstruction/run/rawReaderTRUDigits.cxx | 171 ------------------ 3 files changed, 245 deletions(-) delete mode 100644 DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EMCALChannelData.h delete mode 100644 DataFormats/Detectors/EMCAL/src/EMCALChannelData.cxx delete mode 100644 Detectors/EMCAL/reconstruction/run/rawReaderTRUDigits.cxx diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EMCALChannelData.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EMCALChannelData.h deleted file mode 100644 index 3c014d37e6f9e..0000000000000 --- a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EMCALChannelData.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file EMCALChannelData.h -/// \brief - -/// \class EMCALChannelCalibrator -/// \brief Class to store the data format for calibraton of the EMCal -/// \author Hannah Bossi, Yale University -/// \ingroup DetectorEMCAL -/// \since Feb 11, 2021 - -#ifndef ALICEO2_EMCALCHANNELDATA_H -#define ALICEO2_EMCALCHANNELDATA_H - -#include "Rtypes.h" - -namespace o2 -{ -namespace dataformats -{ -class EMCALChannelData -{ - public: - EMCALChannelData(int cellID, int timestamp, int flags = 0, int events) : mEMCALCellID(cellID), mTimestamp(timestamp), mFlags(flags){}; - EMCALChannelData() = default; - ~EMCALChannelData() = default; - - void setEMCALCellID(int index) { mEMCALCellID = index; } - int getEMCALCellID() const { return mEMCALCellID; } - - void setTimestamp(int ts) { mTimestamp = ts; } - int getTimestamp() const { return mTimestamp; } - - void setFlags(int flags) { mFlags = flags; } - float getFlags() const { return mFlags; } - - private: - int mEMCALCellID; ///< EMCal Cell ID - int mTimestamp; ///< timestamp in seconds - unsigned char mFlags; ///< bit mask with quality flags (to be defined) - - ClassDefNV(EMCALChannelData, 1); -}; -} // namespace dataformats -} // namespace o2 -#endif diff --git a/DataFormats/Detectors/EMCAL/src/EMCALChannelData.cxx b/DataFormats/Detectors/EMCAL/src/EMCALChannelData.cxx deleted file mode 100644 index 8affa29259f7a..0000000000000 --- a/DataFormats/Detectors/EMCAL/src/EMCALChannelData.cxx +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file EMCALChannelData.cxx -/// \brief Class to store the data format for calibraton of the EMCal - -#include "DataFormatsEMCAL/EMCALChannelData.h" - -using namespace o2::dataformats; - -ClassImp(o2::dataformats::EMCALChannelData; diff --git a/Detectors/EMCAL/reconstruction/run/rawReaderTRUDigits.cxx b/Detectors/EMCAL/reconstruction/run/rawReaderTRUDigits.cxx deleted file mode 100644 index 6fc119dc69521..0000000000000 --- a/Detectors/EMCAL/reconstruction/run/rawReaderTRUDigits.cxx +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file rawReaderFileNew.cxx -/// \author Markus Fasel , Oak Ridge National Laboratory - -#include -#include - -#include - -#include "DetectorsRaw/RawFileReader.h" -#include "DetectorsRaw/RDHUtils.h" -#include "EMCALBase/Mapper.h" -#include "EMCALBase/TriggerMappingV2.h" -#include "EMCALReconstruction/AltroDecoder.h" -#include "EMCALReconstruction/RawReaderMemory.h" -#include - -namespace bpo = boost::program_options; -// using namespace o2::emcal; - -int main(int argc, char** argv) -{ - bpo::variables_map vm; - bpo::options_description opt_general("Usage:\n " + std::string(argv[0]) + - " \n" - " Tool will decode the DDLx data for EMCAL 0\n" - "Commands / Options"); - bpo::options_description opt_hidden(""); - bpo::options_description opt_all; - bpo::positional_options_description opt_pos; - - try { - auto add_option = opt_general.add_options(); - add_option("help,h", "Print this help message"); - add_option("verbose,v", bpo::value()->default_value(0), "Select verbosity level [0 = no output]"); - add_option("version", "Print version information"); - add_option("input-file,i", bpo::value()->required(), "Specifies input file."); - add_option("debug,d", bpo::value()->default_value(0), "Select debug output level [0 = no debug output]"); - - opt_all.add(opt_general).add(opt_hidden); - bpo::store(bpo::command_line_parser(argc, argv).options(opt_all).positional(opt_pos).run(), vm); - - if (vm.count("help") || argc == 1) { - std::cout << opt_general << std::endl; - exit(0); - } - - if (vm.count("version")) { - // std::cout << GitInfo(); - exit(0); - } - - bpo::notify(vm); - } catch (bpo::error& e) { - std::cerr << "ERROR: " << e.what() << std::endl - << std::endl; - std::cerr << opt_general << std::endl; - exit(1); - } catch (std::exception& e) { - std::cerr << e.what() << ", application will now exit" << std::endl; - exit(2); - } - - auto rawfilename = vm["input-file"].as(); - - o2::raw::RawFileReader reader; - reader.setDefaultDataOrigin(o2::header::gDataOriginEMC); - reader.setDefaultDataDescription(o2::header::gDataDescriptionRawData); - reader.setDefaultReadoutCardType(o2::raw::RawFileReader::RORC); - reader.addFile(rawfilename); - reader.init(); - - o2::emcal::MappingHandler mapper; - o2::emcal::TriggerMappingV2 triggermapping; - - std::unique_ptr treefile(TFile::Open("trudata.root", "RECREATE")); - TTree trudata("trudata", "Tree with TRU data"); - // branches in tree - struct collisiontrigger { - unsigned long bc; - unsigned long orbit; - } mycollision; - int absFastOR; - int starttime; - std::vector timesamples; - tree->Branch(&mycollision, "collisiontrigger", "bc,orbit/l"); - tree->Branch(&starttime, "starttime", "starttime/i"); - tree->Branch(×amples, "timesamples", ""); // @todo check how to write std::vector to tree; - - while (1) { - int tfID = reader.getNextTFToRead(); - if (tfID >= reader.getNTimeFrames()) { - LOG(info) << "nothing left to read after " << tfID << " TFs read"; - break; - } - std::vector dataBuffer; // where to put extracted data - for (int il = 0; il < reader.getNLinks(); il++) { - auto& link = reader.getLink(il); - std::cout << "Decoding link " << il << std::endl; - - auto sz = link.getNextTFSize(); // size in bytes needed for the next TF of this link - dataBuffer.resize(sz); - link.readNextTF(dataBuffer.data()); - - // Parse - o2::emcal::RawReaderMemory parser(dataBuffer); - while (parser.hasNext()) { - parser.next(); - auto rdh = parser.getRawHeader(); - auto ddl = o2::raw::RDHUtils::getFEEID(parser.getRawHeader()); - // Exclude STU DDLs - if (ddl >= 40) { - continue; - } - - mycollision.bc = o2::raw::RDHUtils::getTriggerBC(rdh); - mycollision.orbit = o2::raw::RDHUtils::getTriggerOrbit(rdh); - - o2::emcal::AltroDecoder decoder(parser); - decoder.decode(); - auto& ddlmapping = mapper.getMappingForDDL(ddl); - - std::cout << decoder.getRCUTrailer() << std::endl; - for (auto& chan : decoder.getChannels()) { - if (ddlmapping.getChannelType(chan.getHardwareAddress) != o2::emcal::ChannelType_t::TRU) { - continue; - } - std::cout << "Hw address: " << chan.getHardwareAddress() << std::endl; - // Get absolute FastOR index - this will tell us where on the EMCAL surface the FastOR is - // TRU index is encoded in column, needs to be converted to an absoluted FastOR ID via the - // trigger mapping. The absoluted FastOR ID can be connected via the geometry to tower IDs - // from the FEC data. - // we are only interested in the FastORs for now, skip patches starting from 96 - auto fastorInTRU = ddlmapping.getColumn(chan.getHardwareAddress()); - if (fastorInTRU >= 96) { - // indices starting from 96 encode patches, not FastORs - continue; - } - auto truindex = triggermapping.getTRUIndexFromOnlineHardareAddree(chan.getHardwareAddress(), ddl, ddl / 2); - auto absFastOrID = triggermapping.getAbsFastORIndexFromIndexInTRU(truindex, fastorInTRU); - - for (auto& bunch : chan.getBunches()) { - std::cout << "BunchLength: " << int(bunch.getBunchLength()) << std::endl; - auto adcs = bunch.getADC(); - int time = bunch.getStartTime(); - starttime = time; - timesamples.clear(); - timesamples.resize(adcs.size()); - std::copy(adcs.begin(), adcs.end(), timesamples.begin()); - trudata.Fill(); - for (int i = adcs.size() - 1; i >= 0; i--) { - std::cout << "Timebin " << time << ", ADC " << adcs[i] << std::endl; - time--; - } - } - } - } - } - reader.setNextTFToRead(++tfID); - } -} \ No newline at end of file From 7b4f8f168ca7051f84d73b9ade285882c048348e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Thu, 19 Feb 2026 10:14:24 +0100 Subject: [PATCH 120/133] TPC: Delete unused files --- .../TPCReconstruction/ClusterContainer.h | 65 ------------------- .../src/time-series-merge-integrator.cxx | 34 ---------- .../TPC/workflow/src/time-series-reader.cxx | 25 ------- 3 files changed, 124 deletions(-) delete mode 100644 Detectors/TPC/reconstruction/include/TPCReconstruction/ClusterContainer.h delete mode 100644 Detectors/TPC/workflow/src/time-series-merge-integrator.cxx delete mode 100644 Detectors/TPC/workflow/src/time-series-reader.cxx diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/ClusterContainer.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/ClusterContainer.h deleted file mode 100644 index d86a845b0fe4c..0000000000000 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/ClusterContainer.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file ClusterContainer.h -/// \brief Container class for TPC clusters -#ifndef _ALICEO2_TPC_ClusterContainer_ -#define _ALICEO2_TPC_ClusterContainer_ - -#include -#include -#include // for Float_t etc - -namespace o2 -{ -namespace tpc -{ - -/// \class ClusterContainer -/// \brief Container class for TPC clusters -class ClusterContainer -{ - public: - // Initialize the clones array - // @param clusterType Possibility to store different types of clusters - // void InitArray(const Char_t* clusterType="o2::tpc::Cluster"); - - /// Add cluster to array - /// @param output, the vector to append to - /// @param cru CRU (sector) - /// @param row Row - /// @param q Total charge of cluster - /// @param qmax Maximum charge in a single cell (pad, time) - /// @param padmean Mean position of cluster in pad direction - /// @param padsigma Sigma of cluster in pad direction - /// @param timemean Mean position of cluster in time direction - /// @param timesigma Sigma of cluster in time direction - template - static ClusterType* addCluster(std::vector* output, - Int_t cru, Int_t row, Float_t qTot, Float_t qMax, - Float_t meanpad, Float_t meantime, Float_t sigmapad, - Float_t sigmatime) - { - assert(output); - output->emplace_back(); // emplace_back a defaut constructed cluster of type ClusterType - auto& cluster = output->back(); - // set its concrete parameters: - // ATTENTION: the order of parameters in setParameters is different than in AddCluster! - cluster.setParameters(cru, row, qTot, qMax, - meanpad, sigmapad, - meantime, sigmatime); - return &cluster; - } -}; -} // namespace tpc -} // namespace o2 - -#endif diff --git a/Detectors/TPC/workflow/src/time-series-merge-integrator.cxx b/Detectors/TPC/workflow/src/time-series-merge-integrator.cxx deleted file mode 100644 index c17b68e307328..0000000000000 --- a/Detectors/TPC/workflow/src/time-series-merge-integrator.cxx +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "TPCWorkflow/TPCMergeTimeSeriesSpec.h" -#include "CommonUtils/ConfigurableParam.h" -#include "Framework/ConfigParamSpec.h" - -using namespace o2::framework; - -void customize(std::vector& workflowOptions) -{ - std::vector options{ - ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, - }; - std::swap(workflowOptions, options); -} - -#include "Framework/runDataProcessing.h" - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - WorkflowSpec wf; - o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::tpc::getTPCMergeTimeSeriesSpec()); - return wf; -} diff --git a/Detectors/TPC/workflow/src/time-series-reader.cxx b/Detectors/TPC/workflow/src/time-series-reader.cxx deleted file mode 100644 index ccedbdf4f9599..0000000000000 --- a/Detectors/TPC/workflow/src/time-series-reader.cxx +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "TPCWorkflow/TPCTimeSeriesReaderSpec.h" -#include "CommonUtils/ConfigurableParam.h" -#include "Framework/ConfigParamSpec.h" - -using namespace o2::framework; - -#include "Framework/runDataProcessing.h" - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - WorkflowSpec wf; - wf.emplace_back(o2::tpc::getTPCTimeSeriesReaderSpec()); - return wf; -} From 1d4b3ef48aef20e6866379301de6e9d94ff234c2 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 17 Feb 2026 22:15:26 +0100 Subject: [PATCH 121/133] DPL: add ability to get the toplevel service registry This will allows non DPL code to still exploit some of the DPL services, like monitoring. --- Framework/Core/CMakeLists.txt | 1 + .../include/Framework/ServiceRegistryRef.h | 2 ++ Framework/Core/src/ServiceRegistryRef.cxx | 25 +++++++++++++++++++ Framework/Core/src/runDataProcessing.cxx | 1 + 4 files changed, 29 insertions(+) create mode 100644 Framework/Core/src/ServiceRegistryRef.cxx diff --git a/Framework/Core/CMakeLists.txt b/Framework/Core/CMakeLists.txt index 7357167a3fcd8..e6a8db1077136 100644 --- a/Framework/Core/CMakeLists.txt +++ b/Framework/Core/CMakeLists.txt @@ -125,6 +125,7 @@ o2_add_library(Framework src/RootArrowFilesystem.cxx src/SendingPolicy.cxx src/ServiceRegistry.cxx + src/ServiceRegistryRef.cxx src/ServiceSpec.cxx src/SimpleResourceManager.cxx src/SimpleRawDeviceService.cxx diff --git a/Framework/Core/include/Framework/ServiceRegistryRef.h b/Framework/Core/include/Framework/ServiceRegistryRef.h index 910d4e726c080..85aad6d70e93b 100644 --- a/Framework/Core/include/Framework/ServiceRegistryRef.h +++ b/Framework/Core/include/Framework/ServiceRegistryRef.h @@ -112,6 +112,8 @@ class ServiceRegistryRef mRegistry.unlock(mSalt); } + static ServiceRegistryRef *globalDeviceRef(ServiceRegistryRef *ref = nullptr); + private: ServiceRegistry& mRegistry; ServiceRegistry::Salt mSalt; diff --git a/Framework/Core/src/ServiceRegistryRef.cxx b/Framework/Core/src/ServiceRegistryRef.cxx new file mode 100644 index 0000000000000..70728ad37eda7 --- /dev/null +++ b/Framework/Core/src/ServiceRegistryRef.cxx @@ -0,0 +1,25 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + + +#include "Framework/ServiceRegistryRef.h" +namespace o2::framework { + +ServiceRegistryRef *ServiceRegistryRef::globalDeviceRef(ServiceRegistryRef *ref) { + static ServiceRegistryRef *globalRef = nullptr; + if (!globalRef) { + globalRef = ref; + } + // We return a copy, so that it can be cache + return globalRef; +} + +} diff --git a/Framework/Core/src/runDataProcessing.cxx b/Framework/Core/src/runDataProcessing.cxx index ced884ebaa1ed..b99b5119e3ce9 100644 --- a/Framework/Core/src/runDataProcessing.cxx +++ b/Framework/Core/src/runDataProcessing.cxx @@ -1429,6 +1429,7 @@ int runStateMachine(DataProcessorSpecs const& workflow, // We initialise this in the driver, because different drivers might have // different versions of the service ServiceRegistry serviceRegistry; + ServiceRegistryRef::globalDeviceRef(new ServiceRegistryRef{serviceRegistry, ServiceRegistry::globalDeviceSalt()}); if ((driverConfig.batch == false || getenv("DPL_DRIVER_REMOTE_GUI") != nullptr) && frameworkId.empty()) { debugGUI = initDebugGUI(); From 813e416361a029fa023fd3fa6116cbb54a700bba Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 17 Feb 2026 22:15:26 +0100 Subject: [PATCH 122/133] CCDB: report stats about CCDB fetches / misses to DPL --- CCDB/include/CCDB/BasicCCDBManager.h | 11 ++++++++- CCDB/src/BasicCCDBManager.cxx | 2 ++ .../include/Framework/DataProcessingStats.h | 3 +++ Framework/Core/src/CommonServices.cxx | 24 +++++++++++++++++++ Framework/Core/src/runDataProcessing.cxx | 1 + 5 files changed, 40 insertions(+), 1 deletion(-) diff --git a/CCDB/include/CCDB/BasicCCDBManager.h b/CCDB/include/CCDB/BasicCCDBManager.h index 71287c2f07d76..fd0fe7aa6d05b 100644 --- a/CCDB/include/CCDB/BasicCCDBManager.h +++ b/CCDB/include/CCDB/BasicCCDBManager.h @@ -20,6 +20,8 @@ #include "CommonUtils/NameConf.h" #include "Framework/DataTakingContext.h" #include "Framework/DefaultsHelpers.h" +#include "Framework/ServiceRegistryRef.h" +#include "Framework/DataProcessingStats.h" #include #include #include @@ -340,6 +342,13 @@ T* CCDBManagerInstance::getForTimeStamp(std::string const& path, long timestamp, } auto end = std::chrono::system_clock::now(); mTimerMS += std::chrono::duration_cast(end - start).count(); + auto *ref = o2::framework::ServiceRegistryRef::globalDeviceRef(); + if (ref && ref->active()) { + auto& stats = ref->get(); + stats.updateStats({(int)o2::framework::ProcessingStatsId::CCDB_CACHE_HIT, o2::framework::DataProcessingStats::Op::Set, (int64_t)mQueries - mFailures - mFetches}); + stats.updateStats({(int)o2::framework::ProcessingStatsId::CCDB_CACHE_MISS, o2::framework::DataProcessingStats::Op::Set, (int64_t)mFetches}); + stats.updateStats({(int)o2::framework::ProcessingStatsId::CCDB_CACHE_FAILURE, o2::framework::DataProcessingStats::Op::Set, (int64_t)mFailures}); + } return ptr; } @@ -391,4 +400,4 @@ class BasicCCDBManager : public CCDBManagerInstance } // namespace o2::ccdb -#endif //O2_BASICCCDBMANAGER_H +#endif // O2_BASICCCDBMANAGER_H diff --git a/CCDB/src/BasicCCDBManager.cxx b/CCDB/src/BasicCCDBManager.cxx index bcf88554578c1..d55fdad960d3a 100644 --- a/CCDB/src/BasicCCDBManager.cxx +++ b/CCDB/src/BasicCCDBManager.cxx @@ -13,6 +13,8 @@ // Created by Sandro Wenzel on 2019-08-14. // #include "CCDB/BasicCCDBManager.h" +#include "Framework/ServiceRegistryRef.h" +#include "Framework/DataProcessingStats.h" #include #include #include diff --git a/Framework/Core/include/Framework/DataProcessingStats.h b/Framework/Core/include/Framework/DataProcessingStats.h index e32523c9abb08..a1f5c0eec5568 100644 --- a/Framework/Core/include/Framework/DataProcessingStats.h +++ b/Framework/Core/include/Framework/DataProcessingStats.h @@ -69,6 +69,9 @@ enum struct ProcessingStatsId : short { RESOURCES_MISSING, RESOURCES_INSUFFICIENT, RESOURCES_SATISFACTORY, + CCDB_CACHE_HIT, + CCDB_CACHE_MISS, + CCDB_CACHE_FAILURE, AVAILABLE_MANAGED_SHM_BASE = 512, }; diff --git a/Framework/Core/src/CommonServices.cxx b/Framework/Core/src/CommonServices.cxx index 6486406a06dca..06bc7969ebf1e 100644 --- a/Framework/Core/src/CommonServices.cxx +++ b/Framework/Core/src/CommonServices.cxx @@ -1152,6 +1152,30 @@ o2::framework::ServiceSpec CommonServices::dataProcessingStats() .scope = Scope::DPL, .minPublishInterval = 0, .maxRefreshLatency = 10000, + .sendInitialValue = true}, + MetricSpec{.name = "ccdb-cache-hit", + .enabled = true, + .metricId = static_cast(ProcessingStatsId::CCDB_CACHE_HIT), + .kind = Kind::UInt64, + .scope = Scope::DPL, + .minPublishInterval = 1000, + .maxRefreshLatency = 10000, + .sendInitialValue = true}, + MetricSpec{.name = "ccdb-cache-miss", + .enabled = true, + .metricId = static_cast(ProcessingStatsId::CCDB_CACHE_MISS), + .kind = Kind::UInt64, + .scope = Scope::DPL, + .minPublishInterval = 1000, + .maxRefreshLatency = 10000, + .sendInitialValue = true}, + MetricSpec{.name = "ccdb-cache-failure", + .enabled = true, + .metricId = static_cast(ProcessingStatsId::CCDB_CACHE_FAILURE), + .kind = Kind::UInt64, + .scope = Scope::DPL, + .minPublishInterval = 1000, + .maxRefreshLatency = 10000, .sendInitialValue = true}}; for (auto& metric : metrics) { diff --git a/Framework/Core/src/runDataProcessing.cxx b/Framework/Core/src/runDataProcessing.cxx index b99b5119e3ce9..815fce47544d0 100644 --- a/Framework/Core/src/runDataProcessing.cxx +++ b/Framework/Core/src/runDataProcessing.cxx @@ -1248,6 +1248,7 @@ std::vector getDumpableMetrics() dumpableMetrics.emplace_back("^total-timeframes.*"); dumpableMetrics.emplace_back("^device_state.*"); dumpableMetrics.emplace_back("^total_wall_time_ms$"); + dumpableMetrics.emplace_back("^ccdb-.*$"); return dumpableMetrics; } From d569998f2457c1eb4f35596b38cf9a4c4bea23a5 Mon Sep 17 00:00:00 2001 From: Francesco Noferini Date: Wed, 18 Feb 2026 17:20:27 +0100 Subject: [PATCH 123/133] path for LHCphase ccdb configurable --- .../calibration/include/TOFCalibration/LHCClockCalibrator.h | 3 +++ Detectors/TOF/calibration/src/LHCClockCalibrator.cxx | 2 +- .../TOF/calibration/testWorkflow/LHCClockCalibratorSpec.h | 4 ++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Detectors/TOF/calibration/include/TOFCalibration/LHCClockCalibrator.h b/Detectors/TOF/calibration/include/TOFCalibration/LHCClockCalibrator.h index aaab8a06e5e86..4c8f5cdae8654 100644 --- a/Detectors/TOF/calibration/include/TOFCalibration/LHCClockCalibrator.h +++ b/Detectors/TOF/calibration/include/TOFCalibration/LHCClockCalibrator.h @@ -99,6 +99,8 @@ class LHCClockCalibrator final : public o2::calibration::TimeSlotCalibration("nbins")); auto slotL = ic.options().get("tf-per-slot"); auto delay = ic.options().get("max-delay"); + std::string path = ic.options().get("output-path"); + mCalibrator = std::make_unique(minEnt, nb); + mCalibrator->setPath(path.data()); mCalibrator->setSlotLength(slotL); mCalibrator->setMaxSlotsDelay(delay); @@ -216,6 +219,7 @@ DataProcessorSpec getLHCClockCalibDeviceSpec(bool useCCDB) AlgorithmSpec{adaptFromTask(ccdbRequest, useCCDB)}, Options{ {"tf-per-slot", VariantType::UInt32, 5u, {"number of TFs per calibration time slot"}}, + {"output-path", VariantType::String, "TOF/Calib/LHCphaseSync", {"path to ccdb output"}}, {"max-delay", VariantType::UInt32, 3u, {"number of slots in past to consider"}}, {"min-entries", VariantType::Int, 500, {"minimum number of entries to fit single time slot"}}, {"nbins", VariantType::Int, 4000, {"number of bins for "}}}}; From 67ab6d5a3922f27ce27376993bf5523b8c5d9e5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Thu, 5 Feb 2026 22:44:32 +0100 Subject: [PATCH 124/133] HMPID: Delete unused files --- .../HMPIDReconstruction/HmpidDecodeRawFile.h | 63 - .../HMPIDReconstruction/HmpidDecodeRawMem.h | 73 -- .../reconstruction/src/HmpidDecodeRawFile.cxx | 158 --- .../reconstruction/src/HmpidDecodeRawMem.cxx | 184 --- .../HMPID/reconstruction/src/HmpidDecoder.cxx | 1134 ----------------- .../HMPIDWorkflow/ClusterizerSpec.h_notused.h | 27 - .../HMPIDWorkflow/DigitReaderSpec.h_notused.h | 53 - 7 files changed, 1692 deletions(-) delete mode 100644 Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawFile.h delete mode 100644 Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawMem.h delete mode 100644 Detectors/HMPID/reconstruction/src/HmpidDecodeRawFile.cxx delete mode 100644 Detectors/HMPID/reconstruction/src/HmpidDecodeRawMem.cxx delete mode 100644 Detectors/HMPID/reconstruction/src/HmpidDecoder.cxx delete mode 100644 Detectors/HMPID/workflow/include/HMPIDWorkflow/ClusterizerSpec.h_notused.h delete mode 100644 Detectors/HMPID/workflow/include/HMPIDWorkflow/DigitReaderSpec.h_notused.h diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawFile.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawFile.h deleted file mode 100644 index e92e8375ad0d0..0000000000000 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawFile.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file HmpidDecodeRawFile.h -/// \author Antonio Franco - INFN Bari -/// \brief Derived Class for decoding Raw Data File stream -/// \version 1.0 -/// \date 24 set 2020 - -#ifndef COMMON_HMPIDDECODERAWFILE_H_ -#define COMMON_HMPIDDECODERAWFILE_H_ - -#include -#include -#include -#include -#include -#include - -#include "HMPIDReconstruction/HmpidDecoder.h" - -#define MAXFILENAMEBUFFER 512 -#define MAXRAWFILEBUFFER RAWBLOCKDIMENSION_W * 4 + 8 - -namespace o2 -{ -namespace hmpid -{ - -class HmpidDecodeRawFile : public HmpidDecoder -{ - public: - HmpidDecodeRawFile(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments); - HmpidDecodeRawFile(int numOfEquipments); - ~HmpidDecodeRawFile(); - - bool setUpStream(void* InpuFileName, long Size); - - private: - bool getBlockFromStream(uint32_t** streamPtr, uint32_t Size); - bool getHeaderFromStream(uint32_t** streamPtr); - bool getWordFromStream(uint32_t* word); - int fileExists(char* filewithpath); - void setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge); - - private: - FILE* fh; - char mInputFile[MAXFILENAMEBUFFER]; - uint32_t mFileBuffer[MAXRAWFILEBUFFER]; -}; - -} // namespace hmpid -} // namespace o2 -#endif /* COMMON_HMPIDDECODERAWFILE_H_ */ diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawMem.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawMem.h deleted file mode 100644 index d5d82d0f238e9..0000000000000 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawMem.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file HmpidDecodeRawMem.h -/// \author Antonio Franco - INFN Bari -/// \brief Derived Class for decoding Raw Data Memory stream -/// \version 1.0 -/// \date 24 set 2020 - -#ifndef COMMON_HMPIDDECODERAWMEM_H_ -#define COMMON_HMPIDDECODERAWMEM_H_ - -#include -#include -#include -#include -#include -#include - -#include "DataFormatsHMP/Digit.h" -#include "HMPIDBase/Geo.h" -#include "HMPIDReconstruction/HmpidDecoder.h" - -using namespace o2; - -namespace o2 -{ -namespace hmpid -{ - -class HmpidDecodeRawMem : public HmpidDecoder -{ - public: - HmpidDecodeRawMem(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments); - HmpidDecodeRawMem(int numOfEquipments); - ~HmpidDecodeRawMem(); - - bool setUpStream(void* Buffer, long BufferLen) override; - - private: - bool getBlockFromStream(uint32_t** streamPtr, uint32_t Size) override; - bool getHeaderFromStream(uint32_t** streamPtr) override; - bool getWordFromStream(uint32_t* word) override; - void setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) override; - - private: -}; - -class HmpidDecodeRawDigit : public HmpidDecodeRawMem -{ - public: - HmpidDecodeRawDigit(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments); - HmpidDecodeRawDigit(int numOfEquipments); - ~HmpidDecodeRawDigit(); - - std::vector mDigits; - - private: - void setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) override; -}; - -} // namespace hmpid -} // namespace o2 -#endif /* COMMON_HMPIDDECODERAWFILE_H_ */ diff --git a/Detectors/HMPID/reconstruction/src/HmpidDecodeRawFile.cxx b/Detectors/HMPID/reconstruction/src/HmpidDecodeRawFile.cxx deleted file mode 100644 index df97a4d2101e0..0000000000000 --- a/Detectors/HMPID/reconstruction/src/HmpidDecodeRawFile.cxx +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file HmpidDecodeRawFile.cxx -/// \author Antonio Franco - INFN Bari -/// \brief Derived Class for decoding Raw Data File stream -/// \version 1.0 -/// \date 24 set 2020 - -/* ------ HISTORY --------- -*/ -#include // for LOG -#include "Framework/Logger.h" - -#include "HMPIDReconstruction/HmpidDecodeRawFile.h" - -using namespace o2::hmpid; - -/// Constructor with the default HMPID equipments map at P2 -/// @param[in] numOfEquipments : number of defined equipments [0..13] -HmpidDecodeRawFile::HmpidDecodeRawFile(int numOfEquipments) - : HmpidDecoder(numOfEquipments) -{ - fh = 0; -} - -/// Constructor with the HMPID address map -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -/// @param[in] *EqIds : the pointer to the Equipments ID array -/// @param[in] *CruIds : the pointer to the CRU ID array -/// @param[in] *LinkIds : the pointer to the Link ID array -HmpidDecodeRawFile::HmpidDecodeRawFile(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments) - : HmpidDecoder(EqIds, CruIds, LinkIds, numOfEquipments) -{ - fh = 0; -} - -/// Destructor -HmpidDecodeRawFile::~HmpidDecodeRawFile() -{ -} - -/// Setup the Input Stream with a File Handle -/// verify the existence and try to open it -/// @param[in] *FileName : the string that contains the File Name -/// @param[in] Size : not used -/// @returns True if the file is opened -/// @throws TH_FILENOTEXISTS Thrown if the file doesn't exists -/// @throws TH_OPENFILE Thrown if Fails to open the file -bool HmpidDecodeRawFile::setUpStream(void* FileName, long Size) -{ - strcpy(mInputFile, (const char*)FileName); - // files section ---- - if (!fileExists(mInputFile)) { - LOG(error) << "The input file " << mInputFile << " does not exist at this time."; - throw TH_FILENOTEXISTS; - } - // open the file - fh = fopen(mInputFile, "rb"); - if (fh == 0) { - LOG(error) << "ERROR to open Input file ! [" << mInputFile << "]"; - throw TH_OPENFILE; - } - - mActualStreamPtr = 0; // sets the pointer to the Buffer - mEndStreamPtr = 0; //sets the End of buffer - mStartStreamPtr = 0; - - return (true); -} - -/// Gets a sized chunk from the stream. Read from the file and update the pointers -/// ATTENTION : in order to optimize the disk accesses the block read pre-load a -/// complete Header+Payload block, the Size parameter is recalculated with the -/// dimension of the pack extract from the header field 'Offeset' -/// -/// verify the existence and try to open it -/// @param[in] **streamPtr : the pointer to the memory buffer -/// @param[in] Size : not used -/// @returns True if the file is opened -/// @throws TH_WRONGFILELEN Thrown if the file doesn't contains enough words -bool HmpidDecodeRawFile::getBlockFromStream(uint32_t** streamPtr, uint32_t Size) -{ - if (Size > MAXRAWFILEBUFFER) - return (false); - int nr = fread(mFileBuffer, sizeof(int32_t), HEADERDIMENSION_W, fh); - if (nr != HEADERDIMENSION_W) { - throw TH_WRONGFILELEN; - } - Size = ((mFileBuffer[2] & 0x0000FFFF) / sizeof(int32_t)) - HEADERDIMENSION_W; - nr = fread(mFileBuffer + HEADERDIMENSION_W, sizeof(int32_t), Size, fh); - LOG(debug) << " getBlockFromStream read " << nr << " of " << Size + HEADERDIMENSION_W << " words !"; - if (nr != Size) { - throw TH_WRONGFILELEN; - } - *streamPtr = mFileBuffer; - mStartStreamPtr = mFileBuffer; - mActualStreamPtr = mFileBuffer; - mEndStreamPtr = mFileBuffer + Size; - return (true); -} - -/// Reads the Header from the file -/// @param[in] **streamPtr : the pointer to the memory buffer -/// @returns True if the header is read -bool HmpidDecodeRawFile::getHeaderFromStream(uint32_t** streamPtr) -{ - bool flag = getBlockFromStream(streamPtr, RAWBLOCKDIMENSION_W); // reads the 8k block - mActualStreamPtr += HEADERDIMENSION_W; // Move forward for the first word - return (flag); -} - -/// Read one word from the pre-load buffer -/// @param[in] *word : the buffer for the read word -/// @returns True every time -bool HmpidDecodeRawFile::getWordFromStream(uint32_t* word) -{ - *word = *mActualStreamPtr; - mActualStreamPtr++; - return (true); -} - -/// ----- Sets the Pad ! ------ -/// this is an overloaded method. In this version the value of the charge -/// is used to update the statistical matrix of the base class -/// -/// @param[in] *eq : the pointer to the Equipment object -/// @param[in] col : the column [0..23] -/// @param[in] dil : the dilogic [0..9] -/// @param[in] ch : the channel [0..47] -/// @param[in] charge : the value of the charge -void HmpidDecodeRawFile::setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) -{ - eq->setPad(col, dil, ch, charge); - return; -} - -/// Checks if the file exists ! -/// @param[in] *filewithpath : the File Name to check -/// @returns True if the file exists -int HmpidDecodeRawFile::fileExists(char* filewithpath) -{ - if (access(filewithpath, F_OK) != -1) { - return (true); - } else { - return (false); - } -} -o2::hmpid::Digit diff --git a/Detectors/HMPID/reconstruction/src/HmpidDecodeRawMem.cxx b/Detectors/HMPID/reconstruction/src/HmpidDecodeRawMem.cxx deleted file mode 100644 index 5a4f2acbfd97b..0000000000000 --- a/Detectors/HMPID/reconstruction/src/HmpidDecodeRawMem.cxx +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file HmpidDecodeRawMem.cxx -/// \author Antonio Franco - INFN Bari -/// \brief Derived Class for decoding Raw Data Memory stream -/// \version 1.0 -/// \date 24 set 2020 - -/* ------ HISTORY --------- -*/ -#include // for LOG -#include "Framework/Logger.h" - -#include "DataFormatsHMP/Digit.h" -#include "HMPIDBase/Geo.h" -#include "HMPIDReconstruction/HmpidDecodeRawMem.h" - -using namespace o2::hmpid; - -/// Constructor : accepts the number of equipments to define -/// The mapping is the default at P2 -/// Allocates instances for all defined equipments -/// normally it is equal to 14 -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -HmpidDecodeRawMem::HmpidDecodeRawMem(int numOfEquipments) - : HmpidDecoder(numOfEquipments) -{ -} - -/// Constructor : accepts the number of equipments to define -/// and their complete address map -/// Allocates instances for all defined equipments -/// -/// The Address map is build from three array -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -/// @param[in] *EqIds : the pointer to the Equipments ID array -/// @param[in] *CruIds : the pointer to the CRU ID array -/// @param[in] *LinkIds : the pointer to the Link ID array -HmpidDecodeRawMem::HmpidDecodeRawMem(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments) - : HmpidDecoder(EqIds, CruIds, LinkIds, numOfEquipments) -{ -} - -/// Destructor -HmpidDecodeRawMem::~HmpidDecodeRawMem() = default; - -/// Setup the Input Stream with a Memory Pointer -/// the buffer length is in byte, some controls are done -/// -/// @param[in] *Buffer : the pointer to Memory buffer -/// @param[in] BufferLen : the length of the buffer (bytes) -/// @returns True if the stream is set -/// @throws TH_NULLBUFFERPOINTER Thrown if the pointer to the buffer is NULL -/// @throws TH_BUFFEREMPTY Thrown if the buffer is empty -/// @throws TH_WRONGBUFFERDIM Thrown if the buffer len is less then one header -bool HmpidDecodeRawMem::setUpStream(void* Buffer, long BufferLen) -{ - long wordsBufferLen = BufferLen / (sizeof(int32_t) / sizeof(char)); // Converts the len in words - if (Buffer == nullptr) { - LOG(error) << "Raw data buffer null Pointer ! "; - throw TH_NULLBUFFERPOINTER; - } - if (wordsBufferLen == 0) { - LOG(error) << "Raw data buffer Empty ! "; - throw TH_BUFFEREMPTY; - } - if (wordsBufferLen < 16) { - LOG(error) << "Raw data buffer less then the Header Dimension = " << wordsBufferLen; - throw TH_WRONGBUFFERDIM; - } - - mActualStreamPtr = (uint32_t*)Buffer; // sets the pointer to the Buffer - mEndStreamPtr = ((uint32_t*)Buffer) + wordsBufferLen; //sets the End of buffer - mStartStreamPtr = ((uint32_t*)Buffer); - // std::cout << " setUpStrem : StPtr=" << mStartStreamPtr << " EndPtr=" << mEndStreamPtr << " Len=" << wordsBufferLen << std::endl; - return (true); -} - -/// Gets a sized chunk from the stream. The stream pointers members are updated -/// @param[in] **streamPtr : the pointer to the memory buffer -/// @param[in] Size : the dimension of the chunk (words) -/// @returns True every time -/// @throw TH_WRONGBUFFERDIM Buffer length shorter then the requested -bool HmpidDecodeRawMem::getBlockFromStream(uint32_t** streamPtr, uint32_t Size) -{ - *streamPtr = mActualStreamPtr; - mActualStreamPtr += Size; - if (mActualStreamPtr > mEndStreamPtr) { - // std::cout << " getBlockFromStream : StPtr=" << mActualStreamPtr << " EndPtr=" << mEndStreamPtr << " Len=" << Size << std::endl; - // std::cout << "Beccato " << std::endl; - // throw TH_WRONGBUFFERDIM; - return (false); - } - return (true); -} - -/// Gets the Header Block from the stream. -/// @param[in] **streamPtr : the pointer to the memory buffer -/// @returns True if the header is read -bool HmpidDecodeRawMem::getHeaderFromStream(uint32_t** streamPtr) -{ - return (getBlockFromStream(streamPtr, mRDHSize)); -} - -/// Gets a Word from the stream. -/// @param[in] *word : the buffer for the read word -/// @returns True if the operation end well -bool HmpidDecodeRawMem::getWordFromStream(uint32_t* word) -{ - uint32_t* appo; - *word = *mActualStreamPtr; - return (getBlockFromStream(&appo, 1)); -} - -/// ----- Sets the Pad ! ------ -/// this is an overloaded method. In this version the value of the charge -/// is used to update the statistical matrix of the base class -/// -/// @param[in] *eq : the pointer to the Equipment object -/// @param[in] col : the column [0..23] -/// @param[in] dil : the dilogic [0..9] -/// @param[in] ch : the channel [0..47] -/// @param[in] charge : the value of the charge -void HmpidDecodeRawMem::setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) -{ - eq->setPad(col, dil, ch, charge); - return; -} - -// ======================================================================================== - -/// Constructor : accepts the number of equipments to define -/// The mapping is the default at P2 -/// Allocates instances for all defined equipments -/// normally it is equal to 14 -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -HmpidDecodeRawDigit::HmpidDecodeRawDigit(int numOfEquipments) - : HmpidDecodeRawMem(numOfEquipments) -{ -} - -/// Constructor : accepts the number of equipments to define -/// and their complete address map -/// Allocates instances for all defined equipments -/// -/// The Address map is build from three array -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -/// @param[in] *EqIds : the pointer to the Equipments ID array -/// @param[in] *CruIds : the pointer to the CRU ID array -/// @param[in] *LinkIds : the pointer to the Link ID array -HmpidDecodeRawDigit::HmpidDecodeRawDigit(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments) - : HmpidDecodeRawMem(EqIds, CruIds, LinkIds, numOfEquipments) -{ -} - -/// Destructor -HmpidDecodeRawDigit::~HmpidDecodeRawDigit() = default; - -/// ----- Sets the Pad ! ------ -/// this is an overloaded method. In this version the value of the charge -/// is used to update the statistical matrix of the base class -/// -/// @param[in] *eq : the pointer to the Equipment object -/// @param[in] col : the column [0..23] -/// @param[in] dil : the dilogic [0..9] -/// @param[in] ch : the channel [0..47] -/// @param[in] charge : the value of the charge -void HmpidDecodeRawDigit::setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) -{ - eq->setPad(col, dil, ch, charge); - mDigits.push_back(o2::hmpid::Digit(charge, eq->getEquipmentId(), col, dil, ch)); - //std::cout << "DI " << mDigits.back() << " "< // for LOG -#include "Framework/Logger.h" -#include "Headers/RAWDataHeader.h" -#include "HMPIDReconstruction/HmpidDecoder.h" -#include "DataFormatsHMP/Digit.h" - -using namespace o2::hmpid; - -// ============= HmpidDecoder Class implementation ======= - -/// Decoding Error Messages Definitions -char HmpidDecoder::sErrorDescription[MAXERRORS][MAXDESCRIPTIONLENGHT] = {"Word that I don't known !", - "Row Marker Word with 0 words", "Duplicated Pad Word !", "Row Marker Wrong/Lost -> to EoE", - "Row Marker Wrong/Lost -> to EoE", "Row Marker reports an ERROR !", "Lost EoE Marker !", "Double EoE marker", - "Wrong size definition in EoE Marker", "Double Mark Word", "Wrong Size in Segment Marker", "Lost EoS Marker !", - "HMPID Header Errors"}; - -/// HMPID Firmware Error Messages Definitions -char HmpidDecoder::sHmpidErrorDescription[MAXHMPIDERRORS][MAXDESCRIPTIONLENGHT] = { - "L0 Missing," - "L1 is received without L0", - "L1A signal arrived before the L1 Latency", "L1A signal arrived after the L1 Latency", - "L1A is missing or L1 timeout", "L1A Message is missing or L1 Message"}; - -/// Constructor : accepts the number of equipments to define -/// The mapping is the default at P2 -/// Allocates instances for all defined equipments -/// normally it is equal to 14 -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -HmpidDecoder::HmpidDecoder(int numOfEquipments) -{ - // The standard definition of HMPID equipments at P2 - int EqIds[] = {0, 1, 2, 3, 4, 5, 8, 9, 6, 7, 10, 11, 12, 13}; - int CruIds[] = {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3}; - int LinkIds[] = {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 0, 1, 2}; - - mNumberOfEquipments = numOfEquipments; - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i] = new HmpidEquipment(EqIds[i], CruIds[i], LinkIds[i]); - } -} - -/// Constructor : accepts the number of equipments to define -/// and their complete address map -/// Allocates instances for all defined equipments -/// -/// The Address map is build from three array -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -/// @param[in] *EqIds : the pointer to the Equipments ID array -/// @param[in] *CruIds : the pointer to the CRU ID array -/// @param[in] *LinkIds : the pointer to the Link ID array -HmpidDecoder::HmpidDecoder(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments) -{ - mNumberOfEquipments = numOfEquipments; - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i] = new HmpidEquipment(EqIds[i], CruIds[i], LinkIds[i]); - } -} - -/// Destructor : remove the Equipments instances -HmpidDecoder::~HmpidDecoder() -{ - for (int i = 0; i < mNumberOfEquipments; i++) { - delete mTheEquipments[i]; - } -} - -/// Init all the members variables. -void HmpidDecoder::init() -{ - mRDHSize = sizeof(o2::header::RAWDataHeader) / sizeof(uint32_t); - - mVerbose = 0; - mHeEvent = 0; - mHeBusy = 0; - mNumberWordToRead = 0; - mPayloadTail = 0; - - mHeFEEID = 0; - mHeSize = 0; - mHeVer = 0; - mHePrior = 0; - mHeStop = 0; - mHePages = 0; - mEquipment = 0; - - mHeOffsetNewPack = 0; - mHeMemorySize = 0; - - mHeDetectorID = 0; - mHeDW = 0; - mHeCruID = 0; - mHePackNum = 0; - mHePAR = 0; - mHePageNum = 0; - mHeLinkNum = 0; - mHeFirmwareVersion = 0; - mHeHmpidError = 0; - mHeBCDI = 0; - mHeORBIT = 0; - mHeTType = 0; - - mActualStreamPtr = nullptr; - mEndStreamPtr = nullptr; - mStartStreamPtr = nullptr; - - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i]->init(); - } -} - -/// Returns the Equipment Index (Pointer of the array) converting -/// the FLP hardware coords (CRU_Id and Link_Id) -/// @param[in] CruId : the CRU ID [0..3] -> FLP 160 = [0,1] FLP 161 = [2,3] -/// @param[in] LinkId : the Link ID [0..3] -/// @returns EquipmentIndex : the index in the Equipment array [0..13] (-1 := error) -int HmpidDecoder::getEquipmentIndex(int CruId, int LinkId) -{ - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->getEquipmentId(CruId, LinkId) != -1) { - return (i); - } - } - return (-1); -} - -/// Returns the Equipment Index (Pointer of the array) converting -/// the Equipment_ID (Firmaware defined Id AKA FFEID) -/// @param[in] EquipmentId : the Equipment ID [0..13] -/// @returns EquipmentIndex : the index in the Equipment array [0..13] (-1 := error) -int HmpidDecoder::getEquipmentIndex(int EquipmentId) -{ - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->getEquipmentId() == EquipmentId) { - return (i); - } - } - return (-1); -} - -/// Returns the Equipment_ID converting the FLP hardware coords -/// @param[in] CruId : the CRU ID [0..3] -> FLP 160 = [0,1] FLP 161 = [2,3] -/// @param[in] LinkId : the Link ID [0..3] -/// @returns EquipmentID : the ID of the Equipment [0..13] (-1 := error) -int HmpidDecoder::getEquipmentID(int CruId, int LinkId) -{ - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->getEquipmentId(CruId, LinkId) != -1) { - return (mTheEquipments[i]->getEquipmentId()); - } - } - return (-1); -} - -/// Scans the BitMap of Raw Data File word and detect the type -/// and the parameters -/// @param[in] wp : the word to analyze -/// @param[out] *p1 : first parameter extract (if it exists) -/// @param[out] *p2 : second parameter extract (if it exists) -/// @param[out] *p3 : third parameter extract (if it exists) -/// @param[out] *p4 : fourth parameter extract (if it exists) -/// @returns Type of Word : the type of word [0..4] (0 := undetect) -int HmpidDecoder::checkType(uint32_t wp, int* p1, int* p2, int* p3, int* p4) -{ - if ((wp & 0x0000ffff) == 0x000036A8 || (wp & 0x0000ffff) == 0x000032A8 || (wp & 0x0000ffff) == 0x000030A0 || (wp & 0x0800ffff) == 0x080010A0) { - *p2 = (wp & 0x03ff0000) >> 16; // Number of words of row - *p1 = wp & 0x0000ffff; - return (WTYPE_ROW); - } - if ((wp & 0xfff00000) >> 20 == 0xAB0) { - *p2 = (wp & 0x000fff00) >> 8; // Number of words of Segment - *p1 = (wp & 0xfff00000) >> 20; - *p3 = wp & 0x0000000F; - if (*p3 < 4 && *p3 > 0) { - return (WTYPE_EOS); - } - } - // #EX MASK Raul 0x3803FF80 # ex mask 0xF803FF80 - this is EoE marker 0586800B0 - if ((wp & 0x0803FF80) == 0x08000080) { - *p1 = (wp & 0x07c00000) >> 22; - *p2 = (wp & 0x003C0000) >> 18; - *p3 = (wp & 0x0000007F); - if (*p1 < 25 && *p2 < 11) { - return (WTYPE_EOE); - } - } - if ((wp & 0x08000000) == 0) { // # this is a pad - // PAD:0000.0ccc.ccdd.ddnn.nnnn.vvvv.vvvv.vvvv :: c=col,d=dilo,n=chan,v=value - *p1 = (wp & 0x07c00000) >> 22; - *p2 = (wp & 0x003C0000) >> 18; - *p3 = (wp & 0x0003F000) >> 12; - *p4 = (wp & 0x00000FFF); - if (*p1 > 0 && *p1 < 25 && *p2 > 0 && *p2 < 11 && *p3 < 48) { - return (WTYPE_PAD); - } - } else { - return (WTYPE_NONE); - } - return (WTYPE_NONE); -} - -/// Checks if is a Raw Marker and extract the Row Size -/// @param[in] wp : the word to check -/// @param[out] *Err : true if an error is detected -/// @param[out] *rowSize : the number of words of the row -/// @param[out] *mark : the row marker -/// @returns True if Row Marker is detected -bool HmpidDecoder::isRowMarker(uint32_t wp, int* Err, int* rowSize, int* mark) -{ - if ((wp & 0x0000ffff) == 0x36A8 || (wp & 0x0000ffff) == 0x32A8 || (wp & 0x0000ffff) == 0x30A0 || (wp & 0x0800ffff) == 0x080010A0) { - *rowSize = (wp & 0x03ff0000) >> 16; // # Number of words of row - *mark = wp & 0x0000ffff; - *Err = false; - return (true); - } else { - *Err = true; - return (false); - } -} - -/// Checks if is a Segment Marker and extracts the Segment number and the size -/// @param[in] wp : the word to check -/// @param[out] *Err : true if an error is detected -/// @param[out] *segSize : the number of words of the segment -/// @param[out] *Seg : the Segment number [1..3] -/// @param[out] *mark : the Segment Marker -/// @returns True if Segment Marker is detected -bool HmpidDecoder::isSegmentMarker(uint32_t wp, int* Err, int* segSize, int* Seg, int* mark) -{ - *Err = false; - if ((wp & 0xfff00000) >> 20 == 0xAB0) { - *segSize = (wp & 0x000fff00) >> 8; // # Number of words of Segment - *mark = (wp & 0xfff00000) >> 20; - *Seg = wp & 0x0000000F; - if (*Seg > 3 || *Seg < 1) { - LOG(info) << " Wrong segment Marker Word, bad Number of segment" << *Seg << "!"; - *Err = true; - } - return (true); - } else { - return (false); - } -} - -/// Checks if is a PAD Word and extracts all the parameters -/// PAD map : 0000.0ccc.ccdd.ddnn.nnnn.vvvv.vvvv.vvvv :: c=col,d=dilo,n=chan,v=value -/// @param[in] wp : the word to check -/// @param[out] *Err : true if an error is detected -/// @param[out] *Col : the column number [1..24] -/// @param[out] *Dilogic : the dilogic number [1..10] -/// @param[out] *Channel : the channel number [0..47] -/// @param[out] *Charge : the value of Charge [0..4095] -/// @returns True if PAD Word is detected -bool HmpidDecoder::isPadWord(uint32_t wp, int* Err, int* Col, int* Dilogic, int* Channel, int* Charge) -{ - *Err = false; - // if ((wp & 0x08000000) != 0) { - if ((wp & 0x08000000) != 0) { - return (false); - } - *Col = (wp & 0x07c00000) >> 22; - *Dilogic = (wp & 0x003C0000) >> 18; - *Channel = (wp & 0x0003F000) >> 12; - *Charge = (wp & 0x00000FFF); - - if ((wp & 0x0ffff) == 0x036A8 || (wp & 0x0ffff) == 0x032A8 || (wp & 0x0ffff) == 0x030A0 || (wp & 0x0ffff) == 0x010A0) { // # ! this is a pad - if (*Dilogic > 10 || *Channel > 47 || *Dilogic < 1 || *Col > 24 || *Col < 1) { - return (false); - } - } else { - if (*Dilogic > 10 || *Channel > 47 || *Dilogic < 1 || *Col > 24 || *Col < 1) { - // LOG(warning) << " Wrong Pad values Col=" << *Col << " Dilogic=" << *Dilogic << " Channel=" << *Channel << " Charge=" << *Charge << " wp:0x" << std::hex << wp << std::dec; - *Err = true; - return (false); - } - } - return (true); -} - -/// Checks if is a EoE Marker and extracts the Column, Dilogic and the size -/// @param[in] wp : the word to check -/// @param[out] *Err : true if an error is detected -/// @param[out] *Col : the column number [1..24] -/// @param[out] *Dilogic : the dilogic number [1..10] -/// @param[out] *Eoesize : the number of words for dilogic -/// @returns True if EoE marker is detected -bool HmpidDecoder::isEoEmarker(uint32_t wp, int* Err, int* Col, int* Dilogic, int* Eoesize) -{ - *Err = false; - // #EX MASK Raul 0x3803FF80 # ex mask 0xF803FF80 - this is EoE marker 0586800B0 - if ((wp & 0x0803FF80) == 0x08000080) { - *Col = (wp & 0x07c00000) >> 22; - *Dilogic = (wp & 0x003C0000) >> 18; - *Eoesize = (wp & 0x0000007F); - if (*Col > 24 || *Dilogic > 10) { - LOG(info) << " EoE size wrong definition. Col=" << *Col << " Dilogic=" << *Dilogic; - *Err = true; - } - return (true); - } else { - return (false); - } -} - -/// Decode the HMPID error BitMap field (5 bits) and returns true if there are -/// errors and in addition the concat string that contains the error messages -/// ATTENTION : the char * outbuf MUST point to a 250 bytes buffer -/// @param[in] ErrorField : the HMPID Error field -/// @param[out] *outbuf : the output buffer that contains the error description -/// @returns True if EoE marker is detected -bool HmpidDecoder::decodeHmpidError(int ErrorField, char* outbuf) -{ - int res = false; - outbuf[0] = '\0'; - for (int i = 0; i < MAXHMPIDERRORS; i++) { - if ((ErrorField & (0x01 << i)) != 0) { - res = true; - strcat(outbuf, sHmpidErrorDescription[i]); - } - } - return (res); -} - -/// This Decode the Raw Data Header, returns the EquipmentIndex -/// that is obtained with the FLP hardware coords -/// -/// ATTENTION : the 'EquipIndex' parameter and the mEquipment member -/// are different data: the first is the pointer in the Equipments instances -/// array, the second is the FEE_ID number -/// -/// The EVENT_NUMBER : actually is calculated from the ORBIT number -/// -/// @param[in] *streamPtrAdr : the pointer to the Header buffer -/// @param[out] *EquipIndex : the Index to the Equipment Object Array [0..13] -/// @returns True every time -/// @throws TH_WRONGEQUIPINDEX Thrown if the Equipment Index is out of boundary (Equipment not recognized) -int HmpidDecoder::decodeHeader(uint32_t* streamPtrAdr, int* EquipIndex) -{ - uint32_t* buffer = streamPtrAdr; // Sets the pointer to buffer - o2::header::RAWDataHeader* hpt = (o2::header::RAWDataHeader*)buffer; - - /* - mHeFEEID = (buffer[0] & 0x000f0000) >> 16; - mHeSize = (buffer[0] & 0x0000ff00) >> 8; - mHeVer = (buffer[0] & 0x000000ff); - mHePrior = (buffer[1] & 0x000000FF); - mHeDetectorID = (buffer[1] & 0x0000FF00) >> 8; - mHeOffsetNewPack = (buffer[2] & 0x0000FFFF); - mHeMemorySize = (buffer[2] & 0xffff0000) >> 16; - mHeDW = (buffer[3] & 0xF0000000) >> 24; - mHeCruID = (buffer[3] & 0x0FF0000) >> 16; - mHePackNum = (buffer[3] & 0x0000FF00) >> 8; - mHeLinkNum = (buffer[3] & 0x000000FF); - mHeBCDI = (buffer[4] & 0x00000FFF); - mHeORBIT = buffer[5]; - mHeTType = buffer[8]; - mHePageNum = (buffer[9] & 0x0000FFFF); - mHeStop = (buffer[9] & 0x00ff0000) >> 16; - mHeBusy = (buffer[12] & 0xfffffe00) >> 9; - mHeFirmwareVersion = buffer[12] & 0x0000000f; - mHeHmpidError = (buffer[12] & 0x000001F0) >> 4; - mHePAR = buffer[13] & 0x0000FFFF; - */ - mHeFEEID = hpt->feeId; - mHeSize = hpt->headerSize; - mHeVer = hpt->version; - mHePrior = hpt->priority; - mHeDetectorID = hpt->sourceID; - mHeOffsetNewPack = hpt->offsetToNext; - mHeMemorySize = hpt->memorySize; - mHeDW = hpt->endPointID; - mHeCruID = hpt->cruID; - mHePackNum = hpt->packetCounter; - mHeLinkNum = hpt->linkID; - mHeBCDI = hpt->bunchCrossing; - mHeORBIT = hpt->orbit; - mHeTType = hpt->triggerType; - mHePageNum = hpt->pageCnt; - mHeStop = hpt->stop; - mHeBusy = (hpt->detectorField & 0xfffffe00) >> 9; - mHeFirmwareVersion = hpt->detectorField & 0x0000000f; - mHeHmpidError = (hpt->detectorField & 0x000001F0) >> 4; - mHePAR = hpt->detectorPAR; - - *EquipIndex = getEquipmentIndex(mHeCruID, mHeLinkNum); - // mEquipment = (*EquipIndex != -1) ? mTheEquipments[*EquipIndex]->getEquipmentId() : -1; - mEquipment = mHeFEEID & 0x000F; - mNumberWordToRead = ((mHeMemorySize - mHeSize) / sizeof(uint32_t)); - mPayloadTail = ((mHeOffsetNewPack - mHeMemorySize) / sizeof(uint32_t)); - - // ---- Event ID : Actualy based on ORBIT NUMBER and BC - mHeEvent = (mHeORBIT << 12) | mHeBCDI; - - LOG(debug) << "FEE-ID=" << mHeFEEID << " HeSize=" << mHeSize << " HePrior=" << mHePrior << " Det.Id=" << mHeDetectorID << " HeMemorySize=" << mHeMemorySize << " HeOffsetNewPack=" << mHeOffsetNewPack; - LOG(debug) << " Equipment=" << mEquipment << " PakCounter=" << mHePackNum << " Link=" << mHeLinkNum << " CruID=" << mHeCruID << " DW=" << mHeDW << " BC=" << mHeBCDI << " ORBIT=" << mHeORBIT; - LOG(debug) << " TType=" << mHeTType << " HeStop=" << mHeStop << " PagesCounter=" << mHePageNum << " FirmVersion=" << mHeFirmwareVersion << " BusyTime=" << mHeBusy << " Error=" << mHeHmpidError << " PAR=" << mHePAR; - LOG(debug) << " EquIdx = " << *EquipIndex << " Event = " << mHeEvent << " Payload : Words to read=" << mNumberWordToRead << " PailoadTail=" << mPayloadTail; - - if (*EquipIndex == -1) { - LOG(error) << "ERROR ! Bad equipment Number: " << mEquipment; - throw TH_WRONGEQUIPINDEX; - } - // std::cout << "HMPID ! Exit decode header" << std::endl; - return (true); -} - -/// Updates some information related to the Event -/// this function is called at the end of the event -/// @param[in] *eq : the pointer to the Equipment Object -void HmpidDecoder::updateStatistics(HmpidEquipment* eq) -{ - eq->mPadsPerEventAverage = ((eq->mPadsPerEventAverage * (eq->mNumberOfEvents - 1)) + eq->mSampleNumber) / (eq->mNumberOfEvents); - eq->mEventSizeAverage = ((eq->mEventSizeAverage * (eq->mNumberOfEvents - 1)) + eq->mEventSize) / (eq->mNumberOfEvents); - eq->mBusyTimeAverage = ((eq->mBusyTimeAverage * eq->mBusyTimeSamples) + eq->mBusyTimeValue) / (++(eq->mBusyTimeSamples)); - if (eq->mSampleNumber == 0) { - eq->mNumberOfEmptyEvents += 1; - } - if (eq->mErrorsCounter > 0) { - eq->mNumberOfWrongEvents += 1; - } - eq->mTotalPads += eq->mSampleNumber; - eq->mTotalErrors += eq->mErrorsCounter; - - //std::cout << ">>>updateStatistics() >>> "<< eq->getEquipmentId() << "="<< eq->mNumberOfEvents<<" :" << eq->mEventSize <<","<< eq->mTotalPads << ", " << eq->mSampleNumber << std::endl; - - return; -} - -/// Evaluates the content of the header and detect the change of the event -/// with the relevant updates... -/// @param[in] EquipmentIndex : the pointer to the Array of Equipments Array -/// @returns the Pointer to the modified Equipment object -HmpidEquipment* HmpidDecoder::evaluateHeaderContents(int EquipmentIndex) -{ - //std::cout << "Enter evaluateHeaderContents.."; - HmpidEquipment* eq = mTheEquipments[EquipmentIndex]; - if (mHeEvent != eq->mEventNumber) { // Is a new event - if (eq->mEventNumber != OUTRANGEEVENTNUMBER) { // skip the first - updateStatistics(eq); // update previous statistics - } - eq->mNumberOfEvents++; - eq->mEventNumber = mHeEvent; - eq->mBusyTimeValue = mHeBusy * 0.00000005; - eq->mEventSize = 0; // reset the event - eq->mSampleNumber = 0; - eq->mErrorsCounter = 0; - mIntReco = {(uint16_t)mHeBCDI, (uint32_t)mHeORBIT}; - } - eq->mEventSize += mNumberWordToRead * sizeof(uint32_t); // Calculate the size in bytes - if (mHeHmpidError != 0) { - LOG(info) << "HMPID Header reports an error : " << mHeHmpidError; - dumpHmpidError(mHeHmpidError); - eq->setError(ERR_HMPID); - } - // std::cout << ".. end evaluateHeaderContents = " << eq->mEventNumber << std::endl; - return (eq); -} - -/// --------------- Decode One Page from Data Buffer --------------- -/// Read the stream, decode the contents and store resuls. -/// ATTENTION : Assumes that the input stream was set -/// @throws TH_WRONGHEADER Thrown if the Fails to decode the Header -/// @param[in] streamBuf : the pointer to the Pointer of the Stream Buffer -void HmpidDecoder::decodePage(uint32_t** streamBuf) -{ - int equipmentIndex; - try { - getHeaderFromStream(streamBuf); - } catch (int e) { - // The stream end ! - LOG(debug) << "End main decoding loop !"; - throw TH_BUFFEREMPTY; - } - try { - decodeHeader(*streamBuf, &equipmentIndex); - } catch (int e) { - LOG(error) << "Failed to decode the Header !"; - throw TH_WRONGHEADER; - } - - HmpidEquipment* eq = evaluateHeaderContents(equipmentIndex); - - uint32_t wpprev = 0; - uint32_t wp = 0; - int newOne = true; - int p1, p2, p3, p4; - int error; - int type; - bool isIt; - - int payIndex = 0; - while (payIndex < mNumberWordToRead) { //start the payload loop word by word - if (newOne == true) { - wpprev = wp; - if (!getWordFromStream(&wp)) { // end the stream - break; - } - type = checkType(wp, &p1, &p2, &p3, &p4); - if (type == WTYPE_NONE) { - if (eq->mWillBePad == true) { // try to recover the first pad ! - type = checkType((wp & 0xF7FFFFFF), &p1, &p2, &p3, &p4); - if (type == WTYPE_PAD && p3 == 0 && eq->mWordsPerDilogicCounter == 0) { - newOne = false; // # reprocess as pad - continue; - } - } - eq->setError(ERR_NOTKNOWN); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_NOTKNOWN] << " [" << wp << "]"; - eq->mWordsPerRowCounter++; - eq->mWordsPerSegCounter++; - payIndex++; - continue; - } - } - if (mEquipment == 8) { - LOG(info) << "Event" << eq->mEventNumber << " >" << std::hex << wp << std::dec << "<" << type; - } - if (eq->mWillBeRowMarker == true) { // #shoud be a Row Marker - if (type == WTYPE_ROW) { - eq->mColumnCounter++; - eq->mWordsPerSegCounter++; - eq->mRowSize = p2; - switch (p2) { - case 0: // Empty column - eq->setError(ERR_ROWMARKEMPTY); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_ROWMARKEMPTY] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = true; - break; - case 0x3FF: // Error in column - eq->setError(ERR_ROWMARKERROR); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_ROWMARKERROR] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = true; - break; - case 0x3FE: // Masked column - LOG(info) << "Equip=" << mEquipment << "The column=" << (eq->mSegment) * 8 + eq->mColumnCounter << " is Masked !"; - eq->mWillBeRowMarker = true; - break; - default: - eq->mWillBeRowMarker = false; - eq->mWillBePad = true; - break; - } - newOne = true; - } else { - if (wp == wpprev) { - eq->setError(ERR_DUPLICATEPAD); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DUPLICATEPAD] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - newOne = true; - } else if (type == WTYPE_EOE) { // # Could be a EoE - eq->mColumnCounter++; - eq->setError(ERR_ROWMARKWRONG); - eq->mWillBeRowMarker = false; - eq->mWillBePad = true; - newOne = true; - } else if (type == WTYPE_PAD) { //# Could be a PAD - eq->mColumnCounter++; - eq->setError(ERR_ROWMARKLOST); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_ROWMARKLOST] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = false; - eq->mWillBePad = true; - newOne = true; - } else if (type == WTYPE_EOS) { // # Could be a EoS - eq->mWillBeRowMarker = false; - eq->mWillBeSegmentMarker = true; - newOne = false; - } else { - eq->mColumnCounter++; - eq->setError(ERR_ROWMARKLOST); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_ROWMARKLOST] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = false; - eq->mWillBePad = true; - newOne = true; - } - } - } else if (eq->mWillBePad == true) { // # We expect a pad - //# PAD:0000.0ccc.ccdd.ddnn.nnnn.vvvv.vvvv.vvvv :: c=col,d=dilo,n=chan,v=value - // c = 1..24 d = 1..10 n = 0..47 - if (type == WTYPE_PAD) { - newOne = true; - if (wp == wpprev) { - eq->setError(ERR_DUPLICATEPAD); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DUPLICATEPAD] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - } else if (p1 != (eq->mSegment * 8 + eq->mColumnCounter)) { // # Manage - // We try to recover the RowMarker misunderstanding - isIt = isRowMarker(wp, &error, &p2, &p1); - if (isIt == true && error == false) { - type = WTYPE_ROW; - newOne = false; - eq->mWillBeEoE = true; - eq->mWillBePad = false; - } else { - LOG(debug) << "Equip=" << mEquipment << " Mismatch in column" - << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mColumnCounter = p1 % 8; - } - } else { - setPad(eq, p1 - 1, p2 - 1, p3, p4); - if (mEquipment == 8) { - LOG(info) << "Event" << eq->mEventNumber << " >" << p1 - 1 << "," << p2 - 1 << "," << p3 << "," << p4; - } - eq->mWordsPerDilogicCounter++; - eq->mSampleNumber++; - if (p3 == 47) { - eq->mWillBeEoE = true; - eq->mWillBePad = false; - } - } - eq->mWordsPerRowCounter++; - eq->mWordsPerSegCounter++; - } else if (type == WTYPE_EOE) { //# the pads are end ok - eq->mWillBeEoE = true; - eq->mWillBePad = false; - newOne = false; - } else if (type == WTYPE_ROW) { // # We Lost the EoE ! - // We try to recover the PAD misunderstanding - isIt = isPadWord(wp, &error, &p1, &p2, &p3, &p4); - if (isIt == true && error == false) { - type = WTYPE_PAD; - newOne = false; // # reprocess as pad - } else { - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = true; - eq->mWillBePad = false; - newOne = false; - } - } else if (type == WTYPE_EOS) { // # We Lost the EoE ! - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeSegmentMarker = true; - eq->mWillBePad = false; - newOne = false; - } - } else if (eq->mWillBeEoE == true) { // # We expect a EoE - if (type == WTYPE_EOE) { - eq->mWordsPerRowCounter++; - eq->mWordsPerSegCounter++; - if (wpprev == wp) { - eq->setError(ERR_DOUBLEEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DOUBLEEOEMARK] << " col=" << p1; - } else if (p3 != eq->mWordsPerDilogicCounter) { - eq->setError(ERR_WRONGSIZEINEOE); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_WRONGSIZEINEOE] << " col=" << p1; - } - eq->mWordsPerDilogicCounter = 0; - if (p2 == 10) { - if (p1 % 8 != 0) { // # we expect the Row Marker - eq->mWillBeRowMarker = true; - } else { - eq->mWillBeSegmentMarker = true; - } - } else { - eq->mWillBePad = true; - } - eq->mWillBeEoE = false; - newOne = true; - } else if (type == WTYPE_EOS) { // We Lost the EoE ! - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeSegmentMarker = true; - eq->mWillBeEoE = false; - newOne = false; - } else if (type == WTYPE_ROW) { //# We Lost the EoE ! - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = true; - eq->mWillBeEoE = false; - newOne = false; - } else if (type == WTYPE_PAD) { // # We Lost the EoE ! - int typb, p1b, p2b, p3b, p4b; - typb = checkType((wp | 0x08000000), &p1b, &p2b, &p3b, &p4b); - if (typb == WTYPE_EOE && p3b == 48) { - type = typb; - p1 = p1b; - p2 = p2b; - p3 = p3b; - p4 = p4b; - newOne = false; // # reprocess as EoE - } else { - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBePad = true; - eq->mWillBeEoE = false; - newOne = false; - } - } - } else if (eq->mWillBeSegmentMarker == true) { // # We expect a EoSegment - if (wpprev == wp) { - eq->setError(ERR_DOUBLEMARKWORD); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DOUBLEMARKWORD] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - newOne = true; - } else if (type == 2) { - if (abs(eq->mWordsPerSegCounter - p2) > 5) { - eq->setError(ERR_WRONGSIZESEGMENTMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_WRONGSIZESEGMENTMARK] << " Seg=" << p2; - } - eq->mWordsPerSegCounter = 0; - eq->mWordsPerRowCounter = 0; - eq->mColumnCounter = 0; - eq->mSegment = p3 % 3; - eq->mWillBeRowMarker = true; - eq->mWillBeSegmentMarker = false; - newOne = true; - } else { - eq->setError(ERR_LOSTEOSMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOSMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeSegmentMarker = false; - eq->mWillBeRowMarker = true; - newOne = false; - } - } - if (newOne) { - payIndex += 1; - } - } - for (int i = 0; i < mPayloadTail; i++) { // move the pointer to skip the Payload Tail - getWordFromStream(&wp); - } -} - -/// --------------- Read Raw Data Buffer --------------- -/// Read the stream, decode the contents and store resuls. -/// ATTENTION : Assumes that the input stream was set -/// @throws TH_WRONGHEADER Thrown if the Fails to decode the Header -bool HmpidDecoder::decodeBuffer() -{ - // ---------resets the PAdMap----------- - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i]->init(); - mTheEquipments[i]->resetPadMap(); - mTheEquipments[i]->resetErrors(); - } - - int type; - int equipmentIndex = -1; - int isIt; - HmpidEquipment* eq; - uint32_t* streamBuf; - LOG(debug) << "Enter decoding !"; - - // Input Stream Main Loop - while (true) { - try { - decodePage(&streamBuf); - } catch (int e) { - LOG(debug) << "End main buffer decoding loop !"; - break; - } - } // this is the end of stream - - // cycle in order to update info for the last event - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->mNumberOfEvents > 0) { - updateStatistics(mTheEquipments[i]); - } - } - return (true); -} - -/// --------- Decode One Page from Data Buffer with Fast Decoding -------- -/// Read the stream, decode the contents and store resuls. -/// ATTENTION : Assumes that the input stream was set -/// @throws TH_WRONGHEADER Thrown if the Fails to decode the Header -/// @param[in] streamBuf : the pointer to the Pointer of the Stream Buffer -void HmpidDecoder::decodePageFast(uint32_t** streamBuf) -{ - int equipmentIndex; - try { - getHeaderFromStream(streamBuf); - } catch (int e) { - // The stream end ! - LOG(info) << "End Fast Page decoding loop !"; - throw TH_BUFFEREMPTY; - } - try { - decodeHeader(*streamBuf, &equipmentIndex); - } catch (int e) { - LOG(info) << "Failed to decode the Header !"; - throw TH_WRONGHEADER; - } - HmpidEquipment* eq = evaluateHeaderContents(equipmentIndex); - uint32_t wpprev = 0; - uint32_t wp = 0; - int newOne = true; - int Column, Dilogic, Channel, Charge; - int pwer; - int payIndex = 0; - while (payIndex < mNumberWordToRead) { //start the payload loop word by word - wpprev = wp; - if (!getWordFromStream(&wp)) { // end the stream - break; - } - if (wp == wpprev) { - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DUPLICATEPAD] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << Column << "]"; - } else { - if (isPadWord(wp, &pwer, &Column, &Dilogic, &Channel, &Charge) == true) { - if (pwer != true) { - setPad(eq, Column - 1, Dilogic - 1, Channel, Charge); - eq->mSampleNumber++; - } - } - } - payIndex += 1; - } - for (int i = 0; i < mPayloadTail; i++) { // move the pointer to skip the Payload Tail - getWordFromStream(&wp); - } - return; -} -/// ---------- Read Raw Data Buffer with Fast Decoding ---------- -/// Read the stream, decode the contents and store resuls. -/// Fast alghoritm : no parsing of control words ! -/// ATTENTION : Assumes that the input stream was set -/// @throws TH_WRONGHEADER Thrown if the Fails to decode the Header -bool HmpidDecoder::decodeBufferFast() -{ - // ---------resets the PAdMap----------- - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i]->init(); - mTheEquipments[i]->resetPadMap(); - } - - uint32_t* streamBuf; - LOG(info) << "Enter FAST decoding !"; - - // Input Stream Main Loop - while (true) { - try { - decodePageFast(&streamBuf); - } catch (int e) { - LOG(info) << " End Buffer Fast Decoding !"; - break; - } - } // this is the end of stream - - // cycle in order to update info for the last event - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->mNumberOfEvents > 0) { - updateStatistics(mTheEquipments[i]); - } - } - return (true); -} - -// ========================================================= - -/// Getter method to extract Statistic Data in Digit Coords -/// @param[in] Module : the HMPID Module number [0..6] -/// @param[in] Column : the HMPID Module Column number [0..143] -/// @param[in] Row : the HMPID Module Row number [0..159] -/// @returns The Number of entries for specified pad -uint16_t HmpidDecoder::getPadSamples(int Module, int Row, int Column) -{ - int e, c, d, h; - o2::hmpid::Digit::absolute2Equipment(Module, Row, Column, &e, &c, &d, &h); - int EqInd = getEquipmentIndex(e); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSamples[c][d][h]); -} - -/// Getter method to extract Statistic Data in Digit Coords -/// @param[in] Module : the HMPID Module number [0..6] -/// @param[in] Column : the HMPID Module Column number [0..143] -/// @param[in] Row : the HMPID Module Row number [0..159] -/// @returns The Sum of Charges for specified pad -double HmpidDecoder::getPadSum(int Module, int Row, int Column) -{ - int e, c, d, h; - o2::hmpid::Digit::absolute2Equipment(Module, Row, Column, &e, &c, &d, &h); - int EqInd = getEquipmentIndex(e); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSum[c][d][h]); -} - -/// Getter method to extract Statistic Data in Digit Coords -/// @param[in] Module : the HMPID Module number [0..6] -/// @param[in] Column : the HMPID Module Column number [0..143] -/// @param[in] Row : the HMPID Module Row number [0..159] -/// @returns The Sum of Square Charges for specified pad -double HmpidDecoder::getPadSquares(int Module, int Row, int Column) -{ - int e, c, d, h; - o2::hmpid::Digit::absolute2Equipment(Module, Row, Column, &e, &c, &d, &h); - int EqInd = getEquipmentIndex(e); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSquares[c][d][h]); -} - -/// Getter method to extract Statistic Data in Hardware Coords -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @param[in] Column : the HMPID Module Column number [0..23] -/// @param[in] Dilogic : the HMPID Module Row number [0..9] -/// @param[in] Channel : the HMPID Module Row number [0..47] -/// @returns The Number of Entries for specified pad -uint16_t HmpidDecoder::getChannelSamples(int EquipmId, int Column, int Dilogic, int Channel) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSamples[Column][Dilogic][Channel]); -} - -/// Getter method to extract Statistic Data in Hardware Coords -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @param[in] Column : the HMPID Module Column number [0..23] -/// @param[in] Dilogic : the HMPID Module Row number [0..9] -/// @param[in] Channel : the HMPID Module Row number [0..47] -/// @returns The Sum of Charges for specified pad -double HmpidDecoder::getChannelSum(int EquipmId, int Column, int Dilogic, int Channel) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSum[Column][Dilogic][Channel]); -} - -/// Getter method to extract Statistic Data in Hardware Coords -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @param[in] Column : the HMPID Module Column number [0..23] -/// @param[in] Dilogic : the HMPID Module Row number [0..9] -/// @param[in] Channel : the HMPID Module Row number [0..47] -/// @returns The Sum of Square Charges for specified pad -double HmpidDecoder::getChannelSquare(int EquipmId, int Column, int Dilogic, int Channel) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSquares[Column][Dilogic][Channel]); -} - -/// Gets the Average Event Size value -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @returns The Average Event Size value ( 0 for wrong Equipment Id) -float HmpidDecoder::getAverageEventSize(int EquipmId) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0.0); - } - return (mTheEquipments[EqInd]->mEventSizeAverage); -} - -/// Gets the Average Busy Time value -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @returns The Average Busy Time value ( 0 for wrong Equipment Id) -float HmpidDecoder::getAverageBusyTime(int EquipmId) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0.0); - } - return (mTheEquipments[EqInd]->mBusyTimeAverage); -} - -// =================================================== -// Methods to dump info - -/// Prints on the standard output the table of decoding -/// errors for one equipment -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -void HmpidDecoder::dumpErrors(int EquipmId) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return; - } - std::cout << "Dump Errors for the Equipment = " << EquipmId << std::endl; - for (int i = 0; i < MAXERRORS; i++) { - std::cout << sErrorDescription[i] << " = " << mTheEquipments[EqInd]->mErrors[i] << std::endl; - } - std::cout << " -------- " << std::endl; - return; -} - -/// Prints on the standard output a Table of statistical -/// decoding information for one equipment -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @type[in] The type of info. 0 = Entries, 1 = Sum, 2 = Sum of squares -void HmpidDecoder::dumpPads(int EquipmId, int type) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return; - } - int Module = EquipmId / 2; - int StartRow = (EquipmId % 2 == 1) ? 80 : 0; - int EndRow = (EquipmId % 2 == 1) ? 160 : 80; - std::cout << "Dump Pads for the Equipment = " << EquipmId << std::endl; - for (int c = 0; c < 144; c++) { - for (int r = StartRow; r < EndRow; r++) { - switch (type) { - case 0: - std::cout << getPadSamples(Module, r, c) << ","; - break; - case 1: - std::cout << getPadSum(Module, r, c) << ","; - break; - case 2: - std::cout << getPadSquares(Module, r, c) << ","; - break; - } - } - std::cout << std::endl; - } - std::cout << " -------- " << std::endl; - return; -} - -/// Prints on the standard output the decoded HMPID error field -/// @param[in] ErrorField : the HMPID readout error field -void HmpidDecoder::dumpHmpidError(int ErrorField) -{ - char printbuf[MAXHMPIDERRORS * MAXDESCRIPTIONLENGHT]; - if (decodeHmpidError(ErrorField, printbuf) == true) { - LOG(error) << "HMPID Error field = " << ErrorField << " : " << printbuf; - } - return; -} - -/// Writes in a ASCCI File the complete report of the decoding -/// procedure -/// @param[in] *summaryFileName : the name of the output file -/// @throws TH_CREATEFILE Thrown if was not able to create the file -void HmpidDecoder::writeSummaryFile(char* summaryFileName) -{ - FILE* fs = fopen(summaryFileName, "w"); - if (fs == nullptr) { - printf("Error opening the file %s !\n", summaryFileName); - throw TH_CREATEFILE; - } - - fprintf(fs, "HMPID Readout Raw Data Decoding Summary File\n"); - fprintf(fs, "Equipment Id\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->getEquipmentId()); - } - fprintf(fs, "\n"); - - fprintf(fs, "Number of events\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mNumberOfEvents); - } - fprintf(fs, "\n"); - - fprintf(fs, "Average Event Size\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%f\t", mTheEquipments[i]->mEventSizeAverage); - } - fprintf(fs, "\n"); - - fprintf(fs, "Total pads\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mTotalPads); - } - fprintf(fs, "\n"); - - fprintf(fs, "Average pads per event\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%f\t", mTheEquipments[i]->mPadsPerEventAverage); - } - fprintf(fs, "\n"); - - fprintf(fs, "Busy Time average\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%e\t", mTheEquipments[i]->mBusyTimeAverage); - } - fprintf(fs, "\n"); - - fprintf(fs, "Event rate\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%e\t", 1 / mTheEquipments[i]->mBusyTimeAverage); - } - fprintf(fs, "\n"); - - fprintf(fs, "Number of Empty Events\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mNumberOfEmptyEvents); - } - fprintf(fs, "\n"); - - fprintf(fs, "-------------Errors--------------------\n"); - fprintf(fs, "Wrong events\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mNumberOfWrongEvents); - } - fprintf(fs, "\n"); - - for (int j = 0; j < MAXERRORS; j++) { - fprintf(fs, "%s\t", sErrorDescription[j]); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mErrors[j]); - } - fprintf(fs, "\n"); - } - - fprintf(fs, "Total errors\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mTotalErrors); - } - fprintf(fs, "\n"); - - fclose(fs); - return; -} diff --git a/Detectors/HMPID/workflow/include/HMPIDWorkflow/ClusterizerSpec.h_notused.h b/Detectors/HMPID/workflow/include/HMPIDWorkflow/ClusterizerSpec.h_notused.h deleted file mode 100644 index 6102ec481c97c..0000000000000 --- a/Detectors/HMPID/workflow/include/HMPIDWorkflow/ClusterizerSpec.h_notused.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef STEER_DIGITIZERWORKFLOW_HMPIDCLUSTERIZER_H_ -#define STEER_DIGITIZERWORKFLOW_HMPIDCLUSTERIZER_H_ - -#include "Framework/DataProcessorSpec.h" - -namespace o2 -{ -namespace hmpid -{ - -o2::framework::DataProcessorSpec getHMPIDClusterizerSpec(bool useMC); - -} // end namespace hmpid -} // end namespace o2 - -#endif /* STEER_DIGITIZERWORKFLOW_HMPIDCLUSTERIZERSPEC_H_ */ diff --git a/Detectors/HMPID/workflow/include/HMPIDWorkflow/DigitReaderSpec.h_notused.h b/Detectors/HMPID/workflow/include/HMPIDWorkflow/DigitReaderSpec.h_notused.h deleted file mode 100644 index eea9b134bd911..0000000000000 --- a/Detectors/HMPID/workflow/include/HMPIDWorkflow/DigitReaderSpec.h_notused.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file DigitReader.h - -#ifndef O2_HMPID_DIGITREADER -#define O2_HMPID_DIGITREADER - -#include "TFile.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" -#include "DataFormatsHMP/Digit.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" - -namespace o2 -{ -namespace hmpid -{ - -class DigitReader : public o2::framework::Task -{ - public: - DigitReader(bool useMC) : mUseMC(useMC) {} - ~DigitReader() override = default; - void init(o2::framework::InitContext& ic) final; - void run(o2::framework::ProcessingContext& pc) final; - - private: - int mState = 0; - bool mUseMC = true; - std::unique_ptr mFile = nullptr; - - std::vector mDigits, *mPdigits = &mDigits; - - o2::dataformats::MCTruthContainer mLabels, *mPlabels = &mLabels; -}; - -/// read simulated HMPID digits from a root file -framework::DataProcessorSpec getDigitReaderSpec(bool useMC); - -} // namespace hmpid -} // namespace o2 - -#endif /* O2_HMPID_DIGITREADER */ From cb599998aa3f2425b7556f9b97075633065966a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Fri, 20 Feb 2026 13:16:53 +0100 Subject: [PATCH 125/133] FIT: Delete unused files (#15031) --- .../include/FDDReconstruction/ReadRaw.h | 68 -------- .../include/FDDSimulation/Digits2Raw.h | 74 --------- .../include/FDDWorkflow/RawDataProcessSpec.h | 61 ------- .../include/FDDWorkflow/RawDataReaderSpec.h | 84 ---------- .../include/FDDWorkflow/RawWorkflow.h | 28 ---- .../FDD/workflow/src/RawDataProcessSpec.cxx | 52 ------ .../FDD/workflow/src/RawDataReaderSpec.cxx | 24 --- .../FIT/FDD/workflow/src/RawWorkflow.cxx | 42 ----- .../FT0Workflow/FT0DataProcessDPLSpec.h | 61 ------- .../FT0Workflow/FT0DataReaderDPLSpec.h | 110 ------------ .../include/FT0Workflow/FT0Workflow.h | 28 ---- .../include/FT0Workflow/RawReaderFT0.h | 156 ------------------ .../workflow/src/FT0DataProcessDPLSpec.cxx | 52 ------ .../FT0/workflow/src/FT0DataReaderDPLSpec.cxx | 24 --- .../FIT/FT0/workflow/src/FT0Workflow.cxx | 45 ----- .../FIT/FT0/workflow/src/RawReaderFT0.cxx | 13 -- 16 files changed, 922 deletions(-) delete mode 100644 Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/ReadRaw.h delete mode 100644 Detectors/FIT/FDD/simulation/include/FDDSimulation/Digits2Raw.h delete mode 100644 Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataProcessSpec.h delete mode 100644 Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataReaderSpec.h delete mode 100644 Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawWorkflow.h delete mode 100644 Detectors/FIT/FDD/workflow/src/RawDataProcessSpec.cxx delete mode 100644 Detectors/FIT/FDD/workflow/src/RawDataReaderSpec.cxx delete mode 100644 Detectors/FIT/FDD/workflow/src/RawWorkflow.cxx delete mode 100644 Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataProcessDPLSpec.h delete mode 100644 Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataReaderDPLSpec.h delete mode 100644 Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h delete mode 100644 Detectors/FIT/FT0/workflow/include/FT0Workflow/RawReaderFT0.h delete mode 100644 Detectors/FIT/FT0/workflow/src/FT0DataProcessDPLSpec.cxx delete mode 100644 Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx delete mode 100644 Detectors/FIT/FT0/workflow/src/FT0Workflow.cxx delete mode 100644 Detectors/FIT/FT0/workflow/src/RawReaderFT0.cxx diff --git a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/ReadRaw.h b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/ReadRaw.h deleted file mode 100644 index 54c8b7b203edb..0000000000000 --- a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/ReadRaw.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file ReadRaw.h -/// \brief Reads raw data and converts to digits -/// \author Maciej.Slupecki@cern.ch, arvind.khuntia@cern.ch, based on the FT0 code -// RAW data format description: DataFormat/Detectors/FIT/FDD/RawEventData - -#ifndef ALICEO2_FDD_READRAW_H_ -#define ALICEO2_FDD_READRAW_H_ - -#include -#include -#include -#include -#include -#include -#include -#include "TBranch.h" -#include "TTree.h" -#include "CommonDataFormat/InteractionRecord.h" -#include "DataFormatsFDD/Digit.h" -#include "DataFormatsFDD/ChannelData.h" -#include "DataFormatsFDD/LookUpTable.h" -#include "DataFormatsFDD/RawEventData.h" - -namespace o2 -{ -namespace fdd -{ -class ReadRaw -{ - public: - ReadRaw() = default; - ReadRaw(bool doConversionToDigits, const std::string inputRawFilePath = "fdd.raw", const std::string outputRawFilePath = "fdddigitsFromRaw.root"); - void readRawData(const LookUpTable& lut); - void writeDigits(const std::string& outputDigitsFilePath); - void close(); - - private: - std::ifstream mRawFileIn; - std::map> mDigitAccum; // digit accumulator - - template - TBranch* getOrMakeBranch(TTree& tree, std::string brname, T* ptr) - { - if (auto br = tree.GetBranch(brname.c_str())) { - br->SetAddress(static_cast(ptr)); - return br; - } - // otherwise make it - return tree.Branch(brname.c_str(), ptr); - } - - ClassDefNV(ReadRaw, 1); -}; - -} // namespace fdd -} // namespace o2 -#endif diff --git a/Detectors/FIT/FDD/simulation/include/FDDSimulation/Digits2Raw.h b/Detectors/FIT/FDD/simulation/include/FDDSimulation/Digits2Raw.h deleted file mode 100644 index 4afcf5da37ae8..0000000000000 --- a/Detectors/FIT/FDD/simulation/include/FDDSimulation/Digits2Raw.h +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file Digits2Raw.h -/// \brief converts digits to raw format -/// \author Maciej.Slupecki@cern.ch -// based on FV0 - -#ifndef ALICEO2_FDD_DIGITS2RAW_H_ -#define ALICEO2_FDD_DIGITS2RAW_H_ - -#include "Headers/RAWDataHeader.h" -#include "CommonDataFormat/InteractionRecord.h" -#include "DataFormatsFDD/RawEventData.h" -#include "DataFormatsFDD/LookUpTable.h" -#include "DataFormatsFDD/ChannelData.h" -#include "DataFormatsFDD/Digit.h" -#include "DetectorsRaw/HBFUtils.h" -#include "DetectorsRaw/RawFileWriter.h" -#include -#include -#include -#include -#include -#include - -namespace o2 -{ -namespace fdd -{ -class Digits2Raw -{ - public: - Digits2Raw() = default; - void readDigits(const std::string& outDir, const std::string& fileDigitsName); - void convertDigits(o2::fdd::Digit bcdigits, - gsl::span pmchannels, - const o2::fdd::LookUpTable& lut); - - o2::raw::RawFileWriter& getWriter() { return mWriter; } - void setFilePerLink(bool v) { mOutputPerLink = v; } - bool getFilePerLink() const { return mOutputPerLink; } - - int carryOverMethod(const header::RDHAny* rdh, const gsl::span data, - const char* ptr, int maxSize, int splitID, - std::vector& trailer, std::vector& header) const; - - private: - static constexpr uint32_t sTcmLink = 2; - static constexpr uint16_t sCruId = 0; - static constexpr uint32_t sEndPointId = sCruId; - - void makeGBTHeader(EventHeader& eventHeader, int link, o2::InteractionRecord const& mIntRecord); - void fillSecondHalfWordAndAddData(int iChannelPerLink, int prevPmLink, const o2::InteractionRecord& ir); - RawEventData mRawEventData; - o2::fdd::Triggers mTriggers; - o2::raw::RawFileWriter mWriter{"FDD"}; - bool mOutputPerLink = false; - ///////////////////////////////////////////////// - - ClassDefNV(Digits2Raw, 1); -}; - -} // namespace fdd -} // namespace o2 -#endif diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataProcessSpec.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataProcessSpec.h deleted file mode 100644 index 6ed465b6181dd..0000000000000 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataProcessSpec.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file RawDataProcessSpec.h - -#ifndef O2_FDD_RAWDATAPROCESSSPEC_H -#define O2_FDD_RAWDATAPROCESSSPEC_H - -#include "Framework/CallbackService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/SerializationMethods.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" - -#include "FDDRaw/DigitBlockFDD.h" -#include "DataFormatsFDD/Digit.h" -#include "DataFormatsFDD/ChannelData.h" - -#include -#include -#include - -using namespace o2::framework; - -namespace o2 -{ -namespace fdd -{ - -class RawDataProcessSpec : public Task -{ - public: - RawDataProcessSpec(bool dumpEventBlocks) : mDumpEventBlocks(dumpEventBlocks) {} - ~RawDataProcessSpec() override = default; - void init(InitContext& ic) final; - void run(ProcessingContext& pc) final; - - private: - bool mDumpEventBlocks; - - o2::header::DataOrigin mOrigin = o2::header::gDataOriginFDD; -}; - -framework::DataProcessorSpec getFDDRawDataProcessSpec(bool dumpProcessor); - -} // namespace fdd -} // namespace o2 - -#endif /* O2_FDDDATAPROCESSDPL_H */ diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataReaderSpec.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataReaderSpec.h deleted file mode 100644 index c3b0349826e98..0000000000000 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataReaderSpec.h +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file RawDataReaderSpec.h - -#ifndef O2_FDD_RAWDATAREADERSPEC_H -#define O2_FDD_RAWDATAREADERSPEC_H - -#include "DataFormatsFDD/LookUpTable.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" -#include "Framework/CallbackService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/SerializationMethods.h" -#include "DPLUtils/DPLRawParser.h" -#include "DetectorsRaw/RDHUtils.h" - -#include -#include -#include -using namespace o2::framework; - -namespace o2 -{ -namespace fdd -{ -template -class RawDataReaderSpec : public Task -{ - public: - RawDataReaderSpec(const RawReader& rawReader) : mRawReader(rawReader) {} - RawDataReaderSpec() = default; - ~RawDataReaderSpec() override = default; - void init(InitContext& ic) final { o2::fdd::SingleLUT::Instance().printFullMap(); } - void run(ProcessingContext& pc) final - { - DPLRawParser parser(pc.inputs()); - mRawReader.clear(); - LOG(info) << "FDD RawDataReaderSpec"; - uint64_t count = 0; - for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { - //Proccessing each page - count++; - auto rdhPtr = reinterpret_cast(it.raw()); - gsl::span payload(it.data(), it.size()); - mRawReader.process(payload, o2::raw::RDHUtils::getLinkID(rdhPtr), int(0)); - } - LOG(info) << "Pages: " << count; - mRawReader.accumulateDigits(); - mRawReader.makeSnapshot(pc); - } - RawReader mRawReader; -}; - -template -framework::DataProcessorSpec getFDDRawDataReaderSpec(const RawReader& rawReader) -{ - LOG(info) << "DataProcessorSpec initDataProcSpec() for RawReaderFDD"; - std::vector outputSpec; - RawReader::prepareOutputSpec(outputSpec); - return DataProcessorSpec{ - "fdd-datareader-dpl", - o2::framework::select("TF:FDD/RAWDATA"), - outputSpec, - adaptFromTask>(rawReader), - Options{}}; -} - -} // namespace fdd -} // namespace o2 - -#endif /* O2_FDDDATAREADERDPL_H */ diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawWorkflow.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawWorkflow.h deleted file mode 100644 index 3bbab66d16497..0000000000000 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawWorkflow.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef O2_FDD_RAWWORKFLOW_H -#define O2_FDD_RAWWORKFLOW_H - -/// @file RawWorkflow.h - -#include "Framework/WorkflowSpec.h" - -namespace o2 -{ -namespace fdd -{ -framework::WorkflowSpec getFDDRawWorkflow(bool useProcess, - bool dumpProcessor, bool dumpReader, - bool disableRootOut); -} // namespace fdd -} // namespace o2 -#endif diff --git a/Detectors/FIT/FDD/workflow/src/RawDataProcessSpec.cxx b/Detectors/FIT/FDD/workflow/src/RawDataProcessSpec.cxx deleted file mode 100644 index bf18db67672c2..0000000000000 --- a/Detectors/FIT/FDD/workflow/src/RawDataProcessSpec.cxx +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file RawDataProcessSpec.cxx - -#include "FDDWorkflow/RawDataProcessSpec.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace fdd -{ -using namespace std; -void RawDataProcessSpec::init(InitContext& ic) -{ -} - -void RawDataProcessSpec::run(ProcessingContext& pc) -{ - LOG(info) << "RawDataProcessSpec running..."; - auto vecDigits = pc.inputs().get>("digits"); - auto vecChannelData = pc.inputs().get>("digch"); - if (mDumpEventBlocks) { - DigitBlockFDD::print(vecDigits, vecChannelData); - } -} - -DataProcessorSpec getFDDRawDataProcessSpec(bool dumpProcessor) -{ - std::vector inputSpec; - inputSpec.emplace_back("digits", o2::header::gDataOriginFDD, "DIGITSBC", 0, Lifetime::Timeframe); - inputSpec.emplace_back("digch", o2::header::gDataOriginFDD, "DIGITSCH", 0, Lifetime::Timeframe); - LOG(info) << "DataProcessorSpec getRawDataProcessSpec"; - return DataProcessorSpec{ - "fdd-dataprocess-dpl-flp", - inputSpec, - Outputs{}, - AlgorithmSpec{adaptFromTask(dumpProcessor)}, - Options{}}; -} - -} // namespace fdd -} // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/RawDataReaderSpec.cxx b/Detectors/FIT/FDD/workflow/src/RawDataReaderSpec.cxx deleted file mode 100644 index 631655d3038ec..0000000000000 --- a/Detectors/FIT/FDD/workflow/src/RawDataReaderSpec.cxx +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file RawDataReaderSpec.cxx - -#include "FDDWorkflow/RawDataReaderSpec.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace fdd -{ - -} // namespace fdd -} // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/RawWorkflow.cxx b/Detectors/FIT/FDD/workflow/src/RawWorkflow.cxx deleted file mode 100644 index c9e5c5be0c81d..0000000000000 --- a/Detectors/FIT/FDD/workflow/src/RawWorkflow.cxx +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file RawWorkflow.cxx - -#include "FDDWorkflow/RawWorkflow.h" -#include "FDDWorkflow/RawDataProcessSpec.h" -#include "FDDWorkflow/RawDataReaderSpec.h" -#include "FDDWorkflow/DigitWriterSpec.h" -#include "FDDWorkflow/RawReaderFDD.h" -namespace o2 -{ -namespace fdd -{ - -framework::WorkflowSpec getFDDRawWorkflow(bool useProcess, - bool dumpProcessor, bool dumpReader, - bool disableRootOut) -{ - LOG(info) << "framework::WorkflowSpec getFDDWorkflow"; - framework::WorkflowSpec specs; - specs.emplace_back(o2::fdd::getFDDRawDataReaderSpec(RawReaderFDD{dumpReader})); - - if (useProcess) { - specs.emplace_back(o2::fdd::getFDDRawDataProcessSpec(dumpProcessor)); - } - if (!disableRootOut) { - specs.emplace_back(o2::fdd::getFDDDigitWriterSpec(false, false)); - } - return specs; -} - -} // namespace fdd -} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataProcessDPLSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataProcessDPLSpec.h deleted file mode 100644 index 7b7e98d50368e..0000000000000 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataProcessDPLSpec.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file FT0DataProcessDPLSpec.h - -#ifndef O2_FT0DATAPROCESSDPLSPEC_H -#define O2_FT0DATAPROCESSDPLSPEC_H - -#include "Framework/CallbackService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/SerializationMethods.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" - -#include "FT0Raw/DigitBlockFT0.h" -#include "DataFormatsFT0/Digit.h" -#include "DataFormatsFT0/ChannelData.h" - -#include -#include -#include - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ - -class FT0DataProcessDPLSpec : public Task -{ - public: - FT0DataProcessDPLSpec(bool dumpEventBlocks) : mDumpEventBlocks(dumpEventBlocks) {} - ~FT0DataProcessDPLSpec() override = default; - void init(InitContext& ic) final; - void run(ProcessingContext& pc) final; - - private: - bool mDumpEventBlocks; - - o2::header::DataOrigin mOrigin = o2::header::gDataOriginFT0; -}; - -framework::DataProcessorSpec getFT0DataProcessDPLSpec(bool dumpProcessor); - -} // namespace ft0 -} // namespace o2 - -#endif /* O2_FT0DATAPROCESSDPL_H */ diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataReaderDPLSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataReaderDPLSpec.h deleted file mode 100644 index 9074f4f7f0f34..0000000000000 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataReaderDPLSpec.h +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file FT0DataReaderDPLSpec.h - -#ifndef O2_FT0DATAREADERDPLSPEC_H -#define O2_FT0DATAREADERDPLSPEC_H -#include "DataFormatsFT0/LookUpTable.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" -#include "Framework/CallbackService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/SerializationMethods.h" -#include "DPLUtils/DPLRawParser.h" -#include "Framework/InputRecordWalker.h" -#include -#include -#include -#include "CommonUtils/VerbosityConfig.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ -template -class FT0DataReaderDPLSpec : public Task -{ - public: - FT0DataReaderDPLSpec(const RawReader& rawReader) : mRawReader(rawReader) {} - FT0DataReaderDPLSpec() = default; - ~FT0DataReaderDPLSpec() override = default; - typedef RawReader RawReader_t; - void init(InitContext& ic) final { o2::ft0::SingleLUT::Instance().printFullMap(); } - void run(ProcessingContext& pc) final - { - // if we see requested data type input with 0xDEADBEEF subspec and 0 payload this means that the "delayed message" - // mechanism created it in absence of real data from upstream. Processor should send empty output to not block the workflow - { - static size_t contDeadBeef = 0; // number of times 0xDEADBEEF was seen continuously - std::vector dummy{InputSpec{"dummy", ConcreteDataMatcher{o2::header::gDataOriginFT0, o2::header::gDataDescriptionRawData, 0xDEADBEEF}}}; - for (const auto& ref : InputRecordWalker(pc.inputs(), dummy)) { - const auto dh = o2::framework::DataRefUtils::getHeader(ref); - auto payloadSize = DataRefUtils::getPayloadSize(ref); - if (payloadSize == 0) { - auto maxWarn = o2::conf::VerbosityConfig::Instance().maxWarnDeadBeef; - if (++contDeadBeef <= maxWarn) { - LOGP(alarm, "Found input [{}/{}/{:#x}] TF#{} 1st_orbit:{} Payload {} : assuming no payload for all links in this TF{}", - dh->dataOrigin.str, dh->dataDescription.str, dh->subSpecification, dh->tfCounter, dh->firstTForbit, payloadSize, - contDeadBeef == maxWarn ? fmt::format(". {} such inputs in row received, stopping reporting", contDeadBeef) : ""); - } - mRawReader.makeSnapshot(pc); // send empty output - return; - } - } - contDeadBeef = 0; // if good data, reset the counter - } - std::vector filter{InputSpec{"filter", ConcreteDataTypeMatcher{o2::header::gDataOriginFT0, o2::header::gDataDescriptionRawData}, Lifetime::Timeframe}}; - DPLRawParser parser(pc.inputs(), filter); - std::size_t count = 0; - for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { - //Proccessing each page - count++; - auto rdhPtr = reinterpret_cast(it.raw()); - gsl::span payload(it.data(), it.size()); - mRawReader.process(payload, o2::raw::RDHUtils::getLinkID(rdhPtr), o2::raw::RDHUtils::getEndPointID(rdhPtr)); - } - LOG(info) << "Pages: " << count; - mRawReader.accumulateDigits(); - mRawReader.makeSnapshot(pc); - mRawReader.clear(); - } - RawReader_t mRawReader; -}; - -template -framework::DataProcessorSpec getFT0DataReaderDPLSpec(const RawReader& rawReader, bool askSTFDist) -{ - LOG(info) << "DataProcessorSpec initDataProcSpec() for RawReaderFT0"; - std::vector outputSpec; - RawReader::prepareOutputSpec(outputSpec); - std::vector inputSpec{{"STF", ConcreteDataTypeMatcher{o2::header::gDataOriginFT0, "RAWDATA"}, Lifetime::Timeframe}}; - if (askSTFDist) { - inputSpec.emplace_back("STFDist", "FLP", "DISTSUBTIMEFRAME", 0, Lifetime::Timeframe); - } - return DataProcessorSpec{ - "ft0-datareader-dpl", - inputSpec, - outputSpec, - adaptFromTask>(rawReader), - Options{}}; -} - -} // namespace ft0 -} // namespace o2 - -#endif /* O2_FT0DATAREADERDPL_H */ diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h deleted file mode 100644 index a4988b2c18fc7..0000000000000 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef O2_FIT_FT0WORKFLOW_H -#define O2_FIT_FT0WORKFLOW_H - -/// @file FT0Workflow.h - -#include "Framework/WorkflowSpec.h" - -namespace o2 -{ -namespace ft0 -{ -framework::WorkflowSpec getFT0Workflow(bool isExtendedMode, bool useProcess, - bool dumpProcessor, bool dumpReader, - bool disableRootOut, bool askSTFDist); -} // namespace ft0 -} // namespace o2 -#endif diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/RawReaderFT0.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/RawReaderFT0.h deleted file mode 100644 index f7729394db652..0000000000000 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/RawReaderFT0.h +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -//file RawReaderFT0.h class for RAW data reading -// -// Artur.Furs -// afurs@cern.ch -// -//Main purpuse is to decode FT0 data blocks and push them to DigitBlockFT0 for proccess -//TODO: prepare wrappers for containers with digits and combine classes below into one template class? -#ifndef ALICEO2_FIT_RAWREADERFT0_H_ -#define ALICEO2_FIT_RAWREADERFT0_H_ -#include -#include -#include -#include "FT0Raw/RawReaderFT0Base.h" - -#include "DataFormatsFT0/Digit.h" -#include "DataFormatsFT0/ChannelData.h" - -#include "Framework/ProcessingContext.h" -#include "Framework/DataAllocator.h" -#include "Framework/OutputSpec.h" -#include - -namespace o2 -{ -namespace ft0 -{ -//Normal TCM mode -template -class RawReaderFT0 : public RawReaderFT0BaseNorm -{ - public: - RawReaderFT0(bool dumpData) : mDumpData(dumpData) {} - RawReaderFT0(const RawReaderFT0&) = default; - - RawReaderFT0() = default; - ~RawReaderFT0() = default; - static constexpr bool sUseTrgInput = useTrgInput; - void clear() - { - mVecDigits.clear(); - if constexpr (sUseTrgInput) { - mVecTriggerInput.clear(); - } - mVecChannelData.clear(); - } - void accumulateDigits() - { - if constexpr (sUseTrgInput) { - getDigits(mVecDigits, mVecChannelData, mVecTriggerInput); - } else { - getDigits(mVecDigits, mVecChannelData); - } - LOG(info) << "Number of Digits: " << mVecDigits.size(); - LOG(info) << "Number of ChannelData: " << mVecChannelData.size(); - if constexpr (sUseTrgInput) { - LOG(info) << "Number of TriggerInput: " << mVecTriggerInput.size(); - } - if (mDumpData) { - DigitBlockFT0::print(mVecDigits, mVecChannelData); - } - } - static void prepareOutputSpec(std::vector& outputSpec) - { - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSBC", 0, o2::framework::Lifetime::Timeframe); - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSCH", 0, o2::framework::Lifetime::Timeframe); - if constexpr (sUseTrgInput) { - outputSpec.emplace_back(o2::header::gDataOriginFT0, "TRIGGERINPUT", 0, o2::framework::Lifetime::Timeframe); - } - } - void makeSnapshot(o2::framework::ProcessingContext& pc) - { - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSBC", 0}, mVecDigits); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSCH", 0}, mVecChannelData); - if constexpr (sUseTrgInput) { - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "TRIGGERINPUT", 0}, mVecTriggerInput); - } - } - bool mDumpData; - std::vector mVecDigits; - std::vector mVecTriggerInput; - std::vector mVecChannelData; -}; - -//Extended TCM mode (additional raw data struct) -template -class RawReaderFT0ext : public RawReaderFT0BaseExt -{ - public: - RawReaderFT0ext(bool dumpData) : mDumpData(dumpData) {} - RawReaderFT0ext(const RawReaderFT0ext&) = default; - static constexpr bool sUseTrgInput = useTrgInput; - RawReaderFT0ext() = default; - ~RawReaderFT0ext() = default; - void clear() - { - mVecDigits.clear(); - mVecChannelData.clear(); - mVecTrgExt.clear(); - if constexpr (sUseTrgInput) { - mVecTriggerInput.clear(); - } - } - void accumulateDigits() - { - if constexpr (sUseTrgInput) { - getDigits(mVecDigits, mVecChannelData, mVecTrgExt, mVecTriggerInput); - } else { - getDigits(mVecDigits, mVecChannelData, mVecTrgExt); - } - LOG(info) << "Number of Digits: " << mVecDigits.size(); - LOG(info) << "Number of ChannelData: " << mVecChannelData.size(); - LOG(info) << "Number of TriggerExt: " << mVecTrgExt.size(); - if (mDumpData) { - DigitBlockFT0ext::print(mVecDigits, mVecChannelData, mVecTrgExt); - } - } - static void prepareOutputSpec(std::vector& outputSpec) - { - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSBC", 0, o2::framework::Lifetime::Timeframe); - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSCH", 0, o2::framework::Lifetime::Timeframe); - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSTRGEXT", 0, o2::framework::Lifetime::Timeframe); - if constexpr (sUseTrgInput) { - outputSpec.emplace_back(o2::header::gDataOriginFT0, "TRIGGERINPUT", 0, o2::framework::Lifetime::Timeframe); - } - } - void makeSnapshot(o2::framework::ProcessingContext& pc) - { - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSBC", 0}, mVecDigits); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSCH", 0}, mVecChannelData); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSTRGEXT", 0}, mVecTrgExt); - if constexpr (sUseTrgInput) { - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "TRIGGERINPUT", 0}, mVecTriggerInput); - } - } - bool mDumpData; - std::vector mVecDigits; - std::vector mVecChannelData; - std::vector mVecTrgExt; - std::vector mVecTriggerInput; -}; - -} // namespace ft0 -} // namespace o2 - -#endif diff --git a/Detectors/FIT/FT0/workflow/src/FT0DataProcessDPLSpec.cxx b/Detectors/FIT/FT0/workflow/src/FT0DataProcessDPLSpec.cxx deleted file mode 100644 index d7a7a689d402f..0000000000000 --- a/Detectors/FIT/FT0/workflow/src/FT0DataProcessDPLSpec.cxx +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file FT0DataProcessDPLSpec.cxx - -#include "FT0Workflow/FT0DataProcessDPLSpec.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ -using namespace std; -void FT0DataProcessDPLSpec::init(InitContext& ic) -{ -} - -void FT0DataProcessDPLSpec::run(ProcessingContext& pc) -{ - LOG(info) << "FT0DataProcessDPLSpec running..."; - auto vecDigits = pc.inputs().get>("digits"); - auto vecChannelData = pc.inputs().get>("digch"); - if (mDumpEventBlocks) { - DigitBlockFT0::print(vecDigits, vecChannelData); - } -} - -DataProcessorSpec getFT0DataProcessDPLSpec(bool dumpProcessor) -{ - std::vector inputSpec; - inputSpec.emplace_back("digits", o2::header::gDataOriginFT0, "DIGITSBC", 0, Lifetime::Timeframe); - inputSpec.emplace_back("digch", o2::header::gDataOriginFT0, "DIGITSCH", 0, Lifetime::Timeframe); - LOG(info) << "DataProcessorSpec getFT0DataProcessDPLSpec"; - return DataProcessorSpec{ - "ft0-dataprocess-dpl-flp", - inputSpec, - Outputs{}, - AlgorithmSpec{adaptFromTask(dumpProcessor)}, - Options{}}; -} - -} // namespace ft0 -} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx b/Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx deleted file mode 100644 index caa642794b561..0000000000000 --- a/Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file FT0DataReaderDPLSpec.cxx - -#include "FT0Workflow/FT0DataReaderDPLSpec.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ - -} // namespace ft0 -} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/FT0Workflow.cxx b/Detectors/FIT/FT0/workflow/src/FT0Workflow.cxx deleted file mode 100644 index 156feb7dd3e2f..0000000000000 --- a/Detectors/FIT/FT0/workflow/src/FT0Workflow.cxx +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file FT0Workflow.cxx - -#include "FT0Workflow/FT0Workflow.h" -#include "FT0Workflow/FT0DataProcessDPLSpec.h" -#include "FT0Workflow/FT0DataReaderDPLSpec.h" -#include "FT0Workflow/FT0DigitWriterSpec.h" -#include "FT0Workflow/RawReaderFT0.h" -namespace o2 -{ -namespace ft0 -{ - -framework::WorkflowSpec getFT0Workflow(bool isExtendedMode, bool useProcess, - bool dumpProcessor, bool dumpReader, - bool disableRootOut, bool askSTFDist) -{ - LOG(info) << "framework::WorkflowSpec getFT0Workflow"; - framework::WorkflowSpec specs; - if (isExtendedMode) { - specs.emplace_back(o2::ft0::getFT0DataReaderDPLSpec(RawReaderFT0ext{dumpReader}, askSTFDist)); - } else { - specs.emplace_back(o2::ft0::getFT0DataReaderDPLSpec(RawReaderFT0{dumpReader}, askSTFDist)); - } - if (useProcess) { - specs.emplace_back(o2::ft0::getFT0DataProcessDPLSpec(dumpProcessor)); - } - if (!disableRootOut) { - specs.emplace_back(o2::ft0::getFT0DigitWriterSpec(false, false)); - } - return specs; -} - -} // namespace ft0 -} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/RawReaderFT0.cxx b/Detectors/FIT/FT0/workflow/src/RawReaderFT0.cxx deleted file mode 100644 index b2ef17e540112..0000000000000 --- a/Detectors/FIT/FT0/workflow/src/RawReaderFT0.cxx +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "FT0Workflow/RawReaderFT0.h" -using namespace o2::ft0; From ca1c2128b44820ff63518d0d298664c716cd4cb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiktor=20Piero=C5=BCak?= <94726725+wpierozak@users.noreply.github.com> Date: Fri, 20 Feb 2026 17:47:38 +0100 Subject: [PATCH 126/133] FT0: Implemented condition on sum of amplitudes in EventsPerBc calibration (#15084) Co-authored-by: wpierozak --- .../include/FT0Calibration/EventsPerBcCalibrator.h | 6 ++++-- Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx | 6 +++--- .../workflow/FT0EventsPerBcProcessor-Workflow.cxx | 3 ++- Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h | 6 +++++- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h b/Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h index f44824517f258..d831cc36201ab 100644 --- a/Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h +++ b/Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h @@ -31,7 +31,7 @@ namespace o2::ft0 { struct EventsPerBcContainer { - EventsPerBcContainer(int32_t minAmplitudeSideA, int32_t minAmplitudeSideC) : mMinAmplitudeSideA(minAmplitudeSideA), mMinAmplitudeSideC(minAmplitudeSideC) {} + EventsPerBcContainer(int32_t minAmplitudeSideA, int32_t minAmplitudeSideC, int32_t minSumOfAmplitude) : mMinAmplitudeSideA(minAmplitudeSideA), mMinAmplitudeSideC(minAmplitudeSideC), mMinSumOfAmplitude(minSumOfAmplitude) {} size_t getEntries() const { return entries; } void print() const; @@ -40,6 +40,7 @@ struct EventsPerBcContainer { const int32_t mMinAmplitudeSideA; const int32_t mMinAmplitudeSideC; + const int32_t mMinSumOfAmplitude; std::array mTvx{0.0}; size_t entries{0}; @@ -56,7 +57,7 @@ class EventsPerBcCalibrator final : public o2::calibration::TimeSlotCalibration< using EventsHistogram = std::array; public: - EventsPerBcCalibrator(uint32_t minNumberOfEntries, int32_t minAmplitudeSideA, int32_t minAmplitudeSideC); + EventsPerBcCalibrator(uint32_t minNumberOfEntries, int32_t minAmplitudeSideA, int32_t minAmplitudeSideC, int32_t minSumOfAmplitude); bool hasEnoughData(const Slot& slot) const override; void initOutput() override; @@ -70,6 +71,7 @@ class EventsPerBcCalibrator final : public o2::calibration::TimeSlotCalibration< const uint32_t mMinNumberOfEntries; const int32_t mMinAmplitudeSideA; const int32_t mMinAmplitudeSideC; + const int32_t mMinSumOfAmplitude; std::vector mTvxPerBcs; std::vector> mTvxPerBcInfos; diff --git a/Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx b/Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx index a2230f51dc4ea..b17c81213cd08 100644 --- a/Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx +++ b/Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx @@ -23,7 +23,7 @@ void EventsPerBcContainer::fill(const o2::dataformats::TFIDInfo& ti, const gsl:: { size_t oldEntries = entries; for (const auto& digit : data) { - if (digit.mTriggers.getVertex() && digit.mTriggers.getAmplA() >= mMinAmplitudeSideA && digit.mTriggers.getAmplC() >= mMinAmplitudeSideC) { + if (digit.mTriggers.getVertex() && digit.mTriggers.getAmplA() >= mMinAmplitudeSideA && digit.mTriggers.getAmplC() >= mMinAmplitudeSideC && (digit.mTriggers.getAmplA() + digit.mTriggers.getAmplC()) >= mMinSumOfAmplitude) { mTvx[digit.mIntRecord.bc]++; entries++; } @@ -45,7 +45,7 @@ void EventsPerBcCalibrator::initOutput() mTvxPerBcInfos.clear(); } -EventsPerBcCalibrator::EventsPerBcCalibrator(uint32_t minNumberOfEntries, int32_t minAmplitudeSideA, int32_t minAmplitudeSideC) : mMinNumberOfEntries(minNumberOfEntries), mMinAmplitudeSideA(minAmplitudeSideA), mMinAmplitudeSideC(minAmplitudeSideC) +EventsPerBcCalibrator::EventsPerBcCalibrator(uint32_t minNumberOfEntries, int32_t minAmplitudeSideA, int32_t minAmplitudeSideC, int32_t minSumOfAmplitude) : mMinNumberOfEntries(minNumberOfEntries), mMinAmplitudeSideA(minAmplitudeSideA), mMinAmplitudeSideC(minAmplitudeSideC), mMinSumOfAmplitude(minSumOfAmplitude) { LOG(info) << "Defined threshold for number of entires per slot: " << mMinNumberOfEntries; LOG(info) << "Defined threshold for side A amplitude for event: " << mMinAmplitudeSideA; @@ -75,7 +75,7 @@ EventsPerBcCalibrator::Slot& EventsPerBcCalibrator::emplaceNewSlot(bool front, T { auto& cont = getSlots(); auto& slot = front ? cont.emplace_front(tstart, tend) : cont.emplace_back(tstart, tend); - slot.setContainer(std::make_unique(mMinAmplitudeSideA, mMinAmplitudeSideC)); + slot.setContainer(std::make_unique(mMinAmplitudeSideA, mMinAmplitudeSideC, mMinSumOfAmplitude)); return slot; } } // namespace o2::ft0 \ No newline at end of file diff --git a/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx index ac7a8e52f53b1..5cef707da2cca 100644 --- a/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx +++ b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx @@ -39,7 +39,8 @@ o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext co {"one-object-per-run", VariantType::Bool, false, {"If set, workflow creates only one calibration object per run"}}, {"min-entries-number", VariantType::UInt32, 5000u, {"Minimum number of entries required for a slot to be valid"}}, {"min-ampl-side-a", VariantType::Int, 0, {"Amplitude threshold for Side A events"}}, - {"min-ampl-side-c", VariantType::Int, 0, {"Amplitude threshold for Side C events"}}}}; + {"min-ampl-side-c", VariantType::Int, 0, {"Amplitude threshold for Side C events"}}, + {"min-sum-of-ampl", VariantType::Int, 0, {"Amplitude threshold for sum of A-side and C-side amplitudes"}}}}; WorkflowSpec workflow; workflow.emplace_back(dataProcessorSpec); diff --git a/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h index c587ab58fcd90..d493e2a606613 100644 --- a/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h +++ b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h @@ -50,8 +50,11 @@ class FT0EventsPerBcProcessor final : public o2::framework::Task if (ic.options().hasOption("min-ampl-side-c")) { mMinAmplitudeSideC = ic.options().get("min-ampl-side-c"); } + if (ic.options().hasOption("min-sum-of-ampl")) { + mMinSumOfAmplitude = ic.options().get("min-sum-of-ampl"); + } - mCalibrator = std::make_unique(mMinNumberOfEntries, mMinAmplitudeSideA, mMinAmplitudeSideC); + mCalibrator = std::make_unique(mMinNumberOfEntries, mMinAmplitudeSideA, mMinAmplitudeSideC, mMinSumOfAmplitude); if (mOneObjectPerRun) { LOG(info) << "Only one object will be created at the end of run"; @@ -119,6 +122,7 @@ class FT0EventsPerBcProcessor final : public o2::framework::Task uint32_t mMinNumberOfEntries; int32_t mMinAmplitudeSideA; int32_t mMinAmplitudeSideC; + int32_t mMinSumOfAmplitude; }; } // namespace o2::calibration #endif \ No newline at end of file From 4728518ad3cee9c3bb9ab424733e7c79d46fc934 Mon Sep 17 00:00:00 2001 From: Stefano Cannito <143754257+scannito@users.noreply.github.com> Date: Fri, 20 Feb 2026 18:13:21 +0100 Subject: [PATCH 127/133] [ALICE] Fix TRK services crossing (#15085) --- .../Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx index 25c59b3c8fd4a..bd27a5bc30f62 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx @@ -264,8 +264,8 @@ void TRKServices::createMiddleServices(TGeoVolume* motherVolume) // Carbon Fiber Cylinder support for the middle tracker float rMinMiddleCarbonSupport = 34.8f; // Arbitrary value float rMaxMiddleCarbonSupport = 35.f; // 2 mm of carbon fiber - const float zLengthMiddleCarbon = 64.2f; - TGeoTube* middleBarrelCarbonSupport = new TGeoTube("TRK_MID_CARBONSUPPORTsh", rMinMiddleCarbonSupport, rMaxMiddleCarbonSupport, zLengthMiddleCarbon); + const float zLengthMiddleCarbon = 129.f; + TGeoTube* middleBarrelCarbonSupport = new TGeoTube("TRK_MID_CARBONSUPPORTsh", rMinMiddleCarbonSupport, rMaxMiddleCarbonSupport, zLengthMiddleCarbon / 2.); TGeoVolume* middleBarrelCarbonSupportVolume = new TGeoVolume("TRK_MID_CARBONSUPPORT", middleBarrelCarbonSupport, medCFiber); middleBarrelCarbonSupportVolume->SetLineColor(kGray); LOGP(info, "Creating carbon fiber support for Middle Tracker"); @@ -318,7 +318,7 @@ void TRKServices::createMiddleServices(TGeoVolume* motherVolume) // Middle barrel connection disks const float rMinMiddleBarrelDisk = 5.68f; const float rMaxMiddleBarrelDisk = 35.f; - const float zLengthMiddleBarrel = 64.2f; + const float zLengthMiddleBarrel = 64.5f; for (auto& orientation : {Orientation::kASide, Orientation::kCSide}) { TGeoTube* middleBarrelConnDiskSIO2 = new TGeoTube(Form("TRK_MIDBARCONN_DISK_SIO2sh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, siO2FiberThick / 2.); TGeoTube* middleBarrelConnDiskPE = new TGeoTube(Form("TRK_MIDBARCONN_DISK_PEsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, peFiberThick / 2.); From 16ee3b839f933da6185705bc9476023d84fc8391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Fri, 20 Feb 2026 21:00:56 +0100 Subject: [PATCH 128/133] [ALICE3] Add proto segmentation of TF3 (#15081) * Use TF3 CAD specifications --- .../base/include/IOTOFBase/IOTOFBaseParam.h | 2 + .../include/IOTOFSimulation/Detector.h | 2 +- .../include/IOTOFSimulation/Layer.h | 22 +- .../ALICE3/IOTOF/simulation/src/Detector.cxx | 44 ++-- .../ALICE3/IOTOF/simulation/src/Layer.cxx | 237 ++++++++++++++---- .../ALICE3/TRK/base/src/GeometryTGeo.cxx | 1 - 6 files changed, 239 insertions(+), 69 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h index bf605797cbfe5..b74fc6d6869dd 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h @@ -26,6 +26,8 @@ struct IOTOFBaseParam : public o2::conf::ConfigurableParamHelper bool enableForwardTOF = true; bool enableBackwardTOF = true; std::string detectorPattern = ""; + bool segmentedInnerTOF = false; // If the inner TOF layer is segmented + bool segmentedOuterTOF = false; // If the outer TOF layer is segmented O2ParamDef(IOTOFBaseParam, "IOTOFBase"); }; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Detector.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Detector.h index f39a43733ccab..f3c4e3ddd6276 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Detector.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Detector.h @@ -60,7 +60,7 @@ class Detector : public o2::base::DetImpl return nullptr; } - void configLayers(bool itof = true, bool otof = true, bool ftof = true, bool btof = true, std::string pattern = ""); + void configLayers(bool itof = true, bool otof = true, bool ftof = true, bool btof = true, std::string pattern = "", bool itofSegmented = false, bool otofSegmented = false); void configServices(); void createMaterials(); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h index b7cc0a05c1c2e..df3687b2b2ea4 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h @@ -14,6 +14,8 @@ #include #include +#include +#include namespace o2 { @@ -23,7 +25,8 @@ class Layer { public: Layer() = default; - Layer(std::string layerName, float rInn, float rOut, float zLength, float zOffset, float layerX2X0, bool isBarrel = true); + Layer(std::string layerName, float rInn, float rOut, float zLength, float zOffset, float layerX2X0, + int layout = kBarrel, int nSegments = 0, float segmentSize = 0.0, int nSensorsPerSegment = 0, double tiltAngle = 0.0); ~Layer() = default; auto getInnerRadius() const { return mInnerRadius; } @@ -33,9 +36,14 @@ class Layer auto getx2X0() const { return mX2X0; } auto getChipThickness() const { return mChipThickness; } auto getName() const { return mLayerName; } - auto getIsBarrel() const { return mIsBarrel; } + auto getLayout() const { return mLayout; } + auto getSegments() const { return mSegments; } + static constexpr int kBarrel = 0; + static constexpr int kDisk = 1; + static constexpr int kBarrelSegmented = 2; + static constexpr int kDiskSegmented = 3; - virtual void createLayer(TGeoVolume* motherVolume){}; + virtual void createLayer(TGeoVolume* motherVolume) {}; protected: std::string mLayerName; @@ -45,7 +53,11 @@ class Layer float mZOffset{0.f}; // Of use when fwd layers float mX2X0; float mChipThickness; - bool mIsBarrel{true}; + int mLayout{kBarrel}; // Identifier of the type of layer layout (barrel, disk, barrel segmented, disk segmented) + // To be used only in case of the segmented layout, to define the number of segments in phi (for barrel) or in r (for disk) + std::pair mSegments{0, 0.0f}; // Number and size of segments in phi (for barrel) or in r (for disk) in case of segmented layout + int mSensorsPerSegment{0}; // Number of sensors along a segment + double mTiltAngle{0.0}; // Tilt angle in degrees to be applied as a rotation around the local center of the segment }; class ITOFLayer : public Layer @@ -53,6 +65,7 @@ class ITOFLayer : public Layer public: using Layer::Layer; virtual void createLayer(TGeoVolume* motherVolume) override; + static std::vector mRegister; }; class OTOFLayer : public Layer @@ -60,6 +73,7 @@ class OTOFLayer : public Layer public: using Layer::Layer; virtual void createLayer(TGeoVolume* motherVolume) override; + static std::vector mRegister; }; class FTOFLayer : public Layer diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx index 3a971e81a610d..0742af3a1340a 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx @@ -40,7 +40,8 @@ Detector::Detector(bool active) auto& iotofPars = IOTOFBaseParam::Instance(); configLayers(iotofPars.enableInnerTOF, iotofPars.enableOuterTOF, iotofPars.enableForwardTOF, iotofPars.enableBackwardTOF, - iotofPars.detectorPattern); + iotofPars.detectorPattern, + iotofPars.segmentedInnerTOF, iotofPars.segmentedOuterTOF); } Detector::~Detector() @@ -56,7 +57,7 @@ void Detector::ConstructGeometry() createGeometry(); } -void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::string pattern) +void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::string pattern, bool itofSegmented, bool otofSegmented) { float radiusInnerTof = 19.f; @@ -65,9 +66,10 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str float lengthOuterTof = 680.f; std::pair radiusRangeDiskTof = {15.f, 100.f}; float zForwardTof = 370.f; + LOG(info) << "Configuring IOTOF layers with '" << pattern << "' pattern"; if (pattern == "") { + LOG(info) << "Default pattern"; } else if (pattern == "v3b") { - LOG(info) << "Configuring IOTOF layers with v3b pattern"; ftof = false; btof = false; } else if (pattern == "v3b1a") { @@ -93,17 +95,25 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str } else { LOG(fatal) << "IOTOF layer pattern " << pattern << " not recognized, exiting"; } - if (itof) { - mITOFLayer = ITOFLayer(std::string{GeometryTGeo::getITOFLayerPattern()}, radiusInnerTof, 0.f, lengthInnerTof, 0.f, 0.02f, true); // iTOF + if (itof) { // iTOF + mITOFLayer = itofSegmented ? ITOFLayer(std::string{GeometryTGeo::getITOFLayerPattern()}, + radiusInnerTof, 0.f, lengthInnerTof, 0.f, 0.02f, ITOFLayer::kBarrelSegmented, + 24, 5.42, 80, 10) + : ITOFLayer(std::string{GeometryTGeo::getITOFLayerPattern()}, + radiusInnerTof, 0.f, lengthInnerTof, 0.f, 0.02f, ITOFLayer::kBarrel); } - if (otof) { - mOTOFLayer = OTOFLayer(std::string{GeometryTGeo::getOTOFLayerPattern()}, radiusOuterTof, 0.f, lengthOuterTof, 0.f, 0.02f, true); // oTOF + if (otof) { // oTOF + mOTOFLayer = otofSegmented ? OTOFLayer(std::string{GeometryTGeo::getOTOFLayerPattern()}, + radiusOuterTof, 0.f, lengthOuterTof, 0.f, 0.02f, OTOFLayer::kBarrelSegmented, + 62, 9.74, 432, 5) + : OTOFLayer(std::string{GeometryTGeo::getOTOFLayerPattern()}, + radiusOuterTof, 0.f, lengthOuterTof, 0.f, 0.02f, OTOFLayer::kBarrel); } if (ftof) { - mFTOFLayer = FTOFLayer(std::string{GeometryTGeo::getFTOFLayerPattern()}, radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, zForwardTof, 0.02f, false); // fTOF + mFTOFLayer = FTOFLayer(std::string{GeometryTGeo::getFTOFLayerPattern()}, radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, zForwardTof, 0.02f, FTOFLayer::kDisk); // fTOF } if (btof) { - mBTOFLayer = BTOFLayer(std::string{GeometryTGeo::getBTOFLayerPattern()}, radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, -zForwardTof, 0.02f, false); // bTOF + mBTOFLayer = BTOFLayer(std::string{GeometryTGeo::getBTOFLayerPattern()}, radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, -zForwardTof, 0.02f, BTOFLayer::kDisk); // bTOF } } @@ -186,14 +196,18 @@ void Detector::defineSensitiveVolumes() // The names of the IOTOF sensitive volumes have the format: IOTOFLayer(0...mLayers.size()-1) auto& iotofPars = IOTOFBaseParam::Instance(); if (iotofPars.enableInnerTOF) { - v = geoManager->GetVolume(GeometryTGeo::getITOFSensorPattern()); - LOGP(info, "Adding IOTOF Sensitive Volume {}", v->GetName()); - AddSensitiveVolume(v); + for (const std::string& itofSensor : ITOFLayer::mRegister) { + v = geoManager->GetVolume(itofSensor.c_str()); + LOGP(info, "Adding IOTOF Sensitive Volume {}", v->GetName()); + AddSensitiveVolume(v); + } } if (iotofPars.enableOuterTOF) { - v = geoManager->GetVolume(GeometryTGeo::getOTOFSensorPattern()); - LOGP(info, "Adding IOTOF Sensitive Volume {}", v->GetName()); - AddSensitiveVolume(v); + for (const std::string& otofSensor : OTOFLayer::mRegister) { + v = geoManager->GetVolume(otofSensor.c_str()); + LOGP(info, "Adding IOTOF Sensitive Volume {}", v->GetName()); + AddSensitiveVolume(v); + } } if (iotofPars.enableForwardTOF) { v = geoManager->GetVolume(GeometryTGeo::getFTOFSensorPattern()); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx index 53c07d1fa4978..169a1271da47e 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx @@ -14,84 +14,225 @@ #include "Framework/Logger.h" +#include +#include #include #include +#include + +#include +#include namespace o2 { namespace iotof { -Layer::Layer(std::string layerName, float rInn, float rOut, float zLength, float zOffset, float layerX2X0, bool isBarrel) - : mLayerName(layerName), mInnerRadius(rInn), mOuterRadius(rOut), mZLength(zLength), mZOffset(zOffset), mX2X0(layerX2X0), mIsBarrel(isBarrel) +Layer::Layer(std::string layerName, float rInn, float rOut, float zLength, float zOffset, float layerX2X0, int layout, int nSegments, float segmentSize, int nSensorsPerSegment, double tiltAngle) + : mLayerName(layerName), + mInnerRadius(rInn), + mOuterRadius(rOut), + mZLength(zLength), + mZOffset(zOffset), + mX2X0(layerX2X0), + mLayout(layout), + mSegments(nSegments, segmentSize), + mSensorsPerSegment(nSensorsPerSegment), + mTiltAngle(tiltAngle) { float Si_X0 = 9.5f; mChipThickness = mX2X0 * Si_X0; - if (isBarrel) { - mOuterRadius = mInnerRadius + mChipThickness; - } else { - mZLength = mChipThickness; + std::string name = ""; + switch (layout) { + case kBarrel: + case kBarrelSegmented: + name = "barrel"; + mOuterRadius = mInnerRadius + mChipThickness; + break; + case kDisk: + case kDiskSegmented: + name = "forward"; + mZLength = mChipThickness; + break; + default: + LOG(fatal) << "Invalid layout " << layout; + } + if (1) { // Sanity checks + if (mInnerRadius > mOuterRadius) { + LOG(fatal) << "Invalid layer dimensions: rInner " << mInnerRadius << " cm is larger than rOuter " << mOuterRadius << " cm"; + } + if ((mSegments.first != 0 || mSegments.second != 0.0f) && (layout != kBarrelSegmented && layout != kDiskSegmented)) { + LOG(fatal) << "Invalid configuration: number of segments " << mSegments.first << " is set for non-segmented layout " << layout; + } + if ((mSegments.first <= 1 || mSegments.second <= 0.0f) && (layout == kBarrelSegmented || layout == kDiskSegmented)) { + LOG(fatal) << "Invalid configuration: number of segments " << mSegments.first << " must be positive for segmented layout " << layout; + } + if (mSensorsPerSegment <= 0 && (layout == kBarrelSegmented || layout == kDiskSegmented)) { + LOG(fatal) << "Invalid configuration: number of sensors per segment " << mSensorsPerSegment << " must be positive for segmented layout " << layout; + } + if (std::abs(mTiltAngle) > 0.1 && (layout != kBarrelSegmented && layout != kDiskSegmented)) { + LOG(fatal) << "Invalid configuration: tilt angle " << mTiltAngle << " is set for non-segmented layout " << layout; + } } - LOGP(info, "TOF: Creating {} layer: rInner: {} (cm) rOuter: {} (cm) zLength: {} (cm) zOffset: {} x2X0: {}", isBarrel ? std::string("barrel") : std::string("forward"), mInnerRadius, mOuterRadius, mZLength, mZOffset, mX2X0); + + LOGP(info, "TOF: Creating {} layer: rInner: {} (cm) rOuter: {} (cm) zLength: {} (cm) zOffset: {} x2X0: {}", name.c_str(), mInnerRadius, mOuterRadius, mZLength, mZOffset, mX2X0); } +std::vector ITOFLayer::mRegister; void ITOFLayer::createLayer(TGeoVolume* motherVolume) { - std::string chipName = o2::iotof::GeometryTGeo::getITOFChipPattern(), - sensName = o2::iotof::GeometryTGeo::getITOFSensorPattern(); - - TGeoTube* sensor = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); - TGeoTube* chip = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); - TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + const std::string chipName = o2::iotof::GeometryTGeo::getITOFChipPattern(); + const std::string sensName = o2::iotof::GeometryTGeo::getITOFSensorPattern(); TGeoMedium* medSi = gGeoManager->GetMedium("TF3_SILICON$"); TGeoMedium* medAir = gGeoManager->GetMedium("TF3_AIR$"); - LOGP(info, "Media: {} {}", (void*)medSi, (void*)medAir); - TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi); - TGeoVolume* chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); - TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); - sensVol->SetLineColor(kRed + 3); - chipVol->SetLineColor(kRed + 3); - layerVol->SetLineColor(kRed + 3); - - LOGP(info, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); - chipVol->AddNode(sensVol, 1, nullptr); - - LOGP(info, "Inserting {} in {} ", chipVol->GetName(), layerVol->GetName()); - layerVol->AddNode(chipVol, 1, nullptr); - - LOGP(info, "Inserting {} in {} ", layerVol->GetName(), motherVolume->GetName()); - motherVolume->AddNode(layerVol, 1, nullptr); + switch (mLayout) { + case kBarrel: { + TGeoTube* sensor = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + TGeoTube* chip = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + + TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi); + TGeoVolume* chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); + TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); + sensVol->SetLineColor(kRed + 3); + chipVol->SetLineColor(kRed + 3); + layerVol->SetLineColor(kRed + 3); + + LOGP(info, "Inserting Barrel {} in {} ", sensVol->GetName(), chipVol->GetName()); + ITOFLayer::mRegister.push_back(sensVol->GetName()); + chipVol->AddNode(sensVol, 1, nullptr); + + LOGP(info, "Inserting Barrel {} in {} ", chipVol->GetName(), layerVol->GetName()); + layerVol->AddNode(chipVol, 1, nullptr); + + LOGP(info, "Inserting Barrel {} in {} ", layerVol->GetName(), motherVolume->GetName()); + motherVolume->AddNode(layerVol, 1, nullptr); + return; + } + case kBarrelSegmented: { + const double circumference = TMath::TwoPi() * 0.5 * (mInnerRadius + mOuterRadius); + const double segmentSize = mSegments.second; // cm circumference / mSegments; + const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius); + TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); + layerVol->SetLineColor(kRed + 3); + + for (int i = 0; i < mSegments.first; ++i) { + LOGP(info, "iTOF: Creating segment {}/{} with size {} and thickness {}cm", i + 1, mSegments.first, segmentSize, (mOuterRadius - mInnerRadius)); + const double hx = 0.5 * segmentSize; + const double hy = 0.5 * (mOuterRadius - mInnerRadius); + const double hz = 0.5 * mZLength; + TGeoBBox* sensor = new TGeoBBox(hy, hx, hz); + TGeoBBox* chip = new TGeoBBox(hy, hx, hz); + const std::string segmentTag = Form("segment%d", i + 1); + TGeoVolume* sensVol = new TGeoVolume(Form("%s_%s", sensName.c_str(), segmentTag.c_str()), sensor, medSi); + TGeoVolume* chipVol = new TGeoVolume(Form("%s_%s", chipName.c_str(), segmentTag.c_str()), chip, medSi); + sensVol->SetLineColor(kRed + 3); + chipVol->SetLineColor(kRed + 3); + + LOGP(info, " Inserting Barrel {} in {} ", sensVol->GetName(), chipVol->GetName()); + ITOFLayer::mRegister.push_back(sensVol->GetName()); + chipVol->AddNode(sensVol, 1, nullptr); + + const double phi = TMath::TwoPi() * i / mSegments.first; + + LOG(info) << " Tilting angle for segment " << i + 1 << ": " << phi * TMath::RadToDeg() << " degrees"; + const double x = avgRadius * TMath::Cos(phi); + const double y = avgRadius * TMath::Sin(phi); + auto* rotation = new TGeoRotation(Form("segmentRot%d", i + 1), phi * TMath::RadToDeg() + mTiltAngle, 0, 0); + auto* transformation = new TGeoCombiTrans(x, y, 0, rotation); + + LOGP(info, "Inserting Barrel {} in {} ", chipVol->GetName(), layerVol->GetName()); + layerVol->AddNode(chipVol, 1 + i, transformation); + } + LOGP(info, "Inserting Barrel {} in {} at r={} cm", layerVol->GetName(), motherVolume->GetName(), avgRadius); + motherVolume->AddNode(layerVol, 1, nullptr); + return; + } + default: + LOG(fatal) << "Invalid layout " << mLayout; + } } +std::vector OTOFLayer::mRegister; void OTOFLayer::createLayer(TGeoVolume* motherVolume) { std::string chipName = o2::iotof::GeometryTGeo::getOTOFChipPattern(), sensName = o2::iotof::GeometryTGeo::getOTOFSensorPattern(); - TGeoTube* sensor = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); - TGeoTube* chip = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); - TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); - TGeoMedium* medSi = gGeoManager->GetMedium("TF3_SILICON$"); TGeoMedium* medAir = gGeoManager->GetMedium("TF3_AIR$"); + LOGP(info, "Media: {} {}", (void*)medSi, (void*)medAir); - TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi); - TGeoVolume* chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); - TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); - sensVol->SetLineColor(kRed + 3); - chipVol->SetLineColor(kRed + 3); - layerVol->SetLineColor(kRed + 3); - - LOGP(info, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); - chipVol->AddNode(sensVol, 1, nullptr); - - LOGP(info, "Inserting {} in {} ", chipVol->GetName(), layerVol->GetName()); - layerVol->AddNode(chipVol, 1, nullptr); - - LOGP(info, "Inserting {} in {} ", layerVol->GetName(), motherVolume->GetName()); - motherVolume->AddNode(layerVol, 1, nullptr); + switch (mLayout) { + case kBarrel: { + TGeoTube* sensor = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + TGeoTube* chip = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + + TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi); + TGeoVolume* chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); + TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); + sensVol->SetLineColor(kRed + 3); + chipVol->SetLineColor(kRed + 3); + layerVol->SetLineColor(kRed + 3); + + LOGP(info, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); + OTOFLayer::mRegister.push_back(sensVol->GetName()); + chipVol->AddNode(sensVol, 1, nullptr); + + LOGP(info, "Inserting {} in {} ", chipVol->GetName(), layerVol->GetName()); + layerVol->AddNode(chipVol, 1, nullptr); + + LOGP(info, "Inserting {} in {} ", layerVol->GetName(), motherVolume->GetName()); + motherVolume->AddNode(layerVol, 1, nullptr); + return; + } + case kBarrelSegmented: { + const double circumference = TMath::TwoPi() * 0.5 * (mInnerRadius + mOuterRadius); + const double segmentSize = mSegments.second; // cm circumference / mSegments; + const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius); + TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); + layerVol->SetLineColor(kRed + 3); + + for (int i = 0; i < mSegments.first; ++i) { + LOGP(info, "oTOF: Creating segment {}/{} with size {} and thickness {}cm", i + 1, mSegments.first, segmentSize, (mOuterRadius - mInnerRadius)); + const double hx = 0.5 * segmentSize; + const double hy = 0.5 * (mOuterRadius - mInnerRadius); + const double hz = 0.5 * mZLength; + TGeoBBox* sensor = new TGeoBBox(hy, hx, hz); + TGeoBBox* chip = new TGeoBBox(hy, hx, hz); + const std::string segmentTag = Form("segment%d", i + 1); + TGeoVolume* sensVol = new TGeoVolume(Form("%s_%s", sensName.c_str(), segmentTag.c_str()), sensor, medSi); + TGeoVolume* chipVol = new TGeoVolume(Form("%s_%s", chipName.c_str(), segmentTag.c_str()), chip, medSi); + sensVol->SetLineColor(kRed + 3); + chipVol->SetLineColor(kRed + 3); + + LOGP(info, " Inserting Barrel {} in {} ", sensVol->GetName(), chipVol->GetName()); + OTOFLayer::mRegister.push_back(sensVol->GetName()); + chipVol->AddNode(sensVol, 1, nullptr); + + const double phi = TMath::TwoPi() * i / mSegments.first; + + LOG(info) << " Tilting angle for segment " << i + 1 << ": " << phi * TMath::RadToDeg() << " degrees"; + const double x = avgRadius * TMath::Cos(phi); + const double y = avgRadius * TMath::Sin(phi); + auto* rotation = new TGeoRotation(Form("segmentRot%d", i + 1), phi * TMath::RadToDeg() + mTiltAngle, 0, 0); + auto* transformation = new TGeoCombiTrans(x, y, 0, rotation); + + LOGP(info, "Inserting Barrel {} in {} ", chipVol->GetName(), layerVol->GetName()); + layerVol->AddNode(chipVol, 1 + i, transformation); + } + LOGP(info, "Inserting Barrel {} in {} at r={} cm", layerVol->GetName(), motherVolume->GetName(), avgRadius); + motherVolume->AddNode(layerVol, 1, nullptr); + return; + } + default: + LOG(fatal) << "Invalid layout " << mLayout; + } } void FTOFLayer::createLayer(TGeoVolume* motherVolume) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx index 059a35520c1a0..d5d37ec00acef 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx @@ -102,7 +102,6 @@ void GeometryTGeo::Build(int loadTrans) mLastChipIndexMLOT.resize(mNumberOfLayersMLOT); /// ML and OT are part of TRK as the same detector, without disks for (int i = 0; i < mNumberOfLayersMLOT; i++) { - std::cout << "Layer MLOT: " << i << std::endl; mNumberOfStaves[i] = extractNumberOfStavesMLOT(i); mNumberOfHalfStaves[i] = extractNumberOfHalfStavesMLOT(i); mNumberOfModules[i] = extractNumberOfModulesMLOT(i); From f23aa72dfdb3779502c9510526dab12715756c40 Mon Sep 17 00:00:00 2001 From: shahoian Date: Sat, 21 Feb 2026 16:25:38 +0100 Subject: [PATCH 129/133] Add missing reset for FV0 channels vector at TF start --- Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx b/Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx index b97186bbf81a8..cdf297b334588 100644 --- a/Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx +++ b/Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx @@ -42,6 +42,7 @@ void ReconstructionDPL::run(ProcessingContext& pc) { mTimer.Start(false); mRecPoints.clear(); + mRecChData.clear(); auto digits = pc.inputs().get>("digits"); auto digch = pc.inputs().get>("digch"); // RS: if we need to process MC truth, uncomment lines below From 354b12edc5d97890ef6ae952be530323b7c41075 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 19 Feb 2026 19:42:12 +0100 Subject: [PATCH 130/133] GPU: Improve some error messages --- GPU/GPUTracking/DataCompression/TPCClusterDecompressor.cxx | 4 ++-- GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.cxx b/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.cxx index cd1717faf178d..51a896c2baf6a 100644 --- a/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.cxx +++ b/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.cxx @@ -43,10 +43,10 @@ int32_t TPCClusterDecompressor::decompress(const CompressedClustersFlat* cluster int32_t TPCClusterDecompressor::decompress(const CompressedClusters* clustersCompressed, o2::tpc::ClusterNativeAccess& clustersNative, std::function allocator, const GPUParam& param, bool deterministicRec) { if (clustersCompressed->nTracks && clustersCompressed->solenoidBz != -1e6f && clustersCompressed->solenoidBz != param.bzkG) { - throw std::runtime_error("Configured solenoid Bz does not match value used for track model encoding"); + throw std::runtime_error("Configured solenoid Bz " + std::to_string(param.bzkG) + " does not match value used for track model encoding " + std::to_string(clustersCompressed->solenoidBz)); } if (clustersCompressed->nTracks && clustersCompressed->maxTimeBin != -1e6 && clustersCompressed->maxTimeBin != param.continuousMaxTimeBin) { - throw std::runtime_error("Configured max time bin does not match value used for track model encoding"); + throw std::runtime_error("Configured max time bin " + std::to_string(param.continuousMaxTimeBin) + " does not match value used for track model encoding " + std::to_string(clustersCompressed->maxTimeBin)); } std::vector clusters[NSECTORS][GPUCA_ROW_COUNT]; std::atomic_flag locks[NSECTORS][GPUCA_ROW_COUNT]; diff --git a/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx index ca1352b3bda1b..89d47d0e1b86c 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx @@ -273,10 +273,10 @@ int32_t GPUChainTracking::RunTPCDecompression() CompressedClusters& inputGPUShadow = DecompressorShadow.mInputGPU; if (cmprClsHost.nTracks && cmprClsHost.solenoidBz != -1e6f && cmprClsHost.solenoidBz != param().bzkG) { - throw std::runtime_error("Configured solenoid Bz does not match value used for track model encoding"); + throw std::runtime_error("Configured solenoid Bz " + std::to_string(param().bzkG) + " does not match value used for track model encoding " + std::to_string(cmprClsHost.solenoidBz)); } if (cmprClsHost.nTracks && cmprClsHost.maxTimeBin != -1e6 && cmprClsHost.maxTimeBin != param().continuousMaxTimeBin) { - throw std::runtime_error("Configured max time bin does not match value used for track model encoding"); + throw std::runtime_error("Configured max time bin " + std::to_string(param().continuousMaxTimeBin) + " does not match value used for track model encoding " + std::to_string(cmprClsHost.maxTimeBin)); } int32_t inputStream = 0; From 480167460a22dfe0a23f8c2285901c739f719c6c Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 15 Oct 2025 09:44:10 +0200 Subject: [PATCH 131/133] GPU: Improve existing debug dumps --- GPU/GPUTracking/Merger/GPUTPCGMMerger.h | 2 + GPU/GPUTracking/Merger/GPUTPCGMMergerDump.cxx | 74 ++++++++++--------- 2 files changed, 40 insertions(+), 36 deletions(-) diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMerger.h b/GPU/GPUTracking/Merger/GPUTPCGMMerger.h index 14974bdec2303..813e3df29e82e 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMerger.h +++ b/GPU/GPUTracking/Merger/GPUTPCGMMerger.h @@ -199,6 +199,8 @@ class GPUTPCGMMerger : public GPUProcessor void DumpRefit(std::ostream& out) const; void DumpFinal(std::ostream& out) const; void DumpLoopers(std::ostream& out) const; + void DumpTrackParam(std::ostream& out) const; + void DumpTrackClusters(std::ostream& out, bool non0StateOnly = false, bool noNDF0 = false) const; template void MergedTrackStreamerInternal(const GPUTPCGMBorderTrack& b1, const GPUTPCGMBorderTrack& b2, const char* name, int32_t sector1, int32_t sector2, int32_t mergeMode, float weight, float frac) const; diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMergerDump.cxx b/GPU/GPUTracking/Merger/GPUTPCGMMergerDump.cxx index 0a83bf47f5725..f6afc46609a11 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMergerDump.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMMergerDump.cxx @@ -43,10 +43,10 @@ using namespace gputpcgmmergertypes; void GPUTPCGMMerger::DumpSectorTracks(std::ostream& out) const { std::streamsize ss = out.precision(); - out << std::setprecision(2); + out << std::setprecision(10); out << "\nTPC Merger Sector Tracks\n"; for (int32_t iSector = 0; iSector < NSECTORS; iSector++) { - out << "Sector Track Info Index " << (mSectorTrackInfoIndex[iSector + 1] - mSectorTrackInfoIndex[iSector]) << " / " << (mSectorTrackInfoIndex[NSECTORS + iSector + 1] - mSectorTrackInfoIndex[NSECTORS + iSector]) << "\n"; + out << "Sector Track Info Sector " << iSector << " Index " << (mSectorTrackInfoIndex[iSector + 1] - mSectorTrackInfoIndex[iSector]) << " / " << (mSectorTrackInfoIndex[NSECTORS + iSector + 1] - mSectorTrackInfoIndex[NSECTORS + iSector]) << "\n"; for (int32_t iGlobal = 0; iGlobal < 2; iGlobal++) { out << " Track type " << iGlobal << "\n"; for (int32_t j = mSectorTrackInfoIndex[iSector + NSECTORS * iGlobal]; j < mSectorTrackInfoIndex[iSector + NSECTORS * iGlobal + 1]; j++) { @@ -134,9 +134,14 @@ void GPUTPCGMMerger::DumpMergedBetweenSectors(std::ostream& out) const void GPUTPCGMMerger::DumpCollected(std::ostream& out) const { - std::streamsize ss = out.precision(); - out << std::setprecision(6); out << "\nTPC Merger Collected Tracks\n"; + DumpTrackParam(out); +} + +void GPUTPCGMMerger::DumpTrackParam(std::ostream& out) const +{ + std::streamsize ss = out.precision(); + out << std::setprecision(10); for (uint32_t i = 0; i < mMemory->nMergedTracks; i++) { const auto& trk = mMergedTracks[i]; const auto& p = trk.GetParam(); @@ -157,33 +162,42 @@ void GPUTPCGMMerger::DumpMergeCE(std::ostream& out) const } } -void GPUTPCGMMerger::DumpFitPrepare(std::ostream& out) const +void GPUTPCGMMerger::DumpTrackClusters(std::ostream& out, bool non0StateOnly, bool noNDF0) const { - out << "\nTPC Merger Refit Prepare\n"; - out << " Sort\n"; - for (uint32_t i = 0; i < mMemory->nMergedTracks; i++) { - out << " " << i << ": " << mTrackOrderAttach[i] << "\n"; - } - out << " Clusters\n"; for (uint32_t j = 0; j < mMemory->nMergedTracks; j++) { const auto& trk = mMergedTracks[j]; - out << " Track " << j << ": "; + if (trk.NClusters() == 0) { + continue; + } + if (noNDF0 && (!trk.OK() || trk.GetParam().GetNDF() < 0)) { + continue; + } + out << " Track " << j << ": (" << trk.NClusters() << "): "; for (uint32_t i = trk.FirstClusterRef(); i < trk.FirstClusterRef() + trk.NClusters(); i++) { - out << j << "/" << (i - trk.FirstClusterRef()) << ": " << mClusters[i].num << "/" << (int32_t)mClusters[i].state << ", "; + if (!non0StateOnly || mClusters[i].state != 0) { + out << j << "/" << (i - trk.FirstClusterRef()) << ": " << (int32_t)mClusters[i].row << "/" << mClusters[i].num << "/" << (int32_t)mClusters[i].state << ", "; + } } out << "\n"; } - uint32_t maxId = mNMaxClusters; +} + +void GPUTPCGMMerger::DumpFitPrepare(std::ostream& out) const +{ + out << "\nTPC Merger Refit Prepare\n"; + out << " Sort\n"; + for (uint32_t i = 0; i < mMemory->nMergedTracks; i++) { + out << " " << i << ": " << mTrackOrderAttach[i] << "\n"; + } + out << " Track Clusters"; + DumpTrackClusters(out); uint32_t j = 0; - for (uint32_t i = 0; i < maxId; i++) { + for (uint32_t i = 0; i < mNMaxClusters; i++) { if ((mClusterAttachment[i] & attachFlagMask) != 0) { - if (++j % 10 == 0) { - out << " Cluster attachment "; + if (j++ % 10 == 0) { + out << "\n Cluster attachment "; } out << i << ": " << (mClusterAttachment[i] & attachTrackMask) << " / " << (mClusterAttachment[i] & attachFlagMask) << " - "; - if (j % 10 == 0) { - out << "\n"; - } } } out << "\n"; @@ -192,7 +206,7 @@ void GPUTPCGMMerger::DumpFitPrepare(std::ostream& out) const void GPUTPCGMMerger::DumpRefit(std::ostream& out) const { std::streamsize ss = out.precision(); - out << std::setprecision(2); + out << std::setprecision(10); out << "\nTPC Merger Refit\n"; for (uint32_t i = 0; i < mMemory->nMergedTracks; i++) { const auto& trk = mMergedTracks[i]; @@ -224,22 +238,10 @@ void GPUTPCGMMerger::DumpLoopers(std::ostream& out) const void GPUTPCGMMerger::DumpFinal(std::ostream& out) const { out << "\nTPC Merger Finalized\n"; - for (uint32_t j = 0; j < mMemory->nMergedTracks; j++) { - const auto& trk = mMergedTracks[j]; - if (trk.NClusters() == 0) { - continue; - } - out << " Track " << j << ": "; - for (uint32_t i = trk.FirstClusterRef(); i < trk.FirstClusterRef() + trk.NClusters(); i++) { - if (mClusters[i].state != 0) { - out << j << "/" << (i - trk.FirstClusterRef()) << ": " << mClusters[i].num << "/" << (int32_t)mClusters[i].state << ", "; - } - } - out << "\n"; - } - uint32_t maxId = mNMaxClusters; + out << "Track Clusters\n"; + DumpTrackClusters(out, true); uint32_t j = 0; - for (uint32_t i = 0; i < maxId; i++) { + for (uint32_t i = 0; i < mNMaxClusters; i++) { if ((mClusterAttachment[i] & attachFlagMask) != 0) { if (++j % 10 == 0) { out << " Cluster attachment "; From a839182762a089481cece2ef7e58a3c7136ac93d Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 19 Feb 2026 22:27:38 +0100 Subject: [PATCH 132/133] GPU TPC: Fix deterministic mode with new cluster removal protection --- GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx b/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx index 260781c17406b..eaf181b741918 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx @@ -1852,7 +1852,7 @@ GPUd() void GPUTPCGMMerger::PrepareForFit1(int32_t nBlocks, int32_t nThreads, in if (CAMath::Abs(trk.GetParam().GetQPt() * Param().qptB5Scaler) <= Param().rec.tpc.rejectQPtB5 && !trk.MergedLooper() && trk.Leg() == 0) { weight |= attachProtect; } - mClusterAttachment[mClusters[trk.FirstClusterRef() + j].num] = weight; + CAMath::AtomicMax(&mClusterAttachment[mClusters[trk.FirstClusterRef() + j].num], weight); CAMath::AtomicAdd(&mSharedCount[mClusters[trk.FirstClusterRef() + j].num], 1u); } if (!trk.CCE() && !trk.MergedLooper()) { From 84a89e877a04d98c1969997a3789449ec26887bb Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 20 Feb 2026 00:03:17 +0100 Subject: [PATCH 133/133] GPU TPC: Do not use fitWithoutProjection for now, sometimes broken --- GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx | 21 +++++++++----------- GPU/GPUTracking/Merger/GPUTPCGMMerger.h | 2 +- GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.h | 3 ++- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx b/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx index eaf181b741918..3622e51bd663f 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx @@ -555,7 +555,7 @@ GPUd() int32_t GPUTPCGMMerger::RefitSectorTrack(GPUTPCGMSectorTrack& sectorTrack prop.SetMaterialTPC(); prop.SetMaxSinPhi(GPUCA_MAX_SIN_PHI); prop.SetSeedingErrors(true); // Larger errors for seeds, better since we don't start with good hypothesis - prop.SetFitInProjections(false); + prop.SetFitInProjections(true); // TODO: Was false, consider reenabling after fitInProjection is fixed prop.SetPolynomialField(&Param().polynomialField); GPUTPCGMTrackParam trk; trk.X() = inTrack->Param().GetX(); @@ -718,9 +718,6 @@ GPUd() void GPUTPCGMMerger::MergeSectorsPrepareStep2(int32_t nBlocks, int32_t nT } else if (iBorder == 3) { // transport to the middle of the sector and rotate vertically to the border on the right dAlpha = -dAlpha; x0 = GPUTPCGeometry::Row2X(63); - } else if (iBorder == 4) { // transport to the middle of the sßector, w/o rotation - dAlpha = 0; - x0 = GPUTPCGeometry::Row2X(63); } const float maxSin = CAMath::Sin(60.f / 180.f * CAMath::Pi()); @@ -783,14 +780,14 @@ GPUd() void GPUTPCGMMerger::MergeSectorsPrepareStep2(int32_t nBlocks, int32_t nT } template <> -GPUd() void GPUTPCGMMerger::MergeBorderTracks<0>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iSector1, GPUTPCGMBorderTrack* B1, int32_t N1, int32_t iSector2, GPUTPCGMBorderTrack* B2, int32_t N2, int32_t mergeMode) +GPUd() void GPUTPCGMMerger::MergeBorderTracks<0>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iSector1, const GPUTPCGMBorderTrack* B1, int32_t N1, int32_t iSector2, const GPUTPCGMBorderTrack* B2, int32_t N2, int32_t mergeMode) { CADEBUG(GPUInfo("\nMERGING Sectors %d %d NTracks %d %d CROSS %d", iSector1, iSector2, N1, N2, mergeMode)); GPUTPCGMBorderRange* range1 = mBorderRange[iSector1]; GPUTPCGMBorderRange* range2 = mBorderRange[iSector2] + *GetConstantMem()->tpcTrackers[iSector2].NTracks(); bool sameSector = (iSector1 == iSector2); for (int32_t itr = iBlock * nThreads + iThread; itr < N1; itr += nThreads * nBlocks) { - GPUTPCGMBorderTrack& b = B1[itr]; + const GPUTPCGMBorderTrack& b = B1[itr]; float d = CAMath::Max(0.5f, 3.5f * CAMath::Sqrt(b.Cov()[1])); if (CAMath::Abs(b.Par()[4]) * Param().qptB5Scaler >= 20) { d *= 2; @@ -809,7 +806,7 @@ GPUd() void GPUTPCGMMerger::MergeBorderTracks<0>(int32_t nBlocks, int32_t nThrea } if (!sameSector) { for (int32_t itr = iBlock * nThreads + iThread; itr < N2; itr += nThreads * nBlocks) { - GPUTPCGMBorderTrack& b = B2[itr]; + const GPUTPCGMBorderTrack& b = B2[itr]; float d = CAMath::Max(0.5f, 3.5f * CAMath::Sqrt(b.Cov()[1])); if (CAMath::Abs(b.Par()[4]) * Param().qptB5Scaler >= 20) { d *= 2; @@ -827,7 +824,7 @@ GPUd() void GPUTPCGMMerger::MergeBorderTracks<0>(int32_t nBlocks, int32_t nThrea } template <> -GPUd() void GPUTPCGMMerger::MergeBorderTracks<1>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iSector1, GPUTPCGMBorderTrack* B1, int32_t N1, int32_t iSector2, GPUTPCGMBorderTrack* B2, int32_t N2, int32_t mergeMode) +GPUd() void GPUTPCGMMerger::MergeBorderTracks<1>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iSector1, const GPUTPCGMBorderTrack* B1, int32_t N1, int32_t iSector2, const GPUTPCGMBorderTrack* B2, int32_t N2, int32_t mergeMode) { #if !defined(GPUCA_GPUCODE_COMPILEKERNELS) GPUTPCGMBorderRange* range1 = mBorderRange[iSector1]; @@ -860,7 +857,7 @@ GPUd() void GPUTPCGMMerger::MergeBorderTracks<3>(int32_t nBlocks, int32_t nThrea } template <> -GPUd() void GPUTPCGMMerger::MergeBorderTracks<2>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iSector1, GPUTPCGMBorderTrack* B1, int32_t N1, int32_t iSector2, GPUTPCGMBorderTrack* B2, int32_t N2, int32_t mergeMode) +GPUd() void GPUTPCGMMerger::MergeBorderTracks<2>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iSector1, const GPUTPCGMBorderTrack* B1, int32_t N1, int32_t iSector2, const GPUTPCGMBorderTrack* B2, int32_t N2, int32_t mergeMode) { // int32_t statAll = 0, statMerged = 0; float factor2ys = Param().rec.tpc.trackMergerFactor2YS; @@ -887,7 +884,7 @@ GPUd() void GPUTPCGMMerger::MergeBorderTracks<2>(int32_t nBlocks, int32_t nThrea i2++; } - GPUTPCGMBorderTrack& b1 = B1[r1.fId]; + const GPUTPCGMBorderTrack& b1 = B1[r1.fId]; if (b1.NClusters() < minNPartHits) { continue; } @@ -904,7 +901,7 @@ GPUd() void GPUTPCGMMerger::MergeBorderTracks<2>(int32_t nBlocks, int32_t nThrea } // do check - GPUTPCGMBorderTrack& b2 = B2[r2.fId]; + const GPUTPCGMBorderTrack& b2 = B2[r2.fId]; #if defined(GPUCA_MERGER_BY_MC_LABEL) && !defined(GPUCA_GPUCODE) int64_t label1 = GetTrackLabel(b1); int64_t label2 = GetTrackLabel(b2); @@ -1019,7 +1016,7 @@ GPUd() void GPUTPCGMMerger::MergeWithinSectorsPrepare(int32_t nBlocks, int32_t n const float maxSin = CAMath::Sin(60.f / 180.f * CAMath::Pi()); for (int32_t itr = iBlock * nThreads + iThread; itr < SectorTrackInfoLocalTotal(); itr += nThreads * nBlocks) { - GPUTPCGMSectorTrack& track = mSectorTrackInfos[itr]; + const GPUTPCGMSectorTrack& track = mSectorTrackInfos[itr]; int32_t iSector = track.Sector(); GPUTPCGMBorderTrack b; if (track.TransportToX(this, x0, Param().bzCLight, b, maxSin)) { diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMerger.h b/GPU/GPUTracking/Merger/GPUTPCGMMerger.h index 813e3df29e82e..8f554c24c1d8c 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMerger.h +++ b/GPU/GPUTracking/Merger/GPUTPCGMMerger.h @@ -226,7 +226,7 @@ class GPUTPCGMMerger : public GPUProcessor private: GPUd() void MergeSectorsPrepareStep2(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iBorder, GPUTPCGMBorderTrack** B, GPUAtomic(uint32_t) * nB, bool useOrigTrackParam = false); template - GPUd() void MergeBorderTracks(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iSector1, GPUTPCGMBorderTrack* B1, int32_t N1, int32_t iSector2, GPUTPCGMBorderTrack* B2, int32_t N2, int32_t mergeMode = 0); + GPUd() void MergeBorderTracks(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iSector1, const GPUTPCGMBorderTrack* B1, int32_t N1, int32_t iSector2, const GPUTPCGMBorderTrack* B2, int32_t N2, int32_t mergeMode = 0); GPUd() void MergeCEFill(const GPUTPCGMSectorTrack* track, const GPUTPCGMMergedTrackHit& cls, int32_t itr); diff --git a/GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.h b/GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.h index 60febbb4428f6..84102cd14ce5c 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.h +++ b/GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.h @@ -54,6 +54,7 @@ class GPUTPCGMSectorTrack GPUd() float SecPhi() const { return mParam.mSecPhi; } GPUd() float DzDs() const { return mParam.mDzDs; } GPUd() float QPt() const { return mParam.mQPt; } + GPUd() const auto& Param() const { return mParam; } GPUd() float TOffset() const { return mTOffset; } GPUd() int32_t LocalTrackId() const { return mLocalTrackId; } @@ -75,7 +76,7 @@ class GPUTPCGMSectorTrack GPUd() void Set(const GPUTPCGMTrackParam& trk, const GPUTPCTrack* sectorTr, float alpha, int32_t sector); GPUd() void SetParam2(const GPUTPCGMTrackParam& trk); GPUd() void Set(const GPUTPCGMMerger* merger, const GPUTPCTrack* sectorTr, float alpha, int32_t sector); - GPUd() void UseParam2() { mParam = mParam2; } + GPUd() void UseParam2() { mParam = mParam2; } // TODO: Clean this up! GPUd() void SetX2(float v) { mParam2.mX = v; } GPUd() float X2() const { return mParam2.mX; }