1919#include " Framework/DataAllocator.h"
2020#include " Framework/ControlService.h"
2121#include " DataFormatsTPC/Digit.h"
22+ #include " TPCSimWorkflow/TPCDigitRootWriterSpec.h"
2223#include " CommonUtils/ConfigurableParam.h"
2324#include " DetectorsRaw/HBFUtilsInitializer.h"
2425#include " TPCSimulation/CommonMode.h"
4647#include < omp.h>
4748#endif
4849#include < TStopwatch.h>
50+ #include " CommonDataFormat/RangeReference.h"
51+
52+ using DigiGroupRef = o2::dataformats::RangeReference<int , int >;
4953
5054using SubSpecificationType = o2::framework::DataAllocator::SubSpecificationType;
5155
@@ -71,6 +75,9 @@ void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions)
7175 workflowOptions.push_back (
7276 ConfigParamSpec{" tpc-sectors" , VariantType::String, sectorDefault.c_str (), {sectorshelp}});
7377
78+ // option to write merged data to file
79+ workflowOptions.push_back (ConfigParamSpec{" writer-mode" , o2::framework::VariantType::Bool, false , {" enable ROOT file output" }});
80+
7481 // option to disable MC truth
7582 workflowOptions.push_back (ConfigParamSpec{" disable-mc" , o2::framework::VariantType::Bool, false , {" disable mc-truth" }});
7683 workflowOptions.push_back (ConfigParamSpec{" configKeyValues" , VariantType::String, " " , {" Semicolon separated key=value strings ..." }});
@@ -104,14 +111,30 @@ void copyHelper<MCTruthContainer>(MCTruthContainer const& origin, MCTruthContain
104111 target.mergeAtBack (origin);
105112}
106113
114+ // a trait to map TPC data types to a DPL channel name
115+ template <typename T>
116+ struct OutputChannelName ;
117+ template <>
118+ struct OutputChannelName <std::vector<o2::tpc::Digit>> {
119+ static constexpr char value[] = " DIGITS" ;
120+ };
121+ template <>
122+ struct OutputChannelName <std::vector<o2::tpc::CommonMode>> {
123+ static constexpr char value[] = " COMMONMODE" ;
124+ };
125+ template <>
126+ struct OutputChannelName <std::vector<DigiGroupRef>> {
127+ static constexpr char value[] = " DIGTRIGGERS" ;
128+ };
129+
107130template <typename T>
108131auto makePublishBuffer (framework::ProcessingContext& pc, int sector, uint64_t activeSectors)
109132{
110- LOG (info) << " PUBLISHING SECTOR " << sector;
133+ LOG (info) << " PUBLISHING SECTOR " << sector << " FOR CHANNEL " << OutputChannelName<T>::value ;
111134
112135 o2::tpc::TPCSectorHeader header{sector};
113136 header.activeSectors = activeSectors;
114- return &pc.outputs ().make <T>(Output{" TPC" , " DIGITS " , static_cast <SubSpecificationType>(sector), header});
137+ return &pc.outputs ().make <T>(Output{" TPC" , OutputChannelName<T>::value , static_cast <SubSpecificationType>(sector), header});
115138}
116139
117140template <>
@@ -187,6 +210,30 @@ void mergeHelper(const char* brprefix, std::vector<int> const& tpcsectors, uint6
187210 }
188211}
189212
213+ template <>
214+ void mergeHelper<std::vector<DigiGroupRef>>(const char * brprefix, std::vector<int > const & tpcsectors, uint64_t activeSectors,
215+ TFile& originfile, framework::ProcessingContext& pc)
216+ {
217+ // specialization for TPC Trigger
218+ auto keyslist = originfile.GetListOfKeys ();
219+ for (int i = 0 ; i < keyslist->GetEntries (); ++i) {
220+ auto key = keyslist->At (i);
221+ int sector = atoi (key->GetName ());
222+ if (std::find (tpcsectors.begin (), tpcsectors.end (), sector) == tpcsectors.end ()) {
223+ // do nothing if sector not wanted
224+ continue ;
225+ }
226+
227+ using AccumType = std::decay_t <decltype (makePublishBuffer<std::vector<DigiGroupRef>>(pc, sector, activeSectors))>;
228+ AccumType accum;
229+ #pragma omp critical
230+ accum = makePublishBuffer<std::vector<DigiGroupRef>>(pc, sector, activeSectors);
231+ // no actual data sent. Continuous mode.
232+
233+ publishBuffer (pc, sector, activeSectors, accum);
234+ }
235+ }
236+
190237void publishMergedTimeframes (std::vector<int > const & lanes, std::vector<int > const & tpcsectors, bool domctruth, framework::ProcessingContext& pc)
191238{
192239 uint64_t activeSectors = 0 ;
@@ -208,13 +255,21 @@ void publishMergedTimeframes(std::vector<int> const& lanes, std::vector<int> con
208255 auto originfile = new TFile (filename.c_str (), " OPEN" );
209256 assert (originfile);
210257
211- // data definitions
258+ // data definitions
212259 using DigitsType = std::vector<o2::tpc::Digit>;
213260 using LabelType = o2::dataformats::MCTruthContainer<o2::MCCompLabel>;
214261 mergeHelper<DigitsType>(" TPCDigit_" , tpcsectors, activeSectors, *originfile, pc);
215262 if (domctruth) {
216263 mergeHelper<LabelType>(" TPCDigitMCTruth_" , tpcsectors, activeSectors, *originfile, pc);
217264 }
265+
266+ // we also merge common modes and publish a (fake) trigger entry
267+ using CommonModeType = std::vector<o2::tpc::CommonMode>;
268+ mergeHelper<CommonModeType>(" TPCCommonMode_" , tpcsectors, activeSectors, *originfile, pc);
269+
270+ using TriggerType = std::vector<DigiGroupRef>;
271+ mergeHelper<TriggerType>(" TPCCommonMode_" , tpcsectors, activeSectors, *originfile, pc);
272+
218273 originfile->Close ();
219274 delete originfile;
220275 }
@@ -257,7 +312,7 @@ class Task
257312// / MC truth information is also aggregated and written out
258313DataProcessorSpec getSpec (std::vector<int > const & laneConfiguration, std::vector<int > const & tpcsectors, bool mctruth, bool publish = true )
259314{
260- // data definitions
315+ // data definitions
261316 using DigitsOutputType = std::vector<o2::tpc::Digit>;
262317 using CommonModeOutputType = std::vector<o2::tpc::CommonMode>;
263318
@@ -266,10 +321,14 @@ DataProcessorSpec getSpec(std::vector<int> const& laneConfiguration, std::vector
266321 // effectively the input expects one sector per subspecification
267322 for (int s = 0 ; s < 36 ; ++s) {
268323 OutputLabel binding{std::to_string (s)};
269- outputs.emplace_back (/* binding, */ " TPC" , " DIGITS" , static_cast <SubSpecificationType>(s), Lifetime::Timeframe);
324+ outputs.emplace_back (" TPC" , " DIGITS" , static_cast <SubSpecificationType>(s), Lifetime::Timeframe);
270325 if (mctruth) {
271- outputs.emplace_back (/* binding, */ " TPC" , " DIGITSMCTR" , static_cast <SubSpecificationType>(s), Lifetime::Timeframe);
326+ outputs.emplace_back (" TPC" , " DIGITSMCTR" , static_cast <SubSpecificationType>(s), Lifetime::Timeframe);
272327 }
328+ // common mode
329+ outputs.emplace_back (" TPC" , " COMMONMODE" , static_cast <SubSpecificationType>(s), Lifetime::Timeframe);
330+ // trigger records
331+ outputs.emplace_back (" TPC" , " DIGTRIGGERS" , static_cast <SubSpecificationType>(s), Lifetime::Timeframe);
273332 }
274333 }
275334
@@ -287,12 +346,25 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext)
287346
288347 auto numlanes = configcontext.options ().get <int >(" tpc-lanes" );
289348 bool mctruth = !configcontext.options ().get <bool >(" disable-mc" );
349+ bool writeout = configcontext.options ().get <bool >(" writer-mode" );
290350 auto tpcsectors = o2::RangeTokenizer::tokenize<int >(configcontext.options ().get <std::string>(" tpc-sectors" ));
291351
292352 std::vector<int > lanes (numlanes);
293353 std::iota (lanes.begin (), lanes.end (), 0 );
294354 specs.emplace_back (o2::tpc::getSpec (lanes, tpcsectors, mctruth));
295355
356+ if (writeout) {
357+ // for now writeout to a ROOT file only works if all sectors
358+ // are included
359+ if (tpcsectors.size () != 36 ) {
360+ LOG (error) << " You currently need to include all TPC sectors in the ROOT writer-mode" ;
361+ } else {
362+ std::vector<int > writerlanes (tpcsectors.size ());
363+ std::iota (writerlanes.begin (), writerlanes.end (), 0 );
364+ specs.emplace_back (o2::tpc::getTPCDigitRootWriterSpec (writerlanes, mctruth));
365+ }
366+ }
367+
296368 // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit
297369 o2::raw::HBFUtilsInitializer hbfIni (configcontext, specs);
298370 return specs;
0 commit comments