From 27ee5223f478b6a320ffcaa2c1f4678da435e566 Mon Sep 17 00:00:00 2001 From: wiechula Date: Tue, 17 Dec 2019 22:41:23 +0100 Subject: [PATCH 01/24] Implement missing updateCRU function call --- .../TPC/calibration/include/TPCCalibration/CalibRawBase.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CalibRawBase.h b/Detectors/TPC/calibration/include/TPCCalibration/CalibRawBase.h index 2e632bc0a20e2..5510714581cb5 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/CalibRawBase.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/CalibRawBase.h @@ -650,6 +650,11 @@ inline Int_t CalibRawBase::update(const PadROCPos& padROCPos, const CRU& cru, co } int rowOffset = 0; + const int nRowIROC = mMapper.getNumberOfRowsROC(0); + const auto& regionInfo = mMapper.getPadRegionInfo(cru.region()); + const int globalRow = row + (cru.isOROC()) * nRowIROC; + const int rowInRegion = globalRow - regionInfo.getGlobalRowOffset(); + switch (mPadSubset) { case PadSubset::ROC: { break; @@ -659,7 +664,6 @@ inline Int_t CalibRawBase::update(const PadROCPos& padROCPos, const CRU& cru, co } case PadSubset::Partition: { const PartitionInfo& partInfo = mMapper.getPartitionInfo(cru.partition()); - const int nRowIROC = mMapper.getNumberOfRowsROC(0); rowOffset = (cru.isOROC()) * nRowIROC; rowOffset -= partInfo.getGlobalRowOffset(); break; @@ -675,7 +679,7 @@ inline Int_t CalibRawBase::update(const PadROCPos& padROCPos, const CRU& cru, co const float signal = float(data[i]); //printf("Call update: %d, %d (%d), %d, %d, %.3f -- cru: %03d, reg: %02d -- FEC: %02d, Chip: %02d, Chn: %02d\n", roc, row, rowOffset, pad, timeBin, signal, cru.number(), cru.region(), fecInfo.getIndex(), fecInfo.getSampaChip(), fecInfo.getSampaChannel()); // TODO: To be implemented - //updateCRU(cru, row, pad, timeBin, signal); + updateCRU(cru, rowInRegion, pad, timeBin, signal); updateROC(roc, row + rowOffset, pad, timeBin, signal); ++timeBin; } From 3a5813756a943295ecdaa14daed74097e275ec3c Mon Sep 17 00:00:00 2001 From: wiechula Date: Thu, 16 Jan 2020 10:30:05 -0500 Subject: [PATCH 02/24] use batch mode for DPL --- Detectors/TPC/reconstruction/macro/runRecoFromDigits.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Detectors/TPC/reconstruction/macro/runRecoFromDigits.sh b/Detectors/TPC/reconstruction/macro/runRecoFromDigits.sh index 59d3c7aa09e4b..4ad3028f7ae17 100755 --- a/Detectors/TPC/reconstruction/macro/runRecoFromDigits.sh +++ b/Detectors/TPC/reconstruction/macro/runRecoFromDigits.sh @@ -48,6 +48,6 @@ done # ===| check for required arguments |=========================================== # ===| command building and execution |========================================= -cmd="o2-tpc-reco-workflow --infile $inputFile --disable-mc --configKeyValues 'TPCHwClusterer.peakChargeThreshold=$qMaxThreshold;TPCHwClusterer.isContinuousReadout=0' --output-type $outputType" +cmd="o2-tpc-reco-workflow -b --infile $inputFile --disable-mc --configKeyValues 'TPCHwClusterer.peakChargeThreshold=$qMaxThreshold;TPCHwClusterer.isContinuousReadout=0' --output-type $outputType" echo $cmd eval $cmd From ab06be3bf417f243be4f6dfe3b27600e6349527e Mon Sep 17 00:00:00 2001 From: wiechula Date: Thu, 16 Jan 2020 10:30:31 -0500 Subject: [PATCH 03/24] add missing pragma --- DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h | 1 + 1 file changed, 1 insertion(+) diff --git a/DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h b/DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h index e5b6456f88c65..183ef90122d62 100644 --- a/DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h +++ b/DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h @@ -39,5 +39,6 @@ #pragma link C++ class o2::tpc::CompressedClustersCounters + ; #pragma link C++ class o2::tpc::CompressedClustersPtrs_helper < o2::tpc::CompressedClustersCounters> + ; #pragma link C++ class o2::tpc::CompressedClusters + ; +#pragma link C++ enum o2::tpc::StatisticsType; #endif From 20fbf35691779643a6bb0b68c17aad8845a37d36 Mon Sep 17 00:00:00 2001 From: wiechula Date: Thu, 16 Jan 2020 10:32:02 -0500 Subject: [PATCH 04/24] add missing pragma --- Detectors/TPC/calibration/src/TPCCalibrationLinkDef.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Detectors/TPC/calibration/src/TPCCalibrationLinkDef.h b/Detectors/TPC/calibration/src/TPCCalibrationLinkDef.h index 4ba4d6444caae..4125bce2d67ca 100644 --- a/Detectors/TPC/calibration/src/TPCCalibrationLinkDef.h +++ b/Detectors/TPC/calibration/src/TPCCalibrationLinkDef.h @@ -17,8 +17,10 @@ #pragma link C++ class o2::tpc::CalibRawBase; #pragma link C++ class o2::tpc::CalibPedestal; #pragma link C++ class o2::tpc::CalibPedestalParam +; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::tpc::CalibPedestalParam> + ; #pragma link C++ class o2::tpc::CalibPulser; #pragma link C++ class o2::tpc::CalibPulserParam +; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::tpc::CalibPulserParam> + ; #pragma link C++ class o2::tpc::CalibTreeDump; #pragma link C++ class o2::tpc::DigitDump; #pragma link C++ class o2::tpc::CalibPadGainTracks; From 9c44769dff6282d7a098bd7bc3381dc6bb8d6b4e Mon Sep 17 00:00:00 2001 From: wiechula Date: Thu, 16 Jan 2020 10:33:28 -0500 Subject: [PATCH 05/24] allow users to set a callback (move to public) --- .../reconstruction/include/TPCReconstruction/RawReaderCRU.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/RawReaderCRU.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/RawReaderCRU.h index 86c271e0906f3..fe606c3b44834 100644 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/RawReaderCRU.h +++ b/Detectors/TPC/reconstruction/include/TPCReconstruction/RawReaderCRU.h @@ -600,6 +600,9 @@ class RawReaderCRU /// copy single events to another file void copyEvents(const std::vector& eventNumbers, std::string outputDirectory, std::ios_base::openmode mode = std::ios_base::openmode(0)); + /// run a data filling callback function + void runADCDataCallback(const ADCRawData& rawData); + //=========================================================================== //===| Nested helper classes |=============================================== // @@ -707,9 +710,6 @@ class RawReaderCRU /// fill adc data to output map void fillADCdataMap(const ADCRawData& rawData); - /// run a data filling callback function - void runADCDataCallback(const ADCRawData& rawData); - ClassDefNV(RawReaderCRU, 0); // raw reader class }; // class RawReaderCRU From 7b2d703e279af3ebc41f8ab5bc748da4779651a0 Mon Sep 17 00:00:00 2001 From: wiechula Date: Thu, 16 Jan 2020 10:36:02 -0500 Subject: [PATCH 06/24] Different options for dumping data to file Implements options for dumping data to file * only the actual calibration output * the full calibration class --- .../include/TPCCalibration/CalibPedestal.h | 2 +- .../include/TPCCalibration/CalibPulser.h | 2 +- .../include/TPCCalibration/CalibRawBase.h | 8 +++- .../TPC/calibration/src/CalibPedestal.cxx | 12 +++-- Detectors/TPC/calibration/src/CalibPulser.cxx | 46 ++++++++++--------- 5 files changed, 42 insertions(+), 28 deletions(-) diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CalibPedestal.h b/Detectors/TPC/calibration/include/TPCCalibration/CalibPedestal.h index 6c0198aa4ae61..f285f93a8e494 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/CalibPedestal.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/CalibPedestal.h @@ -109,7 +109,7 @@ class CalibPedestal : public CalibRawBase StatisticsType getStatisticsType() const { return mStatisticsType; } /// Dump the relevant data to file - void dumpToFile(const std::string filename) final; + void dumpToFile(const std::string filename, uint32_t type = 0) final; /// Dummy end event void endEvent() final{}; diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CalibPulser.h b/Detectors/TPC/calibration/include/TPCCalibration/CalibPulser.h index 55cad2d3d319f..4ea939dafbf97 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/CalibPulser.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/CalibPulser.h @@ -135,7 +135,7 @@ class CalibPulser : public CalibRawBase const CalPad& getQtot() const { return mQtot; } /// Dump the relevant data to file - void dumpToFile(const std::string filename) final; + void dumpToFile(const std::string filename, uint32_t type = 0) final; /// Process the end of one raw reader void endReader() final; diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CalibRawBase.h b/Detectors/TPC/calibration/include/TPCCalibration/CalibRawBase.h index 5510714581cb5..8436ee14bbbc3 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/CalibRawBase.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/CalibRawBase.h @@ -118,7 +118,10 @@ class CalibRawBase void rewindEvents(); /// Dump the relevant data to file - virtual void dumpToFile(const std::string filename) {} + virtual void dumpToFile(const std::string filename, uint32_t type = 0) {} + + /// increment number of events + void incrementNEvents() { ++mNevents; } /// number of processed events size_t getNumberOfProcessedEvents() const { return mNevents; } @@ -129,6 +132,9 @@ class CalibRawBase /// check if present event is complete bool isPresentEventComplete() const { return mRawReaderCRUManager.isEventComplete(mPresentEventNumber); } + /// number of processed time bins in last event + void setNumberOfProcessedTimeBins(size_t timeBins) { mProcessedTimeBins = timeBins; } + /// number of processed time bins in last event size_t getNumberOfProcessedTimeBins() const { return mProcessedTimeBins; } diff --git a/Detectors/TPC/calibration/src/CalibPedestal.cxx b/Detectors/TPC/calibration/src/CalibPedestal.cxx index 59716f11717e0..c32b0b1a0d922 100644 --- a/Detectors/TPC/calibration/src/CalibPedestal.cxx +++ b/Detectors/TPC/calibration/src/CalibPedestal.cxx @@ -149,12 +149,16 @@ void CalibPedestal::resetData() } //______________________________________________________________________________ -void CalibPedestal::dumpToFile(const std::string filename) +void CalibPedestal::dumpToFile(const std::string filename, uint32_t type /* = 0*/) { auto f = std::unique_ptr(TFile::Open(filename.c_str(), "recreate")); - f->WriteObject(&mPedestal, "Pedestals"); - f->WriteObject(&mNoise, "Noise"); - f->Close(); + if (type == 0) { + f->WriteObject(&mPedestal, "Pedestals"); + f->WriteObject(&mNoise, "Noise"); + f->Close(); + } else if (type == 1) { + f->WriteObject(this, "CalibPedestal"); + } } //______________________________________________________________________________ diff --git a/Detectors/TPC/calibration/src/CalibPulser.cxx b/Detectors/TPC/calibration/src/CalibPulser.cxx index 1cedad2d47b91..cb53738591a97 100644 --- a/Detectors/TPC/calibration/src/CalibPulser.cxx +++ b/Detectors/TPC/calibration/src/CalibPulser.cxx @@ -249,29 +249,33 @@ void CalibPulser::analyse() } //______________________________________________________________________________ -void CalibPulser::dumpToFile(const std::string filename) +void CalibPulser::dumpToFile(const std::string filename, uint32_t type /* = 0*/) { auto f = std::unique_ptr(TFile::Open(filename.c_str(), "recreate")); - f->WriteObject(&mT0, "T0"); - f->WriteObject(&mWidth, "Width"); - f->WriteObject(&mQtot, "Qtot"); - - if (mDebugLevel) { - printf("dump debug info\n"); - // temporary arrays for writing the objects - TObjArray vT0; - for (auto& val : mT0Histograms) - vT0.Add(val.get()); - TObjArray vWidth; - for (auto& val : mWidthHistograms) - vWidth.Add(val.get()); - TObjArray vQtot; - for (auto& val : mQtotHistograms) - vQtot.Add(val.get()); - - vT0.Write("T0Histograms", TObject::kSingleKey); - vWidth.Write("WidthHistograms", TObject::kSingleKey); - vQtot.Write("QtotHistograms", TObject::kSingleKey); + if (type == 0) { + f->WriteObject(&mT0, "T0"); + f->WriteObject(&mWidth, "Width"); + f->WriteObject(&mQtot, "Qtot"); + + if (mDebugLevel) { + printf("dump debug info\n"); + // temporary arrays for writing the objects + TObjArray vT0; + for (auto& val : mT0Histograms) + vT0.Add(val.get()); + TObjArray vWidth; + for (auto& val : mWidthHistograms) + vWidth.Add(val.get()); + TObjArray vQtot; + for (auto& val : mQtotHistograms) + vQtot.Add(val.get()); + + vT0.Write("T0Histograms", TObject::kSingleKey); + vWidth.Write("WidthHistograms", TObject::kSingleKey); + vQtot.Write("QtotHistograms", TObject::kSingleKey); + } + } else if (type == 1) { + f->WriteObject(this, "CalibPulser"); } f->Close(); From b2466a9410916e7b58ccf5eb8d638df18d763e45 Mon Sep 17 00:00:00 2001 From: wiechula Date: Thu, 16 Jan 2020 11:50:03 -0500 Subject: [PATCH 07/24] First running version of pedestal calibration in DPL Still many things to be improved... --- .../TPC/calibration/run/calib-pedestal.cxx | 227 ++++++++++++++---- 1 file changed, 180 insertions(+), 47 deletions(-) diff --git a/Detectors/TPC/calibration/run/calib-pedestal.cxx b/Detectors/TPC/calibration/run/calib-pedestal.cxx index 5a7f4063230e5..647de34fdbedc 100644 --- a/Detectors/TPC/calibration/run/calib-pedestal.cxx +++ b/Detectors/TPC/calibration/run/calib-pedestal.cxx @@ -15,18 +15,34 @@ #include "Framework/ControlService.h" #include "Framework/Logger.h" #include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/CompletionPolicyHelpers.h" #include "DPLUtils/RawParser.h" #include "Headers/DataHeader.h" +#include "CommonUtils/ConfigurableParam.h" +#include "TPCCalibration/CalibPedestal.h" +#include "TPCReconstruction/RawReaderCRU.h" #include +#include using namespace o2::framework; +// customize the completion policy +void customize(std::vector& policies) +{ + using o2::framework::CompletionPolicy; + policies.push_back(CompletionPolicyHelpers::defineByName("calib-pedestal", CompletionPolicy::CompletionOp::Consume)); +} + // we need to add workflow options before including Framework/runDataProcessing void customize(std::vector& workflowOptions) { - workflowOptions.push_back( - ConfigParamSpec{ - "input-spec", VariantType::String, "A:FLP/RAWDATA", {"selection string input specs"}}); + std::vector options{ + {"input-spec", VariantType::String, "A:TPC/RAWDATA", {"selection string input specs"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings (e.g.: 'TPCCalibPedestal.FirstTimeBin=10;...')"}}, + {"configFile", VariantType::String, "", {"configuration file for configurable parameters"}}}; + + std::swap(workflowOptions, options); } #include "Framework/runDataProcessing.h" @@ -35,65 +51,182 @@ using RDH = o2::header::RAWDataHeader; void printHeader() { - fmt::print("{:>5} {:>4} {:>4} {:>4} {:>3} {:>4} {:>10} {:>5} {:>1}\n", "PkC", "pCnt", "fId", "Mem", "CRU", "GLID", "HBOrbit", "HB BC", "s"); + LOGP(debug, "{:>5} {:>4} {:>4} {:>4} {:>3} {:>4} {:>10} {:>5} {:>1} {:>10}", "PkC", "pCnt", "fId", "Mem", "CRU", "GLID", "HBOrbit", "HB BC", "s", "Trg"); } void printRDH(const RDH& rdh) { const int globalLinkID = int(rdh.linkID) + (((rdh.word1 >> 32) >> 28) * 12); - fmt::print("{:>5} {:>4} {:>4} {:>4} {:>3} {:>4} {:>10} {:>5} {:>1}\n", (uint64_t)rdh.packetCounter, (uint64_t)rdh.pageCnt, (uint64_t)rdh.feeId, (uint64_t)(rdh.memorySize), (uint64_t)rdh.cruID, (uint64_t)globalLinkID, (uint64_t)rdh.heartbeatOrbit, (uint64_t)rdh.heartbeatBC, (uint64_t)rdh.stop); + LOGP(debug, "{:>5} {:>4} {:>4} {:>4} {:>3} {:>4} {:>10} {:>5} {:>1} {:#010X}", (uint64_t)rdh.packetCounter, (uint64_t)rdh.pageCnt, (uint64_t)rdh.feeId, (uint64_t)(rdh.memorySize), (uint64_t)rdh.cruID, (uint64_t)globalLinkID, (uint64_t)rdh.heartbeatOrbit, (uint64_t)rdh.heartbeatBC, (uint64_t)rdh.stop, (uint64_t)rdh.triggerType); } WorkflowSpec defineDataProcessing(ConfigContext const& config) { + using namespace o2::tpc; - WorkflowSpec workflow; - workflow.emplace_back(DataProcessorSpec{ - "calib-pedestal", - select(config.options().get("input-spec").c_str()), - Outputs{}, - AlgorithmSpec{[](InitContext& setup) { // - return adaptStateless([](InputRecord& inputs, DataAllocator& outputs) { - for (auto& input : inputs) { - const auto* dh = DataRefUtils::getHeader(input); - LOG(INFO) << dh->dataOrigin.as() << "/" << dh->dataDescription.as() << "/" - << dh->subSpecification << " payload size " << dh->payloadSize; - - // there is a bug in InpuRecord::get for vectors of simple types, not catched in - // DataAllocator unit test - //auto data = inputs.get>(input.spec->binding.c_str()); - //LOG(INFO) << "data size " << data.size(); - printHeader(); - try { - const char* pos = input.payload; - const char* last = pos + dh->payloadSize; - - o2::framework::RawParser parser(input.payload, dh->payloadSize); - - //while (pos < last) { - for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { - auto* rdhPtr = it.get_if(); - if (!rdhPtr) { + // set up configuration + o2::conf::ConfigurableParam::updateFromFile(config.options().get("configFile")); + o2::conf::ConfigurableParam::updateFromString(config.options().get("configKeyValues")); + o2::conf::ConfigurableParam::writeINI("o2tpccalibration_configuration.ini"); + + struct ProcessAttributes { + CalibPedestal calibPedestal; + rawreader::RawReaderCRUManager rawReader; + uint32_t lastOrbit{0}; + uint64_t lastTFID{0}; + uint32_t maxEvents{100}; + bool quit{false}; + bool dumped{false}; + }; + + auto initFunction = [](InitContext& ic) { + auto processAttributes = std::make_shared(); + // set up calibration + // TODO: + // it is a bit ugly to use the RawReaderCRUManager for this is. + // At some point the raw reader code should be cleaned up and modularized + { + auto& pedestal = processAttributes->calibPedestal; + pedestal.init(); // initialize configuration via configKeyValues + processAttributes->rawReader.createReader(""); + processAttributes->rawReader.setADCDataCallback([&pedestal](const PadROCPos& padROCPos, const CRU& cru, const gsl::span data) -> Int_t { + Int_t timeBins = pedestal.update(padROCPos, cru, data); + pedestal.setNumberOfProcessedTimeBins(std::max(pedestal.getNumberOfProcessedTimeBins(), size_t(timeBins))); + return timeBins; + }); + processAttributes->maxEvents = static_cast(ic.options().get("max-events")); + } + + auto processingFct = [processAttributes](ProcessingContext& pc) { + // in case the maximum number of events was reached don't do further processing + if (processAttributes->quit) { + return; + } + + if (pc.inputs().isValid("TFID")) { + auto tfid = pc.inputs().get("TFID"); + LOGP(info, "TFid: {}", tfid); + processAttributes->lastTFID = tfid; + } + + for (auto& input : pc.inputs()) { + const auto* dh = DataRefUtils::getHeader(input); + + // ---| only process RAWDATA, there might be a nicer way to do this |--- + if (dh == nullptr || dh->dataDescription != o2::header::gDataDescriptionRawData) { + continue; + } + + // ---| extract hardware information to do the processing |--- + const auto subSpecification = dh->subSpecification; + const auto cruID = subSpecification >> 16; + const auto linkID = ((subSpecification + (subSpecification >> 8)) & 0xFF) - 1; + const auto dataWrapperID = ((subSpecification >> 8) & 0xFF) > 0; + const auto globalLinkID = linkID + dataWrapperID * 12; + + // ---| update hardware information in the reader |--- + auto& reader = processAttributes->rawReader.getReaders()[0]; + reader->forceCRU(cruID); + reader->setLink(globalLinkID); + + LOGP(debug, "Specifier: {}/{}/{}", dh->dataOrigin.as(), dh->dataDescription.as(), dh->subSpecification); + LOGP(debug, "Payload size: {}", dh->payloadSize); + LOGP(debug, "CRU: {}; linkID: {}; dataWrapperID: {}; globalLinkID: {}", cruID, linkID, dataWrapperID, globalLinkID); + + printHeader(); + + // TODO: exception handling needed? + try { + o2::framework::RawParser parser(input.payload, dh->payloadSize); + + // TODO: it would be better to have external event handling and then moving the event processing functionality to CalibRawBase and RawReader to not repeat it in other places + rawreader::ADCRawData rawData; + rawreader::GBTFrame gFrame; + + auto& calibPedestal = processAttributes->calibPedestal; + + for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { + auto* rdhPtr = it.get_if(); + if (!rdhPtr) { + break; + } + const auto& rdh = *rdhPtr; + printRDH(rdh); + // ===| event handling |=== + // + // really ugly, better treatment required extension in DPL + // events are are detected by close by orbit numbers + // might be possible to change this using the TFID information + // + const auto hbOrbit = rdh.heartbeatOrbit; + const auto lastOrbit = processAttributes->lastOrbit; + + if ((lastOrbit > 0) && (hbOrbit > (lastOrbit + 3))) { + calibPedestal.incrementNEvents(); + LOGP(info, "Number of processed events: {} ({})", calibPedestal.getNumberOfProcessedEvents(), processAttributes->maxEvents); + if (calibPedestal.getNumberOfProcessedEvents() >= processAttributes->maxEvents) { + LOGP(info, "Maximm number of events reached ({}), no more processing will be done", processAttributes->maxEvents); + processAttributes->quit = true; break; } - //const auto& rdh = *((RDH*)pos); - const auto& rdh = *rdhPtr; - //const auto payloadSize = rdh.memorySize - rdh.headerSize; - //const auto dataWrapperID = rdh.endPointID; - //const auto linkID = rdh.linkID; - //const auto globalLinkID = linkID + dataWrapperID * 12; - printRDH(rdh); - - //pos += rdh.offsetToNext; } - } catch (const std::runtime_error& e) { - LOG(ERROR) << "can not create raw parser form input data"; - o2::header::hexDump("payload", input.payload, dh->payloadSize, 64); - LOG(ERROR) << e.what(); + + processAttributes->lastOrbit = hbOrbit; + const auto size = it.size(); + auto data = it.data(); + //LOGP(info, "Data size: {}", size); + + int iFrame = 0; + for (int i = 0; i < size; i += 16) { + gFrame.setFrameNumber(iFrame); + gFrame.setPacketNumber(iFrame / 508); + gFrame.readFromMemory(gsl::span(data + i, 16)); + + // extract the half words from the 4 32-bit words + gFrame.getFrameHalfWords(); + + // debug output + //if (CHECK_BIT(mDebugLevel, DebugLevel::GBTFrames)) { + //std::cout << gFrame; + //} + + gFrame.getAdcValues(rawData); + gFrame.updateSyncCheck(false); + + ++iFrame; + } } + + reader->runADCDataCallback(rawData); + } catch (const std::runtime_error& e) { + LOGP(error, "can not create raw parser form input data"); + o2::header::hexDump("payload", input.payload, dh->payloadSize, 64); + LOG(ERROR) << e.what(); } - }); - }}}); + } + + // TODO: For the moment simply dump calibration output to file, to check if everything is working as expected + if (processAttributes->quit && !processAttributes->dumped) { + LOGP(info, "Dumping output"); + processAttributes->calibPedestal.analyse(); + processAttributes->calibPedestal.dumpToFile("pedestals.root"); + processAttributes->dumped = true; + //pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::All); + } + }; + + return processingFct; + }; + + WorkflowSpec workflow; + workflow.emplace_back(DataProcessorSpec{ + "calib-pedestal", + select(config.options().get("input-spec").c_str()), + Outputs{}, + AlgorithmSpec{initFunction}, + Options{{"max-events", VariantType::Int, 100, {"maximum number of events to process"}}}}); + return workflow; } From 2f9c26bd8046cee9e765455fa3042c48f6a04c12 Mon Sep 17 00:00:00 2001 From: wiechula Date: Wed, 29 Jan 2020 22:41:47 +0100 Subject: [PATCH 08/24] Add Configurable Param for DigitDump class --- Detectors/TPC/calibration/CMakeLists.txt | 2 + .../include/TPCCalibration/DigitDump.h | 5 ++- .../include/TPCCalibration/DigitDumpParam.h | 39 +++++++++++++++++++ Detectors/TPC/calibration/src/DigitDump.cxx | 39 ++++++++++++++----- .../TPC/calibration/src/DigitDumpParam.cxx | 17 ++++++++ .../calibration/src/TPCCalibrationLinkDef.h | 2 + 6 files changed, 94 insertions(+), 10 deletions(-) create mode 100644 Detectors/TPC/calibration/include/TPCCalibration/DigitDumpParam.h create mode 100644 Detectors/TPC/calibration/src/DigitDumpParam.cxx diff --git a/Detectors/TPC/calibration/CMakeLists.txt b/Detectors/TPC/calibration/CMakeLists.txt index 372168acecc29..f472ba2ea0655 100644 --- a/Detectors/TPC/calibration/CMakeLists.txt +++ b/Detectors/TPC/calibration/CMakeLists.txt @@ -18,6 +18,7 @@ o2_add_library(TPCCalibration src/CalibPulserParam.cxx src/CalibTreeDump.cxx src/DigitDump.cxx + src/DigitDumpParam.cxx src/CalibPadGainTracks.cxx PUBLIC_LINK_LIBRARIES O2::DataFormatsTPC O2::TPCBase O2::TPCReconstruction ROOT::Minuit @@ -31,6 +32,7 @@ o2_target_root_dictionary(TPCCalibration include/TPCCalibration/CalibPulserParam.h include/TPCCalibration/CalibTreeDump.h include/TPCCalibration/DigitDump.h + include/TPCCalibration/DigitDumpParam.h include/TPCCalibration/CalibPadGainTracks.h include/TPCCalibration/FastHisto.h) diff --git a/Detectors/TPC/calibration/include/TPCCalibration/DigitDump.h b/Detectors/TPC/calibration/include/TPCCalibration/DigitDump.h index dc3a6a136cb66..5d3b2278bda0e 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/DigitDump.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/DigitDump.h @@ -58,6 +58,9 @@ class DigitDump : public CalibRawBase /// default destructor ~DigitDump() override; + /// initialize DigitDump from DigitDumpParam + void init(); + /// update function called once per digit /// /// \param roc readout chamber @@ -128,7 +131,7 @@ class DigitDump : public CalibRawBase void loadNoiseAndPedestal(); /// initialize - void init(); + void initInputOutput(); /// End event function void endEvent() final; diff --git a/Detectors/TPC/calibration/include/TPCCalibration/DigitDumpParam.h b/Detectors/TPC/calibration/include/TPCCalibration/DigitDumpParam.h new file mode 100644 index 0000000000000..4054504480e3d --- /dev/null +++ b/Detectors/TPC/calibration/include/TPCCalibration/DigitDumpParam.h @@ -0,0 +1,39 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file DigitDumpParam.h +/// \brief Implementation of the parameter class for the hardware clusterer +/// \author Jens Wiechula, Jens.Wiechula@ikf.uni-frankfurt.de + +#ifndef ALICEO2_TPC_DigitDumpParam_H_ +#define ALICEO2_TPC_DigitDumpParam_H_ + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2 +{ +namespace tpc +{ + +struct DigitDumpParam : public o2::conf::ConfigurableParamHelper { + int FirstTimeBin{0}; ///< first time bin used in analysis + int LastTimeBin{1000}; ///< first time bin used in analysis + int ADCMin{-100}; ///< minimum adc value + int ADCMax{1024}; ///< maximum adc value + float NoiseThreshold{-1}; ///< zero suppression threshold in noise sigma + std::string PedestalAndNoiseFile{}; ///< file name for the pedestal and nosie file + + O2ParamDef(DigitDumpParam, "TPCDigitDump"); +}; +} // namespace tpc +} // namespace o2 + +#endif // ALICEO2_TPC_HwClustererParam_H_ diff --git a/Detectors/TPC/calibration/src/DigitDump.cxx b/Detectors/TPC/calibration/src/DigitDump.cxx index 66909e28ae799..7df1951c5c560 100644 --- a/Detectors/TPC/calibration/src/DigitDump.cxx +++ b/Detectors/TPC/calibration/src/DigitDump.cxx @@ -19,6 +19,7 @@ #include "TPCBase/Mapper.h" #include "TPCBase/ROC.h" #include "TPCCalibration/DigitDump.h" +#include "TPCCalibration/DigitDumpParam.h" using namespace o2::tpc; @@ -30,6 +31,19 @@ DigitDump::~DigitDump() } } +//______________________________________________________________________________ +void DigitDump::init() +{ + const auto& param = DigitDumpParam::Instance(); + + mFirstTimeBin = param.FirstTimeBin; + mLastTimeBin = param.LastTimeBin; + mADCMin = param.ADCMin; + mADCMax = param.ADCMax; + mNoiseThreshold = param.NoiseThreshold; + mPedestalAndNoiseFile = param.PedestalAndNoiseFile; +} + //______________________________________________________________________________ Int_t DigitDump::updateCRU(const CRU& cru, const Int_t row, const Int_t pad, const Int_t timeBin, const Float_t signal) @@ -38,8 +52,8 @@ Int_t DigitDump::updateCRU(const CRU& cru, const Int_t row, const Int_t pad, return 0; } - if (!mFile) { - init(); + if (!mInitialized) { + initInputOutput(); } Mapper& mapper = Mapper::instance(); @@ -81,9 +95,8 @@ Int_t DigitDump::updateCRU(const CRU& cru, const Int_t row, const Int_t pad, } //______________________________________________________________________________ -void DigitDump::endEvent() +void DigitDump::sortDigits() { - // sort digits for (auto& digits : mDigits) { std::sort(digits.begin(), digits.end(), [](const auto& a, const auto& b) { if (a.getTimeStamp() < b.getTimeStamp()) @@ -93,12 +106,17 @@ void DigitDump::endEvent() return false; }); } +} + +//______________________________________________________________________________ +void DigitDump::endEvent() +{ + // sort digits + sortDigits(); mTree->Fill(); - for (auto& digits : mDigits) { - digits.clear(); - } + clearDigits(); } //______________________________________________________________________________ @@ -136,8 +154,11 @@ void DigitDump::setupOutputTree() } //______________________________________________________________________________ -void DigitDump::init() +void DigitDump::initInputOutput() { loadNoiseAndPedestal(); - setupOutputTree(); + if (!mInMemoryOnly) { + setupOutputTree(); + } + mInitialized = true; } diff --git a/Detectors/TPC/calibration/src/DigitDumpParam.cxx b/Detectors/TPC/calibration/src/DigitDumpParam.cxx new file mode 100644 index 0000000000000..b9cc21ab4eb25 --- /dev/null +++ b/Detectors/TPC/calibration/src/DigitDumpParam.cxx @@ -0,0 +1,17 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file DigitDumpParam.cxx +/// \brief Implementation of the parameter class for the hardware clusterer +/// \author Jens Wiechula, Jens.Wiechula@ikf.uni-frankfurt.de + +#include "TPCCalibration/DigitDumpParam.h" + +O2ParamImpl(o2::tpc::DigitDumpParam); diff --git a/Detectors/TPC/calibration/src/TPCCalibrationLinkDef.h b/Detectors/TPC/calibration/src/TPCCalibrationLinkDef.h index 4125bce2d67ca..df237af65bfd0 100644 --- a/Detectors/TPC/calibration/src/TPCCalibrationLinkDef.h +++ b/Detectors/TPC/calibration/src/TPCCalibrationLinkDef.h @@ -23,6 +23,8 @@ #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::tpc::CalibPulserParam> + ; #pragma link C++ class o2::tpc::CalibTreeDump; #pragma link C++ class o2::tpc::DigitDump; +#pragma link C++ class o2::tpc::DigitDumpParam; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::tpc::DigitDumpParam> + ; #pragma link C++ class o2::tpc::CalibPadGainTracks; #pragma link C++ class o2::tpc::FastHisto +; #pragma link C++ class o2::tpc::FastHisto +; From fd88eaa6121c6e3cba0f590dd3d625ae58f7b816 Mon Sep 17 00:00:00 2001 From: wiechula Date: Mon, 20 Jan 2020 09:05:00 +0100 Subject: [PATCH 09/24] Implement raw to digits in DPL o Add DPL device which converts raw data to digits o Adapt Digit dump for in memory processing --- .../include/TPCCalibration/DigitDump.h | 22 ++ Detectors/TPC/calibration/src/DigitDump.cxx | 1 + Detectors/TPC/workflow/CMakeLists.txt | 9 +- .../include/TPCWorkflow/RawToDigitsSpec.h | 34 +++ .../TPC/workflow/src/RawToDigitsSpec.cxx | 209 ++++++++++++++++++ .../src/tpc-raw-to-digits-workflow.cxx | 109 +++++++++ 6 files changed, 383 insertions(+), 1 deletion(-) create mode 100644 Detectors/TPC/workflow/include/TPCWorkflow/RawToDigitsSpec.h create mode 100644 Detectors/TPC/workflow/src/RawToDigitsSpec.cxx create mode 100644 Detectors/TPC/workflow/src/tpc-raw-to-digits-workflow.cxx diff --git a/Detectors/TPC/calibration/include/TPCCalibration/DigitDump.h b/Detectors/TPC/calibration/include/TPCCalibration/DigitDump.h index 5d3b2278bda0e..a24e18d5c84ab 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/DigitDump.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/DigitDump.h @@ -105,6 +105,26 @@ class DigitDump : public CalibRawBase mLastTimeBin = last; } + /// sort the digits + void sortDigits(); + + /// clear the digits + void clearDigits() + { + for (auto& digits : mDigits) { + digits.clear(); + } + } + + /// set in memory only mode + void setInMemoryOnly(bool mode = true) { mInMemoryOnly = mode; } + + /// get in memory mode + bool getInMemoryMode() const { return mInMemoryOnly; } + + /// return digits for specific sector + std::vector& getDigits(int sector) { return mDigits[sector]; } + private: std::unique_ptr mPedestal{}; ///< CalDet object with pedestal information std::unique_ptr mNoise{}; ///< CalDet object with noise @@ -123,6 +143,8 @@ class DigitDump : public CalibRawBase int mADCMin{-100}; ///< minimum adc value int mADCMax{1024}; ///< maximum adc value float mNoiseThreshold{-1}; ///< zero suppression threshold in noise sigma + bool mInMemoryOnly{false}; ///< if processing is only done in memory, no file writing + bool mInitialized{false}; ///< if init was called /// set up the output tree void setupOutputTree(); diff --git a/Detectors/TPC/calibration/src/DigitDump.cxx b/Detectors/TPC/calibration/src/DigitDump.cxx index 7df1951c5c560..85dd0a1f239c5 100644 --- a/Detectors/TPC/calibration/src/DigitDump.cxx +++ b/Detectors/TPC/calibration/src/DigitDump.cxx @@ -97,6 +97,7 @@ Int_t DigitDump::updateCRU(const CRU& cru, const Int_t row, const Int_t pad, //______________________________________________________________________________ void DigitDump::sortDigits() { + // sort digits for (auto& digits : mDigits) { std::sort(digits.begin(), digits.end(), [](const auto& a, const auto& b) { if (a.getTimeStamp() < b.getTimeStamp()) diff --git a/Detectors/TPC/workflow/CMakeLists.txt b/Detectors/TPC/workflow/CMakeLists.txt index dbecde25fd0fe..5323ec17a7de2 100644 --- a/Detectors/TPC/workflow/CMakeLists.txt +++ b/Detectors/TPC/workflow/CMakeLists.txt @@ -16,15 +16,22 @@ o2_add_library(TPCWorkflow src/ClusterDecoderRawSpec.cxx src/CATrackerSpec.cxx src/TrackReaderSpec.cxx + src/RawToDigitsSpec.cxx TARGETVARNAME targetName PUBLIC_LINK_LIBRARIES O2::Framework O2::DataFormatsTPC - O2::DPLUtils O2::TPCReconstruction) + O2::DPLUtils O2::TPCReconstruction + O2::TPCCalibration O2::TPCSimulation) o2_add_executable(reco-workflow COMPONENT_NAME tpc SOURCES src/tpc-reco-workflow.cxx PUBLIC_LINK_LIBRARIES O2::TPCWorkflow) +o2_add_executable(raw-to-digits-workflow + COMPONENT_NAME tpc + SOURCES src/tpc-raw-to-digits-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::TPCWorkflow) + o2_add_test(workflow COMPONENT_NAME tpc LABELS tpc workflow diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/RawToDigitsSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/RawToDigitsSpec.h new file mode 100644 index 0000000000000..59226e3d745da --- /dev/null +++ b/Detectors/TPC/workflow/include/TPCWorkflow/RawToDigitsSpec.h @@ -0,0 +1,34 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file RawToDigitsSpec.h +/// @author Jens Wiechula +/// @since 2020-01-17 +/// @brief Processor spec for running TPC GBT raw frame to digit converter + +#ifndef TPC_RAWTODIGITSSPEC_H_ +#define TPC_RAWTODIGITSSPEC_H_ + +#include "Framework/DataProcessorSpec.h" +#include + +namespace o2 +{ +namespace tpc +{ + +/// create a processor spec +/// read simulated TPC clusters from file and publish +o2::framework::DataProcessorSpec getRawToDigitsSpec(int channel, const std::string_view inputDef); + +} // end namespace tpc +} // end namespace o2 + +#endif // TPC_RAWTODIGITSSPEC_H_ diff --git a/Detectors/TPC/workflow/src/RawToDigitsSpec.cxx b/Detectors/TPC/workflow/src/RawToDigitsSpec.cxx new file mode 100644 index 0000000000000..67755589593c7 --- /dev/null +++ b/Detectors/TPC/workflow/src/RawToDigitsSpec.cxx @@ -0,0 +1,209 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include "Framework/WorkflowSpec.h" +#include "Framework/ControlService.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/DataRefUtils.h" +#include "Framework/Lifetime.h" +#include "DPLUtils/RawParser.h" +#include "Headers/DataHeader.h" +#include "DataFormatsTPC/TPCSectorHeader.h" +#include "TPCBase/Digit.h" +#include "TPCCalibration/DigitDump.h" +#include "TPCReconstruction/RawReaderCRU.h" +#include "TPCWorkflow/RawToDigitsSpec.h" +#include +#include + +using namespace o2::framework; +using SubSpecificationType = o2::framework::DataAllocator::SubSpecificationType; + +namespace o2 +{ +namespace tpc +{ + +o2::framework::DataProcessorSpec getRawToDigitsSpec(int channel, const std::string_view inputDef) +{ + + struct ProcessAttributes { + DigitDump digitDump; + rawreader::RawReaderCRUManager rawReader; + uint32_t lastOrbit{0}; + uint32_t maxEvents{100}; + uint64_t activeSectors{0}; + bool quit{false}; + }; + + auto initFunction = [channel](InitContext& ic) { + auto processAttributes = std::make_shared(); + // set up calibration + { + auto& digitDump = processAttributes->digitDump; + digitDump.init(); + digitDump.setInMemoryOnly(); + const auto pedestalFile = ic.options().get("pedestal-file"); + LOG(INFO) << "Setting pedestal file: " << pedestalFile; + digitDump.setPedestalAndNoiseFile(pedestalFile); + + processAttributes->rawReader.createReader(""); + processAttributes->rawReader.setADCDataCallback([&digitDump](const PadROCPos& padROCPos, const CRU& cru, const gsl::span data) -> Int_t { + Int_t timeBins = digitDump.update(padROCPos, cru, data); + digitDump.setNumberOfProcessedTimeBins(std::max(digitDump.getNumberOfProcessedTimeBins(), size_t(timeBins))); + return timeBins; + }); + processAttributes->maxEvents = static_cast(ic.options().get("max-events")); + } + + auto processingFct = [processAttributes, channel](ProcessingContext& pc) { + if (processAttributes->quit) { + return; + } + + //uint64_t activeSectors = 0; + + // lambda that snapshots digits to be sent out; prepares and attaches header with sector information + auto snapshotDigits = [&pc, processAttributes, channel](std::vector const& digits, int sector) { + o2::tpc::TPCSectorHeader header{sector}; + header.activeSectors = processAttributes->activeSectors; + // note that snapshoting only works with non-const references (to be fixed?) + pc.outputs().snapshot(Output{"TPC", "DIGITS", static_cast(channel), Lifetime::Timeframe, header}, + const_cast&>(digits)); + }; + + // loop over all inputs + for (auto& input : pc.inputs()) { + const auto* dh = DataRefUtils::getHeader(input); + + // select only RAW data + if (dh->dataDescription != o2::header::gDataDescriptionRawData) { + continue; + } + + const auto subSpecification = dh->subSpecification; + const auto cruID = subSpecification >> 16; + const auto linkID = ((subSpecification + (subSpecification >> 8)) & 0xFF) - 1; + const auto dataWrapperID = ((subSpecification >> 8) & 0xFF) > 0; + const auto globalLinkID = linkID + dataWrapperID * 12; + const auto sector = cruID / 10; + processAttributes->activeSectors |= (0x1 << sector); + + auto& reader = processAttributes->rawReader.getReaders()[0]; + reader->forceCRU(cruID); + reader->setLink(globalLinkID); + + //LOG(INFO) << dh->dataOrigin.as() << "/" << dh->dataDescription.as() << "/" + //<< dh->subSpecification << " payload size " << dh->payloadSize; + //LOG(INFO) << "CRU: " << cruID << " -- linkID: " << linkID << " -- dataWrapperID: " << dataWrapperID << " -- globalLinkID: " << globalLinkID; + + // there is a bug in InpuRecord::get for vectors of simple types, not catched in + // DataAllocator unit test + //auto data = inputs.get>(input.spec->binding.c_str()); + //LOG(INFO) << "data size " << data.size(); + //printHeader(); + try { + o2::framework::RawParser parser(input.payload, dh->payloadSize); + + rawreader::ADCRawData rawData; + rawreader::GBTFrame gFrame; + + for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { + auto* rdhPtr = it.get_if(); + if (!rdhPtr) { + break; + } + const auto& rdh = *rdhPtr; + //printRDH(rdh); + // ugly event handling ... + const auto hbOrbit = rdh.heartbeatOrbit; + const auto lastOrbit = processAttributes->lastOrbit; + + if ((lastOrbit > 0) && (hbOrbit > (lastOrbit + 3))) { + auto& digitDump = processAttributes->digitDump; + digitDump.incrementNEvents(); + LOG(INFO) << fmt::format("Number of processed events: {} ({})", digitDump.getNumberOfProcessedEvents(), processAttributes->maxEvents); + digitDump.sortDigits(); + // add publish here + for (int isector = 0; isector < Sector::MAXSECTOR; ++isector) { + if (processAttributes->activeSectors & (0x1 << isector)) { + snapshotDigits(digitDump.getDigits(isector), isector); + } + } + digitDump.clearDigits(); + + processAttributes->activeSectors = 0; + if (digitDump.getNumberOfProcessedEvents() >= processAttributes->maxEvents) { + LOG(INFO) << fmt::format("Maximm number of events reached ({}), no more processing will be done", processAttributes->maxEvents); + processAttributes->quit = true; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::All); + //pc.services().get().readyToQuit(QuitRequest::Me); + break; + } + } + + processAttributes->lastOrbit = hbOrbit; + const auto size = it.size(); + auto data = it.data(); + //LOG(INFO) << fmt::format("Data size: {}", size); + + int iFrame = 0; + for (int i = 0; i < size; i += 16) { + gFrame.setFrameNumber(iFrame); + gFrame.setPacketNumber(iFrame / 508); + gFrame.readFromMemory(gsl::span(data + i, 16)); + + // extract the half words from the 4 32-bit words + gFrame.getFrameHalfWords(); + + // debug output + //if (CHECK_BIT(mDebugLevel, DebugLevel::GBTFrames)) { + //std::cout << gFrame; + //} + + gFrame.getAdcValues(rawData); + gFrame.updateSyncCheck(false); + + ++iFrame; + } + } + + reader->runADCDataCallback(rawData); + } catch (const std::runtime_error& e) { + LOG(ERROR) << "can not create raw parser form input data"; + o2::header::hexDump("payload", input.payload, dh->payloadSize, 64); + LOG(ERROR) << e.what(); + } + } + }; + + return processingFct; + }; + + std::stringstream id; + id << "TPCDigitizer" << channel; + + std::vector outputs; // define channel by triple of (origin, type id of data to be sent on this channel, subspecification) + outputs.emplace_back("TPC", "DIGITS", static_cast(channel), Lifetime::Timeframe); + + return DataProcessorSpec{ + id.str().c_str(), + select(inputDef.data()), + outputs, + AlgorithmSpec{initFunction}, + Options{ + {"max-events", VariantType::Int, 100, {"maximum number of events to process"}}, + {"pedestal-file", VariantType::String, "", {"file with pedestals and noise for zero suppression"}}}}; +} +} // namespace tpc +} // namespace o2 diff --git a/Detectors/TPC/workflow/src/tpc-raw-to-digits-workflow.cxx b/Detectors/TPC/workflow/src/tpc-raw-to-digits-workflow.cxx new file mode 100644 index 0000000000000..2df4ac5287e0b --- /dev/null +++ b/Detectors/TPC/workflow/src/tpc-raw-to-digits-workflow.cxx @@ -0,0 +1,109 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include "Framework/WorkflowSpec.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/ControlService.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "Framework/Logger.h" +#include "Framework/ConfigParamSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Algorithm/RangeTokenizer.h" +#include "TPCWorkflow/RawToDigitsSpec.h" +#include "TPCWorkflow/RecoWorkflow.h" +#include "TPCBase/Sector.h" +#include +#include + +#include // to detect number of hardware threads + +using namespace o2::framework; + +// customize the completion policy +void customize(std::vector& policies) +{ + using o2::framework::CompletionPolicy; + // we customize the completion policy for the writer since it should stream immediately + policies.push_back(CompletionPolicyHelpers::defineByName("TPCDigitWriter", CompletionPolicy::CompletionOp::Consume)); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // for the TPC it is useful to take at most half of the available (logical) cores due to memory requirements + int defaultlanes = std::max(1u, std::thread::hardware_concurrency() / 2); + const std::string laneshelp("Number of tpc processing lanes. A lane is a pipeline of algorithms."); + + const std::string sectorshelp("List of TPC sectors, comma separated ranges, e.g. 0-3,7,9-15"); + const std::string sectorDefault = "0-" + std::to_string(o2::tpc::Sector::MAXSECTOR - 1); + const std::string tpcrthelp("Run TPC reco workflow to specified output type, currently supported: 'tracks'"); + + std::vector options{ + {"input-spec", VariantType::String, "A:TPC/RAWDATA", {"selection string input specs"}}, + {"dump-digits", o2::framework::VariantType::Bool, false, {"attach digit file dump"}}, + {"tpc-lanes", VariantType::Int, defaultlanes, {laneshelp}}, + {"tpc-sectors", VariantType::String, sectorDefault.c_str(), {sectorshelp}}, + {"tpc-reco-output", VariantType::String, "", {tpcrthelp}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings (e.g.: 'TPCCalibPedestal.FirstTimeBin=10;...')"}}, + {"configFile", VariantType::String, "", {"configuration file for configurable parameters"}}}; + + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +// extract num TPC lanes, a lane is a streaming line of processors (digitizer-clusterizer-etc) +// by default this will be std::max(the number of physical cores, numberofsectors) +// as a temporary means to fully use a machine and as a way to play with different topologies +int getNumTPCLanes(std::vector const& sectors, ConfigContext const& configcontext) +{ + auto lanes = configcontext.options().get("tpc-lanes"); + if (lanes < 0) { + LOG(FATAL) << "tpc-lanes needs to be positive\n"; + return 0; + } + // crosscheck with sectors + return std::min(lanes, (int)sectors.size()); +} + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + + // set up configuration + o2::conf::ConfigurableParam::updateFromFile(configcontext.options().get("configFile")); + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + o2::conf::ConfigurableParam::writeINI("o2-tpc-raw-reco-workflow_configuration.ini"); + + WorkflowSpec specs; + + auto tpcsectors = o2::RangeTokenizer::tokenize(configcontext.options().get("tpc-sectors")); + auto lanes = 1; //getNumTPCLanes(tpcsectors, config); + + int fanoutsize = 0; + for (int l = 0; l < lanes; ++l) { + specs.emplace_back(o2::tpc::getRawToDigitsSpec(fanoutsize, configcontext.options().get("input-spec"))); + fanoutsize++; + } + + // ===| attach the TPC reco workflow |======================================== + // default is to dump digits + std::string_view recoOuput = "digits"; + const auto tpcRecoOutputType = configcontext.options().get("tpc-reco-output"); + if (!tpcRecoOutputType.empty()) { + recoOuput = tpcRecoOutputType.c_str(); + } + + auto tpcRecoWorkflow = o2::tpc::reco_workflow::getWorkflow(tpcsectors, false, lanes, "digitizer", recoOuput.data()); + specs.insert(specs.end(), tpcRecoWorkflow.begin(), tpcRecoWorkflow.end()); + + return specs; +} From 13457ec1b81a04c7ca6599e0bf799e1b60916e1b Mon Sep 17 00:00:00 2001 From: wiechula Date: Fri, 21 Feb 2020 12:06:13 +0100 Subject: [PATCH 10/24] Fix and extend raw to digits workflow * First running version of GBT -> digits -> tracks workflow * Minor cleanup * configurable sector input --- .../include/TPCWorkflow/RawToDigitsSpec.h | 2 +- .../TPC/workflow/src/RawToDigitsSpec.cxx | 85 +++++++++++-------- .../src/tpc-raw-to-digits-workflow.cxx | 17 ++-- 3 files changed, 61 insertions(+), 43 deletions(-) diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/RawToDigitsSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/RawToDigitsSpec.h index 59226e3d745da..c0e1703ee632f 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/RawToDigitsSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/RawToDigitsSpec.h @@ -26,7 +26,7 @@ namespace tpc /// create a processor spec /// read simulated TPC clusters from file and publish -o2::framework::DataProcessorSpec getRawToDigitsSpec(int channel, const std::string_view inputDef); +o2::framework::DataProcessorSpec getRawToDigitsSpec(int channel, const std::string_view inputDef, std::vector const& tpcSectors); } // end namespace tpc } // end namespace o2 diff --git a/Detectors/TPC/workflow/src/RawToDigitsSpec.cxx b/Detectors/TPC/workflow/src/RawToDigitsSpec.cxx index 67755589593c7..ea902d1e34a45 100644 --- a/Detectors/TPC/workflow/src/RawToDigitsSpec.cxx +++ b/Detectors/TPC/workflow/src/RawToDigitsSpec.cxx @@ -8,7 +8,6 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include #include "Framework/WorkflowSpec.h" #include "Framework/ControlService.h" #include "Framework/DataProcessorSpec.h" @@ -22,6 +21,7 @@ #include "TPCCalibration/DigitDump.h" #include "TPCReconstruction/RawReaderCRU.h" #include "TPCWorkflow/RawToDigitsSpec.h" +#include "Framework/Logger.h" #include #include @@ -33,19 +33,23 @@ namespace o2 namespace tpc { -o2::framework::DataProcessorSpec getRawToDigitsSpec(int channel, const std::string_view inputDef) +o2::framework::DataProcessorSpec getRawToDigitsSpec(int channel, const std::string_view inputDef, std::vector const& tpcSectors) { struct ProcessAttributes { - DigitDump digitDump; - rawreader::RawReaderCRUManager rawReader; - uint32_t lastOrbit{0}; - uint32_t maxEvents{100}; - uint64_t activeSectors{0}; - bool quit{false}; + DigitDump digitDump; ///< digits creation class + rawreader::RawReaderCRUManager rawReader; ///< GBT frame decoder + uint32_t lastOrbit{0}; ///< last processed orbit number + uint32_t maxEvents{100}; ///< maximum number of events to process + uint64_t activeSectors{0}; ///< bit mask of active sectors + bool quit{false}; ///< if workflow is ready to quit + std::vector tpcSectors{}; ///< tpc sector configuration }; - auto initFunction = [channel](InitContext& ic) { + // ===| stateful initialization |============================================= + // + auto initFunction = [channel, tpcSectors](InitContext& ic) { + // ===| create and set up processing attributes |=== auto processAttributes = std::make_shared(); // set up calibration { @@ -53,7 +57,7 @@ o2::framework::DataProcessorSpec getRawToDigitsSpec(int channel, const std::stri digitDump.init(); digitDump.setInMemoryOnly(); const auto pedestalFile = ic.options().get("pedestal-file"); - LOG(INFO) << "Setting pedestal file: " << pedestalFile; + LOGP(info, "Setting pedestal file: {}", pedestalFile); digitDump.setPedestalAndNoiseFile(pedestalFile); processAttributes->rawReader.createReader(""); @@ -63,21 +67,27 @@ o2::framework::DataProcessorSpec getRawToDigitsSpec(int channel, const std::stri return timeBins; }); processAttributes->maxEvents = static_cast(ic.options().get("max-events")); + processAttributes->tpcSectors = tpcSectors; } + // ===| data processor |==================================================== + // auto processingFct = [processAttributes, channel](ProcessingContext& pc) { if (processAttributes->quit) { return; } - //uint64_t activeSectors = 0; - - // lambda that snapshots digits to be sent out; prepares and attaches header with sector information + // ===| digit snapshot |=== + // + // lambda that snapshots digits to be sent out; + // prepares and attaches header with sector information + // auto snapshotDigits = [&pc, processAttributes, channel](std::vector const& digits, int sector) { o2::tpc::TPCSectorHeader header{sector}; header.activeSectors = processAttributes->activeSectors; - // note that snapshoting only works with non-const references (to be fixed?) - pc.outputs().snapshot(Output{"TPC", "DIGITS", static_cast(channel), Lifetime::Timeframe, header}, + // digit for now are transported per sector, not per lane + // pc.outputs().snapshot(Output{"TPC", "DIGITS", static_cast(channel), Lifetime::Timeframe, header}, + pc.outputs().snapshot(Output{"TPC", "DIGITS", static_cast(sector), Lifetime::Timeframe, header}, const_cast&>(digits)); }; @@ -90,27 +100,26 @@ o2::framework::DataProcessorSpec getRawToDigitsSpec(int channel, const std::stri continue; } + // ===| extract electronics mapping information |=== const auto subSpecification = dh->subSpecification; const auto cruID = subSpecification >> 16; const auto linkID = ((subSpecification + (subSpecification >> 8)) & 0xFF) - 1; const auto dataWrapperID = ((subSpecification >> 8) & 0xFF) > 0; const auto globalLinkID = linkID + dataWrapperID * 12; const auto sector = cruID / 10; + + // update active sectors processAttributes->activeSectors |= (0x1 << sector); + // set up mapping information for raw reader auto& reader = processAttributes->rawReader.getReaders()[0]; reader->forceCRU(cruID); reader->setLink(globalLinkID); - //LOG(INFO) << dh->dataOrigin.as() << "/" << dh->dataDescription.as() << "/" - //<< dh->subSpecification << " payload size " << dh->payloadSize; - //LOG(INFO) << "CRU: " << cruID << " -- linkID: " << linkID << " -- dataWrapperID: " << dataWrapperID << " -- globalLinkID: " << globalLinkID; + LOGP(debug, "Specifier: {}/{}/{}", dh->dataOrigin.as(), dh->dataDescription.as(), dh->subSpecification); + LOGP(debug, "Payload size: {}", dh->payloadSize); + LOGP(debug, "CRU: {}; linkID: {}; dataWrapperID: {}; globalLinkID: {}", cruID, linkID, dataWrapperID, globalLinkID); - // there is a bug in InpuRecord::get for vectors of simple types, not catched in - // DataAllocator unit test - //auto data = inputs.get>(input.spec->binding.c_str()); - //LOG(INFO) << "data size " << data.size(); - //printHeader(); try { o2::framework::RawParser parser(input.payload, dh->payloadSize); @@ -124,30 +133,34 @@ o2::framework::DataProcessorSpec getRawToDigitsSpec(int channel, const std::stri } const auto& rdh = *rdhPtr; //printRDH(rdh); - // ugly event handling ... + + // ===| event handling |=== + // + // really ugly, better treatment requires extension in DPL + // events are are detected by close by orbit numbers + // const auto hbOrbit = rdh.heartbeatOrbit; const auto lastOrbit = processAttributes->lastOrbit; if ((lastOrbit > 0) && (hbOrbit > (lastOrbit + 3))) { auto& digitDump = processAttributes->digitDump; digitDump.incrementNEvents(); - LOG(INFO) << fmt::format("Number of processed events: {} ({})", digitDump.getNumberOfProcessedEvents(), processAttributes->maxEvents); + LOGP(info, "Number of processed events: {} ({})", digitDump.getNumberOfProcessedEvents(), processAttributes->maxEvents); digitDump.sortDigits(); - // add publish here - for (int isector = 0; isector < Sector::MAXSECTOR; ++isector) { - if (processAttributes->activeSectors & (0x1 << isector)) { - snapshotDigits(digitDump.getDigits(isector), isector); - } + + // publish digits of all configured sectors + for (auto isector : processAttributes->tpcSectors) { + snapshotDigits(digitDump.getDigits(isector), isector); } digitDump.clearDigits(); processAttributes->activeSectors = 0; if (digitDump.getNumberOfProcessedEvents() >= processAttributes->maxEvents) { - LOG(INFO) << fmt::format("Maximm number of events reached ({}), no more processing will be done", processAttributes->maxEvents); + LOGP(info, "Maximum number of events reached ({}), no more processing will be done", processAttributes->maxEvents); processAttributes->quit = true; pc.services().get().endOfStream(); - pc.services().get().readyToQuit(QuitRequest::All); - //pc.services().get().readyToQuit(QuitRequest::Me); + //pc.services().get().readyToQuit(QuitRequest::All); + pc.services().get().readyToQuit(QuitRequest::Me); break; } } @@ -155,7 +168,7 @@ o2::framework::DataProcessorSpec getRawToDigitsSpec(int channel, const std::stri processAttributes->lastOrbit = hbOrbit; const auto size = it.size(); auto data = it.data(); - //LOG(INFO) << fmt::format("Data size: {}", size); + LOGP(debug, "Raw data block payload size: {}", size); int iFrame = 0; for (int i = 0; i < size; i += 16) { @@ -194,7 +207,9 @@ o2::framework::DataProcessorSpec getRawToDigitsSpec(int channel, const std::stri id << "TPCDigitizer" << channel; std::vector outputs; // define channel by triple of (origin, type id of data to be sent on this channel, subspecification) - outputs.emplace_back("TPC", "DIGITS", static_cast(channel), Lifetime::Timeframe); + for (auto isector : tpcSectors) { + outputs.emplace_back("TPC", "DIGITS", static_cast(isector), Lifetime::Timeframe); + } return DataProcessorSpec{ id.str().c_str(), diff --git a/Detectors/TPC/workflow/src/tpc-raw-to-digits-workflow.cxx b/Detectors/TPC/workflow/src/tpc-raw-to-digits-workflow.cxx index 2df4ac5287e0b..dd4c5658cd2e2 100644 --- a/Detectors/TPC/workflow/src/tpc-raw-to-digits-workflow.cxx +++ b/Detectors/TPC/workflow/src/tpc-raw-to-digits-workflow.cxx @@ -31,9 +31,12 @@ using namespace o2::framework; // customize the completion policy void customize(std::vector& policies) { - using o2::framework::CompletionPolicy; // we customize the completion policy for the writer since it should stream immediately - policies.push_back(CompletionPolicyHelpers::defineByName("TPCDigitWriter", CompletionPolicy::CompletionOp::Consume)); + using CompletionPolicy = o2::framework::CompletionPolicy; + using CompletionPolicyHelpers = o2::framework::CompletionPolicyHelpers; + policies.push_back(CompletionPolicyHelpers::defineByName("tpc-cluster-decoder.*", CompletionPolicy::CompletionOp::Consume)); + policies.push_back(CompletionPolicyHelpers::defineByName("tpc-clusterer.*", CompletionPolicy::CompletionOp::Consume)); + policies.push_back(CompletionPolicyHelpers::defineByName("tpc-tracker.*", CompletionPolicy::CompletionOp::Consume)); } // we need to add workflow options before including Framework/runDataProcessing @@ -49,7 +52,6 @@ void customize(std::vector& workflowOptions) std::vector options{ {"input-spec", VariantType::String, "A:TPC/RAWDATA", {"selection string input specs"}}, - {"dump-digits", o2::framework::VariantType::Bool, false, {"attach digit file dump"}}, {"tpc-lanes", VariantType::Int, defaultlanes, {laneshelp}}, {"tpc-sectors", VariantType::String, sectorDefault.c_str(), {sectorshelp}}, {"tpc-reco-output", VariantType::String, "", {tpcrthelp}}, @@ -85,12 +87,13 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) WorkflowSpec specs; - auto tpcsectors = o2::RangeTokenizer::tokenize(configcontext.options().get("tpc-sectors")); - auto lanes = 1; //getNumTPCLanes(tpcsectors, config); + // for the moment no parallel workflow, so just one lane hard coded + auto tpcSectors = o2::RangeTokenizer::tokenize(configcontext.options().get("tpc-sectors")); + auto lanes = 1; //getNumTPCLanes(tpcSectors, config); int fanoutsize = 0; for (int l = 0; l < lanes; ++l) { - specs.emplace_back(o2::tpc::getRawToDigitsSpec(fanoutsize, configcontext.options().get("input-spec"))); + specs.emplace_back(o2::tpc::getRawToDigitsSpec(fanoutsize, configcontext.options().get("input-spec"), tpcSectors)); fanoutsize++; } @@ -102,7 +105,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) recoOuput = tpcRecoOutputType.c_str(); } - auto tpcRecoWorkflow = o2::tpc::reco_workflow::getWorkflow(tpcsectors, false, lanes, "digitizer", recoOuput.data()); + auto tpcRecoWorkflow = o2::tpc::reco_workflow::getWorkflow(tpcSectors, tpcSectors, false, lanes, "digitizer", recoOuput.data()); specs.insert(specs.end(), tpcRecoWorkflow.begin(), tpcRecoWorkflow.end()); return specs; From f38e976654c23edfa9dcc73709f9c45854496362 Mon Sep 17 00:00:00 2001 From: wiechula Date: Mon, 10 Feb 2020 14:47:58 +0100 Subject: [PATCH 11/24] Dump laser tracks to tree, get span of tracks from a bundle --- .../TPC/include/DataFormatsTPC/LaserTrack.h | 13 ++++++++++ DataFormats/Detectors/TPC/src/LaserTrack.cxx | 24 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/LaserTrack.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/LaserTrack.h index 5fcc377df76c5..7f9bbf388dc5a 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/LaserTrack.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/LaserTrack.h @@ -12,6 +12,7 @@ #define ALICEO2_TPC_LASERTRACK #include +#include #include "ReconstructionDataFormats/Track.h" @@ -80,6 +81,18 @@ class LaserTrackContainer /// \return array of laser tracks const auto& getLaserTracks() const { return mLaserTracks; } + /// dump tracks to a tree for simple visualization + static void dumpToTree(const std::string_view fileName); + + /// get span with tracks in one bundle + gsl::span getTracksInBundle(int side, int rod, int bundle) + { + const int startID = LaserTrack::NumberOfTracks / 2 * side + + LaserTrack::BundlesPerRod * LaserTrack::TracksPerBundle * rod + + LaserTrack::TracksPerBundle * bundle; + return gsl::span(&mLaserTracks[startID], LaserTrack::TracksPerBundle); + } + private: std::array mLaserTracks; diff --git a/DataFormats/Detectors/TPC/src/LaserTrack.cxx b/DataFormats/Detectors/TPC/src/LaserTrack.cxx index 27b810931fa37..0700cadb6d05f 100644 --- a/DataFormats/Detectors/TPC/src/LaserTrack.cxx +++ b/DataFormats/Detectors/TPC/src/LaserTrack.cxx @@ -12,11 +12,16 @@ /// \brief Laser track parameters /// \author Jens Wiechula, jens.wiechula@ikf.uni-frankfurt.de +#include #include #include #include +#include #include +#include "TFile.h" +#include "TTree.h" + #include "DataFormatsTPC/LaserTrack.h" using namespace o2::tpc; @@ -45,3 +50,22 @@ void LaserTrackContainer::loadTracksFromFile() mLaserTracks[id] = LaserTrack(id, x, alpha, {p0, p1, p2, p3, p4}); } } + +void LaserTrackContainer::dumpToTree(const std::string_view fileName) +{ + LaserTrackContainer c; + c.loadTracksFromFile(); + const auto& tracks = c.getLaserTracks(); + std::vector vtracks; + + for (const auto& track : tracks) { + vtracks.emplace_back(track); + } + + std::unique_ptr fout(TFile::Open(fileName.data(), "recreate")); + TTree t("laserTracks", "Laser Tracks"); + t.Branch("tracks", &vtracks); + t.Fill(); + fout->Write(); + fout->Close(); +} From c0671c2909a1c1a9729a3a820bd8885201995f8b Mon Sep 17 00:00:00 2001 From: wiechula Date: Thu, 20 Feb 2020 10:58:34 +0100 Subject: [PATCH 12/24] Link based zero suppression format --- DataFormats/Detectors/TPC/CMakeLists.txt | 3 +- .../DataFormatsTPC/ZeroSuppressionLinkBased.h | 125 ++++++++++++++++++ 2 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppressionLinkBased.h diff --git a/DataFormats/Detectors/TPC/CMakeLists.txt b/DataFormats/Detectors/TPC/CMakeLists.txt index af8585fc1800f..0f7bbc7509967 100644 --- a/DataFormats/Detectors/TPC/CMakeLists.txt +++ b/DataFormats/Detectors/TPC/CMakeLists.txt @@ -42,7 +42,8 @@ o2_target_root_dictionary( include/DataFormatsTPC/Defs.h include/DataFormatsTPC/dEdxInfo.h include/DataFormatsTPC/CompressedClusters.h - include/DataFormatsTPC/ZeroSuppression.h) + include/DataFormatsTPC/ZeroSuppression.h + include/DataFormatsTPC/ZeroSuppressionLinkBased.h) o2_add_test( ClusterNative diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppressionLinkBased.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppressionLinkBased.h new file mode 100644 index 0000000000000..e08212d3971a8 --- /dev/null +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppressionLinkBased.h @@ -0,0 +1,125 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ZeroSuppressionLinkBased.h +/// \brief definitions to deal with the link based zero suppression format +/// \author Jens Wiechula + +#ifndef ALICEO2_DATAFORMATSTPC_ZeroSuppressionLinkBased_H +#define ALICEO2_DATAFORMATSTPC_ZeroSuppressionLinkBased_H + +#include + +namespace o2 +{ +namespace tpc +{ +namespace zerosupp_link_based +{ +static constexpr uint32_t DataWordSize = 16; ///< size of header word and data words +static constexpr uint32_t ChannelsPerWord = 10; ///< number of ADC values in one 128b word +static constexpr uint32_t DataBitSize = 12; ///< number of bits of the data representation +static constexpr uint32_t SignificantBits = 2; ///< number of bits used for floating point precision +static constexpr float FloatConversion = 1.f / float(1 << SignificantBits); ///< conversion factor from integer representation to float + +/// header definition of the zero suppressed link based data format +struct Header { + union { + uint64_t word0 = 0; ///< lower 64 bits + struct { /// + uint64_t bitMaskLow : 64; ///< lower bits of the 80 bit bitmask + }; /// + }; /// + /// + union { /// + uint64_t word1 = 0; ///< upper bits of the 80 bit bitmask + struct { /// + uint64_t bitMaskHigh : 16; ///< higher bits of the 80 bit bitmask + uint32_t bunchCrossing : 12; ///< bunch crossing number + uint32_t numWordsPayload : 8; ///< number of 128bit words with 12bit ADC values + uint64_t zero : 28; ///< not used + }; + }; + + std::bitset<80> getChannelBits() + { + return std::bitset<80>((std::bitset<80>(bitMaskHigh) << 64) | std::bitset<80>(bitMaskLow)); + } +}; + +struct Data { + uint64_t adcValues[2]{}; ///< 128bit ADC values (max. 10x12bit) + + /// set ADC 'value' at position 'pos' (0-9) + void setADCValue(uint32_t pos, uint64_t value) + { + if (pos < 5) { + const uint64_t set = (value & uint64_t(0xFFF)) << (pos * 12); + const uint64_t mask = (0xFFFFFFFFFFFFFFFF ^ (uint64_t(0xFFF) << (pos * 12))); + adcValues[0] &= mask; + adcValues[0] |= set; + } else if (pos == 5) { + const uint64_t set1 = (value & uint64_t(0xF)) << (pos * 12); + const uint64_t set2 = (value >> 4) & uint64_t(0xFF); + const uint64_t mask1 = 0x0FFFFFFFFFFFFFFF; + const uint64_t mask2 = (0xFFFFFFFFFFFFFF00 ^ (uint64_t(0xFF) << (pos * 12))); + adcValues[0] &= mask1; + adcValues[0] |= set1; + adcValues[1] &= mask2; + adcValues[1] |= set2; + } else { + const uint64_t set = (value & uint64_t(0xFFF)) << ((pos * 12) + 8); + const uint64_t mask = (0xFFFFFFFFFFF000FF ^ (uint64_t(0xFFF) << ((pos * 12) + 8))); + adcValues[1] &= mask; + adcValues[1] |= set; + } + } + + /// ADC value of channel at position 'pos' (0-9) + uint32_t getADCValue(uint32_t pos) + { + if (pos < 5) { + return (adcValues[0] >> (pos * 12)) & uint64_t(0xFFF); + } else if (pos == 5) { + return (adcValues[0] >> (pos * 12)) | ((adcValues[1] & uint64_t(0xFF)) << 4); + } else { + return (adcValues[1] >> (8 + pos * 12)) & uint64_t(0xFFF); + } + } +}; + +struct Container { + Header header; ///< header data + Data data[0]; ///< 128 bit words with 12bit ADC values + + /// return 12bit ADC value for a specific word in the data stream + uint32_t getADCValue(uint32_t word) { return data[word / ChannelsPerWord].getADCValue(word % ChannelsPerWord); } + + /// return 12bit ADC value for a specific word in the data stream converted to float + float getADCValueFloat(uint32_t word) { return float(getADCValue(word)) * FloatConversion; } + + /// set 12bit ADC value for a specific word in the data stream + void setADCValue(uint32_t word, uint64_t value) { data[word / ChannelsPerWord].setADCValue(word % ChannelsPerWord, value); } + + /// return 12bit ADC value for a specific word in the data stream converted to float + void setADCValueFloat(uint32_t word, float value) { data[word / ChannelsPerWord].setADCValue(word, uint64_t((value + FloatConversion / 2.f) / FloatConversion)); } + + /// get position of next container. Validity check to be done outside! + Container* next() + { + return (Container*)(this + (header.numWordsPayload + 1) * DataWordSize); + } +}; // namespace zerosupp_link_based + +} // namespace zerosupp_link_based +} // namespace tpc +} // namespace o2 + +#endif From 4f475c4b6156d2029d6426d36806f8a27f826ae0 Mon Sep 17 00:00:00 2001 From: wiechula Date: Fri, 28 Feb 2020 12:23:17 +0100 Subject: [PATCH 13/24] Format extension and improvement * Allow for also treating Decoded raw data * Slightly change data laylout --- .../DataFormatsTPC/ZeroSuppressionLinkBased.h | 135 ++++++++++++------ 1 file changed, 95 insertions(+), 40 deletions(-) diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppressionLinkBased.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppressionLinkBased.h index e08212d3971a8..c6275f0a4da49 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppressionLinkBased.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppressionLinkBased.h @@ -23,11 +23,9 @@ namespace tpc { namespace zerosupp_link_based { -static constexpr uint32_t DataWordSize = 16; ///< size of header word and data words -static constexpr uint32_t ChannelsPerWord = 10; ///< number of ADC values in one 128b word -static constexpr uint32_t DataBitSize = 12; ///< number of bits of the data representation -static constexpr uint32_t SignificantBits = 2; ///< number of bits used for floating point precision -static constexpr float FloatConversion = 1.f / float(1 << SignificantBits); ///< conversion factor from integer representation to float + +static constexpr uint32_t DataWordSizeBits = 128; ///< size of header word and data words in bits +static constexpr uint32_t DataWordSizeBytes = DataWordSizeBits / 8; ///< size of header word and data words in bytes /// header definition of the zero suppressed link based data format struct Header { @@ -54,70 +52,127 @@ struct Header { } }; +/// empty header for + +/// ADC data container +/// +/// In case of zero suppressed data, the ADC values are stored with 12 bit +/// 10bit + 2bit precision +/// In case of decoded raw data, the pure 10 bit ADC values are stored +/// +/// The data is packed in 128 bit words, or 2x64 bit. Each 64 bit word has 4 bit +/// padding. +/// So it is either 2 x ((5 x 12 bit) + 4 bit padding), or +/// 2 x ((6 x 10 bit) + 4 bit padding) +template struct Data { + static constexpr uint32_t ChannelsPerHalfWord = DataWordSizeBits / DataBitSizeT / 2; ///< number of ADC values in one 128b word + static constexpr uint32_t DataBitSize = DataBitSizeT; ///< number of bits of the data representation + static constexpr uint32_t SignificantBits = SignificantBitsT; ///< number of bits used for floating point precision + static constexpr uint64_t BitMask = ((uint64_t(1) << DataBitSize) - 1); ///< mask for bits + static constexpr float FloatConversion = 1.f / float(1 << SignificantBits); ///< conversion factor from integer representation to float + uint64_t adcValues[2]{}; ///< 128bit ADC values (max. 10x12bit) /// set ADC 'value' at position 'pos' (0-9) void setADCValue(uint32_t pos, uint64_t value) { - if (pos < 5) { - const uint64_t set = (value & uint64_t(0xFFF)) << (pos * 12); - const uint64_t mask = (0xFFFFFFFFFFFFFFFF ^ (uint64_t(0xFFF) << (pos * 12))); - adcValues[0] &= mask; - adcValues[0] |= set; - } else if (pos == 5) { - const uint64_t set1 = (value & uint64_t(0xF)) << (pos * 12); - const uint64_t set2 = (value >> 4) & uint64_t(0xFF); - const uint64_t mask1 = 0x0FFFFFFFFFFFFFFF; - const uint64_t mask2 = (0xFFFFFFFFFFFFFF00 ^ (uint64_t(0xFF) << (pos * 12))); - adcValues[0] &= mask1; - adcValues[0] |= set1; - adcValues[1] &= mask2; - adcValues[1] |= set2; - } else { - const uint64_t set = (value & uint64_t(0xFFF)) << ((pos * 12) + 8); - const uint64_t mask = (0xFFFFFFFFFFF000FF ^ (uint64_t(0xFFF) << ((pos * 12) + 8))); - adcValues[1] &= mask; - adcValues[1] |= set; - } + const uint32_t word = pos / ChannelsPerHalfWord; + const uint32_t posInWord = pos % ChannelsPerHalfWord; + + const uint64_t set = (value & BitMask) << (posInWord * DataBitSize); + const uint64_t mask = (0xFFFFFFFFFFFFFFFF ^ (BitMask << (posInWord * DataBitSize))); + + adcValues[word] &= mask; + adcValues[word] |= set; + } + + /// set ADC value from float + void setADCValueFloat(uint32_t pos, float value) + { + setADCValue(pos, uint64_t((value + 0.5f * FloatConversion) / FloatConversion)); } - /// ADC value of channel at position 'pos' (0-9) + /// get ADC value of channel at position 'pos' (0-9) uint32_t getADCValue(uint32_t pos) { - if (pos < 5) { - return (adcValues[0] >> (pos * 12)) & uint64_t(0xFFF); - } else if (pos == 5) { - return (adcValues[0] >> (pos * 12)) | ((adcValues[1] & uint64_t(0xFF)) << 4); - } else { - return (adcValues[1] >> (8 + pos * 12)) & uint64_t(0xFFF); - } + const uint32_t word = pos / ChannelsPerHalfWord; + const uint32_t posInWord = pos % ChannelsPerHalfWord; + + return (adcValues[word] >> (posInWord * DataBitSize)) & BitMask; } + + /// get ADC value in float + float getADCValueFloat(uint32_t pos) + { + return float(getADCValue(pos)) * FloatConversion; + } + + /// reset all ADC values + void reset() + { + adcValues[0] = 0; + adcValues[1] = 0; + } +}; + +template +struct ContainerT; + +template +struct ContainerT { + Header header; ///< header data + Data data[0]; ///< 128 bit words with 12bit ADC values + + uint32_t dataWords() { return header.numWordsPayload + 1; } +}; + +template +struct ContainerT { + Data data[0]; ///< 128 bit words with 12bit ADC values + + uint32_t dataWords() { return 7; } }; +/// Container for decoded data, either zero suppressed or pure raw data +/// +/// In case of pure raw data, no header is needed, since all 80 channels will be filled +template struct Container { - Header header; ///< header data - Data data[0]; ///< 128 bit words with 12bit ADC values + static constexpr uint32_t ChannelsPerWord = DataWordSizeBits / DataBitSizeT; ///< number of ADC values in one 128b word + + ContainerT cont; ///< Templated data container /// return 12bit ADC value for a specific word in the data stream - uint32_t getADCValue(uint32_t word) { return data[word / ChannelsPerWord].getADCValue(word % ChannelsPerWord); } + uint32_t getADCValue(uint32_t word) { return cont.data[word / ChannelsPerWord].getADCValue(word % ChannelsPerWord); } /// return 12bit ADC value for a specific word in the data stream converted to float - float getADCValueFloat(uint32_t word) { return float(getADCValue(word)) * FloatConversion; } + float getADCValueFloat(uint32_t word) { return cont.data[word / ChannelsPerWord].getADCValueFloat(word % ChannelsPerWord); } /// set 12bit ADC value for a specific word in the data stream - void setADCValue(uint32_t word, uint64_t value) { data[word / ChannelsPerWord].setADCValue(word % ChannelsPerWord, value); } + void setADCValue(uint32_t word, uint64_t value) { cont.data[word / ChannelsPerWord].setADCValue(word % ChannelsPerWord, value); } /// return 12bit ADC value for a specific word in the data stream converted to float - void setADCValueFloat(uint32_t word, float value) { data[word / ChannelsPerWord].setADCValue(word, uint64_t((value + FloatConversion / 2.f) / FloatConversion)); } + void setADCValueFloat(uint32_t word, float value) { cont.data[word / ChannelsPerWord].setADCValueFloat(word % ChannelsPerWord, value); } + + /// reset all ADC values + void reset() + { + for (int i = 0; i < cont.dataWords(); ++i) { + cont.data[i].reset(); + } + } /// get position of next container. Validity check to be done outside! Container* next() { - return (Container*)(this + (header.numWordsPayload + 1) * DataWordSize); + return (Container*)(this + cont.dataWords() * DataWordSizeBytes); } }; // namespace zerosupp_link_based +using ContainerZS = Container<>; +using ContainerDecoded = Container<10, 0, false>; + } // namespace zerosupp_link_based } // namespace tpc } // namespace o2 From cc46de643e2dc8bfceb1de5b59793ea31efcd145 Mon Sep 17 00:00:00 2001 From: wiechula Date: Wed, 26 Feb 2020 14:45:16 +0100 Subject: [PATCH 14/24] Add option to use Link base ZS in raw reco workflow * Add workflow definiton for link based data decoding and convert it to digits * Modify raw to digits workflow to allow for slecting the decoder type --- Detectors/TPC/workflow/CMakeLists.txt | 1 + .../include/TPCWorkflow/LinkZSToDigitsSpec.h | 33 ++ .../TPC/workflow/src/LinkZSToDigitsSpec.cxx | 305 ++++++++++++++++++ .../src/tpc-raw-to-digits-workflow.cxx | 25 +- 4 files changed, 362 insertions(+), 2 deletions(-) create mode 100644 Detectors/TPC/workflow/include/TPCWorkflow/LinkZSToDigitsSpec.h create mode 100644 Detectors/TPC/workflow/src/LinkZSToDigitsSpec.cxx diff --git a/Detectors/TPC/workflow/CMakeLists.txt b/Detectors/TPC/workflow/CMakeLists.txt index 5323ec17a7de2..c9eb3ba7a7bc5 100644 --- a/Detectors/TPC/workflow/CMakeLists.txt +++ b/Detectors/TPC/workflow/CMakeLists.txt @@ -17,6 +17,7 @@ o2_add_library(TPCWorkflow src/CATrackerSpec.cxx src/TrackReaderSpec.cxx src/RawToDigitsSpec.cxx + src/LinkZSToDigitsSpec.cxx TARGETVARNAME targetName PUBLIC_LINK_LIBRARIES O2::Framework O2::DataFormatsTPC O2::DPLUtils O2::TPCReconstruction diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/LinkZSToDigitsSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/LinkZSToDigitsSpec.h new file mode 100644 index 0000000000000..b3439d3f21861 --- /dev/null +++ b/Detectors/TPC/workflow/include/TPCWorkflow/LinkZSToDigitsSpec.h @@ -0,0 +1,33 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file LinkZSToDigitsSpec.h +/// @author Jens Wiechula +/// @since 2020-02-20 +/// @brief Processor spec for running link based zero suppressed data to digit converter + +#ifndef TPC_LinkZSToDigitsSpec_H_ +#define TPC_LinkZSToDigitsSpec_H_ + +#include "Framework/DataProcessorSpec.h" +#include + +namespace o2 +{ +namespace tpc +{ + +/// Processor to convert link based zero suppressed data to simulation digits +o2::framework::DataProcessorSpec getLinkZSToDigitsSpec(int channel, const std::string_view inputDef, std::vector const& tpcSectors); + +} // end namespace tpc +} // end namespace o2 + +#endif // TPC_LinkZSToDigitsSpec_H_ diff --git a/Detectors/TPC/workflow/src/LinkZSToDigitsSpec.cxx b/Detectors/TPC/workflow/src/LinkZSToDigitsSpec.cxx new file mode 100644 index 0000000000000..db7e0ff87eda7 --- /dev/null +++ b/Detectors/TPC/workflow/src/LinkZSToDigitsSpec.cxx @@ -0,0 +1,305 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include "Framework/WorkflowSpec.h" +#include "Framework/ControlService.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/DataRefUtils.h" +#include "Framework/Lifetime.h" +#include "Framework/Logger.h" +#include "DPLUtils/RawParser.h" +#include "Headers/DataHeader.h" +#include "DataFormatsTPC/TPCSectorHeader.h" +#include "DataFormatsTPC/ZeroSuppressionLinkBased.h" +#include "TPCBase/Digit.h" +#include "TPCBase/CRU.h" +#include "TPCBase/PadSecPos.h" +#include "TPCBase/Mapper.h" +#include "TPCReconstruction/RawReaderCRU.h" +#include "TPCWorkflow/LinkZSToDigitsSpec.h" +#include +#include + +using namespace o2::framework; +using SubSpecificationType = o2::framework::DataAllocator::SubSpecificationType; + +namespace o2 +{ +namespace tpc +{ + +o2::framework::DataProcessorSpec getLinkZSToDigitsSpec(int channel, const std::string_view inputDef, std::vector const& tpcSectors) +{ + + static constexpr int MaxNumberOfBunches = 3564; + + struct ProcessAttributes { + uint32_t lastOrbit{0}; ///< last processed orbit number + uint32_t firstOrbit{0}; ///< first orbit number, required for time bin calculation + uint32_t firstBC{0}; ///< first bunch crossing number, required for time bin calculation + uint32_t maxEvents{100}; ///< maximum number of events to process + uint32_t processedEvents{0}; ///< number of processed events + uint64_t activeSectors{0}; ///< bit mask of active sectors + bool isContinuous{false}; ///< if data is triggered or continuous + bool quit{false}; ///< if workflow is ready to quit + std::vector tpcSectors{}; ///< tpc sector configuration + std::array, Sector::MAXSECTOR> digitsAll{}; ///< digit vector to be stored inside the file + + /// cleanup of digits + void clearDigits() + { + for (auto& digits : digitsAll) { + digits.clear(); + } + } + + /// Digit sorting according to expected output from simulation + void sortDigits() + { + // sort digits + for (auto& digits : digitsAll) { + std::sort(digits.begin(), digits.end(), [](const auto& a, const auto& b) { + if (a.getTimeStamp() < b.getTimeStamp()) + return true; + if ((a.getTimeStamp() == b.getTimeStamp()) && (a.getRow() < b.getRow())) + return true; + return false; + }); + } + } + }; + + // ===| stateful initialization |============================================= + // + auto initFunction = [channel, tpcSectors](InitContext& ic) { + auto processAttributes = std::make_shared(); + // ===| create and set up processing attributes |=== + { + processAttributes->maxEvents = static_cast(ic.options().get("max-events")); + processAttributes->tpcSectors = tpcSectors; + } + + // ===| data processor |==================================================== + // + auto processingFct = [processAttributes, channel](ProcessingContext& pc) { + if (processAttributes->quit) { + return; + } + + // ===| digit snapshot |=== + // + // lambda that snapshots digits to be sent out; + // prepares and attaches header with sector information + // + auto snapshotDigits = [&pc, processAttributes, channel](std::vector const& digits, int sector) { + o2::tpc::TPCSectorHeader header{sector}; + header.activeSectors = processAttributes->activeSectors; + // digit for now are transported per sector, not per lane + // pc.outputs().snapshot(Output{"TPC", "DIGITS", static_cast(channel), Lifetime::Timeframe, header}, + pc.outputs().snapshot(Output{"TPC", "DIGITS", static_cast(sector), Lifetime::Timeframe, header}, + const_cast&>(digits)); + }; + + auto& mapper = Mapper::instance(); + + // loop over all inputs + for (auto& input : pc.inputs()) { + const auto* dh = DataRefUtils::getHeader(input); + + // select only RAW data + if (dh->dataDescription != o2::header::gDataDescriptionRawData) { + continue; + } + + // ===| extract electronics mapping information |=== + const auto subSpecification = dh->subSpecification; + const auto cruID = subSpecification >> 16; + const auto linkID = ((subSpecification + (subSpecification >> 8)) & 0xFF) - 1; + const auto dataWrapperID = ((subSpecification >> 8) & 0xFF) > 0; + const auto globalLinkID = linkID + dataWrapperID * 12; + const auto sector = cruID / 10; + const CRU cru(cruID); + const int fecLinkOffsetCRU = (mapper.getPartitionInfo(cru.partition()).getNumberOfFECs() + 1) / 2; + const int fecInPartition = (globalLinkID % 12) + (globalLinkID > 11) * fecLinkOffsetCRU; + const int regionIter = cruID % 2; + + const int sampaMapping[10] = {0, 0, 1, 1, 2, 3, 3, 4, 4, 2}; + const int channelOffset[10] = {0, 16, 0, 16, 0, 0, 16, 0, 16, 16}; + + processAttributes->activeSectors |= (0x1 << sector); + + LOGP(debug, "Specifier: {}/{}/{}", dh->dataOrigin.as(), dh->dataDescription.as(), dh->subSpecification); + LOGP(debug, "Payload size: {}", dh->payloadSize); + LOGP(debug, "CRU: {}; linkID: {}; dataWrapperID: {}; globalLinkID: {}", cruID, linkID, dataWrapperID, globalLinkID); + + try { + o2::framework::RawParser parser(input.payload, dh->payloadSize); + + for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { + auto* rdhPtr = it.get_if(); + if (!rdhPtr) { + break; + } + + const auto& rdh = *rdhPtr; + + // ===| only accept physics triggers |=== + if (rdh.triggerType != 0x10) { + continue; + } + + // ===| event handling |=== + // + // really ugly, better treatment required extension in DPL + // events are are detected by close by orbit numbers + // + const auto hbOrbit = rdh.heartbeatOrbit; + const auto lastOrbit = processAttributes->lastOrbit; + + if (!processAttributes->firstOrbit) { + processAttributes->firstOrbit = hbOrbit; + if (!processAttributes->isContinuous) { + processAttributes->firstBC = rdh.heartbeatBC; + } + } + + const auto globalBCoffset = ((hbOrbit - processAttributes->firstOrbit) * MaxNumberOfBunches - processAttributes->firstBC); // To be calculated + + if ((lastOrbit > 0) && (hbOrbit > (lastOrbit + 3))) { + ++processAttributes->processedEvents; + LOG(INFO) << fmt::format("Number of processed events: {} ({})", processAttributes->processedEvents, processAttributes->maxEvents); + processAttributes->sortDigits(); + + // publish digits of all configured sectors + for (auto isector : processAttributes->tpcSectors) { + snapshotDigits(processAttributes->digitsAll[isector], isector); + } + processAttributes->clearDigits(); + + processAttributes->activeSectors = 0; + if (processAttributes->processedEvents >= processAttributes->maxEvents) { + LOGP(info, "Maximum number of events reached ({}), no more processing will be done", processAttributes->maxEvents); + processAttributes->quit = true; + pc.services().get().endOfStream(); + //pc.services().get().readyToQuit(QuitRequest::All); + pc.services().get().readyToQuit(QuitRequest::Me); + break; + } + } + + processAttributes->lastOrbit = hbOrbit; + const auto size = it.size(); + auto data = it.data(); + LOGP(debug, "Raw data block payload size: {}", size); + + // cast raw data pointer to link based zero suppression definition + zerosupp_link_based::ContainerZS* zsdata = (zerosupp_link_based::ContainerZS*)data; + const zerosupp_link_based::ContainerZS* const zsdataEnd = (zerosupp_link_based::ContainerZS*)(data + size); + + while (zsdata < zsdataEnd) { + const auto channelBits = zsdata->cont.header.getChannelBits(); + const uint32_t numberOfWords = zsdata->cont.header.numWordsPayload; + assert((channelBits.count() - 1) / 10 == numberOfWords - 1); + + std::size_t processedChannels = 0; + for (std::size_t ichannel = 0; ichannel < channelBits.size(); ++ichannel) { + if (!channelBits[ichannel]) { + continue; + } + + // adc value + const auto adcValue = zsdata->getADCValueFloat(processedChannels); + + // pad mapping + // TODO: verify the assumptions of the channel mapping! + // assumes the following sorting (s_chn is the channel on the sampa), + // in this case for even regiona (lower half fec) + // chn# SAMPA s_chn + // 0 0 0 + // 1 0 1 + // 2 0 16 + // 3 0 17 + // 4 1 0 + // 5 1 1 + // 6 1 16 + // 7 1 17 + // 8 2 0 + // 9 2 1 + // + // 10 0 2 + // 11 0 3 + // 12 0 18 + // 13 0 19 + // 14 1 2 + // 15 1 3 + // 16 1 18 + // 17 1 19 + // 18 2 2 + // 19 2 3 + // + // 20 0 4 + // 21 0 5 + // 22 0 20 + // 23 0 21 + // ... + // For the uneven regions (upper half fec), the sampa ordering + // is 3, 3, 3, 3, 4, 4, 4, 4, 2, 2 + const int istreamm = ((ichannel % 10) / 2); + const int partitionStream = istreamm + regionIter * 5; + const int sampaOnFEC = sampaMapping[partitionStream]; + const int channel = (ichannel % 2) + 2 * (ichannel / 10); + const int channelOnSAMPA = channel + channelOffset[partitionStream]; + + const auto padSecPos = mapper.padSecPos(cru, fecInPartition, sampaOnFEC, channelOnSAMPA); + const auto& padPos = padSecPos.getPadPos(); + int timebin = (globalBCoffset + zsdata->cont.header.bunchCrossing) / 8; // To be calculated + + // add digit + processAttributes->digitsAll[sector].emplace_back(cruID, adcValue, padPos.getRow(), padPos.getPad(), timebin); + ++processedChannels; + } + + // go to next time bin + zsdata = zsdata->next(); + } + } + + } catch (const std::runtime_error& e) { + LOG(ERROR) << "can not create raw parser form input data"; + o2::header::hexDump("payload", input.payload, dh->payloadSize, 64); + LOG(ERROR) << e.what(); + } + } + }; + + return processingFct; + }; + + std::stringstream id; + id << "TPCDigitizer" << channel; + + std::vector outputs; // define channel by triple of (origin, type id of data to be sent on this channel, subspecification) + for (auto isector : tpcSectors) { + outputs.emplace_back("TPC", "DIGITS", static_cast(isector), Lifetime::Timeframe); + } + + return DataProcessorSpec{ + id.str().c_str(), + select(inputDef.data()), + outputs, + AlgorithmSpec{initFunction}, + Options{ + {"max-events", VariantType::Int, 100, {"maximum number of events to process"}}, + {"pedestal-file", VariantType::String, "", {"file with pedestals and noise for zero suppression"}}}}; +} +} // namespace tpc +} // namespace o2 diff --git a/Detectors/TPC/workflow/src/tpc-raw-to-digits-workflow.cxx b/Detectors/TPC/workflow/src/tpc-raw-to-digits-workflow.cxx index dd4c5658cd2e2..8c245bec30d0c 100644 --- a/Detectors/TPC/workflow/src/tpc-raw-to-digits-workflow.cxx +++ b/Detectors/TPC/workflow/src/tpc-raw-to-digits-workflow.cxx @@ -19,6 +19,7 @@ #include "CommonUtils/ConfigurableParam.h" #include "Algorithm/RangeTokenizer.h" #include "TPCWorkflow/RawToDigitsSpec.h" +#include "TPCWorkflow/LinkZSToDigitsSpec.h" #include "TPCWorkflow/RecoWorkflow.h" #include "TPCBase/Sector.h" #include @@ -39,6 +40,16 @@ void customize(std::vector& policies) policies.push_back(CompletionPolicyHelpers::defineByName("tpc-tracker.*", CompletionPolicy::CompletionOp::Consume)); } +enum class DecoderType { + GBT, ///< GBT frame raw decoding + LinkZS ///< Link based zero suppression +}; + +const std::unordered_map DecoderTypeMap{ + {"GBT", DecoderType::GBT}, + {"LinkZS", DecoderType::LinkZS}, +}; + // we need to add workflow options before including Framework/runDataProcessing void customize(std::vector& workflowOptions) { @@ -48,13 +59,15 @@ void customize(std::vector& workflowOptions) const std::string sectorshelp("List of TPC sectors, comma separated ranges, e.g. 0-3,7,9-15"); const std::string sectorDefault = "0-" + std::to_string(o2::tpc::Sector::MAXSECTOR - 1); - const std::string tpcrthelp("Run TPC reco workflow to specified output type, currently supported: 'tracks'"); + const std::string tpcrthelp("Run TPC reco workflow to specified output type, currently supported: 'digits,clusters,tracks'"); + const std::string decoderHelp("Decoder type to use: 'GBT,LinkZS'"); std::vector options{ {"input-spec", VariantType::String, "A:TPC/RAWDATA", {"selection string input specs"}}, {"tpc-lanes", VariantType::Int, defaultlanes, {laneshelp}}, {"tpc-sectors", VariantType::String, sectorDefault.c_str(), {sectorshelp}}, {"tpc-reco-output", VariantType::String, "", {tpcrthelp}}, + {"decoder-type", VariantType::String, "GBT", {decoderHelp}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings (e.g.: 'TPCCalibPedestal.FirstTimeBin=10;...')"}}, {"configFile", VariantType::String, "", {"configuration file for configurable parameters"}}}; @@ -91,9 +104,17 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto tpcSectors = o2::RangeTokenizer::tokenize(configcontext.options().get("tpc-sectors")); auto lanes = 1; //getNumTPCLanes(tpcSectors, config); + const auto decoderType = DecoderTypeMap.at(configcontext.options().get("decoder-type")); + int fanoutsize = 0; for (int l = 0; l < lanes; ++l) { - specs.emplace_back(o2::tpc::getRawToDigitsSpec(fanoutsize, configcontext.options().get("input-spec"), tpcSectors)); + if (decoderType == DecoderType::GBT) { + specs.emplace_back(o2::tpc::getRawToDigitsSpec(fanoutsize, configcontext.options().get("input-spec"), tpcSectors)); + } else if (decoderType == DecoderType::LinkZS) { + specs.emplace_back(o2::tpc::getLinkZSToDigitsSpec(fanoutsize, configcontext.options().get("input-spec"), tpcSectors)); + } else { + LOG(FATAL) << "bad decoder type"; + } fanoutsize++; } From 62f7ed379cc11d55f8009288905f6c9141a0d331 Mon Sep 17 00:00:00 2001 From: wiechula Date: Wed, 4 Mar 2020 15:32:52 +0100 Subject: [PATCH 15/24] Add generic fit on histogrammed array --- Common/MathUtils/include/MathUtils/MathBase.h | 89 ++++++++++++++++++- Common/MathUtils/src/MathUtilsLinkDef.h | 3 + 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/Common/MathUtils/include/MathUtils/MathBase.h b/Common/MathUtils/include/MathUtils/MathBase.h index c6b3894b9a8c8..874774429293d 100644 --- a/Common/MathUtils/include/MathUtils/MathBase.h +++ b/Common/MathUtils/include/MathUtils/MathBase.h @@ -24,8 +24,16 @@ #include "TLinearFitter.h" #include "TVectorD.h" #include "TMath.h" +#include "TF1.h" +#include "Foption.h" +#include "HFitInterface.h" +#include "TFitResultPtr.h" +#include "TFitResult.h" +#include "Fit/Fitter.h" +#include "Fit/BinData.h" +#include "Math/WrappedMultiTF1.h" -#include +#include "Framework/Logger.h" namespace o2 { @@ -33,6 +41,85 @@ namespace math_utils { namespace math_base { +/// fit 1D array of histogrammed data with generic root function +/// +/// The code was extracted out of ROOT to be able to do fitting on an array with histogrammed data +/// instead of root histograms. +/// It is a stripped down version, so does not provide the same functionality. +/// To be used with care. +/// +/// \param[in] nbins size of the array and number of histogram bins +/// \param[in] arr array with elements +/// \param[in] xMin minimum range of the array +/// \param[in] xMax maximum range of the array +/// \param[in] func fit function +/// +/// +//template +//Double_t fitGaus(const size_t nBins, const T *arr, const T xMin, const T xMax, std::vector& param); +template +TFitResultPtr fit(const size_t nBins, const T* arr, const T xMin, const T xMax, TF1& func, std::string_view option = "") +{ + Foption_t fitOption; + ROOT::Fit::FitOptionsMake(ROOT::Fit::kHistogram, option.data(), fitOption); + + ROOT::Fit::DataRange range(xMin, xMax); + ROOT::Fit::DataOptions opt; + ROOT::Fit::BinData fitdata(opt, range); + fitdata.Initialize(nBins, 1); + + // create an empty TFitResult + std::shared_ptr tfr(new TFitResult()); + // create the fitter from an empty fit result + //std::shared_ptr fitter(new ROOT::Fit::Fitter(std::static_pointer_cast(tfr) ) ); + ROOT::Fit::Fitter fitter(tfr); + //ROOT::Fit::FitConfig & fitConfig = fitter->Config(); + + const double binWidth = double(xMax - xMin) / double(nBins); + + for (Int_t ibin = 0; ibin < nBins; ibin++) { + const double x = double(xMin) + double(ibin + 0.5) * binWidth; + const double y = double(arr[ibin]); + const double ey = std::sqrt(y); + fitdata.Add(x, y, ey); + } + + const int special = func.GetNumber(); + const int npar = func.GetNpar(); + bool linear = func.IsLinear(); + if (special == 299 + npar) { + linear = kTRUE; // for polynomial functions + } + // do not use linear fitter in these case + if (fitOption.Bound || fitOption.Like || fitOption.Errors || fitOption.Gradient || fitOption.More || fitOption.User || fitOption.Integral || fitOption.Minuit) { + linear = kFALSE; + } + + if (special != 0 && !fitOption.Bound && !linear) { + if (special == 100) { + ROOT::Fit::InitGaus(fitdata, &func); // gaussian + } else if (special == 400) { + ROOT::Fit::InitGaus(fitdata, &func); // landau (use the same) + } else if (special == 200) { + ROOT::Fit::InitExpo(fitdata, &func); // exponential + } + } + + if ((linear || fitOption.Gradient)) { + fitter.SetFunction(ROOT::Math::WrappedMultiTF1(func)); + } else { + fitter.SetFunction(static_cast(ROOT::Math::WrappedMultiTF1(func))); + } + + // standard least square fit + const bool fitok = fitter.Fit(fitdata, fitOption.ExecPolicy); + if (!fitok) { + LOGP(warning, "bad fit"); + } + + return TFitResultPtr(tfr); +} + /// fast fit of an array with ranges (histogram) with gaussian function /// /// Fitting procedure: diff --git a/Common/MathUtils/src/MathUtilsLinkDef.h b/Common/MathUtils/src/MathUtilsLinkDef.h index 687d68c4d1fcb..7be4951a0bee5 100644 --- a/Common/MathUtils/src/MathUtilsLinkDef.h +++ b/Common/MathUtils/src/MathUtilsLinkDef.h @@ -19,6 +19,9 @@ #pragma link C++ namespace o2::math_utils::math_base; +#pragma link C++ function o2::math_utils::math_base::fit < float>; +#pragma link C++ function o2::math_utils::math_base::fit < double>; + #pragma link C++ function o2::math_utils::math_base::fitGaus < float>; #pragma link C++ function o2::math_utils::math_base::fitGaus < double>; From eba28c881a4f32342c83ddb9cc2fa4aee0f44c45 Mon Sep 17 00:00:00 2001 From: wiechula Date: Wed, 4 Mar 2020 15:33:25 +0100 Subject: [PATCH 16/24] add more robust fit procedure --- .../Detectors/TPC/include/DataFormatsTPC/Defs.h | 5 +++-- Detectors/TPC/calibration/src/CalibPedestal.cxx | 15 ++++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/Defs.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/Defs.h index 97342f8caa22e..5e75d2b708bb0 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/Defs.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/Defs.h @@ -62,8 +62,9 @@ enum class PadSubset : char { /// Statistics type enum class StatisticsType { - GausFit, ///< Use Gaus fit for pedestal and noise - MeanStdDev ///< Use mean and standard deviation + GausFit, ///< Use slow gaus fit (better fit stability) + GausFitFast, ///< Use fast gaus fit (less accurate error treatment) + MeanStdDev ///< Use mean and standard deviation }; // default point definitions for PointND, PointNDlocal, PointNDglobal are in diff --git a/Detectors/TPC/calibration/src/CalibPedestal.cxx b/Detectors/TPC/calibration/src/CalibPedestal.cxx index c32b0b1a0d922..d30411ed23a32 100644 --- a/Detectors/TPC/calibration/src/CalibPedestal.cxx +++ b/Detectors/TPC/calibration/src/CalibPedestal.cxx @@ -21,6 +21,7 @@ #include "TPCCalibration/CalibPedestal.h" using namespace o2::tpc; +using o2::math_utils::math_base::fit; using o2::math_utils::math_base::fitGaus; using o2::math_utils::math_base::getStatisticsData; using o2::math_utils::math_base::StatisticsData; @@ -29,10 +30,10 @@ CalibPedestal::CalibPedestal(PadSubset padSubset) : CalibRawBase(padSubset), mFirstTimeBin(0), mLastTimeBin(500), - mADCMin(0), - mADCMax(120), + mADCMin(20), + mADCMax(140), mNumberOfADCs(mADCMax - mADCMin + 1), - mStatisticsType(StatisticsType::GausFit), + mStatisticsType(StatisticsType::GausFitFast), mPedestal("Pedestals", padSubset), mNoise("Noise", padSubset), mADCdata() @@ -114,9 +115,16 @@ void CalibPedestal::analyse() float pedestal{}; float noise{}; + TF1 fg("fg", "gaus"); + fg.SetRange(mADCMin - 0.5f, mADCMax + 1.5f); + for (Int_t ichannel = 0; ichannel < numberOfPads; ++ichannel) { size_t offset = ichannel * mNumberOfADCs; if (mStatisticsType == StatisticsType::GausFit) { + fit(mNumberOfADCs, array + offset, float(mADCMin) - 0.5f, float(mADCMax + 1) - 0.5f, fg); // -0.5 since ADC values are discrete + pedestal = fg.GetParameter(1); + noise = fg.GetParameter(2); + } else if (mStatisticsType == StatisticsType::GausFitFast) { fitGaus(mNumberOfADCs, array + offset, float(mADCMin) - 0.5f, float(mADCMax + 1) - 0.5f, fitValues); // -0.5 since ADC values are discrete pedestal = fitValues[1]; noise = fitValues[2]; @@ -125,6 +133,7 @@ void CalibPedestal::analyse() pedestal = data.mCOG; noise = data.mStdDev; } + noise = std::abs(noise); // noise can be negative in gaus fit calROCPedestal.setValue(ichannel, pedestal); calROCNoise.setValue(ichannel, noise); From f01b883333446cf7c49f3860cc8ca8bca0305261 Mon Sep 17 00:00:00 2001 From: wiechula Date: Wed, 11 Mar 2020 09:34:01 +0100 Subject: [PATCH 17/24] add option to store debub histograms --- Detectors/TPC/calibration/macro/runPedestal.C | 25 ++++++++++++++++++- .../TPC/calibration/macro/runPedestal.sh | 7 ++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/Detectors/TPC/calibration/macro/runPedestal.C b/Detectors/TPC/calibration/macro/runPedestal.C index 3df9468eff213..926af70d99e33 100644 --- a/Detectors/TPC/calibration/macro/runPedestal.C +++ b/Detectors/TPC/calibration/macro/runPedestal.C @@ -13,11 +13,13 @@ #include #include #include "TFile.h" +#include "TSystem.h" +#include "TH2.h" #include "TPCCalibration/CalibPedestal.h" #include "TPCCalibration/CalibRawBase.h" #endif -void runPedestal(std::vector fileInfos, TString outputFileName = "", Int_t nevents = 100, Int_t adcMin = 0, Int_t adcMax = 1100, Int_t firstTimeBin = 0, Int_t lastTimeBin = 450, Int_t statisticsType = 0, uint32_t verbosity = 0, uint32_t debugLevel = 0, Int_t firstEvent = 0) +void runPedestal(std::vector fileInfos, TString outputFileName = "", Int_t nevents = 100, Int_t adcMin = 0, Int_t adcMax = 1100, Int_t firstTimeBin = 0, Int_t lastTimeBin = 450, Int_t statisticsType = 0, uint32_t verbosity = 0, uint32_t debugLevel = 0, Int_t firstEvent = 0, Bool_t debugOutput = false) { using namespace o2::tpc; CalibPedestal ped; //(PadSubset::Region); @@ -52,5 +54,26 @@ void runPedestal(std::vector fileInfos, TString outputFileName } ped.dumpToFile(outputFileName.Data()); + auto calibPedestal = ped.getPedestal(); + + if (debugOutput) { + TString debugFile = gSystem->DirName(outputFileName); + debugFile.Append("/"); + debugFile.Append("pedestals_debug.root"); + TFile f(debugFile, "recreate"); + TObjArray arr(72); + for (int i = 0; i < 72; ++i) { + const auto& rocPedestal = calibPedestal.getCalArray(i); + + if (!(std::abs(rocPedestal.getSum()) > 0)) { + continue; + } + + auto ch = ped.createControlHistogram(ROC(i)); + arr.Add(static_cast(ch)); + } + arr.Write("histos", TObject::kSingleKey); + f.Write(); + } std::cout << "To display the pedestals run: root.exe $calibMacroDir/drawNoiseAndPedestal.C'(\"" << outputFileName << "\")'\n"; } diff --git a/Detectors/TPC/calibration/macro/runPedestal.sh b/Detectors/TPC/calibration/macro/runPedestal.sh index 8e57955e8ece5..e7d645fb23774 100755 --- a/Detectors/TPC/calibration/macro/runPedestal.sh +++ b/Detectors/TPC/calibration/macro/runPedestal.sh @@ -21,6 +21,7 @@ usage() { echo " -s, --statType= : statistics type - 0: Gaus fit (default), 1: Mean and StdDev" echo " -v, --verbosity= : set verbosity level for raw reader" echo " -d, --debugLevel= : set debug level for raw reader" + echo " -w, --writeDebug : write debug output histograms" echo " -h, --help : show this help message" } @@ -42,12 +43,13 @@ lastTimeBin=450 statisticsType=0 verbosity=0 debugLevel=0 +writeDebug=0 adcMin=0 adcMax=1100 # ===| parse command line options |============================================= -OPTIONS=$(getopt -l "fileInfo:,outputFile:,firstTimeBin:,lastTimeBin:,nevents:,adcMin:,adcMax:,statType:,verbosity:,debugLevel:,help" -o "i:o:t:f:l:n:m:x:s:v:d:h" -n "runPedestal.sh" -- "$@") +OPTIONS=$(getopt -l "fileInfo:,outputFile:,firstTimeBin:,lastTimeBin:,nevents:,adcMin:,adcMax:,statType:,verbosity:,debugLevel:,writeDebug,help" -o "i:o:t:f:l:n:m:x:s:v:d:wh" -n "runPedestal.sh" -- "$@") if [ $? != 0 ] ; then usageAndExit @@ -68,6 +70,7 @@ while true; do -s|--statType) statisticsType=$2; shift 2;; -v|--verbosity) verbosity=$2; shift 2;; -d|--debugLevel) debugLevel=$2; shift 2;; + -w|--writeDebug) writeDebug=1; shift;; -h|--help) usageAndExit;; *) echo "Internal error!" ; exit 1 ;; esac @@ -90,7 +93,7 @@ fi fileInfo=$(echo $fileInfo | sed "s|^|{\"|;s|,|:$lastTimeBin\",\"|g;s|$|\"}|") # ===| command building and execution |========================================= -cmd="root.exe -b -q -l -n -x $O2_SRC/Detectors/TPC/calibration/macro/runPedestal.C'($fileInfo,\"$outputFile\", $nevents, $adcMin, $adcMax, $firstTimeBin, $lastTimeBin, $statisticsType, $verbosity, $debugLevel)'" +cmd="root.exe -b -q -l -n -x $O2_SRC/Detectors/TPC/calibration/macro/runPedestal.C'($fileInfo,\"$outputFile\", $nevents, $adcMin, $adcMax, $firstTimeBin, $lastTimeBin, $statisticsType, $verbosity, $debugLevel, 0, $writeDebug)'" #cmd="perf record -g -o perf.log root.exe -b -q -l -n -x $O2_SRC/Detectors/TPC/calibration/macro/runPedestal.C'($fileInfo,\"$outputFile\", $nevents, $adcMin, $adcMax, $firstTimeBin, $lastTimeBin, $statisticsType, $verbosity, $debugLevel)'" #cmd="valgrind --tool=callgrind --dump-instr=yes --dump-instr=yes root.exe -b -q -l -n -x $O2_SRC/Detectors/TPC/calibration/macro/runPedestal.C'($fileInfo,\"$outputFile\", $nevents, $adcMin, $adcMax, $firstTimeBin, $lastTimeBin, $statisticsType, $verbosity, $debugLevel)'" echo "running: $cmd" From 709b37ca9ab3445d5861e2b4c1c00b5c97817dd5 Mon Sep 17 00:00:00 2001 From: wiechula Date: Wed, 11 Mar 2020 10:40:45 +0100 Subject: [PATCH 18/24] fix math base --- Common/MathUtils/include/MathUtils/MathBase.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/Common/MathUtils/include/MathUtils/MathBase.h b/Common/MathUtils/include/MathUtils/MathBase.h index 874774429293d..f4c26c4a0adfc 100644 --- a/Common/MathUtils/include/MathUtils/MathBase.h +++ b/Common/MathUtils/include/MathUtils/MathBase.h @@ -55,8 +55,6 @@ namespace math_base /// \param[in] func fit function /// /// -//template -//Double_t fitGaus(const size_t nBins, const T *arr, const T xMin, const T xMax, std::vector& param); template TFitResultPtr fit(const size_t nBins, const T* arr, const T xMin, const T xMax, TF1& func, std::string_view option = "") { From a347102a464bf8edad88e78f944cf8339ba401dc Mon Sep 17 00:00:00 2001 From: wiechula Date: Wed, 11 Mar 2020 10:41:33 +0100 Subject: [PATCH 19/24] Add functionality for summary report creation --- Detectors/TPC/base/include/TPCBase/Painter.h | 16 ++- Detectors/TPC/base/src/Painter.cxx | 122 +++++++++++++++++-- Detectors/TPC/base/src/TPCBaseLinkDef.h | 27 ++-- 3 files changed, 141 insertions(+), 24 deletions(-) diff --git a/Detectors/TPC/base/include/TPCBase/Painter.h b/Detectors/TPC/base/include/TPCBase/Painter.h index e1a6eaf851e6d..41b1b87f224f1 100644 --- a/Detectors/TPC/base/include/TPCBase/Painter.h +++ b/Detectors/TPC/base/include/TPCBase/Painter.h @@ -16,6 +16,7 @@ /// \author Jens Wiechula, Jens.Wiechula@ikf.uni-frankfurt.de /// +#include #include "DataFormatsTPC/Defs.h" class TH1; @@ -48,7 +49,7 @@ namespace painter /// \param CalDet object to draw /// \return TCanvas containing CalDet content template -TCanvas* draw(const CalDet& calDet); +TCanvas* draw(const CalDet& calDet, int nbins1D = 300, float xMin1D = 0, float xMax1D = 0); /// Drawing of a CalDet object /// \param CalArray object to draw @@ -70,6 +71,19 @@ TH2* getHistogram2D(const CalDet& calDet, Side side); template TH2* getHistogram2D(const CalArray& calArray); +/// Create summary canvases for a CalDet object +/// +/// 1 Canvas with 2D and 1D distributions for each side +/// 1 Canvas with 2D distributions for all ROCs +/// 1 Canvas with 1D distributions for all ROCs +/// \param CalDet object to draw +/// \param nbins1D number of bins used for the 1D projections +/// \param xMin1D minimum value for 1D distribution (xMin = 0 and xMax = 0 for auto scaling) +/// \param xMax1D maximum value for 1D distribution (xMin = 0 and xMax = 0 for auto scaling) +/// \return TCanvas containing CalDet content +template +std::vector makeSummaryCanvases(const CalDet& calDet, int nbins1D = 300, float xMin1D = 0, float xMax1D = 0, bool onlyFilled = true); + } // namespace painter } // namespace tpc diff --git a/Detectors/TPC/base/src/Painter.cxx b/Detectors/TPC/base/src/Painter.cxx index e60993b331ac2..ede2f413556d3 100644 --- a/Detectors/TPC/base/src/Painter.cxx +++ b/Detectors/TPC/base/src/Painter.cxx @@ -10,6 +10,7 @@ #include #include +#include #include "TString.h" #include "TAxis.h" @@ -27,7 +28,7 @@ using namespace o2::tpc; template -TCanvas* painter::draw(const CalDet& calDet) +TCanvas* painter::draw(const CalDet& calDet, int nbins1D, float xMin1D, float xMax1D) { using DetType = CalDet; using CalType = CalArray; @@ -48,15 +49,15 @@ TCanvas* painter::draw(const CalDet& calDet) TH1::SetDefaultBufferSize(Sector::MAXSECTOR * mapper.getPadsInSector()); auto hAside1D = new TH1F(Form("h_Aside_1D_%s", name.c_str()), Form("%s (A-Side)", title), - 300, 0, 0); //TODO: modify ranges + nbins1D, xMin1D, xMax1D); //TODO: modify ranges auto hCside1D = new TH1F(Form("h_Cside_1D_%s", name.c_str()), Form("%s (C-Side)", title), - 300, 0, 0); //TODO: modify ranges + nbins1D, xMin1D, xMax1D); //TODO: modify ranges - auto hAside2D = new TH2F(Form("h_Aside_2D_%s;x (cm);y (cm)", name.c_str()), Form("%s (A-Side)", title), + auto hAside2D = new TH2F(Form("h_Aside_2D_%s", name.c_str()), Form("%s (A-Side);x (cm);y (cm)", title), 300, -300, 300, 300, -300, 300); - auto hCside2D = new TH2F(Form("h_Cside_2D_%s;x (cm);y (cm)", name.c_str()), Form("%s (C-Side)", title), + auto hCside2D = new TH2F(Form("h_Cside_2D_%s", name.c_str()), Form("%s (C-Side);x (cm);y (cm)", title), 300, -300, 300, 300, -300, 300); for (ROC roc; !roc.looped(); ++roc) { @@ -82,8 +83,15 @@ TCanvas* painter::draw(const CalDet& calDet) } } + if (xMax1D > xMin1D) { + hAside2D->SetMinimum(xMin1D); + hAside2D->SetMaximum(xMax1D); + hCside2D->SetMinimum(xMin1D); + hCside2D->SetMaximum(xMax1D); + } + // ===| Draw histograms |===================================================== - auto c = new TCanvas(Form("c_%s", name.c_str()), title); + auto c = new TCanvas(Form("c_%s", name.c_str()), title, 1000, 1000); c->Divide(2, 2); c->cd(1); @@ -130,8 +138,8 @@ TH2* painter::getHistogram2D(const CalDet& calDet, Side side) std::replace(name.begin(), name.end(), ' ', '_'); const char side_name = (side == Side::A) ? 'A' : 'C'; - auto h2D = new TH2F(Form("h_%cside_2D_%s;x (cm);y (cm)", side_name, name.c_str()), - Form("%s (%c-Side)", title, side_name), + auto h2D = new TH2F(Form("h_%cside_2D_%s", side_name, name.c_str()), + Form("%s (%c-Side);x (cm);y (cm)", title, side_name), 300, -300, 300, 300, -300, 300); for (ROC roc; !roc.looped(); ++roc) { @@ -189,30 +197,118 @@ TH2* painter::getHistogram2D(const CalArray& calArray) return hist; } +template +std::enable_if_t::value, bool> hasData(const CalArray& cal) +{ + return std::abs(cal.getSum()) > T{0}; +} + +template +std::enable_if_t::value, bool> hasData(const CalArray& cal) +{ + return cal.getSum() > T{0}; +} + +template +std::vector painter::makeSummaryCanvases(const CalDet& calDet, int nbins1D, float xMin1D, float xMax1D, bool onlyFilled) +{ + + std::vector vecCanvases; + + auto nROCs = calDet.getData().size(); + + if (onlyFilled) { + nROCs = 0; + for (size_t iroc = 0; iroc < calDet.getData().size(); ++iroc) { + const auto& roc = calDet.getCalArray(iroc); + + if (hasData(roc)) { + ++nROCs; + } + } + + if (!nROCs) { + return vecCanvases; + } + } + + // ===| set up canvases |=== + const std::string_view calName = calDet.getName(); + auto cSides = draw(calDet, nbins1D, xMin1D, xMax1D); + auto cROCs1D = new TCanvas(fmt::format("c_ROCs_{}_1D", calName).data(), fmt::format("{} values for each ROC", calName).data(), 1400, 1000); + auto cROCs2D = new TCanvas(fmt::format("c_ROCs_{}_2D", calName).data(), fmt::format("{} values for each ROC", calName).data(), 1400, 1000); + vecCanvases.emplace_back(cSides); + vecCanvases.emplace_back(cROCs1D); + vecCanvases.emplace_back(cROCs2D); + + cROCs1D->DivideSquare(nROCs); + cROCs2D->DivideSquare(nROCs); + + // ===| produce plots for each ROC |=== + size_t pad = 1; + for (size_t iroc = 0; iroc < calDet.getData().size(); ++iroc) { + const auto& roc = calDet.getCalArray(iroc); + + if (onlyFilled && !hasData(roc)) { + continue; + } + + // ===| 1D histogram |=== + auto h1D = new TH1F(fmt::format("h_{}_{:02d}", calName, iroc).data(), fmt::format("{} distribution ROC {:02d};ADC value", calName, iroc).data(), nbins1D, xMin1D, xMax1D); + for (const auto& val : roc.getData()) { + h1D->Fill(val); + } + + // ===| 2D histogram |=== + auto h2D = painter::getHistogram2D(roc); + h2D->SetStats(0); + if (xMax1D > xMin1D) { + h2D->SetMinimum(xMin1D); + h2D->SetMaximum(xMax1D); + } + h2D->SetUniqueID(iroc); + + cROCs1D->cd(pad); + h1D->Draw(); + + cROCs2D->cd(pad); + h2D->Draw("colz"); + + ++pad; + } + + return vecCanvases; +} + // ===| explicit instantiations |=============================================== // this is required to force the compiler to create instances with the types // we usually would like to deal with -template TCanvas* painter::draw(const CalDet& calDet); +template TCanvas* painter::draw(const CalDet& calDet, int, float, float); +template std::vector painter::makeSummaryCanvases(const CalDet& calDet, int, float, float, bool); template TCanvas* painter::draw(const CalArray& calArray); template TH2* painter::getHistogram2D(const CalDet& calDet, Side side); template TH2* painter::getHistogram2D(const CalArray& calArray); -template TCanvas* painter::draw(const CalDet& calDet); +template TCanvas* painter::draw(const CalDet& calDet, int, float, float); +template std::vector painter::makeSummaryCanvases(const CalDet& calDet, int, float, float, bool); template TCanvas* painter::draw(const CalArray& calArray); template TH2* painter::getHistogram2D(const CalDet& calDet, Side side); template TH2* painter::getHistogram2D(const CalArray& calArray); -template TCanvas* painter::draw(const CalDet& calDet); +template TCanvas* painter::draw(const CalDet& calDet, int, float, float); +template std::vector painter::makeSummaryCanvases(const CalDet& calDet, int, float, float, bool); template TCanvas* painter::draw(const CalArray& calArray); template TH2* painter::getHistogram2D(const CalDet& calDet, Side side); template TH2* painter::getHistogram2D(const CalArray& calArray); -template TCanvas* painter::draw(const CalDet& calDet); +template TCanvas* painter::draw(const CalDet& calDet, int, float, float); +template std::vector painter::makeSummaryCanvases(const CalDet& calDet, int, float, float, bool); template TCanvas* painter::draw(const CalArray& calArray); template TH2* painter::getHistogram2D(const CalDet& calDet, Side side); template TH2* painter::getHistogram2D(const CalArray& calArray); -template TCanvas* painter::draw(const CalDet& calDet); +template TCanvas* painter::draw(const CalDet& calDet, int, float, float); +template std::vector painter::makeSummaryCanvases(const CalDet& calDet, int, float, float, bool); template TCanvas* painter::draw(const CalArray& calArray); template TH2* painter::getHistogram2D(const CalDet& calDet, Side side); template TH2* painter::getHistogram2D(const CalArray& calArray); diff --git a/Detectors/TPC/base/src/TPCBaseLinkDef.h b/Detectors/TPC/base/src/TPCBaseLinkDef.h index 0d1f552d9a8e5..cea3dcd4a93c6 100644 --- a/Detectors/TPC/base/src/TPCBaseLinkDef.h +++ b/Detectors/TPC/base/src/TPCBaseLinkDef.h @@ -47,18 +47,25 @@ #pragma link C++ class o2::tpc::Sector; #pragma link C++ namespace o2::tpc::painter; -#pragma link C++ function o2::tpc::painter::draw(o2::tpc::CalArray ); -#pragma link C++ function o2::tpc::painter::draw(o2::tpc::CalArray ); -#pragma link C++ function o2::tpc::painter::draw(o2::tpc::CalArray ); -#pragma link C++ function o2::tpc::painter::draw(o2::tpc::CalArray ); -#pragma link C++ function o2::tpc::painter::draw(o2::tpc::CalArray ); +#pragma link C++ function o2::tpc::painter::draw(o2::tpc::CalArray &); +#pragma link C++ function o2::tpc::painter::draw(o2::tpc::CalArray &); +#pragma link C++ function o2::tpc::painter::draw(o2::tpc::CalArray &); +#pragma link C++ function o2::tpc::painter::draw(o2::tpc::CalArray &); +#pragma link C++ function o2::tpc::painter::draw(o2::tpc::CalArray &); -#pragma link C++ function o2::tpc::painter::draw(o2::tpc::CalDet ); -#pragma link C++ function o2::tpc::painter::draw(o2::tpc::CalDet ); -#pragma link C++ function o2::tpc::painter::draw(o2::tpc::CalDet ); -#pragma link C++ function o2::tpc::painter::draw(o2::tpc::CalDet ); -#pragma link C++ function o2::tpc::painter::draw(o2::tpc::CalDet ); +#pragma link C++ function o2::tpc::painter::draw(o2::tpc::CalDet &, int, float, float); +#pragma link C++ function o2::tpc::painter::draw(o2::tpc::CalDet &, int, float, float); +#pragma link C++ function o2::tpc::painter::draw(o2::tpc::CalDet &, int, float, float); +#pragma link C++ function o2::tpc::painter::draw(o2::tpc::CalDet &, int, float, float); +#pragma link C++ function o2::tpc::painter::draw(o2::tpc::CalDet &, int, float, float); +#pragma link C++ function o2::tpc::painter::makeSummaryCanvases(o2::tpc::CalDet &, int, float, float, bool); +#pragma link C++ function o2::tpc::painter::makeSummaryCanvases(o2::tpc::CalDet &, int, float, float, bool); +#pragma link C++ function o2::tpc::painter::makeSummaryCanvases(o2::tpc::CalDet &, int, float, float, bool); +#pragma link C++ function o2::tpc::painter::makeSummaryCanvases(o2::tpc::CalDet &, int, float, float, bool); +#pragma link C++ function o2::tpc::painter::makeSummaryCanvases(o2::tpc::CalDet &, int, float, float, bool); + +//#pragma link C++ class std::vector + ; #pragma link C++ class o2::tpc::ParameterDetector; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::tpc::ParameterDetector> + ; #pragma link C++ class o2::tpc::ParameterElectronics; From c05d6429c2d605604d6a925d325ab24b3685c4e8 Mon Sep 17 00:00:00 2001 From: wiechula Date: Wed, 11 Mar 2020 10:42:24 +0100 Subject: [PATCH 20/24] add summary report option --- .../calibration/macro/drawNoiseAndPedestal.C | 105 +++++++++++++++--- Detectors/TPC/calibration/macro/drawPulser.C | 82 ++++++++++++-- 2 files changed, 157 insertions(+), 30 deletions(-) diff --git a/Detectors/TPC/calibration/macro/drawNoiseAndPedestal.C b/Detectors/TPC/calibration/macro/drawNoiseAndPedestal.C index a0f5e6aa874da..50ddc69496cf3 100644 --- a/Detectors/TPC/calibration/macro/drawNoiseAndPedestal.C +++ b/Detectors/TPC/calibration/macro/drawNoiseAndPedestal.C @@ -23,29 +23,66 @@ /// Helper to get active histogram and bin information TH1* GetBinInfoXY(int& binx, int& biny, float& bincx, float& bincy); +/// Save canvases in arr to png, pdf, root +void SaveCanvases(TObjArray* arr, std::string_view outDir); + +/// Save a single canvas to outDir using the object name +void SaveCanvas(TCanvas* c, std::string_view outDir); + /// Add fec information to the active histogram title void addFECInfo(); /// Open pedestalFile and retrieve noise and pedestal values /// Draw then in separate canvases and add an executable to be able to add /// FEC information to the title -void drawNoiseAndPedestal(TString pedestalFile) +/// \param pedestalFile input file name +/// \param mode 0: one canvas per ROC 2D and 1D, 1: one canvas per full TPC, one canvas with all ROCs, for each noise and pedestal +/// \param outDir output directory to store plos in. Don't store if empty +/// \return Array with canvases +TObjArray* drawNoiseAndPedestal(TString pedestalFile, int mode = 0, std::string_view outDir = "") { + if ((mode != 0) && (mode != 1)) { + return 0x0; + } + + TObjArray* arrCanvases = new TObjArray; + arrCanvases->SetName("NoiseAndPedestals"); + using namespace o2::tpc; TFile f(pedestalFile); gROOT->cd(); // ===| load noise and pedestal from file |=== CalDet dummy; - CalDet* pedestal = nullptr; - CalDet* noise = nullptr; - f.GetObject("Pedestals", pedestal); - f.GetObject("Noise", noise); + CalDet* calPedestal = nullptr; + CalDet* calNoise = nullptr; + f.GetObject("Pedestals", calPedestal); + f.GetObject("Noise", calNoise); + + // mode 1 handling + if (mode == 1) { + auto arrPedestals = painter::makeSummaryCanvases(*calPedestal, 120, 20.f, 120.f); + auto arrNoise = painter::makeSummaryCanvases(*calNoise, 100, 0.f, 5.f); + + for (auto c : arrPedestals) { + arrCanvases->Add(c); + } + + for (auto c : arrNoise) { + arrCanvases->Add(c); + } + + if (outDir.size()) { + SaveCanvases(arrCanvases, outDir); + } + + return arrCanvases; + } // ===| loop over all ROCs |================================================== - for (int iroc = 0; iroc < pedestal->getData().size(); ++iroc) { - const auto& rocPedestal = pedestal->getCalArray(iroc); - const auto& rocNoise = noise->getCalArray(iroc); + for (size_t iroc = 0; iroc < calPedestal->getData().size(); ++iroc) { + const auto& rocPedestal = calPedestal->getCalArray(iroc); + const auto& rocNoise = calNoise->getCalArray(iroc); // only draw if valid data if (!(std::abs(rocPedestal.getSum() + rocNoise.getSum()) > 0)) { @@ -53,12 +90,18 @@ void drawNoiseAndPedestal(TString pedestalFile) } // ===| histograms for noise and pedestal |=== - auto hPedestal = new TH1F(Form("hPedestal%02d", iroc), Form("Pedestal distribution ROC %02d;ADC value", iroc), 150, 0, 150); - auto hNoise = new TH1F(Form("hNoise%02d", iroc), Form("Noise distribution ROC %02d;ADC value", iroc), 100, 0, 5); + auto hPedestal = new TH1F(Form("hPedestal%02zu", iroc), Form("Pedestal distribution ROC %02zu;ADC value", iroc), 150, 0, 150); + auto hNoise = new TH1F(Form("hNoise%02zu", iroc), Form("Noise distribution ROC %02zu;ADC value", iroc), 100, 0, 5); auto hPedestal2D = painter::getHistogram2D(rocPedestal); hPedestal2D->SetStats(0); + hPedestal2D->SetMinimum(20); + hPedestal2D->SetMaximum(140); + hPedestal2D->SetUniqueID(iroc); auto hNoise2D = painter::getHistogram2D(rocNoise); hNoise2D->SetStats(0); + hNoise2D->SetMinimum(0); + hNoise2D->SetMaximum(5); + hNoise2D->SetUniqueID(iroc); // ===| fill 1D histograms |=== for (const auto& val : rocPedestal.getData()) { @@ -74,22 +117,31 @@ void drawNoiseAndPedestal(TString pedestalFile) } // ===| draw histograms |=== - auto cPedestal = new TCanvas(Form("cPedestal%02d", iroc), Form("Pedestals ROC %02d", iroc)); + auto cPedestal = new TCanvas(Form("cPedestal%02zu", iroc), Form("Pedestals ROC %02zu", iroc)); hPedestal->Draw(); - auto cNoise = new TCanvas(Form("cNoise%02d", iroc), Form("Noise ROC %02d", iroc)); + auto cNoise = new TCanvas(Form("cNoise%02zu", iroc), Form("Noise ROC %02zu", iroc)); hNoise->Draw(); - auto cPedestal2D = new TCanvas(Form("cPedestal2D%0d", iroc), Form("Pedestals2D ROC %02d", iroc)); - cPedestal2D->AddExec(Form("addFECInfoPedestal%02d", iroc), "addFECInfo()"); + auto cPedestal2D = new TCanvas(Form("cPedestal2D%02zu", iroc), Form("Pedestals2D ROC %02zu", iroc)); + cPedestal2D->AddExec(Form("addFECInfoPedestal%02zu", iroc), "addFECInfo()"); hPedestal2D->Draw("colz"); - hPedestal2D->SetUniqueID(iroc); - auto cNoise2D = new TCanvas(Form("cNoise2D%02d", iroc), Form("Noise2D ROC %02d", iroc)); - cNoise2D->AddExec(Form("addFECInfoNoise%02d", iroc), "addFECInfo()"); + auto cNoise2D = new TCanvas(Form("cNoise2D%02zu", iroc), Form("Noise2D ROC %02zu", iroc)); + cNoise2D->AddExec(Form("addFECInfoNoise%02zu", iroc), "addFECInfo()"); hNoise2D->Draw("colz"); - hNoise2D->SetUniqueID(iroc); + + arrCanvases->Add(cPedestal); + arrCanvases->Add(cPedestal2D); + arrCanvases->Add(cNoise); + arrCanvases->Add(cNoise2D); } + + if (outDir.size()) { + SaveCanvases(arrCanvases, outDir); + } + + return arrCanvases; } TH1* GetBinInfoXY(int& binx, int& biny, float& bincx, float& bincy) @@ -163,3 +215,20 @@ void addFECInfo() h->SetTitle(title.Data()); } + +void SaveCanvases(TObjArray* arr, std::string_view outDir) +{ + for (auto c : *arr) { + SaveCanvas(static_cast(c), outDir); + } + + std::unique_ptr outFile(TFile::Open(TString::Format("%s/NoiseAndPedestalCanvases.root", outDir.data()), "recreate")); + arr->Write(arr->GetName(), TObject::kSingleKey); + outFile->Close(); +} + +void SaveCanvas(TCanvas* c, std::string_view outDir) +{ + c->SaveAs(TString::Format("%s/%s.png", outDir.data(), c->GetName())); + c->SaveAs(TString::Format("%s/%s.pdf", outDir.data(), c->GetName())); +} diff --git a/Detectors/TPC/calibration/macro/drawPulser.C b/Detectors/TPC/calibration/macro/drawPulser.C index 86edfc4c95ca7..5e411bd72c500 100644 --- a/Detectors/TPC/calibration/macro/drawPulser.C +++ b/Detectors/TPC/calibration/macro/drawPulser.C @@ -26,27 +26,63 @@ TH1* GetBinInfoXY(int& binx, int& biny, float& bincx, float& bincy); /// Add fec information to the active histogram title void addFECInfo(); +/// Save canvases in arr to png, pdf, root +void SaveCanvases(TObjArray* arr, std::string_view outDir); + +/// Save a single canvas to outDir using the object name +void SaveCanvas(TCanvas* c, std::string_view outDir); + /// Open pedestalFile and retrieve noise and pedestal values /// Draw then in separate canvases and add an executable to be able to add /// FEC information to the title -void drawPulser(TString pulserFile) +TObjArray* drawPulser(TString pulserFile, int mode = 0, std::string_view outDir = "") { + if ((mode != 0) && (mode != 1)) { + return 0x0; + } + + TObjArray* arrCanvases = new TObjArray; + arrCanvases->SetName("Pulser"); + using namespace o2::tpc; TFile f(pulserFile); gROOT->cd(); // ===| load pulser from file |=== CalDet dummy; - CalDet*t0 = nullptr, *width = nullptr, *qtot = nullptr; - f.GetObject("T0", t0); - f.GetObject("Width", width); - f.GetObject("Qtot", qtot); + CalDet*calT0 = nullptr, *calWidth = nullptr, *calQtot = nullptr; + f.GetObject("T0", calT0); + f.GetObject("Width", calWidth); + f.GetObject("Qtot", calQtot); + + // mode 1 handling + if (mode == 1) { + auto arrT0 = painter::makeSummaryCanvases(*calT0, 100, 238.f, 240.f); + auto arrWidth = painter::makeSummaryCanvases(*calWidth, 100, 0.38f, 0.57f); + auto arrQtot = painter::makeSummaryCanvases(*calQtot, 100, 20.f, 280.f); + + for (auto c : arrT0) { + arrCanvases->Add(c); + } + for (auto c : arrWidth) { + arrCanvases->Add(c); + } + for (auto c : arrQtot) { + arrCanvases->Add(c); + } + + if (outDir.size()) { + SaveCanvases(arrCanvases, outDir); + } + + return arrCanvases; + } // ===| loop over all ROCs |================================================== - for (int iroc = 0; iroc < int(t0->getData().size()); ++iroc) { - const auto& rocT0 = t0->getCalArray(iroc); - const auto& rocWidth = width->getCalArray(iroc); - const auto& rocQtot = qtot->getCalArray(iroc); + for (int iroc = 0; iroc < int(calT0->getData().size()); ++iroc) { + const auto& rocT0 = calT0->getCalArray(iroc); + const auto& rocWidth = calWidth->getCalArray(iroc); + const auto& rocQtot = calQtot->getCalArray(iroc); // only draw if valid data if (!(std::abs(rocT0.getSum() + rocWidth.getSum() + rocQtot.getSum()) > 0)) { @@ -70,7 +106,7 @@ void drawPulser(TString pulserFile) const float minQtot = medianQtot - 50; const float maxQtot = medianQtot + rangeQtot; - // ===| histograms for t0, width and qtot |=== + // ===| histograms for calT0, calWidth and calQtot |=== auto hT0 = new TH1F(Form("hT0%02d", iroc), Form("T0 distribution ROC %02d;time bins (0.2 #mus)", iroc), 100, minT0, maxT0); auto hWidth = new TH1F(Form("hWidth%02d", iroc), Form("Width distribution ROC %02d;time bins (0.2 #mus)", iroc), 100, minWidth, maxWidth); auto hQtot = new TH1F(Form("hQtot%02d", iroc), Form("Qtot distribution ROC %02d;ADC counts", iroc), 100, minQtot, maxQtot); @@ -138,9 +174,14 @@ void drawPulser(TString pulserFile) cPulser->cd(6); hQtot->Draw(); - cPulser->SaveAs(Form("%s.png", cPulser->GetName())); - cPulser->SaveAs(Form("%s.pdf", cPulser->GetName())); + arrCanvases->Add(cPulser); + } + + if (outDir.size()) { + SaveCanvases(arrCanvases, outDir); } + + return arrCanvases; } TH1* GetBinInfoXY(int& binx, int& biny, float& bincx, float& bincy) @@ -214,3 +255,20 @@ void addFECInfo() h->SetTitle(title.Data()); } + +void SaveCanvases(TObjArray* arr, std::string_view outDir) +{ + for (auto c : *arr) { + SaveCanvas(static_cast(c), outDir); + } + + std::unique_ptr outFile(TFile::Open(TString::Format("%s/PulserCanvases.root", outDir.data()), "recreate")); + arr->Write(arr->GetName(), TObject::kSingleKey); + outFile->Close(); +} + +void SaveCanvas(TCanvas* c, std::string_view outDir) +{ + c->SaveAs(TString::Format("%s/%s.png", outDir.data(), c->GetName())); + c->SaveAs(TString::Format("%s/%s.pdf", outDir.data(), c->GetName())); +} From 19206a828da3ec569471976ffb4a1917ff60798a Mon Sep 17 00:00:00 2001 From: wiechula Date: Mon, 16 Mar 2020 15:29:12 +0100 Subject: [PATCH 21/24] Add common utilities for drawing, saving, tokenizing, ... --- Detectors/TPC/base/CMakeLists.txt | 4 +- Detectors/TPC/base/include/TPCBase/Utils.h | 58 +++++++++ Detectors/TPC/base/src/TPCBaseLinkDef.h | 6 + Detectors/TPC/base/src/Utils.cxx | 131 +++++++++++++++++++++ 4 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 Detectors/TPC/base/include/TPCBase/Utils.h create mode 100644 Detectors/TPC/base/src/Utils.cxx diff --git a/Detectors/TPC/base/CMakeLists.txt b/Detectors/TPC/base/CMakeLists.txt index 8abfe3d1a5ec1..b9a70e55b847d 100644 --- a/Detectors/TPC/base/CMakeLists.txt +++ b/Detectors/TPC/base/CMakeLists.txt @@ -32,6 +32,7 @@ o2_add_library(TPCBase src/PartitionInfo.cxx src/ROC.cxx src/Sector.cxx + src/Utils.cxx PUBLIC_LINK_LIBRARIES Vc::Vc Boost::boost O2::DataFormatsTPC O2::CCDB FairRoot::Base) @@ -58,7 +59,8 @@ o2_target_root_dictionary(TPCBase include/TPCBase/ParameterGEM.h include/TPCBase/PartitionInfo.h include/TPCBase/ROC.h - include/TPCBase/Sector.h) + include/TPCBase/Sector.h + include/TPCBase/Utils.h) o2_add_test(Base COMPONENT_NAME tpc diff --git a/Detectors/TPC/base/include/TPCBase/Utils.h b/Detectors/TPC/base/include/TPCBase/Utils.h new file mode 100644 index 0000000000000..d3ff0d5e1e1fd --- /dev/null +++ b/Detectors/TPC/base/include/TPCBase/Utils.h @@ -0,0 +1,58 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_TPC_UTILS_H_ +#define ALICEO2_TPC_UTILS_H_ + +/// +/// \file Utils.h +/// \author Jens Wiechula, Jens.Wiechula@ikf.uni-frankfurt.de +/// + +#include +#include + +class TObjArray; +class TCanvas; +class TH1; + +namespace o2 +{ +namespace tpc +{ + +template +class CalDet; + +template +class CalArray; + +/// \namespace utils +/// \brief Common utility functions +/// +/// Common utility functions for drawing, saving, ... +/// +/// origin: TPC +/// \author Jens Wiechula, Jens.Wiechula@ikf.uni-frankfurt.de + +namespace utils +{ + +const std::vector tokenize(const std::string_view input, const std::string_view pattern); +TH1* getBinInfoXY(int& binx, int& biny, float& bincx, float& bincy); +void addFECInfo(); +void saveCanvases(TObjArray& arr, std::string_view outDir, std::string_view types = "png,pdf", std::string_view rootFileName = ""); +void saveCanvas(TCanvas& c, std::string_view outDir, std::string_view types); + +} // namespace utils +} // namespace tpc +} // namespace o2 + +#endif diff --git a/Detectors/TPC/base/src/TPCBaseLinkDef.h b/Detectors/TPC/base/src/TPCBaseLinkDef.h index cea3dcd4a93c6..3fdef9306a5e0 100644 --- a/Detectors/TPC/base/src/TPCBaseLinkDef.h +++ b/Detectors/TPC/base/src/TPCBaseLinkDef.h @@ -77,4 +77,10 @@ #pragma link C++ struct o2::tpc::ParameterGEM; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::tpc::ParameterGEM> + ; +#pragma link C++ namespace o2::tpc::utils; +#pragma link C++ function o2::tpc::utils::tokenize(const std::string_view, const std::string_view); +#pragma link C++ function o2::tpc::utils::getBinInfoXY(int&, int&, float&, float&); +#pragma link C++ function o2::tpc::utils::addFECInfo(); +#pragma link C++ function o2::tpc::utils::saveCanvases(TObjArray*, std::string_view, std::string_view, std::string_view); +#pragma link C++ function o2::tpc::utils::saveCanvas(TCanvas*, std::string_view, std::string_view); #endif diff --git a/Detectors/TPC/base/src/Utils.cxx b/Detectors/TPC/base/src/Utils.cxx new file mode 100644 index 0000000000000..4a2401c3eace2 --- /dev/null +++ b/Detectors/TPC/base/src/Utils.cxx @@ -0,0 +1,131 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#include +#include + +#include "TObject.h" +#include "TObjArray.h" +#include "TCanvas.h" +#include "TH1.h" +#include "TFile.h" + +#include "TPCBase/Mapper.h" +#include "TPCBase/Utils.h" + +using namespace o2::tpc; + +/// Inspired by https://stackoverflow.com/questions/9435385/split-a-string-using-c11 +/// could be optimized for speed, see e.g. https://stackoverflow.com/questions/14205096/c11-regex-slower-than-python +const std::vector utils::tokenize(const std::string_view input, const std::string_view pattern) +{ + // passing -1 as the submatch index parameter performs splitting + std::regex re(pattern.data()); + std::cregex_token_iterator + first{input.begin(), input.end(), re, -1}, + last; + return {first, last}; +} + +TH1* utils::getBinInfoXY(int& binx, int& biny, float& bincx, float& bincy) +{ + TObject* select = gPad->GetSelected(); + if (!select) { + return nullptr; + } + if (!select->InheritsFrom("TH2")) { + gPad->SetUniqueID(0); + return nullptr; + } + + TH1* h = (TH1*)select; + gPad->GetCanvas()->FeedbackMode(kTRUE); + + const int px = gPad->GetEventX(); + const int py = gPad->GetEventY(); + const float xx = gPad->AbsPixeltoX(px); + const float x = gPad->PadtoX(xx); + const float yy = gPad->AbsPixeltoY(py); + const float y = gPad->PadtoX(yy); + binx = h->GetXaxis()->FindBin(x); + biny = h->GetYaxis()->FindBin(y); + bincx = h->GetXaxis()->GetBinCenter(binx); + bincy = h->GetYaxis()->GetBinCenter(biny); + //printf("binx, biny: %d %d\n",binx,biny); + + return h; +} + +void utils::addFECInfo() +{ + using namespace o2::tpc; + const int event = gPad->GetEvent(); + if (event != 51) { + return; + } + + int binx, biny; + float bincx, bincy; + TH1* h = utils::getBinInfoXY(binx, biny, bincx, bincy); + if (!h) { + return; + } + + const float binValue = h->GetBinContent(binx, biny); + const int row = int(std::floor(bincx)); + const int cpad = int(std::floor(bincy)); + + const auto& mapper = Mapper::instance(); + + const int roc = h->GetUniqueID(); + if (roc < 0 || roc >= (int)ROC::MaxROC) + return; + if (row < 0 || row >= (int)mapper.getNumberOfRowsROC(roc)) + return; + const int nPads = mapper.getNumberOfPadsInRowROC(roc, row); + const int pad = cpad + nPads / 2; + //printf("row %d, cpad %d, pad %d, nPads %d\n", row, cpad, pad, nPads); + if (pad < 0 || pad >= (int)nPads) { + return; + } + const int channel = mapper.getPadNumberInROC(PadROCPos(roc, row, pad)); + + const auto& fecInfo = mapper.getFECInfo(PadROCPos(roc, row, pad)); + + std::string title("#splitline{#lower[.1]{#scale[.5]{"); + title += (roc / 18 % 2 == 0) ? "A" : "C"; + title += fmt::format("{:02d} ({:02d}) row: {:02d}, pad: {:03d}, globalpad: {:05d} (in roc)}}}{#scale[.5]{FEC: {:02d}, Chip: {:02d}, Chn: {:02d}, Value: {:.3f}}}", + roc % 18, roc, row, pad, channel, fecInfo.getIndex(), fecInfo.getSampaChip(), fecInfo.getSampaChannel(), binValue); + + h->SetTitle(title.data()); +} + +void utils::saveCanvases(TObjArray& arr, std::string_view outDir, std::string_view types, std::string_view rootFileName) +{ + for (auto c : arr) { + utils::saveCanvas(*static_cast(c), outDir, types); + } + + if (rootFileName.size()) { + std::unique_ptr outFile(TFile::Open(fmt::format("{}/NoiseAndPedestalCanvases.root", outDir).data(), "recreate")); + arr.Write(arr.GetName(), TObject::kSingleKey); + outFile->Close(); + } +} + +void utils::saveCanvas(TCanvas& c, std::string_view outDir, std::string_view types) +{ + const auto typesVec = tokenize(types, ","); + for (const auto& type : typesVec) { + c.SaveAs(fmt::format("{}/{}.{}", outDir, c.GetName(), type).data()); + } +} From 862d1260ff97cb4f61ff532e0edc2a8090c39220 Mon Sep 17 00:00:00 2001 From: wiechula Date: Mon, 16 Mar 2020 15:29:52 +0100 Subject: [PATCH 22/24] Adapt with new common utils --- .../calibration/macro/drawNoiseAndPedestal.C | 116 ++---------------- Detectors/TPC/calibration/macro/drawPulser.C | 39 +----- 2 files changed, 14 insertions(+), 141 deletions(-) diff --git a/Detectors/TPC/calibration/macro/drawNoiseAndPedestal.C b/Detectors/TPC/calibration/macro/drawNoiseAndPedestal.C index 50ddc69496cf3..974e62940bf6d 100644 --- a/Detectors/TPC/calibration/macro/drawNoiseAndPedestal.C +++ b/Detectors/TPC/calibration/macro/drawNoiseAndPedestal.C @@ -15,23 +15,12 @@ #include "TFile.h" #include "TPCBase/CalDet.h" #include "TPCBase/Painter.h" +#include "TPCBase/Utils.h" #include "TPad.h" #include "TCanvas.h" #include "TH1F.h" #endif -/// Helper to get active histogram and bin information -TH1* GetBinInfoXY(int& binx, int& biny, float& bincx, float& bincy); - -/// Save canvases in arr to png, pdf, root -void SaveCanvases(TObjArray* arr, std::string_view outDir); - -/// Save a single canvas to outDir using the object name -void SaveCanvas(TCanvas* c, std::string_view outDir); - -/// Add fec information to the active histogram title -void addFECInfo(); - /// Open pedestalFile and retrieve noise and pedestal values /// Draw then in separate canvases and add an executable to be able to add /// FEC information to the title @@ -39,7 +28,7 @@ void addFECInfo(); /// \param mode 0: one canvas per ROC 2D and 1D, 1: one canvas per full TPC, one canvas with all ROCs, for each noise and pedestal /// \param outDir output directory to store plos in. Don't store if empty /// \return Array with canvases -TObjArray* drawNoiseAndPedestal(TString pedestalFile, int mode = 0, std::string_view outDir = "") +TObjArray* drawNoiseAndPedestal(std::string_view pedestalFile, int mode = 0, std::string_view outDir = "") { if ((mode != 0) && (mode != 1)) { return 0x0; @@ -49,7 +38,7 @@ TObjArray* drawNoiseAndPedestal(TString pedestalFile, int mode = 0, std::string_ arrCanvases->SetName("NoiseAndPedestals"); using namespace o2::tpc; - TFile f(pedestalFile); + TFile f(pedestalFile.data()); gROOT->cd(); // ===| load noise and pedestal from file |=== @@ -61,7 +50,7 @@ TObjArray* drawNoiseAndPedestal(TString pedestalFile, int mode = 0, std::string_ // mode 1 handling if (mode == 1) { - auto arrPedestals = painter::makeSummaryCanvases(*calPedestal, 120, 20.f, 120.f); + auto arrPedestals = painter::makeSummaryCanvases(*calPedestal, 120, 20.f, 140.f); auto arrNoise = painter::makeSummaryCanvases(*calNoise, 100, 0.f, 5.f); for (auto c : arrPedestals) { @@ -73,7 +62,7 @@ TObjArray* drawNoiseAndPedestal(TString pedestalFile, int mode = 0, std::string_ } if (outDir.size()) { - SaveCanvases(arrCanvases, outDir); + utils::saveCanvases(*arrCanvases, outDir, "png,pdf", "NoiseAndPedestalCanvases.root"); } return arrCanvases; @@ -124,11 +113,11 @@ TObjArray* drawNoiseAndPedestal(TString pedestalFile, int mode = 0, std::string_ hNoise->Draw(); auto cPedestal2D = new TCanvas(Form("cPedestal2D%02zu", iroc), Form("Pedestals2D ROC %02zu", iroc)); - cPedestal2D->AddExec(Form("addFECInfoPedestal%02zu", iroc), "addFECInfo()"); + cPedestal2D->AddExec(Form("addFECInfoPedestal%02zu", iroc), "o2::tpc::utils::addFECInfo()"); hPedestal2D->Draw("colz"); auto cNoise2D = new TCanvas(Form("cNoise2D%02zu", iroc), Form("Noise2D ROC %02zu", iroc)); - cNoise2D->AddExec(Form("addFECInfoNoise%02zu", iroc), "addFECInfo()"); + cNoise2D->AddExec(Form("addFECInfoNoise%02zu", iroc), "o2::tpc::utils::addFECInfo()"); hNoise2D->Draw("colz"); arrCanvases->Add(cPedestal); @@ -138,97 +127,8 @@ TObjArray* drawNoiseAndPedestal(TString pedestalFile, int mode = 0, std::string_ } if (outDir.size()) { - SaveCanvases(arrCanvases, outDir); + utils::saveCanvases(*arrCanvases, outDir, "png,pdf", "NoiseAndPedestalCanvases.root"); } return arrCanvases; } - -TH1* GetBinInfoXY(int& binx, int& biny, float& bincx, float& bincy) -{ - TObject* select = gPad->GetSelected(); - if (!select) - return 0x0; - if (!select->InheritsFrom("TH2")) { - gPad->SetUniqueID(0); - return 0x0; - } - - TH1* h = (TH1*)select; - gPad->GetCanvas()->FeedbackMode(kTRUE); - - const int px = gPad->GetEventX(); - const int py = gPad->GetEventY(); - const float xx = gPad->AbsPixeltoX(px); - const float x = gPad->PadtoX(xx); - const float yy = gPad->AbsPixeltoY(py); - const float y = gPad->PadtoX(yy); - binx = h->GetXaxis()->FindBin(x); - biny = h->GetYaxis()->FindBin(y); - bincx = h->GetXaxis()->GetBinCenter(binx); - bincy = h->GetYaxis()->GetBinCenter(biny); - //printf("binx, biny: %d %d\n",binx,biny); - - return h; -} - -void addFECInfo() -{ - using namespace o2::tpc; - const int event = gPad->GetEvent(); - if (event != 51) { - return; - } - - int binx, biny; - float bincx, bincy; - TH1* h = GetBinInfoXY(binx, biny, bincx, bincy); - if (!h) { - return; - } - - const float binValue = h->GetBinContent(binx, biny); - const int row = int(TMath::Floor(bincx)); - const int cpad = int(TMath::Floor(bincy)); - - const auto& mapper = Mapper::instance(); - - const int roc = h->GetUniqueID(); - if (roc < 0 || roc >= (int)ROC::MaxROC) - return; - if (row < 0 || row >= (int)mapper.getNumberOfRowsROC(roc)) - return; - const int nPads = mapper.getNumberOfPadsInRowROC(roc, row); - const int pad = cpad + nPads / 2; - //printf("row %d, cpad %d, pad %d, nPads %d\n", row, cpad, pad, nPads); - if (pad < 0 || pad >= (int)nPads) { - return; - } - const int channel = mapper.getPadNumberInROC(PadROCPos(roc, row, pad)); - - const auto& fecInfo = mapper.getFECInfo(PadROCPos(roc, row, pad)); - - TString title("#splitline{#lower[.1]{#scale[.5]{"); - title += (roc / 18 % 2 == 0) ? "A" : "C"; - title += Form("%02d (%02d) row: %02d, pad: %03d, globalpad: %05d (in roc)}}}{#scale[.5]{FEC: %02d, Chip: %02d, Chn: %02d, Value: %.3f}}", - roc % 18, roc, row, pad, channel, fecInfo.getIndex(), fecInfo.getSampaChip(), fecInfo.getSampaChannel(), binValue); - - h->SetTitle(title.Data()); -} - -void SaveCanvases(TObjArray* arr, std::string_view outDir) -{ - for (auto c : *arr) { - SaveCanvas(static_cast(c), outDir); - } - - std::unique_ptr outFile(TFile::Open(TString::Format("%s/NoiseAndPedestalCanvases.root", outDir.data()), "recreate")); - arr->Write(arr->GetName(), TObject::kSingleKey); - outFile->Close(); -} - -void SaveCanvas(TCanvas* c, std::string_view outDir) -{ - c->SaveAs(TString::Format("%s/%s.png", outDir.data(), c->GetName())); - c->SaveAs(TString::Format("%s/%s.pdf", outDir.data(), c->GetName())); -} diff --git a/Detectors/TPC/calibration/macro/drawPulser.C b/Detectors/TPC/calibration/macro/drawPulser.C index 5e411bd72c500..d499010138a3d 100644 --- a/Detectors/TPC/calibration/macro/drawPulser.C +++ b/Detectors/TPC/calibration/macro/drawPulser.C @@ -15,23 +15,13 @@ #include "TFile.h" #include "TPCBase/CalDet.h" #include "TPCBase/Painter.h" +#include "TPCBase/Utils.h" #include "TPad.h" #include "TCanvas.h" #include "TH1F.h" #include "TString.h" #endif -TH1* GetBinInfoXY(int& binx, int& biny, float& bincx, float& bincy); - -/// Add fec information to the active histogram title -void addFECInfo(); - -/// Save canvases in arr to png, pdf, root -void SaveCanvases(TObjArray* arr, std::string_view outDir); - -/// Save a single canvas to outDir using the object name -void SaveCanvas(TCanvas* c, std::string_view outDir); - /// Open pedestalFile and retrieve noise and pedestal values /// Draw then in separate canvases and add an executable to be able to add /// FEC information to the title @@ -72,7 +62,7 @@ TObjArray* drawPulser(TString pulserFile, int mode = 0, std::string_view outDir } if (outDir.size()) { - SaveCanvases(arrCanvases, outDir); + utils::saveCanvases(*arrCanvases, outDir, "png,pdf", "PulserCanvases.root"); } return arrCanvases; @@ -151,17 +141,17 @@ TObjArray* drawPulser(TString pulserFile, int mode = 0, std::string_view outDir cPulser->Divide(3, 2); cPulser->cd(1); - gPad->AddExec(Form("addFECInfoT0%02d", iroc), "addFECInfo()"); + gPad->AddExec(Form("addFECInfoT0%02d", iroc), "o2::tpc::utils::addFECInfo()"); hT02D->Draw("colz"); hT02D->SetUniqueID(iroc); cPulser->cd(2); - gPad->AddExec(Form("addFECInfoWidth%02d", iroc), "addFECInfo()"); + gPad->AddExec(Form("addFECInfoWidth%02d", iroc), "o2::tpc::utils::addFECInfo()"); hWidth2D->Draw("colz"); hWidth2D->SetUniqueID(iroc); cPulser->cd(3); - gPad->AddExec(Form("addFECInfoQtot%02d", iroc), "addFECInfo()"); + gPad->AddExec(Form("addFECInfoQtot%02d", iroc), "o2::tpc::utils::addFECInfo()"); hQtot2D->Draw("colz"); hQtot2D->SetUniqueID(iroc); @@ -178,7 +168,7 @@ TObjArray* drawPulser(TString pulserFile, int mode = 0, std::string_view outDir } if (outDir.size()) { - SaveCanvases(arrCanvases, outDir); + utils::saveCanvases(*arrCanvases, outDir, "png,pdf", "PulserCanvases.root"); } return arrCanvases; @@ -255,20 +245,3 @@ void addFECInfo() h->SetTitle(title.Data()); } - -void SaveCanvases(TObjArray* arr, std::string_view outDir) -{ - for (auto c : *arr) { - SaveCanvas(static_cast(c), outDir); - } - - std::unique_ptr outFile(TFile::Open(TString::Format("%s/PulserCanvases.root", outDir.data()), "recreate")); - arr->Write(arr->GetName(), TObject::kSingleKey); - outFile->Close(); -} - -void SaveCanvas(TCanvas* c, std::string_view outDir) -{ - c->SaveAs(TString::Format("%s/%s.png", outDir.data(), c->GetName())); - c->SaveAs(TString::Format("%s/%s.pdf", outDir.data(), c->GetName())); -} From 92a1a1196faed07c792b70ad54398f01bbece672 Mon Sep 17 00:00:00 2001 From: wiechula Date: Sun, 29 Mar 2020 13:53:54 +0200 Subject: [PATCH 23/24] macro for pedestal and threshold files for CRU configuration --- Detectors/TPC/calibration/CMakeLists.txt | 3 + .../calibration/macro/preparePedestalFiles.C | 304 ++++++++++++++++++ .../calibration/macro/preparePedestalFiles.sh | 63 ++++ 3 files changed, 370 insertions(+) create mode 100644 Detectors/TPC/calibration/macro/preparePedestalFiles.C create mode 100755 Detectors/TPC/calibration/macro/preparePedestalFiles.sh diff --git a/Detectors/TPC/calibration/CMakeLists.txt b/Detectors/TPC/calibration/CMakeLists.txt index f472ba2ea0655..db2761d28adcd 100644 --- a/Detectors/TPC/calibration/CMakeLists.txt +++ b/Detectors/TPC/calibration/CMakeLists.txt @@ -65,3 +65,6 @@ o2_add_test_root_macro(macro/dumpDigits.C o2_add_test_root_macro(macro/extractGainMap.C PUBLIC_LINK_LIBRARIES O2::TPCCalibration LABELS tpc) +o2_add_test_root_macro(macro/preparePedestalFiles.C + PUBLIC_LINK_LIBRARIES O2::TPCCalibration + LABELS tpc) diff --git a/Detectors/TPC/calibration/macro/preparePedestalFiles.C b/Detectors/TPC/calibration/macro/preparePedestalFiles.C new file mode 100644 index 0000000000000..c32349185f798 --- /dev/null +++ b/Detectors/TPC/calibration/macro/preparePedestalFiles.C @@ -0,0 +1,304 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include + +#include "TFile.h" +#include "TROOT.h" +#include "TSystem.h" + +#include "TPCBase/Mapper.h" +#include "TPCBase/CalDet.h" +#include "TPCBase/Utils.h" +#endif + +struct LinkInfo { + LinkInfo(int cru, int link) : cru(cru), globalLinkID(link) {} + int cru{0}; + int globalLinkID{0}; + + bool operator<(const LinkInfo& other) const + { + if (cru < other.cru) { + return true; + } + if ((cru == other.cru) && (globalLinkID < other.globalLinkID)) { + return true; + } + return false; + } +}; + +using ValueArray = std::array; +using DataMap = std::map; + +void writeValues(const std::string_view fileName, const DataMap& map); +int getHWChannel(int sampa, int channel, int regionIter); + +template +uint32_t getADCValue(float value) +{ + static constexpr uint32_t DataBitSize = DataBitSizeT; ///< number of bits of the data representation + static constexpr uint32_t SignificantBits = SignificantBitsT; ///< number of bits used for floating point precision + static constexpr uint64_t BitMask = ((uint64_t(1) << DataBitSize) - 1); ///< mask for bits + static constexpr float FloatConversion = 1.f / float(1 << SignificantBits); ///< conversion factor from integer representation to float + + const auto adc = uint32_t((value + 0.5f * FloatConversion) / FloatConversion) & BitMask; + assert(std::abs(value - adc * FloatConversion) < FloatConversion); + + return adc; +} + +void preparePedestalFiles(const std::string_view pedestalFileName, const std::string_view outputDir = "./", float sigmaNoise = 3, float minADC = 2, float pedestalOffset = 0) +{ + static constexpr float FloatConversion = 1.f / float(1 << 2); + + using namespace o2::tpc; + const auto& mapper = Mapper::instance(); + + TFile f(pedestalFileName.data()); + gROOT->cd(); + + // ===| load noise and pedestal from file |=== + CalDet output("Pedestals"); + CalDet* calPedestal = nullptr; + CalDet* calNoise = nullptr; + f.GetObject("Pedestals", calPedestal); + f.GetObject("Noise", calNoise); + + DataMap pedestalValues; + DataMap thresholdlValues; + + // ===| prepare values |=== + for (size_t iroc = 0; iroc < calPedestal->getData().size(); ++iroc) { + const ROC roc(iroc); + + const auto& rocPedestal = calPedestal->getCalArray(iroc); + const auto& rocNoise = calNoise->getCalArray(iroc); + auto& rocOut = output.getCalArray(iroc); + + const int padOffset = (iroc > 35) ? mapper.getPadsInIROC() : 0; + + // skip empty + if (!(std::abs(rocPedestal.getSum() + rocNoise.getSum()) > 0)) { + continue; + } + + //loop over pads + for (size_t ipad = 0; ipad < rocPedestal.getData().size(); ++ipad) { + const int globalPad = ipad + padOffset; + const FECInfo& fecInfo = mapper.fecInfo(globalPad); + const CRU cru = mapper.getCRU(roc.getSector(), globalPad); + const uint32_t region = cru.region(); + const int cruID = cru.number(); + //int globalLinkID = fecInfo.getIndex(); + + const PartitionInfo& partInfo = mapper.getMapPartitionInfo()[cru.partition()]; + const int nFECs = partInfo.getNumberOfFECs(); + const int fecOffset = (nFECs + 1) / 2; + const int fecInPartition = fecInfo.getIndex() - partInfo.getSectorFECOffset(); + const int dataWrapperID = fecInPartition >= fecOffset; + const int globalLinkID = (fecInPartition % fecOffset) + dataWrapperID * 12; + + const float pedestal = rocPedestal.getValue(ipad); + const float noise = rocNoise.getValue(ipad); + const float threshold = std::max(sigmaNoise * noise, minADC); + + const int hwChannel = getHWChannel(fecInfo.getSampaChip(), fecInfo.getSampaChannel(), region % 2); + //printf("%4d %4d %4d %4d %4d: %u\n", cru.number(), globalLinkID, hwChannel, fecInfo.getSampaChip(), fecInfo.getSampaChannel(), getADCValue(pedestal)); + + const auto adcPedestal = getADCValue(pedestal); + const auto adcThreshold = getADCValue(threshold); + pedestalValues[LinkInfo(cruID, globalLinkID)][hwChannel] = adcPedestal; + thresholdlValues[LinkInfo(cruID, globalLinkID)][hwChannel] = adcThreshold; + rocOut.setValue(ipad, adcPedestal * FloatConversion); + //if ((iroc == 1) && (ipad < 10)) { + //const auto val = pedestalValues[LinkInfo(cruID, globalLinkID)][hwChannel]; + //printf("%4d %4d %4d %4d %4d %4d: %u (%u)\n", cru.number(), globalLinkID, ipad, hwChannel, fecInfo.getSampaChip(), fecInfo.getSampaChannel(), adcPedestal, val); + //} + //if ((cruID == 11) && (globalLinkID == 0)) { + //const auto val = pedestalValues[LinkInfo(cruID, globalLinkID)][hwChannel]; + //printf("%4d %4d %4d %4d %4d: %u (%u)\n", cru.number(), globalLinkID, hwChannel, fecInfo.getSampaChip(), fecInfo.getSampaChannel(), getADCValue(pedestal), val); + //} + } + } + + writeValues((outputDir + "/pedestal_values.txt").Data(), pedestalValues); + writeValues((outputDir + "/threshold_values.txt").Data(), thresholdlValues); + + TFile fout("/tmp/outPed.direct.root", "recreate"); + fout.WriteObject(&output, "Pedestals"); +} + +/// return the hardware channel number as mapped in the CRU +// +int getHWChannel(int sampa, int channel, int regionIter) +{ + const int sampaOffet[5] = {0, 4, 8, 0, 4}; + if (regionIter && sampa == 2) { + channel -= 16; + } + int outch = sampaOffet[sampa] + ((channel % 16) % 2) + 2 * (channel / 16) + (channel % 16) / 2 * 10; + return outch; +} + +/// write values of map to fileName +// +void writeValues(const std::string_view fileName, const DataMap& map) +{ + std::ofstream str(fileName.data(), std::ofstream::out); + + static constexpr float FloatConversion = 1.f / float(1 << 2); + for (const auto& [linkInfo, data] : map) { + int iter = 0; + std::string values; + for (const auto& val : data) { + if (values.size()) { + values += ","; + } + values += std::to_string(val); + //printf("%3d: %4d (%.2f),\n", iter, val, val * FloatConversion); + ++iter; + } + //printf("%4d %4d %s\n", linkInfo.cru, linkInfo.globalLinkID, values.data()); + //printf("\n\n"); + + str << linkInfo.cru << " " + << linkInfo.globalLinkID << " " + << values << "\n"; + } +} + +std::tuple getSampaInfo(int hwChannel, int cruID) +{ + static constexpr int sampaMapping[10] = {0, 0, 1, 1, 2, 3, 3, 4, 4, 2}; + static constexpr int channelOffset[10] = {0, 16, 0, 16, 0, 0, 16, 0, 16, 16}; + const int regionIter = cruID % 2; + + const int istreamm = ((hwChannel % 10) / 2); + const int partitionStream = istreamm + regionIter * 5; + const int sampaOnFEC = sampaMapping[partitionStream]; + const int channel = (hwChannel % 2) + 2 * (hwChannel / 10); + const int channelOnSAMPA = channel + channelOffset[partitionStream]; + + return {sampaOnFEC, channelOnSAMPA}; +} + +/// Test input channel mapping vs output channel mapping +void testChannelMapping(int cruID = 0) +{ + const int sampaMapping[10] = {0, 0, 1, 1, 2, 3, 3, 4, 4, 2}; + const int channelOffset[10] = {0, 16, 0, 16, 0, 0, 16, 0, 16, 16}; + const int regionIter = cruID % 2; + + for (std::size_t ichannel = 0; ichannel < 80; ++ichannel) { + const int istreamm = ((ichannel % 10) / 2); + const int partitionStream = istreamm + regionIter * 5; + const int sampaOnFEC = sampaMapping[partitionStream]; + const int channel = (ichannel % 2) + 2 * (ichannel / 10); + const int channelOnSAMPA = channel + channelOffset[partitionStream]; + + const size_t outch = getHWChannel(sampaOnFEC, channelOnSAMPA, regionIter); + printf("%4zu %4d %4d : %4zu %s\n", outch, sampaOnFEC, channelOnSAMPA, ichannel, (outch != ichannel) ? "============" : ""); + } +} + +o2::tpc::CalDet getCalPad(const std::string_view fileName, const std::string_view outFile = "", std::string_view outName = "") +{ + using namespace o2::tpc; + const auto& mapper = Mapper::instance(); + + static constexpr float FloatConversion = 1.f / float(1 << 2); + + int cruID{0}; + int globalLinkID{0}; + int sampaOnFEC{0}; + int channelOnSAMPA{0}; + std::string values; + CalDet calPad(gSystem->BaseName(fileName.data())); + + std::string line; + std::ifstream infile(fileName.data(), std::ifstream::in); + if (!infile.is_open()) { + std::cout << "could not open file " << fileName << "\n"; + return calPad; + } + + while (std::getline(infile, line)) { + std::stringstream streamLine(line); + streamLine >> cruID >> globalLinkID >> values; + + const CRU cru(cruID); + const PartitionInfo& partInfo = mapper.getMapPartitionInfo()[cru.partition()]; + const int nFECs = partInfo.getNumberOfFECs(); + const int fecOffset = (nFECs + 1) / 2; + const int fecInPartition = (globalLinkID < fecOffset) ? globalLinkID : fecOffset + globalLinkID % 12; + //printf("%4d, %4d, %4d : %s\n", cruID, globalLinkID, fecInPartition, values.data()); + //printf("%4d, %4d, %4d\n", cruID, globalLinkID, fecInPartition); + + int hwChannel{0}; + for (const auto& val : utils::tokenize(values, ",")) { + std::tie(sampaOnFEC, channelOnSAMPA) = getSampaInfo(hwChannel, cru); + const PadROCPos padROCPos = mapper.padROCPos(cru, fecInPartition, sampaOnFEC, channelOnSAMPA); + const float set = FloatConversion * std::stof(val); + calPad.getCalArray(padROCPos.getROC()).setValue(padROCPos.getRow(), padROCPos.getPad(), set); + //printf("%3d, %3d, %3d (%3d, %3d, %3d): %3d (%.2f)\n", hwChannel, sampaOnFEC, channelOnSAMPA, int(padROCPos.getROC()), padROCPos.getRow(), padROCPos.getPad(), std::stoi(val), set); + ++hwChannel; + } + } + + if (outFile.size()) { + TFile f(outFile.data(), "recreate"); + if (!outName.size()) { + outName = calPad.getName(); + } + f.WriteObject(&calPad, outName.data()); + } + return calPad; +} + +void debugDiff(std::string_view file1, std::string_view file2, std::string_view objName) +{ + using namespace o2::tpc; + CalPad dummy; + CalPad* calPad1{nullptr}; + CalPad* calPad2{nullptr}; + + std::unique_ptr tFile1(TFile::Open(file1.data())); + std::unique_ptr tFile2(TFile::Open(file2.data())); + gROOT->cd(); + + tFile1->GetObject(objName.data(), calPad1); + tFile2->GetObject(objName.data(), calPad2); + + for (size_t iroc = 0; iroc < calPad1->getData().size(); ++iroc) { + const auto& calArray1 = calPad1->getCalArray(iroc); + const auto& calArray2 = calPad2->getCalArray(iroc); + // skip empty + if (!(std::abs(calArray1.getSum() + calArray2.getSum()) > 0)) { + continue; + } + + for (size_t ipad = 0; ipad < calArray1.getData().size(); ++ipad) { + const auto val1 = calArray1.getValue(ipad); + const auto val2 = calArray2.getValue(ipad); + + if (std::abs(val2 - val1) >= 0.25) { + printf("%2zu %5zu : %.5f - %.5f = %.2f\n", iroc, ipad, val2, val1, val2 - val1); + } + } + } +} + diff --git a/Detectors/TPC/calibration/macro/preparePedestalFiles.sh b/Detectors/TPC/calibration/macro/preparePedestalFiles.sh new file mode 100755 index 0000000000000..bac489312512a --- /dev/null +++ b/Detectors/TPC/calibration/macro/preparePedestalFiles.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +usage() { + usage="Usage: +preparePedestalFiles.sh [optional arguments] + +required arguments +-i, --inputFile= : input file name + +optional arguments: +-o, --outputDir= : set output directory for (default: ./) +-m, --minADC= : minimal ADC value accepted for threshold (default: 2) +-s, --sigmaNoise= : number of sigmas for the threshold (default: 3) +-h, --help : show this help message" + + echo "$usage" +} + +usageAndExit() { + usage + if [[ "$0" =~ preparePedestalFiles.sh ]]; then + exit 0 + else + return 0 + fi +} + +# ===| default variable values |================================================ +fileInfo= +outputFile="./" +minADC=2 +sigmaNoise=3 + +# ===| parse command line options |============================================= +OPTIONS=$(getopt -l "inputFile:,outputDir:,minADC:,sigmaNoise:,help" -o "i:o:t:m:s:h" -n "preparePedestalFiles.sh" -- "$@") + +if [ $? != 0 ] ; then + usageAndExit +fi + +eval set -- "$OPTIONS" + +while true; do + case "$1" in + --) shift; break;; + -i|--inputFile) inputFile=$2; shift 2;; + -o|--outputDir) outputDir=$2; shift 2;; + -m|--minADC) minADC=$2; shift 2;; + -s|--sigmaNoise) sigmaNoise=$2; shift 2;; + -h|--help) usageAndExit;; + *) echo "Internal error!" ; exit 1 ;; + esac +done + +# ===| check for required arguments |=========================================== +if [[ -z "$inputFile" ]]; then + usageAndExit +fi + +# ===| command building and execution |========================================= +cmd="root.exe -b -q -l -n -x $O2_SRC/Detectors/TPC/calibration/macro/preparePedestalFiles.C+g'(\"$inputFile\",\"$outputDir\", $sigmaNoise, $minADC)'" +echo "running: $cmd" +eval $cmd From 1e79b5953cd9c3199758582cfd5716731d5bd547 Mon Sep 17 00:00:00 2001 From: wiechula Date: Sun, 29 Mar 2020 15:15:24 +0200 Subject: [PATCH 24/24] Cleanup macro for CRU configuration --- .../calibration/macro/preparePedestalFiles.C | 88 ++++++++++--------- .../calibration/macro/preparePedestalFiles.sh | 2 +- 2 files changed, 49 insertions(+), 41 deletions(-) diff --git a/Detectors/TPC/calibration/macro/preparePedestalFiles.C b/Detectors/TPC/calibration/macro/preparePedestalFiles.C index c32349185f798..0f4e437a9409c 100644 --- a/Detectors/TPC/calibration/macro/preparePedestalFiles.C +++ b/Detectors/TPC/calibration/macro/preparePedestalFiles.C @@ -47,20 +47,30 @@ using DataMap = std::map; void writeValues(const std::string_view fileName, const DataMap& map); int getHWChannel(int sampa, int channel, int regionIter); +/// convert float to integer with fixed precision and max number of digits template -uint32_t getADCValue(float value) +constexpr uint32_t floatToFixedSize(float value) { - static constexpr uint32_t DataBitSize = DataBitSizeT; ///< number of bits of the data representation - static constexpr uint32_t SignificantBits = SignificantBitsT; ///< number of bits used for floating point precision - static constexpr uint64_t BitMask = ((uint64_t(1) << DataBitSize) - 1); ///< mask for bits - static constexpr float FloatConversion = 1.f / float(1 << SignificantBits); ///< conversion factor from integer representation to float + constexpr uint32_t DataBitSize = DataBitSizeT; ///< number of bits of the data representation + constexpr uint32_t SignificantBits = SignificantBitsT; ///< number of bits used for floating point precision + constexpr uint64_t BitMask = ((uint64_t(1) << DataBitSize) - 1); ///< mask for bits + constexpr float FloatConversion = 1.f / float(1 << SignificantBits); ///< conversion factor from integer representation to float const auto adc = uint32_t((value + 0.5f * FloatConversion) / FloatConversion) & BitMask; - assert(std::abs(value - adc * FloatConversion) < FloatConversion); + assert(std::abs(value - adc * FloatConversion) <= 0.5f * FloatConversion); return adc; } +template +constexpr float fixedSizeToFloat(uint32_t value) +{ + constexpr uint32_t SignificantBits = SignificantBitsT; ///< number of bits used for floating point precision + constexpr float FloatConversion = 1.f / float(1 << SignificantBits); ///< conversion factor from integer representation to float + + return float(value) * FloatConversion; +} + void preparePedestalFiles(const std::string_view pedestalFileName, const std::string_view outputDir = "./", float sigmaNoise = 3, float minADC = 2, float pedestalOffset = 0) { static constexpr float FloatConversion = 1.f / float(1 << 2); @@ -103,6 +113,8 @@ void preparePedestalFiles(const std::string_view pedestalFileName, const std::st const CRU cru = mapper.getCRU(roc.getSector(), globalPad); const uint32_t region = cru.region(); const int cruID = cru.number(); + const int sampa = fecInfo.getSampaChip(); + const int sampaChannel = fecInfo.getSampaChannel(); //int globalLinkID = fecInfo.getIndex(); const PartitionInfo& partInfo = mapper.getMapPartitionInfo()[cru.partition()]; @@ -112,34 +124,32 @@ void preparePedestalFiles(const std::string_view pedestalFileName, const std::st const int dataWrapperID = fecInPartition >= fecOffset; const int globalLinkID = (fecInPartition % fecOffset) + dataWrapperID * 12; - const float pedestal = rocPedestal.getValue(ipad); - const float noise = rocNoise.getValue(ipad); + float pedestal = rocPedestal.getValue(ipad); + float noise = std::abs(rocNoise.getValue(ipad)); // it seems with the new fitting procedure, the noise can also be negative, since in gaus sigma is quadratic + if ((pedestal < 0) || (pedestal > 1023) || (noise < 0) || (noise > 1023)) { + printf("Bad pedestal or noise value in ROC %2zu, CRU %3d, fec in CRU: %2d, SAMPA: %d, channel: %2d, pedestal: %.4f, noise %.4f, setting both to 0\n", iroc, cruID, fecInPartition, sampa, sampaChannel, pedestal, noise); + pedestal = 0; + noise = 0; + } const float threshold = std::max(sigmaNoise * noise, minADC); - const int hwChannel = getHWChannel(fecInfo.getSampaChip(), fecInfo.getSampaChannel(), region % 2); + const int hwChannel = getHWChannel(sampa, sampaChannel, region % 2); + // for debugging //printf("%4d %4d %4d %4d %4d: %u\n", cru.number(), globalLinkID, hwChannel, fecInfo.getSampaChip(), fecInfo.getSampaChannel(), getADCValue(pedestal)); - const auto adcPedestal = getADCValue(pedestal); - const auto adcThreshold = getADCValue(threshold); + const auto adcPedestal = floatToFixedSize(pedestal); + const auto adcThreshold = floatToFixedSize(threshold); pedestalValues[LinkInfo(cruID, globalLinkID)][hwChannel] = adcPedestal; thresholdlValues[LinkInfo(cruID, globalLinkID)][hwChannel] = adcThreshold; - rocOut.setValue(ipad, adcPedestal * FloatConversion); - //if ((iroc == 1) && (ipad < 10)) { - //const auto val = pedestalValues[LinkInfo(cruID, globalLinkID)][hwChannel]; - //printf("%4d %4d %4d %4d %4d %4d: %u (%u)\n", cru.number(), globalLinkID, ipad, hwChannel, fecInfo.getSampaChip(), fecInfo.getSampaChannel(), adcPedestal, val); - //} - //if ((cruID == 11) && (globalLinkID == 0)) { - //const auto val = pedestalValues[LinkInfo(cruID, globalLinkID)][hwChannel]; - //printf("%4d %4d %4d %4d %4d: %u (%u)\n", cru.number(), globalLinkID, hwChannel, fecInfo.getSampaChip(), fecInfo.getSampaChannel(), getADCValue(pedestal), val); + // for debugging + //if(!(std::abs(pedestal - fixedSizeToFloat(adcPedestal)) <= 0.5 * 0.25)) { + //printf("%4d %4d %4d %4d %4d: %u %.2f %.4f %.4f\n", cru.number(), globalLinkID, hwChannel, sampa, sampaChannel, adcPedestal, fixedSizeToFloat(adcPedestal), pedestal, pedestal - fixedSizeToFloat(adcPedestal)); //} } } writeValues((outputDir + "/pedestal_values.txt").Data(), pedestalValues); writeValues((outputDir + "/threshold_values.txt").Data(), thresholdlValues); - - TFile fout("/tmp/outPed.direct.root", "recreate"); - fout.WriteObject(&output, "Pedestals"); } /// return the hardware channel number as mapped in the CRU @@ -155,25 +165,19 @@ int getHWChannel(int sampa, int channel, int regionIter) } /// write values of map to fileName -// +/// void writeValues(const std::string_view fileName, const DataMap& map) { std::ofstream str(fileName.data(), std::ofstream::out); - static constexpr float FloatConversion = 1.f / float(1 << 2); for (const auto& [linkInfo, data] : map) { - int iter = 0; std::string values; for (const auto& val : data) { if (values.size()) { values += ","; } values += std::to_string(val); - //printf("%3d: %4d (%.2f),\n", iter, val, val * FloatConversion); - ++iter; } - //printf("%4d %4d %s\n", linkInfo.cru, linkInfo.globalLinkID, values.data()); - //printf("\n\n"); str << linkInfo.cru << " " << linkInfo.globalLinkID << " " @@ -181,6 +185,7 @@ void writeValues(const std::string_view fileName, const DataMap& map) } } +/// convert HW mapping to sampa and channel number std::tuple getSampaInfo(int hwChannel, int cruID) { static constexpr int sampaMapping[10] = {0, 0, 1, 1, 2, 3, 3, 4, 4, 2}; @@ -197,6 +202,8 @@ std::tuple getSampaInfo(int hwChannel, int cruID) } /// Test input channel mapping vs output channel mapping +/// +/// Consistency check of mapping void testChannelMapping(int cruID = 0) { const int sampaMapping[10] = {0, 0, 1, 1, 2, 3, 3, 4, 4, 2}; @@ -215,7 +222,11 @@ void testChannelMapping(int cruID = 0) } } -o2::tpc::CalDet getCalPad(const std::string_view fileName, const std::string_view outFile = "", std::string_view outName = "") +/// create cal pad object from HW value file +/// +/// if outputFile is set, write the object to file +/// if calPadName is set use it for the object name in the file. Otherwise the basename of the fileName is used +o2::tpc::CalDet getCalPad(const std::string_view fileName, const std::string_view outputFile = "", std::string_view calPadName = "") { using namespace o2::tpc; const auto& mapper = Mapper::instance(); @@ -245,30 +256,28 @@ o2::tpc::CalDet getCalPad(const std::string_view fileName, const std::str const int nFECs = partInfo.getNumberOfFECs(); const int fecOffset = (nFECs + 1) / 2; const int fecInPartition = (globalLinkID < fecOffset) ? globalLinkID : fecOffset + globalLinkID % 12; - //printf("%4d, %4d, %4d : %s\n", cruID, globalLinkID, fecInPartition, values.data()); - //printf("%4d, %4d, %4d\n", cruID, globalLinkID, fecInPartition); int hwChannel{0}; for (const auto& val : utils::tokenize(values, ",")) { std::tie(sampaOnFEC, channelOnSAMPA) = getSampaInfo(hwChannel, cru); const PadROCPos padROCPos = mapper.padROCPos(cru, fecInPartition, sampaOnFEC, channelOnSAMPA); - const float set = FloatConversion * std::stof(val); + const float set = fixedSizeToFloat(uint32_t(std::stoi(val))); calPad.getCalArray(padROCPos.getROC()).setValue(padROCPos.getRow(), padROCPos.getPad(), set); - //printf("%3d, %3d, %3d (%3d, %3d, %3d): %3d (%.2f)\n", hwChannel, sampaOnFEC, channelOnSAMPA, int(padROCPos.getROC()), padROCPos.getRow(), padROCPos.getPad(), std::stoi(val), set); ++hwChannel; } } - if (outFile.size()) { - TFile f(outFile.data(), "recreate"); - if (!outName.size()) { - outName = calPad.getName(); + if (outputFile.size()) { + TFile f(outputFile.data(), "recreate"); + if (!calPadName.size()) { + calPadName = calPad.getName(); } - f.WriteObject(&calPad, outName.data()); + f.WriteObject(&calPad, calPadName.data()); } return calPad; } +/// debug differences between two cal pad objects void debugDiff(std::string_view file1, std::string_view file2, std::string_view objName) { using namespace o2::tpc; @@ -301,4 +310,3 @@ void debugDiff(std::string_view file1, std::string_view file2, std::string_view } } } - diff --git a/Detectors/TPC/calibration/macro/preparePedestalFiles.sh b/Detectors/TPC/calibration/macro/preparePedestalFiles.sh index bac489312512a..d5ea493b26cf7 100755 --- a/Detectors/TPC/calibration/macro/preparePedestalFiles.sh +++ b/Detectors/TPC/calibration/macro/preparePedestalFiles.sh @@ -27,7 +27,7 @@ usageAndExit() { # ===| default variable values |================================================ fileInfo= -outputFile="./" +outputDir="./" minADC=2 sigmaNoise=3