diff --git a/Detectors/TPC/workflow/CMakeLists.txt b/Detectors/TPC/workflow/CMakeLists.txt index fe7c9175968b5..233a3ee782a62 100644 --- a/Detectors/TPC/workflow/CMakeLists.txt +++ b/Detectors/TPC/workflow/CMakeLists.txt @@ -45,6 +45,7 @@ o2_add_library(TPCWorkflow src/TPCTimeSeriesWriterSpec.cxx src/TPCScalerSpec.cxx src/TPCPressureTemperatureSpec.cxx + src/TPCDigitRootWriterSpec.cxx TARGETVARNAME targetName PUBLIC_LINK_LIBRARIES O2::Framework O2::DataFormatsTPC O2::DPLUtils O2::TPCReconstruction diff --git a/Steer/DigitizerWorkflow/src/TPCDigitRootWriterSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCDigitRootWriterSpec.h similarity index 100% rename from Steer/DigitizerWorkflow/src/TPCDigitRootWriterSpec.h rename to Detectors/TPC/workflow/include/TPCWorkflow/TPCDigitRootWriterSpec.h diff --git a/Detectors/TPC/workflow/src/ChunkedDigitPublisher.cxx b/Detectors/TPC/workflow/src/ChunkedDigitPublisher.cxx index adf0cba944c03..64261f035149c 100644 --- a/Detectors/TPC/workflow/src/ChunkedDigitPublisher.cxx +++ b/Detectors/TPC/workflow/src/ChunkedDigitPublisher.cxx @@ -19,16 +19,19 @@ #include "Framework/DataAllocator.h" #include "Framework/ControlService.h" #include "DataFormatsTPC/Digit.h" +#include "DataFormatsTPC/TPCSectorHeader.h" #include "CommonUtils/ConfigurableParam.h" #include "DetectorsRaw/HBFUtilsInitializer.h" #include "TPCSimulation/CommonMode.h" #include "DetectorsBase/Detector.h" +#include "DPLUtils/MakeRootTreeWriterSpec.h" #include #include #include #include #include "Algorithm/RangeTokenizer.h" #include "TPCBase/Sector.h" +#include "TPCWorkflow/TPCDigitRootWriterSpec.h" #include #include #include @@ -52,6 +55,13 @@ using SubSpecificationType = o2::framework::DataAllocator::SubSpecificationType; using namespace o2::framework; using namespace o2::header; +using namespace o2::framework; +using namespace o2::header; + +template +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; +using DigitOutputType = std::vector; + void customize(std::vector& policies) { o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); @@ -74,6 +84,7 @@ void customize(std::vector& workflowOptions) // option to disable MC truth workflowOptions.push_back(ConfigParamSpec{"disable-mc", o2::framework::VariantType::Bool, false, {"disable mc-truth"}}); workflowOptions.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}); + workflowOptions.push_back(ConfigParamSpec{"write-digits-file", VariantType::Int, 0, {"Enable writing of tpcdigis.root file"}}); o2::raw::HBFUtilsInitializer::addConfigOption(workflowOptions); } @@ -120,6 +131,15 @@ auto makePublishBuffer(framework::ProcessingContext& pc, int s return new MCTruthContainer(); } +template <> +auto makePublishBuffer>(framework::ProcessingContext& pc, int sector, uint64_t activeSectors) +{ + LOG(info) << "PUBLISHING COMMONMODE SECTOR " << sector; + o2::tpc::TPCSectorHeader header{sector}; + header.activeSectors = activeSectors; + return &pc.outputs().make>(Output{"TPC", "COMMONMODE", static_cast(sector), header}); +} + template void publishBuffer(framework::ProcessingContext& pc, int sector, uint64_t activeSectors, T* accum) { @@ -133,16 +153,27 @@ void publishBuffer(framework::ProcessingContext& pc, int secto LOG(info) << "PUBLISHING MC LABELS " << accum->getNElements(); o2::tpc::TPCSectorHeader header{sector}; header.activeSectors = activeSectors; - using LabelType = std::decay_t>(Output{"", "", 0}))>; - LabelType* sharedlabels; + #pragma omp critical - sharedlabels = &pc.outputs().make>( - Output{"TPC", "DIGITSMCTR", static_cast(sector), header}); + { + // Convert to IOMCTruthContainerView format as expected by TPC reader + std::vector flattened; + accum->flatten_to(flattened); - accum->flatten_to(*sharedlabels); + auto& sharedlabels = pc.outputs().make( + Output{"TPC", "DIGITSMCTR", static_cast(sector), header}); + sharedlabels.adopt(std::move(flattened)); + } delete accum; } +template <> +void publishBuffer>(framework::ProcessingContext& pc, int sector, uint64_t activeSectors, std::vector* accum) +{ + // CommonMode data is already published by makePublishBuffer, so nothing special needed here + LOG(info) << "PUBLISHED COMMONMODE DATA FOR SECTOR " << sector << " SIZE " << accum->size(); +} + template void mergeHelper(const char* brprefix, std::vector const& tpcsectors, uint64_t activeSectors, TFile& originfile, framework::ProcessingContext& pc) @@ -187,7 +218,7 @@ void mergeHelper(const char* brprefix, std::vector const& tpcsectors, uint6 } } -void publishMergedTimeframes(std::vector const& lanes, std::vector const& tpcsectors, bool domctruth, framework::ProcessingContext& pc) +void publishMergedTimeframes(std::vector const& lanes, std::vector const& tpcsectors, bool domctruth, bool writeDigitsFile, framework::ProcessingContext& pc) { uint64_t activeSectors = 0; for (auto s : tpcsectors) { @@ -215,15 +246,49 @@ void publishMergedTimeframes(std::vector const& lanes, std::vector con if (domctruth) { mergeHelper("TPCDigitMCTruth_", tpcsectors, activeSectors, *originfile, pc); } + if (writeDigitsFile) { + using CommonModeType = std::vector; + // Try to read common mode data from file, but don't fail if it doesn't exist + try { + mergeHelper("TPCCommonMode_", tpcsectors, activeSectors, *originfile, pc); + } catch (const std::exception& e) { + LOG(warning) << "CommonMode data not found in file, creating empty data: " << e.what(); + // Create empty common mode data for each sector + for (auto sector : tpcsectors) { + o2::tpc::TPCSectorHeader header{sector}; + header.activeSectors = activeSectors; + auto& emptyCommonMode = pc.outputs().make>( + Output{"TPC", "COMMONMODE", static_cast(sector), header}); + // emptyCommonMode is already empty by default + } + } + } originfile->Close(); delete originfile; } + + // Create trigger information for continuous mode (one trigger covering all digits) + if (writeDigitsFile) { + for (auto sector : tpcsectors) { + o2::tpc::TPCSectorHeader header{sector}; + header.activeSectors = activeSectors; + auto& triggers = pc.outputs().make>>( + Output{"TPC", "DIGTRIGGERS", static_cast(sector), header}); + + // For continuous mode, create a single trigger that covers all digits + // We need to count the digits for this sector across all files + // For now, create a placeholder trigger - the actual digit count will be determined + // by the digit writer when it processes the actual digits + triggers.emplace_back(0, 1); // placeholder: covers digits from 0 to 1 (will be adjusted by writer) + LOG(info) << "Created continuous mode trigger for sector " << sector; + } + } } class Task { public: - Task(std::vector laneConfig, std::vector tpcsectors, bool mctruth) : mLanes(laneConfig), mTPCSectors(tpcsectors), mDoMCTruth(mctruth) + Task(std::vector laneConfig, std::vector tpcsectors, bool mctruth, bool writeDigitsFile) : mLanes(laneConfig), mTPCSectors(tpcsectors), mDoMCTruth(mctruth), mWriteDigitsFile(writeDigitsFile) { } @@ -233,7 +298,7 @@ class Task TStopwatch w; w.Start(); - publishMergedTimeframes(mLanes, mTPCSectors, mDoMCTruth, pc); + publishMergedTimeframes(mLanes, mTPCSectors, mDoMCTruth, mWriteDigitsFile, pc); pc.services().get().endOfStream(); pc.services().get().readyToQuit(QuitRequest::Me); @@ -248,6 +313,7 @@ class Task private: bool mDoMCTruth = true; + bool mWriteDigitsFile = false; std::vector mLanes; std::vector mTPCSectors; }; @@ -255,7 +321,7 @@ class Task /// create the processor spec /// describing a processor aggregating digits for various TPC sectors and writing them to file /// MC truth information is also aggregated and written out -DataProcessorSpec getSpec(std::vector const& laneConfiguration, std::vector const& tpcsectors, bool mctruth, bool publish = true) +void getSpec(WorkflowSpec& specs, std::vector const& laneConfiguration, std::vector const& tpcsectors, bool mctruth, bool publish = true, bool writeDigitsFile = false) { //data definitions using DigitsOutputType = std::vector; @@ -270,11 +336,17 @@ DataProcessorSpec getSpec(std::vector const& laneConfiguration, std::vector if (mctruth) { outputs.emplace_back(/*binding,*/ "TPC", "DIGITSMCTR", static_cast(s), Lifetime::Timeframe); } + if (writeDigitsFile) { + outputs.emplace_back(/*binding,*/ "TPC", "COMMONMODE", static_cast(s), Lifetime::Timeframe); + outputs.emplace_back(/*binding,*/ "TPC", "DIGTRIGGERS", static_cast(s), Lifetime::Timeframe); + } } } + specs.emplace_back(DataProcessorSpec{"TPCDigitMerger", {}, outputs, AlgorithmSpec{o2::framework::adaptFromTask(laneConfiguration, tpcsectors, mctruth, writeDigitsFile)}, Options{}}); - return DataProcessorSpec{ - "TPCDigitMerger", {}, outputs, AlgorithmSpec{o2::framework::adaptFromTask(laneConfiguration, tpcsectors, mctruth)}, Options{}}; + if (writeDigitsFile) { + specs.push_back(getTPCDigitRootWriterSpec(tpcsectors, mctruth)); + } } } // end namespace tpc @@ -287,11 +359,12 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto numlanes = configcontext.options().get("tpc-lanes"); bool mctruth = !configcontext.options().get("disable-mc"); + bool writeDigitsFile = configcontext.options().get("write-digits-file"); auto tpcsectors = o2::RangeTokenizer::tokenize(configcontext.options().get("tpc-sectors")); std::vector lanes(numlanes); std::iota(lanes.begin(), lanes.end(), 0); - specs.emplace_back(o2::tpc::getSpec(lanes, tpcsectors, mctruth)); + o2::tpc::getSpec(specs, lanes, tpcsectors, mctruth, true, writeDigitsFile); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); diff --git a/Steer/DigitizerWorkflow/src/TPCDigitRootWriterSpec.cxx b/Detectors/TPC/workflow/src/TPCDigitRootWriterSpec.cxx similarity index 99% rename from Steer/DigitizerWorkflow/src/TPCDigitRootWriterSpec.cxx rename to Detectors/TPC/workflow/src/TPCDigitRootWriterSpec.cxx index 9bc9b9ba45e71..c676b03aa4162 100644 --- a/Steer/DigitizerWorkflow/src/TPCDigitRootWriterSpec.cxx +++ b/Detectors/TPC/workflow/src/TPCDigitRootWriterSpec.cxx @@ -14,7 +14,7 @@ /// @since 2018-04-19 /// @brief Processor spec for a ROOT file writer for TPC digits -#include "TPCDigitRootWriterSpec.h" +#include "TPCWorkflow/TPCDigitRootWriterSpec.h" #include "DataFormatsTPC/TPCSectorHeader.h" #include "CommonDataFormat/RangeReference.h" #include "Framework/InputRecord.h" diff --git a/Steer/DigitizerWorkflow/CMakeLists.txt b/Steer/DigitizerWorkflow/CMakeLists.txt index babc5fce4d864..a4418672dbe50 100644 --- a/Steer/DigitizerWorkflow/CMakeLists.txt +++ b/Steer/DigitizerWorkflow/CMakeLists.txt @@ -24,7 +24,6 @@ o2_add_executable(digitizer-workflow src/CPVDigitizerSpec.cxx src/SimReaderSpec.cxx src/SimpleDigitizerWorkflow.cxx - src/TPCDigitRootWriterSpec.cxx src/TPCDigitizerSpec.cxx src/ZDCDigitizerSpec.cxx src/TOFDigitizerSpec.cxx @@ -59,6 +58,7 @@ o2_add_executable(digitizer-workflow O2::TOFReconstruction O2::TOFWorkflowIO O2::TPCSimulation + O2::TPCWorkflow O2::TRDSimulation O2::TRDWorkflow O2::TRDWorkflowIO diff --git a/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx b/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx index e86ee47550f13..6c803f48ecede 100644 --- a/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx +++ b/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx @@ -30,7 +30,7 @@ // for TPC #include "TPCDigitizerSpec.h" -#include "TPCDigitRootWriterSpec.h" +#include "TPCWorkflow/TPCDigitRootWriterSpec.h" #include "TPCBase/Sector.h" #include "TPCBase/CDBInterface.h" // needed in order to init the **SHARED** polyadist file (to be done before the digitizers initialize)