Project
Loading...
Searching...
No Matches
SimpleDigitizerWorkflow.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
12#include <boost/program_options.hpp>
13#include <boost/lexical_cast.hpp>
14
22#include "Framework/InputSpec.h"
24#include "SimReaderSpec.h"
30
31// for TPC
32#include "TPCDigitizerSpec.h"
34#include "TPCBase/Sector.h"
36// needed in order to init the **SHARED** polyadist file (to be done before the digitizers initialize)
38
39// for ITSMFT
41#include "ITSMFTDigitizerSpec.h"
43
44#ifdef ENABLE_UPGRADES
45// for ITS3
46#include "ITS3DigitizerSpec.h"
48
49// for alice 3 TRK
50#include "TRKDigitizerSpec.h"
52
53// for alice 3 TF3
54#include "IOTOFDigitizerSpec.h"
56#endif
57
58// for TOF
59#include "TOFDigitizerSpec.h"
61#include "TOFBase/CalibTOFapi.h"
62
63// for FT0
64#include "FT0DigitizerSpec.h"
65#include "FT0DigitWriterSpec.h"
66
67// for CTP
68#include "CTPDigitizerSpec.h"
70
71// for FV0
72#include "FV0DigitizerSpec.h"
73#include "FV0DigitWriterSpec.h"
74
75// for FDD
76#include "FDDDigitizerSpec.h"
78
79// for EMCal
82
83// for HMPID
84#include "HMPIDDigitizerSpec.h"
86
87// for TRD
92
93// for MUON MCH
94#include "MCHDigitizerSpec.h"
96
97// for MID
98#include "MIDDigitizerSpec.h"
99#include "MIDDigitWriterSpec.h"
100
101// for PHOS
102#include "PHOSDigitizerSpec.h"
103#include "PHOSDigitWriterSpec.h"
104
105// for CPV
106#include "CPVDigitizerSpec.h"
107#include "CPVDigitWriterSpec.h"
108
109// for ZDC
110#include "ZDCDigitizerSpec.h"
112
113// GRP
115#include "GRPUpdaterSpec.h"
116
117#include <cstdlib>
118// this is somewhat assuming that a DPL workflow will run on one node
119#include <thread> // to detect number of hardware threads
120#include <string>
121#include <sstream>
122#include <cmath>
123#include <unistd.h> // for getppid
124#include <type_traits>
127
128using namespace o2::framework;
129
130bool gIsMaster = false; // a global variable indicating if this is the master workflow process
131 // (an individual DPL processor will still build the workflow but is not
132 // considered master)
133
134// ------------------------------------------------------------------
135
136// customize the completion policy
137void customize(std::vector<o2::framework::CompletionPolicy>& policies)
138{
140 // we customize the completion policy for the writer since it should stream immediately
141 policies.push_back(CompletionPolicyHelpers::defineByName("TPCDigitWriter", CompletionPolicy::CompletionOp::Consume));
142 policies.push_back(CompletionPolicyHelpers::consumeWhenAnyWithAllConditions("TPCDigitizer.*"));
143 policies.push_back(CompletionPolicyHelpers::defineByName("tpc-cluster-decoder.*", CompletionPolicy::CompletionOp::Consume));
144 policies.push_back(CompletionPolicyHelpers::defineByName("tpc-clusterer.*", CompletionPolicy::CompletionOp::Consume));
145}
146
147// ------------------------------------------------------------------
148
149// we need to add workflow options before including Framework/runDataProcessing
150void customize(std::vector<o2::framework::ConfigParamSpec>& workflowOptions)
151{
152 // for the TPC it is useful to take at most half of the available (logical) cores due to memory requirements
153 int defaultlanes = std::max(1u, std::thread::hardware_concurrency() / 2);
154 std::string laneshelp("Number of tpc processing lanes. A lane is a pipeline of algorithms.");
155 workflowOptions.push_back(
156 ConfigParamSpec{"tpc-lanes", VariantType::Int, defaultlanes, {laneshelp}});
157
158 std::string sectorshelp("List of TPC sectors, comma separated ranges, e.g. 0-3,7,9-15");
159 std::string sectorDefault = "0-" + std::to_string(o2::tpc::Sector::MAXSECTOR - 1);
160 workflowOptions.push_back(
161 ConfigParamSpec{"tpc-sectors", VariantType::String, sectorDefault.c_str(), {sectorshelp}});
162
163 std::string onlyhelp("Comma separated list of detectors to accept. Takes precedence over the skipDet option. (Default is none)");
164 workflowOptions.push_back(
165 ConfigParamSpec{"onlyDet", VariantType::String, "none", {onlyhelp}});
166
167 std::string skiphelp("Comma separate list of detectors to skip/ignore. (Default is none)");
168 workflowOptions.push_back(
169 ConfigParamSpec{"skipDet", VariantType::String, "none", {skiphelp}});
170
171 // especially useful if digit files are required later on in a simulation chain.
172 // so if --onlyDet <detlist> is set, one can then be sure to find all those digi files, especially those for which the detector
173 // hit files do not exist (e.g. because the detector was not readout during data taking)
174 std::string forceaccepthelp("Whether or not to always rely on accept/skip filters for detectors, independent of GRP content");
175 workflowOptions.push_back(
176 ConfigParamSpec{"forceSelectedDets", VariantType::Bool, false, {forceaccepthelp}});
177
178 std::string onlyctxhelp("Produce only the digitization context; Don't actually digitize");
179 workflowOptions.push_back(ConfigParamSpec{"only-context", o2::framework::VariantType::Bool, false, {onlyctxhelp}});
180
181 // we support only output type 'tracks' for the moment
182 std::string tpcrthelp("deprecated option, please connect workflows on the command line by pipe");
183 workflowOptions.push_back(
184 ConfigParamSpec{"tpc-reco-type", VariantType::String, "", {tpcrthelp}});
185
186 // Option to write TPC digits internaly, without forwarding to a special writer instance.
187 // This is useful in GRID productions with small available memory.
188 workflowOptions.push_back(ConfigParamSpec{"tpc-chunked-writer", o2::framework::VariantType::Bool, false, {"Write independent TPC digit chunks as soon as they can be flushed."}});
189 workflowOptions.push_back(ConfigParamSpec{"tpc-distortion-type", o2::framework::VariantType::Int, 0, {"Simulate distortions in the TPC (0=no distortions, 1=distortions without scaling, 2=distortions with CTP scaling)"}});
190
191 std::string simhelp("Comma separated list of simulation prefixes (for background, signal productions)");
192 workflowOptions.push_back(
193 ConfigParamSpec{"sims", VariantType::String, "o2sim", {simhelp}});
194
195 // option allowing to set parameters
196 std::string keyvaluehelp("Semicolon separated key=value strings (e.g.: 'TPC.gasDensity=1;...')");
197 workflowOptions.push_back(
198 ConfigParamSpec{"configKeyValues", VariantType::String, "", {keyvaluehelp}});
199 workflowOptions.push_back(
200 ConfigParamSpec{"configFile", VariantType::String, "", {"configuration file for configurable parameters"}});
201
202 // option to disable MC truth
203 workflowOptions.push_back(ConfigParamSpec{"disable-mc", o2::framework::VariantType::Bool, false, {"disable mc-truth"}});
204
205 // option to disable INI file writing
206 workflowOptions.push_back(ConfigParamSpec{"disable-write-ini", o2::framework::VariantType::Bool, false, {"disable INI config write"}});
207
208 // option to use/not use CCDB for TOF
209 workflowOptions.push_back(ConfigParamSpec{"use-ccdb-tof", o2::framework::VariantType::Bool, false, {"enable access to ccdb tof calibration objects"}});
210 workflowOptions.push_back(ConfigParamSpec{"ccdb-tof-sa", o2::framework::VariantType::Bool, false, {"enable access to ccdb tof calibration objects via CCDBManager (obsolete remap to use-ccdb-tof)"}});
211 workflowOptions.push_back(ConfigParamSpec{"tof-drm-bitmask", o2::framework::VariantType::Int, (int)o2::tof::CalibTOFapi::DRM_ORBIT_MISMATCH, {"bit mask of DRM critical errors"}});
212
213 // option to use/not use CCDB for FT0
214 workflowOptions.push_back(ConfigParamSpec{"use-ccdb-ft0", o2::framework::VariantType::Bool, false, {"enable access to ccdb ft0 calibration objects"}});
215
216 // option to use/not use CCDB for EMCAL
217 workflowOptions.push_back(ConfigParamSpec{"no-use-ccdb-emc", o2::framework::VariantType::Bool, false, {"Disable access to ccdb EMCAL simulation objects"}});
218
219 // option to require/not require CTP MB inputs in EMCAL
220 workflowOptions.push_back(ConfigParamSpec{"no-require-ctpinputs-emc", o2::framework::VariantType::Bool, false, {"Disable requirement of CTP min. bias inputs in EMCAL simulation"}});
221
222 // option to use or not use the Trap Simulator after digitisation (debate of digitization or reconstruction is for others)
223 workflowOptions.push_back(ConfigParamSpec{"disable-trd-trapsim", VariantType::Bool, false, {"disable the trap simulation of the TRD"}});
224 workflowOptions.push_back(ConfigParamSpec{"trd-digit-downscaling", VariantType::Int, 1, {"only keep TRD digits for every n-th trigger"}});
225
226 workflowOptions.push_back(ConfigParamSpec{"combine-devices", VariantType::Bool, false, {"combined multiple DPL worker/writer devices"}});
227
228 // to enable distribution of triggers
229 workflowOptions.push_back(ConfigParamSpec{"with-trigger", VariantType::Bool, false, {"enable distribution of CTP trigger digits"}});
230
231 // option to propagate CTP Lumi scaler counts (if >=0) into the CTP digits
232 workflowOptions.push_back(ConfigParamSpec{"store-ctp-lumi", VariantType::Float, -1.f, {"store CTP lumi scaler in CTP digits (if >= 0)"}});
234}
235
236void customize(std::vector<o2::framework::DispatchPolicy>& policies)
237{
239 // we customize all devices to dispatch data immediately
240 auto matcher = [](auto const& spec) {
241 return spec.name == "SimReader";
242 };
243 policies.push_back({"prompt-for-simreader", matcher, DispatchOp::WhenReady});
244}
245
247{
248 const auto& hbfu = o2::raw::HBFUtils::Instance();
249 const auto offset = int64_t(hbfu.getFirstIRofTF({0, hbfu.orbitFirstSampled}).orbit);
250 const auto increment = int64_t(hbfu.nHBFPerTF);
251 const auto startTime = hbfu.startTime;
252 const auto orbitFirst = hbfu.orbitFirst;
253 dh.firstTForbit = offset + increment * dh.tfCounter;
254 dh.runNumber = hbfu.runNumber;
255 dph.creation = startTime + (dh.firstTForbit - orbitFirst) * o2::constants::lhc::LHCOrbitMUS * 1.e-3;
256}
257
258void customize(std::vector<o2::framework::CallbacksPolicy>& policies)
259{
260 // we customize the time information sent in DPL headers
261 policies.push_back(o2::framework::CallbacksPolicy{
262 [](o2::framework::DeviceSpec const& spec, o2::framework::ConfigContext const& context) -> bool {
263 return true;
264 },
266 // simple linear enumeration from already updated HBFUtils (set via config key values)
269 setTimingInfoInHeaders(dh, dph);
270 LOG(info) << "Setting DPL-header firstTForbit to " << dh.firstTForbit;
271 LOG(info) << "Setting DPL-header runNumber to " << dh.runNumber;
272 LOG(info) << "Setting DPL-header timeframe creation time to " << dph.creation;
273 });
274 }} // end of struct
275 );
276}
277
278// ------------------------------------------------------------------
279
281
282// extract num TPC lanes, a lane is a streaming line of processors (digitizer-clusterizer-etc)
283// by default this will be std::max(the number of physical cores, numberofsectors)
284// as a temporary means to fully use a machine and as a way to play with different topologies
285int getNumTPCLanes(std::vector<int> const& sectors, ConfigContext const& configcontext)
286{
287 auto lanes = configcontext.options().get<int>("tpc-lanes");
288 if (lanes < 0) {
289 if (gIsMaster) {
290 LOG(fatal) << "tpc-lanes needs to be positive\n";
291 }
292 return 0;
293 }
294 // crosscheck with sectors
295 return std::min(lanes, (int)sectors.size());
296}
297
298// ------------------------------------------------------------------
299
300void initTPC(long timestamp)
301{
302 // We only want to do this for the DPL master
303 // I am not aware of an easy way to query if "I am DPL master" so
304 // using for the moment a mechanism defining/setting an environment variable
305 // with the parent ID and query inside forks if this environment variable exists
306 // (it assumes fundamentally that the master executes this function first)
307 std::stringstream streamthis;
308 std::stringstream streamparent;
309
310 streamthis << "TPCGEMINIT_PID" << getpid();
311 streamparent << "TPCGEMINIT_PID" << getppid();
312 if (getenv(streamparent.str().c_str())) {
313 LOG(debug) << "GEM ALREADY INITIALIZED ... SKIPPING HERE";
314 return;
315 }
316
317 LOG(debug) << "INITIALIZING TPC GEMAmplification";
318 setenv(streamthis.str().c_str(), "ON", 1);
319
321 cdb.setUseDefaults();
322
323 // IMPORTANT: load ParameterGEM, ParameterGas and CalPadGainFull from CCDB to correctly init GEMAmplification
324 auto& ccdbManager = o2::ccdb::BasicCCDBManager::instance();
325 ccdbManager.getSpecific<o2::tpc::ParameterGEM>(o2::tpc::CDBTypeMap.at(o2::tpc::CDBType::ParGEM), timestamp);
326 LOGP(info, "initTPC: TPC GEM param, Gas param + CalPadGainFull updated for time {}", timestamp);
327 ccdbManager.getSpecific<o2::tpc::CalPad>(o2::tpc::CDBTypeMap.at(o2::tpc::CDBType::CalPadGainFull), timestamp);
328 ccdbManager.getSpecific<o2::tpc::ParameterGas>(o2::tpc::CDBTypeMap.at(o2::tpc::CDBType::ParGas), timestamp);
329
331 o2::tpc::ParameterGas::Instance().printKeyValues(true, true);
332
333 // by invoking this constructor we make sure that a common file will be created
334 // in future we should take this from OCDB and just forward per message
335 const static auto& ampl = o2::tpc::GEMAmplification::instance();
336}
337
338// ------------------------------------------------------------------
339void publish_master_env(const char* key, const char* value)
340{
341 // publish env variables as process master
342 std::stringstream str;
343 str << "O2SIMDIGIINTERNAL_" << getpid() << "_" << key;
344 LOG(info) << "Publishing master key " << str.str();
345 setenv(str.str().c_str(), value, 1);
346}
347
348const char* get_master_env(const char* key)
349{
350 // access internal env variables published by master process
351 std::stringstream str;
352 str << "O2SIMDIGIINTERNAL_" << getppid() << "_" << key;
353 // LOG(info) << "Looking up master key " << str.str();
354 return getenv(str.str().c_str());
355}
356
357std::shared_ptr<o2::parameters::GRPObject> readGRP(std::string const& inputGRP)
358{
359 auto grp = o2::parameters::GRPObject::loadFrom(inputGRP);
360 if (!grp) {
361 LOG(error) << "This workflow needs a valid GRP file to start";
362 return nullptr;
363 }
364 if (gIsMaster) {
365 grp->print();
366 }
367 return std::shared_ptr<o2::parameters::GRPObject>(grp);
368}
369
370// ------------------------------------------------------------------
371
372// Split a given string on a separator character
373std::vector<std::string> splitString(std::string const& src, char sep)
374{
375 std::vector<std::string> fields;
376 std::string token;
377 std::istringstream ss(src);
378
379 while (std::getline(ss, token, sep)) {
380 if (!token.empty()) {
381 fields.push_back(token);
382 }
383 }
384
385 return fields;
386}
387// ------------------------------------------------------------------
388
389// Filters detectors based on a white/black list provided via the onlyDet/skipDet CLI args
391 // detlist: A character-separated list of detectors
392 // unsetVal: The value when the option is unset
393 // separator: The character that separates the list of detectors defined in option
394 // mustContain: The nature of this DetFilterer. If true, it is a white lister
395 // i.e. option defines the list of allowed detectors. If false
396 // it is a black lister i.e defines the list of disallowed detectors.
397 DetFilterer(std::string const& detlist, std::string const& unsetVal, char separator, bool doWhiteListing)
398 {
399 // option is not set, nothing to do
400 if (detlist.compare(unsetVal) == 0) {
401 return;
402 }
403
404 std::vector<std::string> tokens = splitString(detlist, separator);
405
406 // Convert a vector of strings to one of o2::detectors::DetID
407 for (auto& token : tokens) {
408 ids.emplace_back(token.c_str());
409 }
410
411 isWhiteLister = doWhiteListing;
412 }
413
414 // isSet determines if a detector list was provided
415 // against which to filter
416 bool isSet()
417 {
418 return ids.size() > 0;
419 }
420
421 // accept determines if a given detector should be accepted
423 {
424 bool found = std::find(ids.begin(), ids.end(), id) != ids.end();
425 return found == isWhiteLister;
426 }
427
428 private:
429 std::vector<o2::detectors::DetID> ids;
430 bool isWhiteLister; // true = accept only detectors in the ids vector
431};
432
433// Helper function to define a white listing DetFilterer
434DetFilterer whitelister(std::string optionVal, std::string unsetValue, char separator)
435{
436 return DetFilterer(optionVal, unsetValue, separator, true);
437}
438
439// Helper function to define a black listing DetFilterer
440DetFilterer blacklister(std::string optionVal, std::string unsetValue, char separator)
441{
442 return DetFilterer(optionVal, unsetValue, separator, false);
443}
444
445// ------------------------------------------------------------------
446
450{
451 // check if we merely construct the topology to create help options
452 // if this is the case we don't need to read from GRP
453 bool helpasked = configcontext.helpOnCommandLine();
454 bool ismaster = isMasterWorkflowDefinition(configcontext);
455 gIsMaster = ismaster;
456
457 std::string dplProcessName = whoAmI(configcontext);
458 bool isDPLinternal = isInternalDPL(dplProcessName);
459 bool isDumpWorkflow = isDumpWorkflowInvocation(configcontext);
460 bool initServices = !isDPLinternal && !isDumpWorkflow && !ismaster;
461 // Reserve one entry which will be filled with the SimReaderSpec
462 // at the end. This places the processor at the beginning of the
463 // workflow in the upper left corner of the GUI.
464 WorkflowSpec specs(1);
465 WorkflowSpec digitizerSpecs; // collecting everything producing digits
466 WorkflowSpec writerSpecs; // collecting everything writing digits to files
467
468 using namespace o2::conf;
469 ConfigurableParam::updateFromFile(configcontext.options().get<std::string>("configFile"));
470
471 // Update the (declared) parameters if changed from the command line
472 // Note: In the future this should be done only on a dedicated processor managing
473 // the parameters and then propagated automatically to all devices
474 ConfigurableParam::updateFromString(configcontext.options().get<std::string>("configKeyValues"));
475 const auto& hbfu = o2::raw::HBFUtils::Instance();
476
477 // which sim productions to overlay and digitize
478 auto simPrefixes = splitString(configcontext.options().get<std::string>("sims"), ',');
479 // First, read the GRP to detect which components need instantiations
480 std::shared_ptr<o2::parameters::GRPObject const> grp(nullptr);
481
482 // lambda to access the GRP time start
483 auto getGRPStartTime = [](o2::parameters::GRPObject const* grp) {
484 const auto GRPTIMEKEY = "GRPTIMESTART";
485 if (gIsMaster && grp) {
486 // we publish a couple of things as environment variables
487 // this saves loading from ROOT file and hence duplicated file reading and
488 // initialization of the ROOT engine in each DPL device
489 auto t = grp->getTimeStart();
490 publish_master_env(GRPTIMEKEY, std::to_string(t).c_str());
491 return t;
492 } else {
493 auto tstr = get_master_env(GRPTIMEKEY);
494 if (!tstr) {
495 LOG(fatal) << "Expected env value not found";
496 }
497 // LOG(info) << "Found entry " << tstr;
498 return boost::lexical_cast<uint64_t>(tstr);
499 }
500 };
501
502 if (!helpasked) {
503 if (gIsMaster) {
504 grp = readGRP(simPrefixes[0]);
505 if (!grp) {
506 return WorkflowSpec{};
507 }
508 getGRPStartTime(grp.get());
509 }
510 if (!hbfu.startTime) { // HBFUtils.startTime was not set from the command line, set it from GRP
511 hbfu.setValue("HBFUtils.startTime", std::to_string(getGRPStartTime(grp.get())));
512 }
513 }
514
515 auto grpfile = o2::base::NameConf::getGRPFileName(simPrefixes[0]);
516 if (initServices) {
517 // init on a high level, the time for the CCDB queries
518 // we expect that digitizers do not play with the manager themselves
519 // this will only be needed until digitizers take CCDB objects via DPL mechanism
520
521 // fix the timestamp for CCDB manager in the same way as for DPL-CCDB-fetcher
524 setTimingInfoInHeaders(dh, dph);
525 LOG(info) << "Setting timestamp of BasicCCDBManager to " << dph.creation;
527 // activate caching
529 // this is asking the manager to check validity only locally - no further query to server done
531 }
532 // update the digitization configuration with the right geometry file
533 // we take the geometry from the first simPrefix (could actually check if they are
534 // all compatible)
535 ConfigurableParam::setValue("DigiParams.digitizationgeometry_prefix", simPrefixes[0]);
536 ConfigurableParam::setValue("DigiParams.grpfile", grpfile);
537
538 LOG(info) << "MC-TRUTH " << !configcontext.options().get<bool>("disable-mc");
539 bool mctruth = !configcontext.options().get<bool>("disable-mc");
540 ConfigurableParam::setValue("DigiParams", "mctruth", mctruth);
541
542 // write the configuration used for the digitizer workflow
543 // (In the case, in which we call multiple processes to do digitization,
544 // only one of them should write this file ... but take the complete configKeyValue line)
545 if (ismaster) {
546 if (!configcontext.options().get<bool>("disable-write-ini")) {
548 }
549 }
550
551 // onlyDet takes precedence on skipDet
552 DetFilterer filterers[2] = {
553 whitelister(configcontext.options().get<std::string>("onlyDet"), "none", ','),
554 blacklister(configcontext.options().get<std::string>("skipDet"), "none", ',')};
555
556 auto accept = [&configcontext, &filterers](o2::detectors::DetID id) {
557 for (auto& f : filterers) {
558 if (f.isSet()) {
559 return f.accept(id);
560 }
561 }
562 // accept all if neither onlyDet/skipDet are provided
563 return true;
564 };
565
566 // lambda to extract detectors which are enabled in the workflow
567 // will complain if user gave wrong input in construction of DetID
568 auto isEnabled = [&configcontext, &filterers, accept, grp, helpasked](o2::detectors::DetID id) {
569 auto isInGRPReadout = [grp](o2::detectors::DetID id) {
570 std::stringstream str;
571 str << "GRPDETKEY_" << id.getName();
572 if (gIsMaster and grp.get() != nullptr) {
573 auto ok = grp->isDetReadOut(id);
574 if (ok) {
575 publish_master_env(str.str().c_str(), "ON");
576 }
577 return ok;
578 } else {
579 // we should have published important GRP info as
580 // environment variables in order to not having to read GRP via ROOT
581 // in all the processes
582 return get_master_env(str.str().c_str()) != nullptr;
583 }
584 };
585
586 if (helpasked) {
587 return true;
588 }
589 if (configcontext.options().get<bool>("only-context")) {
590 // no detector necessary if we are asked to produce only the digitization context
591 return false;
592 }
593 auto accepted = accept(id);
594
595 // always comply with the filter choice?
596 auto forceAccepted = configcontext.options().get<bool>("forceSelectedDets");
597 bool is_ingrp = isInGRPReadout(id);
598 // final decision on whether or not this detector will be digitized
599 auto isRun = accepted && (forceAccepted || is_ingrp);
600 if (gIsMaster) {
601 LOG(info) << id.getName()
602 << " is in grp? " << (is_ingrp ? "yes" : "no") << ";"
603 << " is taken although not in grp? " << (!is_ingrp && (accepted && forceAccepted) ? "yes" : "no") << ";"
604 << " is skipped? " << (!accepted ? "yes" : "no") << ";"
605 << " is run? " << (isRun ? "yes" : "no");
606 }
607 return isRun;
608 };
609
610 std::vector<o2::detectors::DetID> detList; // list of participating detectors
611
612 // keeps track of which tpc sectors to process
613 std::vector<int> tpcsectors;
614
615 if (isEnabled(o2::detectors::DetID::TPC)) {
616 if (!helpasked && ismaster) {
617 initTPC(hbfu.startTime);
618 }
619
620 tpcsectors = o2::RangeTokenizer::tokenize<int>(configcontext.options().get<std::string>("tpc-sectors"));
621 // only one lane for the help printout
622 auto lanes = helpasked ? 1 : getNumTPCLanes(tpcsectors, configcontext);
623 detList.emplace_back(o2::detectors::DetID::TPC);
624
625 auto internalwrite = configcontext.options().get<bool>("tpc-chunked-writer");
626 auto distortionType = configcontext.options().get<int>("tpc-distortion-type");
627 WorkflowSpec tpcPipelines = o2::tpc::getTPCDigitizerSpec(lanes, tpcsectors, mctruth, internalwrite, distortionType);
628 specs.insert(specs.end(), tpcPipelines.begin(), tpcPipelines.end());
629
630 if (configcontext.options().get<std::string>("tpc-reco-type").empty() == false) {
631 throw std::runtime_error("option 'tpc-reco-type' is deprecated, please connect workflows on the command line by pipe");
632 }
633 if (!internalwrite) {
634 // for writing digits to disc
635 specs.emplace_back(o2::tpc::getTPCDigitRootWriterSpec(tpcsectors, mctruth));
636 }
637 }
638
639 // first 36 channels are reserved for the TPC
640 const int firstOtherChannel = 36;
641 int fanoutsize = firstOtherChannel;
642
643 // the ITS part
644 if (isEnabled(o2::detectors::DetID::ITS)) {
645 detList.emplace_back(o2::detectors::DetID::ITS);
647 // connect the ITS digitization
648 digitizerSpecs.emplace_back(o2::itsmft::getITSDigitizerSpec(fanoutsize++, mctruth, doStag));
649 // connect ITS digit writer
650 writerSpecs.emplace_back(o2::itsmft::getITSDigitWriterSpec(mctruth, doStag));
651 }
652
653#ifdef ENABLE_UPGRADES
654 // the ITS3 part
655 if (isEnabled(o2::detectors::DetID::IT3)) {
656 detList.emplace_back(o2::detectors::DetID::IT3);
658 // connect the ITS digitization
659 specs.emplace_back(o2::its3::getITS3DigitizerSpec(fanoutsize++, mctruth, doStag));
660 // // connect ITS digit writer
661 specs.emplace_back(o2::its3::getITS3DigitWriterSpec(mctruth, doStag));
662 }
663
664 // the ALICE 3 TRK part
665 if (isEnabled(o2::detectors::DetID::TRK)) {
666 detList.emplace_back(o2::detectors::DetID::TRK);
667 // connect the ALICE 3 TRK digitization
668 specs.emplace_back(o2::trkft3::getTRKDigitizerSpec(fanoutsize++, mctruth));
669 // connect the ALICE 3 TRK digit writer
670 specs.emplace_back(o2::trk::getTRKDigitWriterSpec(mctruth));
671 }
672
673 // the ALICE 3 FT3 part
674 if (isEnabled(o2::detectors::DetID::FT3)) {
675 detList.emplace_back(o2::detectors::DetID::FT3);
676 specs.emplace_back(o2::trkft3::getFT3DigitizerSpec(fanoutsize++, mctruth));
677 specs.emplace_back(o2::trk::getFT3DigitWriterSpec(mctruth));
678 }
679
680 // the ALICE 3 IOTOF part
681 if (isEnabled(o2::detectors::DetID::TF3)) {
682 detList.emplace_back(o2::detectors::DetID::TF3);
683 // connect the ALICE 3 IOTOF digitization
684 specs.emplace_back(o2::iotof::getIOTOFDigitizerSpec(fanoutsize++, mctruth));
685 // connect the ALICE 3 IOTOF digit writer
686 specs.emplace_back(o2::iotof::getIOTOFDigitWriterSpec(mctruth));
687 }
688#endif
689
690 // the MFT part
691 if (isEnabled(o2::detectors::DetID::MFT)) {
692 detList.emplace_back(o2::detectors::DetID::MFT);
694 // connect the MFT digitization
695 digitizerSpecs.emplace_back(o2::itsmft::getMFTDigitizerSpec(fanoutsize++, mctruth, doStag));
696 // connect MFT digit writer
697 writerSpecs.emplace_back(o2::itsmft::getMFTDigitWriterSpec(mctruth, doStag));
698 }
699
700 // the TOF part
701 if (isEnabled(o2::detectors::DetID::TOF)) {
702 auto useCCDB = configcontext.options().get<bool>("use-ccdb-tof");
703 useCCDB |= configcontext.options().get<bool>("ccdb-tof-sa");
704 auto ccdb_url_tof = o2::base::NameConf::getCCDBServer();
705 auto timestamp = o2::raw::HBFUtils::Instance().startTime / 1000;
706 detList.emplace_back(o2::detectors::DetID::TOF);
707 auto maskDRM = (uint32_t)configcontext.options().get<int>("tof-drm-bitmask");
708 // connect the TOF digitization
709 // printf("TOF Setting: use-ccdb = %d ---- ccdb url=%s ---- timestamp=%ld\n", useCCDB, ccdb_url_tof.c_str(), timestamp);
710
711 digitizerSpecs.emplace_back(o2::tof::getTOFDigitizerSpec(fanoutsize++, useCCDB, mctruth, ccdb_url_tof.c_str(), timestamp, maskDRM));
712 // add TOF digit writer
713 writerSpecs.emplace_back(o2::tof::getTOFDigitWriterSpec(mctruth));
714 }
715
716 // the FT0 part
717 if (isEnabled(o2::detectors::DetID::FT0)) {
718 auto useCCDB = configcontext.options().get<bool>("use-ccdb-ft0");
719 auto timestamp = o2::raw::HBFUtils::Instance().startTime;
720 detList.emplace_back(o2::detectors::DetID::FT0);
721 // connect the FT0 digitization
722 specs.emplace_back(o2::ft0::getFT0DigitizerSpec(fanoutsize++, mctruth, useCCDB));
723 // connect the FIT digit writer
724 writerSpecs.emplace_back(o2::ft0::getFT0DigitWriterSpec(mctruth));
725 }
726
727 // the FV0 part
728 if (isEnabled(o2::detectors::DetID::FV0)) {
729 detList.emplace_back(o2::detectors::DetID::FV0);
730 // connect the FV0 digitization
731 digitizerSpecs.emplace_back(o2::fv0::getFV0DigitizerSpec(fanoutsize++, mctruth));
732 // connect the FV0 digit writer
733 writerSpecs.emplace_back(o2::fv0::getFV0DigitWriterSpec(mctruth));
734 }
735
736 // the EMCal part
737 if (isEnabled(o2::detectors::DetID::EMC)) {
738 auto useCCDB = !configcontext.options().get<bool>("no-use-ccdb-emc");
739 bool requireCTPInputs = !configcontext.options().get<bool>("no-require-ctpinputs-emc");
740 detList.emplace_back(o2::detectors::DetID::EMC);
741 // connect the EMCal digitization
742 digitizerSpecs.emplace_back(o2::emcal::getEMCALDigitizerSpec(fanoutsize++, requireCTPInputs, detList, mctruth, useCCDB));
743 // connect the EMCal digit writer
744 writerSpecs.emplace_back(o2::emcal::getEMCALDigitWriterSpec(mctruth));
745 }
746
747 // add HMPID
748 if (isEnabled(o2::detectors::DetID::HMP)) {
749 detList.emplace_back(o2::detectors::DetID::HMP);
750 // connect the HMP digitization
751 digitizerSpecs.emplace_back(o2::hmpid::getHMPIDDigitizerSpec(fanoutsize++, mctruth));
752 // connect the HMP digit writer
753 writerSpecs.emplace_back(o2::hmpid::getHMPIDDigitWriterSpec(mctruth));
754 }
755
756 // add ZDC
757 if (isEnabled(o2::detectors::DetID::ZDC)) {
758 detList.emplace_back(o2::detectors::DetID::ZDC);
759 // connect the ZDC digitization
760 digitizerSpecs.emplace_back(o2::zdc::getZDCDigitizerSpec(fanoutsize++, mctruth));
761 // connect the ZDC digit writer
762 writerSpecs.emplace_back(o2::zdc::getZDCDigitWriterDPLSpec(mctruth, true));
763 }
764
765 // add TRD
766 if (isEnabled(o2::detectors::DetID::TRD)) {
767 detList.emplace_back(o2::detectors::DetID::TRD);
768 // connect the TRD digitization
769 specs.emplace_back(o2::trd::getTRDDigitizerSpec(fanoutsize++, mctruth));
770 auto disableTrapSim = configcontext.options().get<bool>("disable-trd-trapsim");
771 auto trdDigitDownscaling = configcontext.options().get<int>("trd-digit-downscaling");
772 if (!disableTrapSim) {
773 // connect the TRD TRAP simulator
774 specs.emplace_back(o2::trd::getTRDTrapSimulatorSpec(mctruth, trdDigitDownscaling));
775 // connect to the device to write out the tracklets.
776 specs.emplace_back(o2::trd::getTRDTrackletWriterSpec(mctruth));
777 // connect the TRD digit writer expecting input from TRAP simulation
778 specs.emplace_back(o2::trd::getTRDDigitWriterSpec(mctruth, false));
779 } else {
780 // connect the TRD digit writer expecting input from TRD digitizer
781 specs.emplace_back(o2::trd::getTRDDigitWriterSpec(mctruth, true));
782 }
783 }
784
785 // add MUON MCH
786 if (isEnabled(o2::detectors::DetID::MCH)) {
787 detList.emplace_back(o2::detectors::DetID::MCH);
788 // connect the MUON MCH digitization
789 digitizerSpecs.emplace_back(o2::mch::getMCHDigitizerSpec(fanoutsize++, mctruth));
790 // connect the MUON MCH digit writer
791 writerSpecs.emplace_back(o2::mch::getMCHDigitWriterSpec(mctruth));
792 }
793
794 // add MID
795 if (isEnabled(o2::detectors::DetID::MID)) {
796 detList.emplace_back(o2::detectors::DetID::MID);
797 // connect the MID digitization
798 digitizerSpecs.emplace_back(o2::mid::getMIDDigitizerSpec(fanoutsize++, mctruth));
799 // connect the MID digit writer
800 writerSpecs.emplace_back(o2::mid::getMIDDigitWriterSpec(mctruth));
801 }
802
803 // add FDD
804 if (isEnabled(o2::detectors::DetID::FDD)) {
805 detList.emplace_back(o2::detectors::DetID::FDD);
806 // connect the FDD digitization
807 digitizerSpecs.emplace_back(o2::fdd::getFDDDigitizerSpec(fanoutsize++, mctruth));
808 // connect the FDD digit writer
809 writerSpecs.emplace_back(o2::fdd::getFDDDigitWriterSpec(mctruth));
810 }
811
812 // the PHOS part
813 if (isEnabled(o2::detectors::DetID::PHS)) {
814 detList.emplace_back(o2::detectors::DetID::PHS);
815 // connect the PHOS digitization
816 digitizerSpecs.emplace_back(o2::phos::getPHOSDigitizerSpec(fanoutsize++, mctruth));
817 // add PHOS writer
818 writerSpecs.emplace_back(o2::phos::getPHOSDigitWriterSpec(mctruth));
819 }
820
821 // the CPV part
822 if (isEnabled(o2::detectors::DetID::CPV)) {
823 detList.emplace_back(o2::detectors::DetID::CPV);
824 // connect the CPV digitization
825 digitizerSpecs.emplace_back(o2::cpv::getCPVDigitizerSpec(fanoutsize++, mctruth));
826 // add PHOS writer
827 writerSpecs.emplace_back(o2::cpv::getCPVDigitWriterSpec(mctruth));
828 }
829 // the CTP part
830 if (isEnabled(o2::detectors::DetID::CTP)) {
831 detList.emplace_back(o2::detectors::DetID::CTP);
832 float lumiScaler = configcontext.options().get<float>("store-ctp-lumi");
833 // connect the CTP digitization
834 specs.emplace_back(o2::ctp::getCTPDigitizerSpec(fanoutsize++, detList, lumiScaler));
835 // connect the CTP digit writer
836 specs.emplace_back(o2::ctp::getDigitWriterSpec(lumiScaler >= 0));
837 }
838 // GRP updater: must come after all detectors since requires their list
839 if (!configcontext.options().get<bool>("only-context")) {
840 writerSpecs.emplace_back(o2::parameters::getGRPUpdaterSpec(simPrefixes[0], detList));
841 }
842
843 bool combine = configcontext.options().get<bool>("combine-devices");
844 if (!combine) {
845 for (auto& s : digitizerSpecs) {
846 specs.push_back(s);
847 }
848 for (auto& s : writerSpecs) {
849 specs.push_back(s);
850 }
851 } else {
852 std::vector<DataProcessorSpec> remaining;
853 // Skip combined device creation for an empty collection as specCombiner would
854 // return a device with no sub-tasks. This problem was discovered with run 537401 where both FT0,CTP detectors
855 // bypass digitizerSpecs and FV0 and EMC are not included in the run.
856 if (!digitizerSpecs.empty()) {
857 specs.push_back(specCombiner("Digitizations", digitizerSpecs, remaining));
858 }
859 if (!writerSpecs.empty()) {
860 specs.push_back(specCombiner("Writers", writerSpecs, remaining));
861 }
862 for (auto& s : remaining) {
863 specs.push_back(s);
864 }
865 }
866
867 // For reasons of offering homegenous behaviour (consistent options to outside scripts),
868 // we require that at least one of the devices above listens to the DPL CCDB fetcher.
869 // Verify this or insert a dummy channel in one of the devices. (This cannot be done in the SimReader
870 // as the SimReader is the source device injecting the timing information).
871 // In future this code can serve as a check that all digitizers access CCDB via the DPL fetcher.
872 bool haveCCDBInputSpec = false;
873 for (auto spec : specs) {
874 for (auto in : spec.inputs) {
875 if (in.lifetime == Lifetime::Condition) {
876 haveCCDBInputSpec = true;
877 break;
878 }
879 }
880 }
881 if (!haveCCDBInputSpec && specs.size() > 0) {
882 LOG(info) << "No one uses DPL CCDB .. injecting a dummy CCDB query into " << specs.back().name;
883 specs.back().inputs.emplace_back("_dummyOrbitReset", "CTP", "ORBITRESET", 0, Lifetime::Condition,
884 ccdbParamSpec("CTP/Calib/OrbitReset"));
885 }
886
887 // The SIM Reader. NEEDS TO BE LAST
888 bool withTrigger = configcontext.options().get<bool>("with-trigger");
889 LOG(info) << " TRIGGER " << withTrigger;
890 specs[0] = o2::steer::getSimReaderSpec({firstOtherChannel, fanoutsize}, simPrefixes, tpcsectors, withTrigger);
891 return specs;
892}
Simple interface to the CDB manager.
Class to use TOF calibration (decalibration, calibration)
std::vector< std::shared_ptr< arrow::Field > > fields
std::ostringstream debug
Definition of the GEM amplification.
Header of the General Run Parameters object.
char error[CURL_ERROR_SIZE]
Definition of the Names Generator class.
Helper function to tokenize sequences and ranges of integral numbers.
std::vector< std::string > splitString(std::string const &src, char sep)
const char * get_master_env(const char *key)
int getNumTPCLanes(std::vector< int > const &sectors, ConfigContext const &configcontext)
DetFilterer whitelister(std::string optionVal, std::string unsetValue, char separator)
WorkflowSpec defineDataProcessing(ConfigContext const &configcontext)
This function hooks up the the workflow specifications into the DPL driver.
void setTimingInfoInHeaders(o2::header::DataHeader &dh, o2::framework::DataProcessingHeader &dph)
DetFilterer blacklister(std::string optionVal, std::string unsetValue, char separator)
void customize(std::vector< o2::framework::CompletionPolicy > &policies)
void initTPC(long timestamp)
void publish_master_env(const char *key, const char *value)
std::shared_ptr< o2::parameters::GRPObject > readGRP(std::string const &inputGRP)
StringRef key
static std::string getGRPFileName(const std::string_view prefix=STANDARDSIMPREFIX)
Definition NameConf.cxx:59
static constexpr std::string_view DIGITIZATIONCONFIGFILE
Definition NameConf.h:89
static std::string getCCDBServer()
Definition NameConf.cxx:123
static BasicCCDBManager & instance()
void setCaching(bool v)
disable or enable caching
void setTimestamp(long t)
set timestamp cache for all queries
void setLocalObjectValidityChecking(bool v=true)
set the flag to check object validity before CCDB query
void printKeyValues(bool showProv=true, bool useLogger=false, bool withPadding=true, bool showHash=true) const final
static void writeINI(std::string const &filename, std::string const &keyOnly="")
Static class with identifiers, bitmasks and names for ALICE detectors.
Definition DetID.h:60
ConfigParamRegistry & options() const
static GRPObject * loadFrom(const std::string &grpFileName="")
static CDBInterface & instance()
static GEMAmplification & instance()
Default constructor.
static constexpr int MAXSECTOR
Definition Sector.h:44
GLenum src
Definition glcorearb.h:1767
GLuint * ids
Definition glcorearb.h:647
GLdouble f
Definition glcorearb.h:310
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLintptr offset
Definition glcorearb.h:660
GLuint id
Definition glcorearb.h:650
DataProcessorSpec getCPVDigitizerSpec(int channel, bool mctruth)
Create new digitizer spec.
o2::framework::DataProcessorSpec getCPVDigitWriterSpec(bool mctruth=true)
framework::DataProcessorSpec getDigitWriterSpec(bool raw=true)
o2::framework::DataProcessorSpec getCTPDigitizerSpec(int channel, std::vector< o2::detectors::DetID > &detList, float ctpLumiScaler, bool mctruth)
o2::framework::DataProcessorSpec getEMCALDigitWriterSpec(bool mctruth=true)
Create new digits writer spec.
o2::framework::DataProcessorSpec getEMCALDigitizerSpec(int channel, bool requireCTPInput, const std::vector< o2::detectors::DetID > &detList, bool mctruth=true, bool useccdb=true)
Create new digitizer spec.
o2::framework::DataProcessorSpec getFDDDigitizerSpec(int channel, bool mctruth)
o2::framework::DataProcessorSpec getFDDDigitWriterSpec(bool mctruth=true, bool trigInp=true)
Defining ITS Vertex explicitly as messageable.
Definition Cartesian.h:288
o2::framework::DataProcessorSpec specCombiner(std::string const &name, std::vector< DataProcessorSpec > const &speccollection, std::vector< DataProcessorSpec > &remaining)
bool isInternalDPL(std::string const &name)
bool isMasterWorkflowDefinition(ConfigContext const &configcontext)
std::vector< ConfigParamSpec > ccdbParamSpec(std::string const &path, int runDependent, std::vector< CCDBMetadata > metadata={}, int qrate=0)
bool isDumpWorkflowInvocation(ConfigContext const &configcontext)
std::string whoAmI(ConfigContext const &configcontext)
std::vector< DataProcessorSpec > WorkflowSpec
o2::framework::DataProcessorSpec getFT0DigitizerSpec(int channel, bool mctruth, bool useCCDB)
framework::DataProcessorSpec getFT0DigitWriterSpec(bool mctruth=true, bool trigInp=true)
create a processor spec
o2::framework::DataProcessorSpec getFV0DigitizerSpec(int channel, bool mctruth)
framework::DataProcessorSpec getFV0DigitWriterSpec(bool mctruth=true, bool trigInp=true)
create a processor spec
o2::framework::DataProcessorSpec getHMPIDDigitWriterSpec(bool mctruth=true)
o2::framework::DataProcessorSpec getHMPIDDigitizerSpec(int channel, bool mctruth)
o2::framework::DataProcessorSpec getIOTOFDigitWriterSpec(bool mctruth=true, bool dec=false, bool calib=false)
o2::framework::DataProcessorSpec getIOTOFDigitizerSpec(int channel, bool mctruth)
DataProcessorSpec getITS3DigitizerSpec(int channel, bool mctruth, bool doStag)
o2::framework::DataProcessorSpec getITS3DigitWriterSpec(bool mctruth=true, bool doStag=false, bool dec=false, bool calib=false)
o2::framework::DataProcessorSpec getMFTDigitWriterSpec(bool mctruth=true, bool doStag=false, bool dec=false, bool calib=false)
o2::framework::DataProcessorSpec getITSDigitWriterSpec(bool mctruth=true, bool doStag=false, bool dec=false, bool calib=false)
DataProcessorSpec getMFTDigitizerSpec(int channel, bool mctruth, bool doStag)
DataProcessorSpec getITSDigitizerSpec(int channel, bool mctruth, bool doStag)
o2::framework::DataProcessorSpec getMCHDigitWriterSpec(bool mctruth)
o2::framework::DataProcessorSpec getMCHDigitizerSpec(int channel, bool mctruth)
o2::framework::DataProcessorSpec getMIDDigitWriterSpec(bool mctruth)
o2::framework::DataProcessorSpec getMIDDigitizerSpec(int channel, bool mctruth)
o2::framework::DataProcessorSpec getGRPUpdaterSpec(const std::string &prefix, const std::vector< o2::detectors::DetID > &detList)
create the processor spec
o2::framework::DataProcessorSpec getPHOSDigitWriterSpec(bool mctruth)
DataProcessorSpec getPHOSDigitizerSpec(int channel, bool mctruth)
Create new digitizer spec.
DataProcessorSpec getSimReaderSpec(SubspecRange range, const std::vector< std::string > &simprefixes, const std::vector< int > &tpcsectors, bool withTrigger)
o2::framework::DataProcessorSpec getTOFDigitWriterSpec(bool useMC=1, bool writeErr=0)
DataProcessorSpec getTOFDigitizerSpec(int channel, bool useCCDB, bool mctruth, std::string ccdb_url, int timestamp, uint32_t maskDRM)
const std::unordered_map< CDBType, const std::string > CDBTypeMap
Storage name in CCDB for each calibration and parameter type.
Definition CDBTypes.h:98
o2::framework::DataProcessorSpec getTPCDigitRootWriterSpec(std::vector< int > const &laneConfiguration, bool mctruth)
o2::framework::DataProcessorSpec getTPCDigitizerSpec(int channel, bool writeGRP, bool mctruth, bool internalwriter, int distortionType)
@ CalPadGainFull
Full pad gain calibration.
@ ParGas
Parameter for Gas.
o2::framework::DataProcessorSpec getTRDDigitWriterSpec(bool mctruth=true, bool inpFromDigitizer=true)
o2::framework::DataProcessorSpec getTRDDigitizerSpec(int channel, bool mctruth=true)
o2::framework::DataProcessorSpec getTRDTrapSimulatorSpec(bool useMC, int digitDownscaling)
o2::framework::DataProcessorSpec getTRDTrackletWriterSpec(bool useMC)
o2::framework::DataProcessorSpec getFT3DigitWriterSpec(bool mctruth=true, bool dec=false, bool calib=false)
o2::framework::DataProcessorSpec getTRKDigitWriterSpec(bool mctruth=true, bool dec=false, bool calib=false)
DataProcessorSpec getTRKDigitizerSpec(int channel, bool mctruth)
DataProcessorSpec getFT3DigitizerSpec(int channel, bool mctruth)
o2::framework::DataProcessorSpec getZDCDigitizerSpec(int channel, bool mctruth)
framework::DataProcessorSpec getZDCDigitWriterDPLSpec(bool mctruth, bool simVersion)
create a processor spec
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
DetFilterer(std::string const &detlist, std::string const &unsetVal, char separator, bool doWhiteListing)
bool accept(o2::detectors::DetID id)
static CompletionPolicy defineByName(std::string const &name, CompletionPolicy::CompletionOp op)
static CompletionPolicy consumeWhenAnyWithAllConditions(const char *name, CompletionPolicy::Matcher matcher)
When any of the parts of the record have been received, consume them.
the main header struct
Definition DataHeader.h:623
TFCounterType tfCounter
Definition DataHeader.h:684
TForbitType firstTForbit
Definition DataHeader.h:679
RunNumberType runNumber
Definition DataHeader.h:689
static bool isITSStaggeringEnabled(o2::framework::ConfigContext const &cfgc)
static bool isMFTStaggeringEnabled(o2::framework::ConfigContext const &cfgc)
static void addConfigOption(std::vector< o2::framework::ConfigParamSpec > &opts)
uint64_t startTime
absolute time in ms corresponding to the start of the MC run
Definition HBFUtils.h:144
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
const std::string str