3131#include < CommonUtils/FileSystemUtils.h>
3232#include " Algorithm/RangeTokenizer.h"
3333#include " TPCBase/Sector.h"
34+ #include " TPCWorkflow/TPCDigitRootWriterSpec.h"
3435#include < TFile.h>
3536#include < TTree.h>
3637#include < TBranch.h>
@@ -130,6 +131,15 @@ auto makePublishBuffer<MCTruthContainer>(framework::ProcessingContext& pc, int s
130131 return new MCTruthContainer ();
131132}
132133
134+ template <>
135+ auto makePublishBuffer<std::vector<o2::tpc::CommonMode>>(framework::ProcessingContext& pc, int sector, uint64_t activeSectors)
136+ {
137+ LOG (info) << " PUBLISHING COMMONMODE SECTOR " << sector;
138+ o2::tpc::TPCSectorHeader header{sector};
139+ header.activeSectors = activeSectors;
140+ return &pc.outputs ().make <std::vector<o2::tpc::CommonMode>>(Output{" TPC" , " COMMONMODE" , static_cast <SubSpecificationType>(sector), header});
141+ }
142+
133143template <typename T>
134144void publishBuffer (framework::ProcessingContext& pc, int sector, uint64_t activeSectors, T* accum)
135145{
@@ -143,16 +153,27 @@ void publishBuffer<MCTruthContainer>(framework::ProcessingContext& pc, int secto
143153 LOG (info) << " PUBLISHING MC LABELS " << accum->getNElements ();
144154 o2::tpc::TPCSectorHeader header{sector};
145155 header.activeSectors = activeSectors;
146- using LabelType = std::decay_t <decltype (pc.outputs ().make <o2::dataformats::ConstMCTruthContainer<o2::MCCompLabel>>(Output{" " , " " , 0 }))>;
147- LabelType* sharedlabels;
156+
148157#pragma omp critical
149- sharedlabels = &pc.outputs ().make <o2::dataformats::ConstMCTruthContainer<o2::MCCompLabel>>(
150- Output{" TPC" , " DIGITSMCTR" , static_cast <SubSpecificationType>(sector), header});
158+ {
159+ // Convert to IOMCTruthContainerView format as expected by TPC reader
160+ std::vector<char > flattened;
161+ accum->flatten_to (flattened);
151162
152- accum->flatten_to (*sharedlabels);
163+ auto & sharedlabels = pc.outputs ().make <o2::dataformats::IOMCTruthContainerView>(
164+ Output{" TPC" , " DIGITSMCTR" , static_cast <SubSpecificationType>(sector), header});
165+ sharedlabels.adopt (std::move (flattened));
166+ }
153167 delete accum;
154168}
155169
170+ template <>
171+ void publishBuffer<std::vector<o2::tpc::CommonMode>>(framework::ProcessingContext& pc, int sector, uint64_t activeSectors, std::vector<o2::tpc::CommonMode>* accum)
172+ {
173+ // CommonMode data is already published by makePublishBuffer, so nothing special needed here
174+ LOG (info) << " PUBLISHED COMMONMODE DATA FOR SECTOR " << sector << " SIZE " << accum->size ();
175+ }
176+
156177template <typename T>
157178void mergeHelper (const char * brprefix, std::vector<int > const & tpcsectors, uint64_t activeSectors,
158179 TFile& originfile, framework::ProcessingContext& pc)
@@ -197,7 +218,7 @@ void mergeHelper(const char* brprefix, std::vector<int> const& tpcsectors, uint6
197218 }
198219}
199220
200- void publishMergedTimeframes (std::vector<int > const & lanes, std::vector<int > const & tpcsectors, bool domctruth, framework::ProcessingContext& pc)
221+ void publishMergedTimeframes (std::vector<int > const & lanes, std::vector<int > const & tpcsectors, bool domctruth, bool writeDigitsFile, framework::ProcessingContext& pc)
201222{
202223 uint64_t activeSectors = 0 ;
203224 for (auto s : tpcsectors) {
@@ -225,15 +246,49 @@ void publishMergedTimeframes(std::vector<int> const& lanes, std::vector<int> con
225246 if (domctruth) {
226247 mergeHelper<LabelType>(" TPCDigitMCTruth_" , tpcsectors, activeSectors, *originfile, pc);
227248 }
249+ if (writeDigitsFile) {
250+ using CommonModeType = std::vector<o2::tpc::CommonMode>;
251+ // Try to read common mode data from file, but don't fail if it doesn't exist
252+ try {
253+ mergeHelper<CommonModeType>(" TPCCommonMode_" , tpcsectors, activeSectors, *originfile, pc);
254+ } catch (const std::exception& e) {
255+ LOG (warning) << " CommonMode data not found in file, creating empty data: " << e.what ();
256+ // Create empty common mode data for each sector
257+ for (auto sector : tpcsectors) {
258+ o2::tpc::TPCSectorHeader header{sector};
259+ header.activeSectors = activeSectors;
260+ auto & emptyCommonMode = pc.outputs ().make <std::vector<o2::tpc::CommonMode>>(
261+ Output{" TPC" , " COMMONMODE" , static_cast <SubSpecificationType>(sector), header});
262+ // emptyCommonMode is already empty by default
263+ }
264+ }
265+ }
228266 originfile->Close ();
229267 delete originfile;
230268 }
269+
270+ // Create trigger information for continuous mode (one trigger covering all digits)
271+ if (writeDigitsFile) {
272+ for (auto sector : tpcsectors) {
273+ o2::tpc::TPCSectorHeader header{sector};
274+ header.activeSectors = activeSectors;
275+ auto & triggers = pc.outputs ().make <std::vector<o2::dataformats::RangeReference<int , int >>>(
276+ Output{" TPC" , " DIGTRIGGERS" , static_cast <SubSpecificationType>(sector), header});
277+
278+ // For continuous mode, create a single trigger that covers all digits
279+ // We need to count the digits for this sector across all files
280+ // For now, create a placeholder trigger - the actual digit count will be determined
281+ // by the digit writer when it processes the actual digits
282+ triggers.emplace_back (0 , 1 ); // placeholder: covers digits from 0 to 1 (will be adjusted by writer)
283+ LOG (info) << " Created continuous mode trigger for sector " << sector;
284+ }
285+ }
231286}
232287
233288class Task
234289{
235290 public:
236- Task (std::vector<int > laneConfig, std::vector<int > tpcsectors, bool mctruth) : mLanes (laneConfig), mTPCSectors (tpcsectors), mDoMCTruth (mctruth)
291+ Task (std::vector<int > laneConfig, std::vector<int > tpcsectors, bool mctruth, bool writeDigitsFile ) : mLanes (laneConfig), mTPCSectors (tpcsectors), mDoMCTruth (mctruth), mWriteDigitsFile (writeDigitsFile )
237292 {
238293 }
239294
@@ -243,7 +298,7 @@ class Task
243298
244299 TStopwatch w;
245300 w.Start ();
246- publishMergedTimeframes (mLanes , mTPCSectors , mDoMCTruth , pc);
301+ publishMergedTimeframes (mLanes , mTPCSectors , mDoMCTruth , mWriteDigitsFile , pc);
247302
248303 pc.services ().get <ControlService>().endOfStream ();
249304 pc.services ().get <ControlService>().readyToQuit (QuitRequest::Me);
@@ -258,6 +313,7 @@ class Task
258313
259314 private:
260315 bool mDoMCTruth = true ;
316+ bool mWriteDigitsFile = false ;
261317 std::vector<int > mLanes ;
262318 std::vector<int > mTPCSectors ;
263319};
@@ -280,54 +336,16 @@ void getSpec(WorkflowSpec& specs, std::vector<int> const& laneConfiguration, std
280336 if (mctruth) {
281337 outputs.emplace_back (/* binding,*/ " TPC" , " DIGITSMCTR" , static_cast <SubSpecificationType>(s), Lifetime::Timeframe);
282338 }
339+ if (writeDigitsFile) {
340+ outputs.emplace_back (/* binding,*/ " TPC" , " COMMONMODE" , static_cast <SubSpecificationType>(s), Lifetime::Timeframe);
341+ outputs.emplace_back (/* binding,*/ " TPC" , " DIGTRIGGERS" , static_cast <SubSpecificationType>(s), Lifetime::Timeframe);
342+ }
283343 }
284344 }
285-
286- specs.emplace_back (DataProcessorSpec{" TPCDigitMerger" , {}, outputs, AlgorithmSpec{o2::framework::adaptFromTask<Task>(laneConfiguration, tpcsectors, mctruth)}, Options{}});
345+ specs.emplace_back (DataProcessorSpec{" TPCDigitMerger" , {}, outputs, AlgorithmSpec{o2::framework::adaptFromTask<Task>(laneConfiguration, tpcsectors, mctruth, writeDigitsFile)}, Options{}});
287346
288347 if (writeDigitsFile) {
289- // Simplified approach for ChunkedDigitPublisher - no trigger handling needed
290- // since we're merging pre-processed digit chunks
291-
292- auto getIndex = [tpcsectors](o2::framework::DataRef const & ref) -> size_t {
293- auto const * tpcSectorHeader = o2::framework::DataRefUtils::getHeader<o2::tpc::TPCSectorHeader*>(ref);
294- if (!tpcSectorHeader) {
295- throw std::runtime_error (" TPC sector header missing in header stack" );
296- }
297- if (tpcSectorHeader->sector () < 0 ) {
298- // special data sets, don't write
299- return ~(size_t )0 ;
300- }
301- size_t index = 0 ;
302- for (auto const & sector : tpcsectors) {
303- if (sector == tpcSectorHeader->sector ()) {
304- return index;
305- }
306- ++index;
307- }
308- throw std::runtime_error (" sector " + std::to_string (tpcSectorHeader->sector ()) + " not configured for writing" );
309- };
310-
311- auto getName = [tpcsectors](std::string base, size_t index) -> std::string {
312- return base + " _" + std::to_string (tpcsectors.at (index));
313- };
314-
315- // Simple branch definitions without custom fill handlers
316- auto digitsdef = BranchDefinition<DigitsOutputType>{InputSpec{" digits" , ConcreteDataTypeMatcher{" TPC" , " DIGITS" }},
317- " TPCDigit" , " digits-branch-name" ,
318- tpcsectors.size (),
319- getIndex,
320- getName};
321-
322- // MC truth branch: write ConstMCTruthContainer directly
323- auto labelsdef = BranchDefinition<o2::dataformats::ConstMCTruthContainer<o2::MCCompLabel>>{InputSpec{" labelinput" , ConcreteDataTypeMatcher{" TPC" , " DIGITSMCTR" }},
324- " TPCDigitMCTruth" , " labels-branch-name" ,
325- (mctruth ? tpcsectors.size () : 0 ),
326- getIndex,
327- getName};
328-
329- specs.push_back (MakeRootTreeWriterSpec (" TPCDigitWriter" , " tpcdigits.root" , " o2sim" ,
330- std::move (digitsdef), std::move (labelsdef))());
348+ specs.push_back (getTPCDigitRootWriterSpec (tpcsectors, mctruth));
331349 }
332350}
333351
@@ -341,7 +359,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext)
341359
342360 auto numlanes = configcontext.options ().get <int >(" tpc-lanes" );
343361 bool mctruth = !configcontext.options ().get <bool >(" disable-mc" );
344- bool writeDigitsFile = configcontext.options ().get <bool >(" write-digits-file" );
362+ bool writeDigitsFile = configcontext.options ().get <int >(" write-digits-file" );
345363 auto tpcsectors = o2::RangeTokenizer::tokenize<int >(configcontext.options ().get <std::string>(" tpc-sectors" ));
346364
347365 std::vector<int > lanes (numlanes);
0 commit comments