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,31 @@ 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+
122+ template <>
123+ struct OutputChannelName <std::vector<o2::tpc::CommonMode>> {
124+ static constexpr char value[] = " COMMONMODE" ;
125+ };
126+ template <>
127+ struct OutputChannelName <std::vector<DigiGroupRef>> {
128+ static constexpr char value[] = " DIGTRIGGERS" ;
129+ };
130+
107131template <typename T>
108132auto makePublishBuffer (framework::ProcessingContext& pc, int sector, uint64_t activeSectors)
109133{
110- LOG (info) << " PUBLISHING SECTOR " << sector;
134+ LOG (info) << " PUBLISHING SECTOR " << sector << " FOR CHANNEL " << OutputChannelName<T>::value ;
111135
112136 o2::tpc::TPCSectorHeader header{sector};
113137 header.activeSectors = activeSectors;
114- return &pc.outputs ().make <T>(Output{" TPC" , " DIGITS " , static_cast <SubSpecificationType>(sector), header});
138+ return &pc.outputs ().make <T>(Output{" TPC" , OutputChannelName<T>::value , static_cast <SubSpecificationType>(sector), header});
115139}
116140
117141template <>
@@ -187,6 +211,31 @@ void mergeHelper(const char* brprefix, std::vector<int> const& tpcsectors, uint6
187211 }
188212}
189213
214+ template <>
215+ void mergeHelper<std::vector<DigiGroupRef>>(const char * brprefix, std::vector<int > const & tpcsectors, uint64_t activeSectors,
216+ TFile& originfile, framework::ProcessingContext& pc)
217+ {
218+ // specialization for TPC Trigger
219+ auto keyslist = originfile.GetListOfKeys ();
220+ for (int i = 0 ; i < keyslist->GetEntries (); ++i) {
221+ auto key = keyslist->At (i);
222+ int sector = atoi (key->GetName ());
223+ if (std::find (tpcsectors.begin (), tpcsectors.end (), sector) == tpcsectors.end ()) {
224+ // do nothing if sector not wanted
225+ continue ;
226+ }
227+
228+ using AccumType = std::decay_t <decltype (makePublishBuffer<std::vector<DigiGroupRef>>(pc, sector, activeSectors))>;
229+ AccumType accum;
230+ #pragma omp critical
231+ accum = makePublishBuffer<std::vector<DigiGroupRef>>(pc, sector, activeSectors);
232+ // no actual data sent. Continuous mode.
233+
234+ // some data (labels are published slightly differently)
235+ publishBuffer (pc, sector, activeSectors, accum);
236+ }
237+ }
238+
190239void publishMergedTimeframes (std::vector<int > const & lanes, std::vector<int > const & tpcsectors, bool domctruth, framework::ProcessingContext& pc)
191240{
192241 uint64_t activeSectors = 0 ;
@@ -208,13 +257,21 @@ void publishMergedTimeframes(std::vector<int> const& lanes, std::vector<int> con
208257 auto originfile = new TFile (filename.c_str (), " OPEN" );
209258 assert (originfile);
210259
211- // data definitions
260+ // data definitions
212261 using DigitsType = std::vector<o2::tpc::Digit>;
213262 using LabelType = o2::dataformats::MCTruthContainer<o2::MCCompLabel>;
214263 mergeHelper<DigitsType>(" TPCDigit_" , tpcsectors, activeSectors, *originfile, pc);
215264 if (domctruth) {
216265 mergeHelper<LabelType>(" TPCDigitMCTruth_" , tpcsectors, activeSectors, *originfile, pc);
217266 }
267+
268+ // we also merge common modes and publish a (fake) trigger entry
269+ using CommonModeType = std::vector<o2::tpc::CommonMode>;
270+ mergeHelper<CommonModeType>(" TPCCommonMode_" , tpcsectors, activeSectors, *originfile, pc);
271+
272+ using TriggerType = std::vector<DigiGroupRef>;
273+ mergeHelper<TriggerType>(" TPCCommonMode_" , tpcsectors, activeSectors, *originfile, pc);
274+
218275 originfile->Close ();
219276 delete originfile;
220277 }
@@ -257,7 +314,7 @@ class Task
257314// / MC truth information is also aggregated and written out
258315DataProcessorSpec getSpec (std::vector<int > const & laneConfiguration, std::vector<int > const & tpcsectors, bool mctruth, bool publish = true )
259316{
260- // data definitions
317+ // data definitions
261318 using DigitsOutputType = std::vector<o2::tpc::Digit>;
262319 using CommonModeOutputType = std::vector<o2::tpc::CommonMode>;
263320
@@ -266,10 +323,14 @@ DataProcessorSpec getSpec(std::vector<int> const& laneConfiguration, std::vector
266323 // effectively the input expects one sector per subspecification
267324 for (int s = 0 ; s < 36 ; ++s) {
268325 OutputLabel binding{std::to_string (s)};
269- outputs.emplace_back (/* binding, */ " TPC" , " DIGITS" , static_cast <SubSpecificationType>(s), Lifetime::Timeframe);
326+ outputs.emplace_back (" TPC" , " DIGITS" , static_cast <SubSpecificationType>(s), Lifetime::Timeframe);
270327 if (mctruth) {
271- outputs.emplace_back (/* binding, */ " TPC" , " DIGITSMCTR" , static_cast <SubSpecificationType>(s), Lifetime::Timeframe);
328+ outputs.emplace_back (" TPC" , " DIGITSMCTR" , static_cast <SubSpecificationType>(s), Lifetime::Timeframe);
272329 }
330+ // common mode
331+ outputs.emplace_back (" TPC" , " COMMONMODE" , static_cast <SubSpecificationType>(s), Lifetime::Timeframe);
332+ // trigger records
333+ outputs.emplace_back (" TPC" , " DIGTRIGGERS" , static_cast <SubSpecificationType>(s), Lifetime::Timeframe);
273334 }
274335 }
275336
@@ -287,12 +348,25 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext)
287348
288349 auto numlanes = configcontext.options ().get <int >(" tpc-lanes" );
289350 bool mctruth = !configcontext.options ().get <bool >(" disable-mc" );
351+ bool writeout = configcontext.options ().get <bool >(" writer-mode" );
290352 auto tpcsectors = o2::RangeTokenizer::tokenize<int >(configcontext.options ().get <std::string>(" tpc-sectors" ));
291353
292354 std::vector<int > lanes (numlanes);
293355 std::iota (lanes.begin (), lanes.end (), 0 );
294356 specs.emplace_back (o2::tpc::getSpec (lanes, tpcsectors, mctruth));
295357
358+ if (writeout) {
359+ // for now writeout to a ROOT file only works if all sectors
360+ // are included
361+ if (tpcsectors.size () != 36 ) {
362+ LOG (error) << " You currently need to include all TPC sectors in the ROOT writer-mode" ;
363+ } else {
364+ std::vector<int > writerlanes (tpcsectors.size ());
365+ std::iota (writerlanes.begin (), writerlanes.end (), 0 );
366+ specs.emplace_back (o2::tpc::getTPCDigitRootWriterSpec (writerlanes, mctruth));
367+ }
368+ }
369+
296370 // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit
297371 o2::raw::HBFUtilsInitializer hbfIni (configcontext, specs);
298372 return specs;
0 commit comments