diff --git a/Modules/TRD/CMakeLists.txt b/Modules/TRD/CMakeLists.txt index f763e09ab2..834c3aeea9 100644 --- a/Modules/TRD/CMakeLists.txt +++ b/Modules/TRD/CMakeLists.txt @@ -2,7 +2,7 @@ add_library(O2QcTRD) -target_sources(O2QcTRD PRIVATE src/DigitsTask.cxx +target_sources(O2QcTRD PRIVATE src/TrackletsCheck.cxx src/TrackletsTask.cxx src/PulseHeightCheck.cxx src/Noise.cxx src/PulseHeight.cxx src/RawData.cxx src/DigitsTask.cxx src/DigitsCheck.cxx) target_include_directories( @@ -20,6 +20,12 @@ install(TARGETS O2QcTRD add_root_dictionary(O2QcTRD HEADERS + include/TRD/TrackletsCheck.h + include/TRD/TrackletsTask.h + include/TRD/PulseHeightCheck.h + include/TRD/Noise.h + include/TRD/PulseHeight.h + include/TRD/RawData.h include/TRD/DigitsTask.h include/TRD/DigitsCheck.h LINKDEF include/TRD/LinkDef.h diff --git a/Modules/TRD/RawData.json b/Modules/TRD/RawData.json new file mode 100644 index 0000000000..a73ce4d808 --- /dev/null +++ b/Modules/TRD/RawData.json @@ -0,0 +1,66 @@ +{ + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "ccdb-test.cern.ch:8080", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" + }, + "Activity": { + "number": "504419", + "type": "2", + "periodName": "", "": "Period name - e.g. LHC22c, LHC22c1b_test", + "passName": "", "": "Pass type - e.g. spass, cpass1", + "provenance": "qc", "": "Provenance - qc or qc_mc depending whether it is normal data or monte carlo data" + }, + "monitoring": { + "url": "infologger:///debug?qc" + }, + "consul": { + "url": "http://consul-test.cern.ch:8500" + }, + "conditionDB": { + "url": "ccdb-test.cern.ch:8080" + } + }, + "tasks": { + "RawDataTask": { + "active": "true", + "className": "o2::quality_control_modules::trd::RawData", + "moduleName": "QcTRD", + "detectorName": "TRD", + "cycleDurationSeconds": "1", + "maxNumberCycles": "-1", + "dataSource_comment": "no comment", + "dataSource": { + "type": "direct", + "query": "digits:TRD/DIGITS;tracklets:TRD/TRACKLETS;triggers:TRD/TRKTRGRD;rawstats:TRD/RAWSTATS" + }, + "taskParameters": { + "myOwnKey": "emptyfornow" + }, + "location": "remote", + "saveObjectsToFile":"qcrootobjects.root" + } + }, + "checks": { + "QcCheck": { + "active": "false", + "className": "o2::quality_control_modules::trd::RawDataCheck", + "moduleName": "QcTRD", + "policy": "OnAny", + "detectorName": "TRD", + "dataSource": [{ + "type": "Task", + "name": "RawDataTask", + "MOs": ["trackletsperevent"] + }] + } + } + }, + "dataSamplingPolicies": [ + ] +} + diff --git a/Modules/TRD/include/TRD/DigitsTask.h b/Modules/TRD/include/TRD/DigitsTask.h index 002bf7d3a8..f91dc35573 100644 --- a/Modules/TRD/include/TRD/DigitsTask.h +++ b/Modules/TRD/include/TRD/DigitsTask.h @@ -20,6 +20,7 @@ #include "QualityControl/TaskInterface.h" class TH1F; +class TH2F; using namespace o2::quality_control::core; @@ -45,9 +46,48 @@ class DigitsTask final : public TaskInterface void endOfCycle() override; void endOfActivity(Activity& activity) override; void reset() override; + void buildHistograms(); + void drawLinesMCM(TH2F* histo); private: - TH1F* mADC = nullptr; + //limits + std::pair mDriftRegion; + std::pair mPulseHeightPeakRegion; + + std::shared_ptr mDigitsPerEvent = nullptr; + std::shared_ptr mTotalChargevsTimeBin = nullptr; // + std::shared_ptr mDigitHCID = nullptr; + std::shared_ptr mParsingErrors = nullptr; + + std::array, 540> mClusterAmplitudeChamber; + std::array, 6> mNClsLayer; ///[layer]->Fill(sm - 0.5 + col / 144., startRow[istack]+row); + std::shared_ptr mADCvalue; //->Fill(value); + std::array, 18> mADC; //[sm]->Fill(value); + std::array, 18> mADCTB; //[sm]->Fill(time, value); + std::array, 18> mADCTBfull; //[sm]->Fill(time, value); + std::shared_ptr mNCls; //->Fill(sm); + std::array, 18> mHCMCM; //[sm]->Fill(sum); + std::array, 18> mClsSM; //[sm]->Fill(sum); + std::array, 18> mcLStBsm; //[SM]->fILL(TIme, sum); + std::shared_ptr mClsTb; //->Fill(time, sum); + std::shared_ptr mClsChargeFirst; //->Fill(sum, (1.*sum/sumU) -1.); + std::shared_ptr mClsChargeTb; //->Fill(time, sum); + std::shared_ptr mClsChargeTbCycle; //->Fill(time, sum); + std::shared_ptr mClsNTb; //->Fill(time); + std::shared_ptr mClsAmp; //->Fill(sum); + std::shared_ptr mClsAmpDrift; //->Fill(sum); + std::shared_ptr mClsAmpTb; //, "ClsAmpTb", "ClsAmpTb", 30, -0.5, 29.5); + std::shared_ptr mClsAmpCh; // + std::array, 18> mClsDetAmp; //[sm]->Fill(detLoc, sum); + std::array, 540> mClsAmpChamber; //[iChamber]->Fill(sum); + std::shared_ptr mClsSector; //, "ClsSector", "ClsSector", nSMs, -0.5, 17.5, 500, -0.5, 999.5); + std::shared_ptr mClsStack; //, "ClsStack", "ClsStack", 5, -0.5, 4.5, 500, -0.5, 999.5); + std::array, 18> mClsDetTime; //[sm]->Fill(detLoc, time, sum); + std::array, 10> mClsChargeTbTigg; //[trgg]->Fill(time, sum); + std::shared_ptr mClsChargeTbTrigHM; //->Fill(time, sum); + std::shared_ptr mClsChargeTbTrigMinBias; //->Fill(time, sum); + std::shared_ptr mClsChargeTbTrigTRDL1; //->Fill(time, sum); + std::array, 18> mClsTbSM; }; } // namespace o2::quality_control_modules::trd diff --git a/Modules/TRD/include/TRD/LinkDef.h b/Modules/TRD/include/TRD/LinkDef.h index 872e29e102..54c27726ce 100644 --- a/Modules/TRD/include/TRD/LinkDef.h +++ b/Modules/TRD/include/TRD/LinkDef.h @@ -6,4 +6,10 @@ #pragma link C++ class o2::quality_control_modules::trd::DigitsTask+; #pragma link C++ class o2::quality_control_modules::trd::DigitsCheck+; +#pragma link C++ class o2::quality_control_modules::trd::RawData + ; +#pragma link C++ class o2::quality_control_modules::trd::PulseHeight + ; +#pragma link C++ class o2::quality_control_modules::trd::Noise + ; +#pragma link C++ class o2::quality_control_modules::trd::PulseHeightCheck + ; +#pragma link C++ class o2::quality_control_modules::trd::TrackletsTask + ; +#pragma link C++ class o2::quality_control_modules::trd::TrackletsCheck + ; #endif diff --git a/Modules/TRD/include/TRD/Noise.h b/Modules/TRD/include/TRD/Noise.h new file mode 100644 index 0000000000..8ea033713a --- /dev/null +++ b/Modules/TRD/include/TRD/Noise.h @@ -0,0 +1,54 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// 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 Noise.h +/// \author My Name +/// + +#ifndef QC_MODULE_TRD_TRDNOISE_H +#define QC_MODULE_TRD_TRDNOISE_H + +#include "QualityControl/TaskInterface.h" + +class TH1F; + +using namespace o2::quality_control::core; + +namespace o2::quality_control_modules::trd +{ + +/// \brief Example Quality Control DPL Task +/// \author My Name +class Noise final : public TaskInterface +{ + public: + /// \brief Constructor + Noise() = default; + /// Destructor + ~Noise() override; + + // Definition of the methods for the template method pattern + void initialize(o2::framework::InitContext& ctx) override; + void startOfActivity(Activity& activity) override; + void startOfCycle() override; + void monitorData(o2::framework::ProcessingContext& ctx) override; + void endOfCycle() override; + void endOfActivity(Activity& activity) override; + void reset() override; + + private: + TH1F* mHistogram = nullptr; +}; + +} // namespace o2::quality_control_modules::trd + +#endif // QC_MODULE_TRD_TRDNOISE_H diff --git a/Modules/TRD/include/TRD/PulseHeight.h b/Modules/TRD/include/TRD/PulseHeight.h new file mode 100644 index 0000000000..52a0a5daa8 --- /dev/null +++ b/Modules/TRD/include/TRD/PulseHeight.h @@ -0,0 +1,71 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// 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 PulseHeight.h +/// \author My Name +/// + +#ifndef QC_MODULE_TRD_TRDPULSEHEIGHT_H +#define QC_MODULE_TRD_TRDPULSEHEIGHT_H + +#include "QualityControl/TaskInterface.h" +#include + +class TH1F; +class TH1D; +class TH2F; +class TH2D; + +using namespace o2::quality_control::core; + +namespace o2::quality_control_modules::trd +{ + +/// \brief Example Quality Control DPL Task +/// \author My Name +class PulseHeight final : public TaskInterface +{ + public: + /// \brief Constructor + PulseHeight() = default; + /// Destructor + ~PulseHeight() override; + + // Definition of the methods for the template method pattern + void initialize(o2::framework::InitContext& ctx) override; + void startOfActivity(Activity& activity) override; + void startOfCycle() override; + void monitorData(o2::framework::ProcessingContext& ctx) override; + void endOfCycle() override; + void endOfActivity(Activity& activity) override; + void reset() override; + void buildHistograms(); + + private: + std::shared_ptr mPulseHeight = nullptr; + std::shared_ptr mPulseHeightScaled = nullptr; + std::shared_ptr mTotalPulseHeight2D = nullptr; + std::array, 18> mPulseHeight2DperSM; //ph2DSM; + std::shared_ptr mPulseHeight2 = nullptr; + std::shared_ptr mPulseHeightScaled2 = nullptr; + std::shared_ptr mTotalPulseHeight2D2 = nullptr; + std::array, 18> mPulseHeight2DperSM2; //ph2DSM; + std::pair mDriftRegion; + std::pair mPulseHeightPeakRegion; + std::shared_ptr mPulseHeightDuration; + std::shared_ptr mPulseHeightDuration1; + std::shared_ptr mPulseHeightDurationDiff; +}; + +} // namespace o2::quality_control_modules::trd + +#endif // QC_MODULE_TRD_TRDPULSEHEIGHT_H diff --git a/Modules/TRD/include/TRD/PulseHeightCheck.h b/Modules/TRD/include/TRD/PulseHeightCheck.h new file mode 100644 index 0000000000..e7240f59ab --- /dev/null +++ b/Modules/TRD/include/TRD/PulseHeightCheck.h @@ -0,0 +1,46 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// 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 PulseHeightCheck.h +/// \author My Name +/// + +#ifndef QC_MODULE_TRD_TRDPULSEHEIGHTCHECK_H +#define QC_MODULE_TRD_TRDPULSEHEIGHTCHECK_H + +#include "QualityControl/CheckInterface.h" + +namespace o2::quality_control_modules::trd +{ + +/// \brief Example QC Check +/// \author My Name +class PulseHeightCheck : public o2::quality_control::checker::CheckInterface +{ + public: + /// Default constructor + PulseHeightCheck() = default; + /// Destructor + ~PulseHeightCheck() override = default; + + // Override interface + void configure(std::string name) override; + Quality check(std::map>* moMap) override; + void beautify(std::shared_ptr mo, Quality checkResult = Quality::Null) override; + std::string getAcceptedType() override; + + ClassDefOverride(PulseHeightCheck, 1); +}; + +} // namespace o2::quality_control_modules::trd + +#endif // QC_MODULE_TRD_TRDPULSEHEIGHTCHECK_H diff --git a/Modules/TRD/include/TRD/RawData.h b/Modules/TRD/include/TRD/RawData.h new file mode 100644 index 0000000000..8e97bc5a6c --- /dev/null +++ b/Modules/TRD/include/TRD/RawData.h @@ -0,0 +1,74 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// 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 RawData.h +/// \author My Name +/// + +#ifndef QC_MODULE_TRD_TRDRAWDATA_H +#define QC_MODULE_TRD_TRDRAWDATA_H + +#include "QualityControl/TaskInterface.h" +#include "DataFormatsTRD/RawDataStats.h" +#include + +class TH1F; +class TH2F; + +using namespace o2::quality_control::core; + +namespace o2::quality_control_modules::trd +{ + +/// \brief Example Quality Control DPL Task +/// \author My Name +class RawData final : public TaskInterface +{ + public: + /// \brief Constructor + RawData() = default; + /// Destructor + ~RawData() override; + + // Definition of the methods for the template method pattern + void initialize(o2::framework::InitContext& ctx) override; + void startOfActivity(Activity& activity) override; + void startOfCycle() override; + void monitorData(o2::framework::ProcessingContext& ctx) override; + void endOfCycle() override; + void endOfActivity(Activity& activity) override; + void reset() override; + + void buildHistograms(); + void resetHistograms(); + + private: + TH1F* mDataAcceptance = nullptr; + TH2F* mDataVolumePerHalfSector = nullptr; + TH2F* mDataVolumePerHalfSectorCru = nullptr; + TH1F* mTotalChargevsTimeBin = nullptr; // + TH1F* mDigitHCID = nullptr; + TH1F* mTimeFrameTime = nullptr; + TH1F* mTrackletParsingTime = nullptr; + TH1F* mDigitParsingTime = nullptr; + TH1F* mDataVersions = nullptr; + TH1F* mDataVersionsMajor = nullptr; + TH1F* mParsingErrors = nullptr; + std::array mLinkErrors; + std::array mParsingErrors2d; + + std::array fClusterChamberAmplitude; +}; + +} // namespace o2::quality_control_modules::trd + +#endif // QC_MODULE_TRD_TRDRAWDATA_H diff --git a/Modules/TRD/include/TRD/TrackletsCheck.h b/Modules/TRD/include/TRD/TrackletsCheck.h new file mode 100644 index 0000000000..82f729a394 --- /dev/null +++ b/Modules/TRD/include/TRD/TrackletsCheck.h @@ -0,0 +1,46 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// 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 TrackletsCheck.h +/// \author My Name +/// + +#ifndef QC_MODULE_TRD_TRDTRACKLETSCHECK_H +#define QC_MODULE_TRD_TRDTRACKLETSCHECK_H + +#include "QualityControl/CheckInterface.h" + +namespace o2::quality_control_modules::trd +{ + +/// \brief Example QC Check +/// \author My Name +class TrackletsCheck : public o2::quality_control::checker::CheckInterface +{ + public: + /// Default constructor + TrackletsCheck() = default; + /// Destructor + ~TrackletsCheck() override = default; + + // Override interface + void configure(std::string name) override; + Quality check(std::map>* moMap) override; + void beautify(std::shared_ptr mo, Quality checkResult = Quality::Null) override; + std::string getAcceptedType() override; + + ClassDefOverride(TrackletsCheck, 1); +}; + +} // namespace o2::quality_control_modules::trd + +#endif // QC_MODULE_TRD_TRDTRACKLETSCHECK_H diff --git a/Modules/TRD/include/TRD/TrackletsTask.h b/Modules/TRD/include/TRD/TrackletsTask.h new file mode 100644 index 0000000000..d8459834ea --- /dev/null +++ b/Modules/TRD/include/TRD/TrackletsTask.h @@ -0,0 +1,63 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// 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 TrackletsTask.h +/// \author My Name +/// + +#ifndef QC_MODULE_TRD_TRDTRACKLETSTASK_H +#define QC_MODULE_TRD_TRDTRACKLETSTASK_H + +#include "QualityControl/TaskInterface.h" + +class TH1F; +class TH2F; + +using namespace o2::quality_control::core; + +namespace o2::quality_control_modules::trd +{ + +/// \brief Example Quality Control DPL Task +/// \author My Name +class TrackletsTask final : public TaskInterface +{ + public: + /// \brief Constructor + TrackletsTask() = default; + /// Destructor + ~TrackletsTask() override; + + // Definition of the methods for the template method pattern + void initialize(o2::framework::InitContext& ctx) override; + void startOfActivity(Activity& activity) override; + void startOfCycle() override; + void monitorData(o2::framework::ProcessingContext& ctx) override; + void endOfCycle() override; + void endOfActivity(Activity& activity) override; + void reset() override; + void buildHistograms(); + void drawLinesMCM(TH2F* histo); + + private: + std::array, 18> moHCMCM; + std::shared_ptr mTrackletSlope = nullptr; + std::shared_ptr mTrackletSlopeRaw = nullptr; + std::shared_ptr mTrackletHCID = nullptr; + std::shared_ptr mTrackletPosition = nullptr; + std::shared_ptr mTrackletPositionRaw = nullptr; + std::shared_ptr mTrackletsPerEvent = nullptr; +}; + +} // namespace o2::quality_control_modules::trd + +#endif // QC_MODULE_TRD_TRDTRACKLETSTASK_H diff --git a/Modules/TRD/src/DigitsTask.cxx b/Modules/TRD/src/DigitsTask.cxx index 538f049dd3..99a42479d2 100644 --- a/Modules/TRD/src/DigitsTask.cxx +++ b/Modules/TRD/src/DigitsTask.cxx @@ -1,15 +1,14 @@ -/// -/// \file TRDDigitQcTask.cxx -/// \author Tokozani Mtetwa -/// - #include #include #include +#include + #include #include "DataFormatsTRD/Constants.h" #include "DataFormatsTRD/Digit.h" +#include "DataFormatsTRD/Tracklet64.h" +#include "DataFormatsTRD/TriggerRecord.h" #include "QualityControl/ObjectsManager.h" #include "QualityControl/TaskInterface.h" #include "QualityControl/QcInfoLogger.h" @@ -20,8 +19,203 @@ namespace o2::quality_control_modules::trd { DigitsTask::~DigitsTask() { - if (mADC) { - delete mADC; + } + + void DigitsTask::drawLinesMCM(TH2F* histo) + { + + TLine* l; + Int_t nPos[o2::trd::constants::NSTACK - 1] = { 16, 32, 44, 60 }; + + for (Int_t iStack = 0; iStack < o2::trd::constants::NSTACK - 1; ++iStack) { + l = new TLine(nPos[iStack] - 0.5, -0.5, nPos[iStack] - 0.5, 47.5); + l->SetLineStyle(2); + histo->GetListOfFunctions()->Add(l); + } + + for (Int_t iLayer = 0; iLayer < o2::trd::constants::NLAYER; ++iLayer) { + l = new TLine(0.5, iLayer * 8 - 0.5, 75.5, iLayer * 8 - 0.5); + l->SetLineStyle(2); + histo->GetListOfFunctions()->Add(l); + } + } + + void DigitsTask::buildHistograms() + { + mDigitHCID.reset(new TH1F("digithcid", "Digit distribution over Halfchambers", 1080, 0, 1080)); + getObjectsManager()->startPublishing(mDigitHCID.get()); + mDigitsPerEvent.reset(new TH1F("digitsperevent", "Digits per Event", 10000, 0, 10000)); + getObjectsManager()->startPublishing(mDigitsPerEvent.get()); + + mClsAmpCh.reset(new TH1F("ClsAmpCh", "Reconstructed mean amplitude;Amplitude (ADC);# chambers", 100, 25, 125)); + mClsAmpCh->GetXaxis()->SetTitle("Amplitude (ADC)"); + mClsAmpCh->GetYaxis()->SetTitle("# chambers"); + getObjectsManager()->startPublishing(mClsAmpCh.get()); + + mClsNTb.reset(new TH1F("ClsNTb", "ClsNTb", 30, -0.5, 29.5)); + mClsNTb->SetTitle("Number of clusters;Timebin"); + mClsNTb->GetXaxis()->SetTitle("Number Of Clusters"); + mClsNTb->GetYaxis()->SetTitle("Timebin"); + getObjectsManager()->startPublishing(mClsNTb.get()); + + mClsChargeFirst.reset(new TH2F("ClsChargeFirst", "ClsChargeFirst", 100, 0, 1000, 101, -0.2, 0.2)); + mClsChargeFirst->GetXaxis()->SetTitle("Timebin of the maximum signal"); + mClsChargeFirst->GetYaxis()->SetTitle("Timebin"); + getObjectsManager()->startPublishing(mClsChargeFirst.get()); + getObjectsManager()->setDefaultDrawOptions(mClsChargeFirst->GetName(), "COLZ"); + + mClsChargeTb.reset(new TH1F("ClsChargeTb", "ClsChargeTb", 30, -0.5, 29.5)); + getObjectsManager()->startPublishing(mClsChargeTb.get()); + + mClsChargeTbCycle.reset(new TH1F("ClsChargeTbCycle", "ClsChargeTbCycle", 30, -0.5, 29.5)); + getObjectsManager()->startPublishing(mClsChargeTbCycle.get()); + + mClsAmp.reset(new TH1F("ClsAmp", "Amplitude of clusters", 200, -0.5, 1999.5)); + mClsAmp->GetXaxis()->SetTitle("Amplitude (ADC)"); + mClsAmp->GetYaxis()->SetTitle("Counts"); + getObjectsManager()->startPublishing(mClsAmp.get()); + + mClsAmpDrift.reset(new TH1F("ClsAmpDrift", "Amplitude of clusters in drift region", 500, -0.5, 999.5)); + mClsAmpDrift->GetXaxis()->SetTitle("Amplitude (ADC)"); + mClsAmpDrift->GetYaxis()->SetTitle("Counts"); + getObjectsManager()->startPublishing(mClsAmpDrift.get()); + + for (Int_t layer = 0; layer < o2::trd::constants::NLAYER; ++layer) { + std::string label = fmt::format("NClsLayer_{0}", layer); + std::string title = fmt::format("Illumination layer {0};Sectors;Padrows (z)", layer); + mNClsLayer[layer].reset(new TH2F(label.c_str(), title.c_str(), 18 * 8, -0.5, 17.5, 76, -0.5, 75.5)); + getObjectsManager()->startPublishing(mNClsLayer[layer].get()); + getObjectsManager()->setDefaultDrawOptions(label.c_str(), "COLZ"); + } + mADCvalue.reset(new TH1D("ADCvalue", "ADC value distribution;ADC value;Counts", 500, -0.5, 499.5)); + getObjectsManager()->startPublishing(mADCvalue.get()); + char buffer[256]; + for (Int_t iSM = 0; iSM < o2::trd::constants::NSECTOR; ++iSM) { + + std::string label = fmt::format("HCMCM_{0}", iSM); + std::string title = fmt::format("MCM in Digits data stream for sector {0}", iSM); + mHCMCM[iSM].reset(new TH2F(label.c_str(), title.c_str(), 76, -0.5, 75.5, 8 * 6, -0.5, 8 * 6 - 0.5)); + mHCMCM[iSM]->GetXaxis()->SetTitle("PadRow"); + mHCMCM[iSM]->GetYaxis()->SetTitle("PadCol"); + getObjectsManager()->startPublishing(mHCMCM[iSM].get()); + getObjectsManager()->setDefaultDrawOptions(mHCMCM[iSM]->GetName(), "COLZ"); + drawLinesMCM(mHCMCM[iSM].get()); + + label = fmt::format("ClsDetAmp_{0}", iSM); + title = fmt::format("Cluster amplitude chamberwise in SM {0}", iSM); + mClsDetAmp[iSM].reset(new TH2F(label.c_str(), title.c_str(), 30, -0.5, 29.5, 500, -0.5, 999.5)); + mClsDetAmp[iSM]->GetXaxis()->SetTitle("Chamber"); + mClsDetAmp[iSM]->GetYaxis()->SetTitle("Amplitude"); + getObjectsManager()->startPublishing(mClsDetAmp[iSM].get()); + getObjectsManager()->setDefaultDrawOptions(mClsDetAmp[iSM]->GetName(), "COLZ"); + + label = fmt::format("ADCTB_{0}", iSM); + title = fmt::format("Signal in Timebins for sector {0}", iSM); + mADCTB[iSM].reset(new TH2F(label.c_str(), title.c_str(), 30, -0.5, 29.5, 128, -0.5, 127.5)); + mADCTB[iSM]->GetXaxis()->SetTitle("Timebin"); + mADCTB[iSM]->GetYaxis()->SetTitle("ADC"); + getObjectsManager()->startPublishing(mADCTB[iSM].get()); + getObjectsManager()->setDefaultDrawOptions(mADCTB[iSM]->GetName(), "COLZ"); + + label = fmt::format("ADCTBfull_{0}", iSM); + title = fmt::format("Signal in Timebins full for sector {0}", iSM); + mADCTBfull[iSM].reset(new TH2F(label.c_str(), title.c_str(), 30, -0.5, 29.5, 128, -0.5, 127.5)); + mADCTBfull[iSM]->GetXaxis()->SetTitle("Timebin"); + mADCTBfull[iSM]->GetYaxis()->SetTitle("ADC"); + getObjectsManager()->startPublishing(mADCTBfull[iSM].get()); + getObjectsManager()->setDefaultDrawOptions(mADCTBfull[iSM]->GetName(), "COLZ"); + + label = fmt::format("ADC_{0}", iSM); + title = fmt::format("ADC value distribution for sector {0}", iSM); + mADC[iSM].reset(new TH1F(label.c_str(), title.c_str(), 500, -0.5, 499.5)); + mADC[iSM]->GetXaxis()->SetTitle("ADC value"); + getObjectsManager()->startPublishing(mADC[iSM].get()); + mADC[iSM]->GetYaxis()->SetTitle("Counts"); + + // clusters + label = fmt::format("ClsSM_{0}", iSM); + title = fmt::format("Cluster for Sector {0}", iSM); + mClsSM[iSM].reset(new TH1F(label.c_str(), title.c_str(), 100, 0, 200)); + mClsSM[iSM]->GetXaxis()->SetTitle("ADC value"); + mClsSM[iSM]->GetYaxis()->SetTitle("Counts"); + getObjectsManager()->startPublishing(mClsSM[iSM].get()); + + label = fmt::format("ClsTbSM_{0}", iSM); + title = fmt::format("Cluster Timbin for sector {0}", iSM); + mClsTbSM[iSM].reset(new TH2F(label.c_str(), title.c_str(), 30, -0.5, 29.5, 10, 0, 200)); + mClsTbSM[iSM]->GetXaxis()->SetTitle("Timebin"); + mClsTbSM[iSM]->GetYaxis()->SetTitle("ADC"); + getObjectsManager()->startPublishing(mClsTbSM[iSM].get()); + getObjectsManager()->setDefaultDrawOptions(mClsTbSM[iSM]->GetName(), "COLZ"); + + label = fmt::format("ClsDetTime_{0}", iSM); + title = fmt::format("Cluster amplitude chamberwise for sector {0}", iSM); + mClsDetTime[iSM].reset(new TH2F(label.c_str(), title.c_str(), 30, -0.5, 29.5, 30, -0.5, 29.5)); + mClsDetTime[iSM]->GetXaxis()->SetTitle("Chamber"); + mClsDetTime[iSM]->GetYaxis()->SetTitle("Timebin"); + getObjectsManager()->startPublishing(mClsDetTime[iSM].get()); + getObjectsManager()->setDefaultDrawOptions(mClsDetTime[iSM]->GetName(), "COLZ"); + } + + //TH1F* mNCls;//->Fill(sm); + mNCls.reset(new TH1F("NCls", "Total number of clusters per sector", 18, -0.5, 17.5)); + mNCls->SetTitle("Total number of clusters per sector;Sector;Counts"); + mNCls->GetXaxis()->SetTitle("Sector"); + mNCls->GetYaxis()->SetTitle("Counts"); + //getObjectsManager()->startPublishing(mNCls.get()); + + mClsAmp.reset(new TH1F("NClsAmp", "Total number of clusters per sector", 200, -0.5, 199.5)); + mClsAmp->GetXaxis()->SetTitle("Sector"); + mClsAmp->GetYaxis()->SetTitle("Counts"); + //getObjectsManager()->startPublishing(mClsAmp.get()); + + mClsTb.reset(new TH2F("ClsTb", "Cluster vs. timebin", 30, -0.5, 29.5, 200, 0, 2000)); + mClsTb->GetXaxis()->SetTitle("Timebin"); + mClsTb->GetYaxis()->SetTitle("Cluster"); + //getObjectsManager()->startPublishing(mClsTb.get()); + + mClsAmpTb.reset(new TH1F("ClsAmpTb", "Cluster amplitude vs. timebin", 30, -0.5, 29.5)); + mClsAmpTb->GetXaxis()->SetTitle("Timebin"); + mClsAmpTb->GetYaxis()->SetTitle("Amplitude"); + // getObjectsManager()->startPublishing(mClsAmpTb.get()); + + mClsSector.reset(new TH2F("ClsSector", "Cluster amplidue per sector", 18, -0.5, 17.5, 500, -0.5, 999.5)); + mClsSector->GetXaxis()->SetTitle("Sector"); + mClsSector->GetYaxis()->SetTitle("Amplitude"); + getObjectsManager()->startPublishing(mClsSector.get()); + + mClsStack.reset(new TH2F("ClsStack", "Cluster amplitude per stack", 5, -0.5, 4.5, 500, -0.5, 999.5)); + mClsStack->GetXaxis()->SetTitle("Stack"); + mClsStack->GetYaxis()->SetTitle("Amplitude"); + getObjectsManager()->startPublishing(mClsStack.get()); + mClsStack->SetTitle(";Stack;Amplitude"); + + // local histos + for (int det = 0; det < 540; ++det) { + std::string label = fmt::format("clsAmp_{0}", det); + std::string title = fmt::format("Cluster Amplitude for chamber {0}", det); + mClusterAmplitudeChamber[det].reset(new TH1F(label.c_str(), title.c_str(), 300, -0.5, 299.5)); + //TODO come back to figure out proper names mClusterAmplitudeChamber[det]->GetXaxis()->SetTitle("PadRow"); + //mClusterAmplitudeChamber[det]->GetYaxis()->SetTitle("Amplitude"); + getObjectsManager()->startPublishing(mClusterAmplitudeChamber[det].get()); + } + for (int trgg = 0; trgg < 10; ++trgg) { + if (trgg == 3) { + std::string label = fmt::format("ClsChargeTbTrgg_{0}", trgg); + std::string title = fmt::format("Total charge vs. time bin", trgg); + // mClsChargeTbTigg[trgg]->SetDescription("This plot shows the pulse height distribution. It should have a peak between the two indicated lines. If not, you will receive a warning"); + mClsChargeTbTigg[trgg].reset(new TH1F(label.c_str(), title.c_str(), 30, -0.5, 29.5)); + mClsChargeTbTigg[trgg]->GetXaxis()->SetTitle("time bin"); + mClsChargeTbTigg[trgg]->GetYaxis()->SetTitle("total charge"); + getObjectsManager()->startPublishing(mClsChargeTbTigg[trgg].get()); + } else { + std::string label = fmt::format("ClsChargeTbTrgg_{0}", trgg); + std::string title = fmt::format("Total charge vs. timebin, trigger class {0}", trgg); + mClsChargeTbTigg[trgg].reset(new TH1F(label.c_str(), title.c_str(), 30, -0.5, 29.5)); + mClsChargeTbTigg[trgg]->GetXaxis()->SetTitle("time bin"); + mClsChargeTbTigg[trgg]->GetYaxis()->SetTitle("total charge"); + getObjectsManager()->startPublishing(mClsChargeTbTigg[trgg].get()); + } } } @@ -30,23 +224,41 @@ namespace o2::quality_control_modules::trd ILOG(Info) << "initialize TRDDigitQcTask" << ENDM; // QcInfoLogger is used. FairMQ logs will go to there as well. // this is how to get access to custom parameters defined in the config file at qc.tasks..taskParameters - if (auto param = mCustomParameters.find("trdDigits"); param != mCustomParameters.end()) { - ILOG(Info) << "Custom parameter - trdDigits: " << param->second << ENDM; - } - - mADC = new TH1F("hADC", ";ADC value;Counts", 1024, 0, 1023); - getObjectsManager()->startPublishing(mADC); - //getObjectsManager()->setDisplayHint(mADC->GetName(), "LOGY"); - getObjectsManager()->addMetadata(mADC->GetName(), "custom", "34"); - + if (auto param = mCustomParameters.find("peakregionstart"); param != mCustomParameters.end()) { + mDriftRegion.first = stof(param->second); + ILOG(Info, Support) << "configure() : using peakregionstart = " << mDriftRegion.first << ENDM; + } else { + mDriftRegion.first = 7.0; + ILOG(Info, Support) << "configure() : using default dritfregionstart = " << mDriftRegion.first << ENDM; + } + if (auto param = mCustomParameters.find("peakregionend"); param != mCustomParameters.end()) { + mDriftRegion.second = stof(param->second); + ILOG(Info, Support) << "configure() : using peakregionstart = " << mDriftRegion.second << ENDM; + } else { + mDriftRegion.second = 20.0; + ILOG(Info, Support) << "configure() : using default dritfregionstart = " << mDriftRegion.second << ENDM; + } + if (auto param = mCustomParameters.find("pulsheightpeaklower"); param != mCustomParameters.end()) { + mPulseHeightPeakRegion.first = stof(param->second); + ILOG(Info, Support) << "configure() : using pulsehheightlower = " << mPulseHeightPeakRegion.first << ENDM; + } else { + mPulseHeightPeakRegion.first = 1.0; + ILOG(Info, Support) << "configure() : using default pulseheightlower = " << mPulseHeightPeakRegion.first << ENDM; + } + if (auto param = mCustomParameters.find("pulsheightpeakupper"); param != mCustomParameters.end()) { + mPulseHeightPeakRegion.second = stof(param->second); + ILOG(Info, Support) << "configure() : using pulsehheightupper = " << mPulseHeightPeakRegion.second << ENDM; + } else { + mPulseHeightPeakRegion.second = 5.0; + ILOG(Info, Support) << "configure() : using default pulseheightupper = " << mPulseHeightPeakRegion.second << ENDM; + } + buildHistograms(); } void DigitsTask::startOfActivity(Activity& /*activity*/) { ILOG(Info) << "startOfActivity" << ENDM; - mADC->Reset(); - } //set stats/stacs void DigitsTask::startOfCycle() @@ -54,56 +266,217 @@ namespace o2::quality_control_modules::trd ILOG(Info) << "startOfCycle" << ENDM; } + bool digitindexcompare(unsigned int A, unsigned int B, const std::vector& originalDigits) + { + // sort into ROC:padrow + const o2::trd::Digit *a, *b; + a = &originalDigits[A]; + b = &originalDigits[B]; + if (a->getDetector() < b->getDetector()) { + return 1; + } + if (a->getDetector() > b->getDetector()) { + return 0; + } + if (a->getPadRow() < b->getPadRow()) { + return 1; + } + if (a->getPadRow() > b->getPadRow()) { + return 0; + } + if (a->getPadCol() > b->getPadCol()) { + return 0; + } + return 0; + } void DigitsTask::monitorData(o2::framework::ProcessingContext& ctx) { for (auto&& input : ctx.inputs()) { - // // get message header - if (input.header != nullptr && input.payload != nullptr) { - // const auto* header = header::get(input.header); - // // // get payload of a specific input, which is a char array. - // ILOG(Info) << "payload size: " << (header->payloadSize) << ENDM; - //mHistogram->Fill(header->payloadSize); - - //reading the digit vector - auto inputDigits = ctx.inputs().get>("random"); - std::vector msgDigits(inputDigits.begin(), inputDigits.end()); - for(auto digit : msgDigits ) - { - int det = digit.getDetector(); - // int row = digit.getRow(); - // int pad = digit.getPad(); - auto adcs = digit.getADC(); - - ILOG(Info) << " Detectors" << det <Fill(adc); + if (input.header != nullptr && input.payload != nullptr) { + + auto digits = ctx.inputs().get>("digits"); + std::vector digitv(digits.begin(), digits.end()); + + if (digitv.size() == 0) + continue; + auto tracklets = ctx.inputs().get>("tracklets"); // still deciding if we will ever need the tracklets here. + auto triggerrecords = ctx.inputs().get>("triggers"); + // create a vector indexing into the digits in question + std::vector digitsIndex(digitv.size()); + std::iota(digitsIndex.begin(), digitsIndex.end(), 0); + // we now have sorted digits, can loop sequentially and be going over det/row/pad + for (auto& trigger : triggerrecords) { + if (trigger.getNumberOfDigits() == 0) + continue; //bail if we have no digits in this trigger + //now sort digits to det,row,pad + std::sort(std::begin(digitsIndex) + trigger.getFirstDigit(), std::begin(digitsIndex) + trigger.getFirstDigit() + trigger.getNumberOfDigits(), + [&digitv](unsigned int i, unsigned int j) { return digitindexcompare(i, j, digitv); }); + /////////////////////////////////////////////////// + // Go through all chambers (using digits) + const int adcThresh = 13; + const int clsCutoff = 1000; + const int startRow[5] = { 0, 16, 32, 44, 60 }; + if (trigger.getNumberOfDigits() > 10000) { + mDigitsPerEvent->Fill(9999); + } else { + mDigitsPerEvent->Fill(trigger.getNumberOfDigits()); } - mADC->Draw(); - - } - } - } - } - - void DigitsTask::endOfCycle() - { - ILOG(Info) << "endOfCycle" << ENDM; - } - - void DigitsTask::endOfActivity(Activity& /*activity*/) - { - ILOG(Info) << "endOfActivity" << ENDM; - } - - void DigitsTask::reset() - { - // clean all the monitor objects here - ILOG(Info) << "Resetting the histogram" << ENDM; - mADC->Reset(); - } - } // namespace o2::quality_control_modules::trd + + for (int currentdigit = trigger.getFirstDigit() + 1; currentdigit < trigger.getFirstDigit() + trigger.getNumberOfDigits() - 1; ++currentdigit) { // -1 and +1 as we are looking for consecutive digits pre and post the current one indexed. + + int detector = digits[digitsIndex[currentdigit]].getDetector(); + int sm = detector / 30; + int detLoc = detector % 30; + int layer = detector % 6; + int istack = detLoc / 6; + int iChamber = sm * 30 + istack * o2::trd::constants::NLAYER + layer; + int nADChigh = 0; + int stackoffset = istack * o2::trd::constants::NROWC1; + if (istack >= 2) + stackoffset -= 4; // account for stack 2 having 4 less. + //std::cout << "padrow:" << digits[digitsIndex[currentdigit]].getPadRow() << " stackoffset: " << stackoffset << " padcol:" <Fill(digits[digitsIndex[currentdigit]].getPadRow() + stackoffset, digits[digitsIndex[currentdigit]].getPadCol()); + mDigitHCID->Fill(digits[digitsIndex[currentdigit]].getHCId()); + // after updating the 2 above histograms the first and last digits are of no use, as we are looking for 3 neighbouring digits after this. + + int row = 0, col = 0; + //do we have 3 digits next to each other: + std::tuple a, b, c; + a = std::make_tuple(digits[digitsIndex[currentdigit - 1]].getDetector(), digits[digitsIndex[currentdigit - 1]].getPadRow(), digits[digitsIndex[currentdigit - 1]].getPadCol()); + b = std::make_tuple(digits[digitsIndex[currentdigit]].getDetector(), digits[digitsIndex[currentdigit]].getPadRow(), digits[digitsIndex[currentdigit]].getPadCol()); + c = std::make_tuple(digits[digitsIndex[currentdigit + 1]].getDetector(), digits[digitsIndex[currentdigit + 1]].getPadRow(), digits[digitsIndex[currentdigit + 1]].getPadCol()); + auto [det1, row1, col1] = a; + auto [det2, row2, col2] = b; + auto [det3, row3, col3] = c; + // check we 3 consecutive adc + bool consecutive = false; + if (det1 == det2 && det2 == det3 && row1 == row2 && row2 == row3 && col1 + 1 == col2 && col2 + 1 == col3) { + consecutive = true; + } + // illumination + mNClsLayer[layer]->Fill(sm - 0.5 + col / 144., startRow[istack] + row); + for (int time = 1; time < o2::trd::constants::TIMEBINS - 1; ++time) { + int digitindex = digitsIndex[currentdigit]; + int digitindexbelow = digitsIndex[currentdigit - 1]; + int digitindexabove = digitsIndex[currentdigit + 1]; + int value = digits[digitsIndex[currentdigit]].getADC()[time]; + if (value > adcThresh) + nADChigh++; + + mADCvalue->Fill(value); + mADC[sm]->Fill(value); + mADCTB[sm]->Fill(time, value); + mADCTBfull[sm]->Fill(time, value); + + if (consecutive) { + // clusterize + if (value > digits[digitindexbelow].getADC()[time] && + value > digits[digitindexabove].getADC()[time]) { + //How do we determine this? HV/LME/other? + //if(ChamberHasProblems(sm,istack,layer)) continue; + + value -= 10; + + int value = digits[digitindex].getADC()[time]; + int valueLU = (digits[digitindexbelow].getADC()[time - 1] < 10) ? 0 : digits[digitindexbelow].getADC()[time - 1] - 10; + int valueRU = (digits[digitindexabove].getADC()[time - 1] < 10) ? 0 : digits[digitindexabove].getADC()[time - 1] - 10; + int valueU = digits[digitindex].getADC()[time - 1] - 10; + + int valueLD = (digits[digitindexbelow].getADC()[time + 1] < 10) ? 0 : digits[digitindexbelow].getADC()[time + 1] - 10; + int valueRD = (digits[digitindexabove].getADC()[time + 1] < 10) ? 0 : digits[digitindexabove].getADC()[time + 1] - 10; + int valueD = digits[digitindex].getADC()[time + 1] - 10; + + int valueL = (digits[digitindexbelow].getADC()[time] < 10) ? 0 : digits[digitindexbelow].getADC()[time] - 10; + int valueR = (digits[digitindexabove].getADC()[time] < 10) ? 0 : digits[digitindexabove].getADC()[time] - 10; + + int sum = value + valueL + valueR; + int sumU = valueU + valueLU + valueRU; + int sumD = valueD + valueLD + valueRD; + + if (sumU < 10 || sumD < 10) + continue; + if (TMath::Abs(1. * sum / sumU - 1) < 0.01) + continue; + if (TMath::Abs(1. * sum / sumD - 1) < 0.01) + continue; + if (TMath::Abs(1. * sumU / sumD - 1) < 0.01) + continue; + + mNCls->Fill(sm); + mClsSM[sm]->Fill(sum); + mClsTbSM[sm]->Fill(time, sum); + mClsTb->Fill(time, sum); + mClsChargeFirst->Fill(sum, (1. * sum / sumU) - 1.); + mClsChargeFirst->Fill(sum, (1. * sum / sumD) - 1.); + + if (sum > 10 && sum < clsCutoff) { + mClsChargeTb->Fill(time, sum); + mClsChargeTbCycle->Fill(time, sum); + mClsNTb->Fill(time); + } + + mClsAmp->Fill(sum); + + if (time > mDriftRegion.first && time < mDriftRegion.second) { + mClsAmpDrift->Fill(sum); + mClsDetAmp[sm]->Fill(detLoc, sum); + mClusterAmplitudeChamber[iChamber]->Fill(sum); + } + + mClsSector->Fill(sm, sum); + mClsStack->Fill(istack, sum); + + // chamber by chamber drift time + if (sum > 10) // && sum < clsCutoff) + { + mClsDetTime[sm]->Fill(detLoc, time, sum); + //mClsDetTimeN[idSM]->Fill(iChamber, k); + } + + // Fill pulse height plot according to demanded trigger + /* TODO implement the different triggers, read the header in the cruhalfchamber this is lost on the epn, + * if(IsRun(PHYSICSRUN)){ + } + else if(IsRun(STANDALONE)) + else if(IsRun(CALIBRATION)) // the one with digits + */ + mClsChargeTbTigg[3]->Fill(time, sum); + + } // if consecutive adc ( 3 next to each other and we are on the middle on + } // loop over time-bins + } // loop over col-pads + // fNumberOfSignalADCsInCycle += nADChigh; + } // for loop over digits + } // loop over triggers + + } //if input is valid + } // for loop over inputs + } + + void DigitsTask::endOfCycle() + { + ILOG(Info) << "endOfCycle" << ENDM; + + for (Int_t det = 0; det < 540; det++) { + if (mClusterAmplitudeChamber[det]->GetEntries() > 0) { + mClsAmpCh->Fill(mClusterAmplitudeChamber[det]->GetMean()); + } + } + } + + void DigitsTask::endOfActivity(Activity& /*activity*/) + { + ILOG(Info) << "endOfActivity" << ENDM; + } + + void DigitsTask::reset() + { + // clean all the monitor objects here + ILOG(Info) << "Resetting the histogram" << ENDM; + mDigitsPerEvent->Reset(); + mDigitHCID->Reset(); + //TODO reset all the other spectra + } + } // namespace o2::quality_control_modules::trd diff --git a/Modules/TRD/src/Noise.cxx b/Modules/TRD/src/Noise.cxx new file mode 100644 index 0000000000..8766237363 --- /dev/null +++ b/Modules/TRD/src/Noise.cxx @@ -0,0 +1,137 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// 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 Noise.cxx +/// \author My Name +/// + +#include +#include + +#include "QualityControl/QcInfoLogger.h" +#include "TRD/Noise.h" +#include +#include + +namespace o2::quality_control_modules::trd +{ + +Noise::~Noise() +{ + delete mHistogram; +} + +void Noise::initialize(o2::framework::InitContext& /*ctx*/) +{ + ILOG(Info, Support) << "initialize Noise" << ENDM; // QcInfoLogger is used. FairMQ logs will go to there as well. + + // this is how to get access to custom parameters defined in the config file at qc.tasks..taskParameters + if (auto param = mCustomParameters.find("myOwnKey"); param != mCustomParameters.end()) { + ILOG(Info, Devel) << "Custom parameter - myOwnKey: " << param->second << ENDM; + } + + mHistogram = new TH1F("example", "example", 20, 0, 30000); + getObjectsManager()->startPublishing(mHistogram); + getObjectsManager()->startPublishing(new TH1F("example2", "example2", 20, 0, 30000)); + try { + getObjectsManager()->addMetadata(mHistogram->GetName(), "custom", "34"); + } catch (...) { + // some methods can throw exceptions, it is indicated in their doxygen. + // In case it is recoverable, it is recommended to catch them and do something meaningful. + // Here we don't care that the metadata was not added and just log the event. + ILOG(Warning, Support) << "Metadata could not be added to " << mHistogram->GetName() << ENDM; + } +} + +void Noise::startOfActivity(Activity& activity) +{ + ILOG(Info, Support) << "startOfActivity " << activity.mId << ENDM; + mHistogram->Reset(); +} + +void Noise::startOfCycle() +{ + ILOG(Info, Support) << "startOfCycle" << ENDM; +} + +void Noise::monitorData(o2::framework::ProcessingContext& ctx) +{ + // In this function you can access data inputs specified in the JSON config file, for example: + // "query": "random:ITS/RAWDATA/0" + // which is correspondingly ://(input.header); + // get payload of a specific input, which is a char array. + // const char* payload = input.payload; + + // for the sake of an example, let's fill the histogram with payload sizes + mHistogram->Fill(header->payloadSize); + } + + // 2. Using get("") + + // get the payload of a specific input, which is a char array. "random" is the binding specified in the config file. + // auto payload = ctx.inputs().get("random").payload; + + // get payload of a specific input, which is a structure array: + // const auto* header = header::get(ctx.inputs().get("random").header); + // struct s {int a; double b;}; + // auto array = ctx.inputs().get("random"); + // for (int j = 0; j < header->payloadSize / sizeof(s); ++j) { + // int i = array.get()[j].a; + // } + + // get payload of a specific input, which is a root object + // auto h = ctx.inputs().get("histos"); + // Double_t stats[4]; + // h->GetStats(stats); + // auto s = ctx.inputs().get("string"); + // LOG(info) << "String is " << s->GetString().Data(); + + // 3. Access CCDB. If it is enough to retrieve it once, do it in initialize(). + // Remember to delete the object when the pointer goes out of scope or it is no longer needed. + // TObject* condition = TaskInterface::retrieveCondition("QcTask/example"); // put a valid condition path here + // if (condition) { + // LOG(info) << "Retrieved " << condition->ClassName(); + // delete condition; + // } +} + +void Noise::endOfCycle() +{ + ILOG(Info, Support) << "endOfCycle" << ENDM; +} + +void Noise::endOfActivity(Activity& /*activity*/) +{ + ILOG(Info, Support) << "endOfActivity" << ENDM; +} + +void Noise::reset() +{ + // clean all the monitor objects here + + ILOG(Info, Support) << "Resetting the histogram" << ENDM; + mHistogram->Reset(); +} + +} // namespace o2::quality_control_modules::trd diff --git a/Modules/TRD/src/PulseHeight.cxx b/Modules/TRD/src/PulseHeight.cxx new file mode 100644 index 0000000000..d8625d3129 --- /dev/null +++ b/Modules/TRD/src/PulseHeight.cxx @@ -0,0 +1,421 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// 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 PulseHeight.cxx +/// \author My Name +/// + +#include +#include +#include +#include +#include + +#include "QualityControl/QcInfoLogger.h" +#include "TRD/PulseHeight.h" +#include +#include +#include +#include +#include +#include "DataFormatsTRD/Digit.h" +#include "DataFormatsTRD/Tracklet64.h" +#include "DataFormatsTRD/TriggerRecord.h" + +namespace o2::quality_control_modules::trd +{ + +PulseHeight::~PulseHeight() +{ +} + +void PulseHeight::buildHistograms() +{ + mPulseHeight.reset(new TH1F("mPulseHeight", "Pulse height plot", 30, -0.5, 29.5)); + getObjectsManager()->startPublishing(mPulseHeight.get()); + mPulseHeight2.reset(new TH1F("mPulseHeight2", "Pulse height plot v2", 30, -0.5, 29.5)); + getObjectsManager()->startPublishing(mPulseHeight2.get()); + + mPulseHeightScaled.reset(new TH1F("mPulseHeightScaled", "Scaled Pulse height plot", 30, -0.5, 29.5)); + getObjectsManager()->startPublishing(mPulseHeightScaled.get()); + mPulseHeightScaled2.reset(new TH1F("mPulseHeightScaled2", "Scaled Pulse height plot", 30, -0.5, 29.5)); + getObjectsManager()->startPublishing(mPulseHeightScaled2.get()); + + mTotalPulseHeight2D.reset(new TH2F("TotalPulseHeight", "Total Pulse Height", 30, 0., 30., 200, 0., 200.)); + getObjectsManager()->startPublishing(mTotalPulseHeight2D.get()); + getObjectsManager()->setDefaultDrawOptions(mTotalPulseHeight2D->GetName(), "COLZ"); + mTotalPulseHeight2D2.reset(new TH2F("TotalPulseHeight2", "Total Pulse Height", 30, 0., 30., 200, 0., 200.)); + getObjectsManager()->startPublishing(mTotalPulseHeight2D2.get()); + getObjectsManager()->setDefaultDrawOptions(mTotalPulseHeight2D2->GetName(), "COLZ"); + + for (int count = 0; count < 18; ++count) { + std::string label = fmt::format("pulseheight2d_sm_{0}", count); + std::string title = fmt::format("Pulse Height Spectrum for SM {0}", count); + TH1F* h = new TH1F(label.c_str(), title.c_str(), 30, -0.5, 29.5); + mPulseHeight2DperSM[count].reset(h); + getObjectsManager()->startPublishing(h); + label = fmt::format("pulseheight2d2_sm_{0}", count); + title = fmt::format("Pulse Height Spectrum v 2 for SM {0}", count); + TH1F* h2 = new TH1F(label.c_str(), title.c_str(), 30, -0.5, 29.5); + mPulseHeight2DperSM2[count].reset(h2); + getObjectsManager()->startPublishing(h2); + getObjectsManager()->setDefaultDrawOptions(h->GetName(), "COLZ"); + getObjectsManager()->setDefaultDrawOptions(h2->GetName(), "COLZ"); + } + + mPulseHeightDuration.reset(new TH1F("mPulseHeightDuration", "Pulse height duration", 10000, 0, 5.0)); + getObjectsManager()->startPublishing(mPulseHeightDuration.get()); + mPulseHeightDuration1.reset(new TH1F("mPulseHeightDuration1", "Pulse height duration v2", 10000, 0, 5.0)); + getObjectsManager()->startPublishing(mPulseHeightDuration1.get()); + mPulseHeightDurationDiff.reset(new TH1F("mPulseHeightDurationDiff", "Pulse height plot v2", 100000, -5.0, 5.0)); + getObjectsManager()->startPublishing(mPulseHeightDurationDiff.get()); +} +void PulseHeight::initialize(o2::framework::InitContext& /*ctx*/) +{ + ILOG(Info, Support) << "initialize PulseHeight" << ENDM; // QcInfoLogger is used. FairMQ logs will go to there as well. + if (auto param = mCustomParameters.find("peakregionstart"); param != mCustomParameters.end()) { + mDriftRegion.first = stof(param->second); + ILOG(Info, Support) << "configure() : using peakregionstart = " << mDriftRegion.first << ENDM; + } else { + mDriftRegion.first = 7.0; + ILOG(Info, Support) << "configure() : using default dritfregionstart = " << mDriftRegion.first << ENDM; + } + if (auto param = mCustomParameters.find("peakregionend"); param != mCustomParameters.end()) { + mDriftRegion.second = stof(param->second); + ILOG(Info, Support) << "configure() : using peakregionstart = " << mDriftRegion.second << ENDM; + } else { + mDriftRegion.second = 20.0; + ILOG(Info, Support) << "configure() : using default dritfregionstart = " << mDriftRegion.second << ENDM; + } + if (auto param = mCustomParameters.find("pulsheightpeaklower"); param != mCustomParameters.end()) { + mPulseHeightPeakRegion.first = stof(param->second); + ILOG(Info, Support) << "configure() : using pulsehheightlower = " << mPulseHeightPeakRegion.first << ENDM; + } else { + mPulseHeightPeakRegion.first = 1.0; + ILOG(Info, Support) << "configure() : using default pulseheightlower = " << mPulseHeightPeakRegion.first << ENDM; + } + if (auto param = mCustomParameters.find("pulsheightpeakupper"); param != mCustomParameters.end()) { + mPulseHeightPeakRegion.second = stof(param->second); + ILOG(Info, Support) << "configure() : using pulsehheightupper = " << mPulseHeightPeakRegion.second << ENDM; + } else { + mPulseHeightPeakRegion.second = 5.0; + ILOG(Info, Support) << "configure() : using default pulseheightupper = " << mPulseHeightPeakRegion.second << ENDM; + } + buildHistograms(); +} + +void PulseHeight::startOfActivity(Activity& activity) +{ + ILOG(Info, Support) << "startOfActivity " << activity.mId << ENDM; +} + +void PulseHeight::startOfCycle() +{ + ILOG(Info, Support) << "startOfCycle" << ENDM; +} + +bool pulseheightdigitindexcompare(unsigned int A, unsigned int B, const std::vector& originalDigits) +{ + // sort into ROC:padrow + const o2::trd::Digit *a, *b; + a = &originalDigits[A]; + b = &originalDigits[B]; + if (a->getDetector() < b->getDetector()) { + return 1; + } + if (a->getDetector() > b->getDetector()) { + return 0; + } + if (a->getPadRow() < b->getPadRow()) { + return 1; + } + if (a->getPadRow() > b->getPadRow()) { + return 0; + } + if (a->getPadCol() < b->getPadCol()) { + return 1; + } + return 0; +} + +void PulseHeight::monitorData(o2::framework::ProcessingContext& ctx) +{ + auto digits = ctx.inputs().get>("digits"); + auto tracklets = ctx.inputs().get>("tracklets"); + auto triggerrecords = ctx.inputs().get>("triggers"); + uint16_t tbsum[540][16][144]; + std::map, o2::trd::ArrayADC> dataMap; + uint64_t digitcount = 0; + std::vector digitIndex; + auto start = std::chrono::steady_clock::now(); + int triggercount = 0; + for (auto& trigger : triggerrecords) { + uint64_t numtracklets = trigger.getNumberOfTracklets(); + uint64_t numdigits = trigger.getNumberOfDigits(); + + int tbmax = 0; + int tbhi = 0; + int tblo = 0; + + int det = 0; + int row = 0; + int pad = 0; + int channel = 0; + + memset(tbsum, 0, sizeof(tbsum)); + dataMap.clear(); + for (int i = trigger.getFirstDigit(); i < trigger.getFirstDigit() + trigger.getNumberOfDigits(); ++i) { + + //digit comes in sorted by hcid. + //resort them by chamber,row, pad, this then removes the need for the map. + // + // loop over det, pad, row? + channel = digits[i].getChannel(); + if (channel == 0 || channel == 1 || channel == 20) { + continue; + } + + auto adcs = digits[i].getADC(); + det = digits[i].getDetector(); + row = digits[i].getPadRow(); + pad = digits[i].getPadCol(); + int supermod = det / 30; + + tbsum[det][row][pad] = digits[i].getADCsum(); + + if (tbsum[det][row][pad] >= 400) { + dataMap.insert(std::make_pair(std::make_tuple(det, row, pad), adcs)); + } else { + tbsum[det][row][pad] = 0; + } + } // end digitcont + + //std::cout << "start updating..... trigger:" << triggercount++ << " with " << trigger.getNumberOfDigits() << " digits " << std::endl; + for (int d = 0; d < 540; d++) { + int sector = d / 30; + for (int r = 0; r < 16; r++) { + for (int c = 2; c < 142; c++) { + if (d == 50 && tbsum[d][r][c] > 0) { + //std::cout << "updating on detector 50 " << d << " " << r << " " << c-1 << "("< tbsum[d][r][c - 1] && tbsum[d][r][c] > tbsum[d][r][c + 1]) { + if (tbsum[d][r][c - 1] > tbsum[d][r][c + 1]) { + tbmax = tbsum[d][r][c]; + tbhi = tbsum[d][r][c - 1]; + tblo = tbsum[d][r][c + 1]; + auto adcMax = dataMap.find(std::make_tuple(d, r, c)); + auto adcHi = dataMap.find(std::make_tuple(d, r, c - 1)); + auto adcLo = dataMap.find(std::make_tuple(d, r, c + 1)); + + if (dataMap.find(std::make_tuple(d, r, c - 2)) == dataMap.end()) { + if (tblo > 400) { + int phVal = 0; + //std::cout << "updatea " << d << " " << r << " " << c-1 << "("<second)[tb] + (adcHi->second)[tb] + (adcLo->second)[tb]); + //TODO do we have a corresponding tracklet? + mPulseHeight->Fill(tb, phVal); + mTotalPulseHeight2D->Fill(tb, phVal); + mPulseHeight2DperSM[sector]->Fill(tb, phVal); + } + } + } else { + auto adcHiNeighbour = dataMap.find(std::make_tuple(d, r, c - 2)); + if (tblo > 400) { + //std::cout << "updateb " << d << " " << r << " " << c-1 << "("<second)[tb] + (adcHi->second)[tb] + (adcLo->second)[tb]); + mPulseHeight->Fill(tb, phVal); + mTotalPulseHeight2D->Fill(tb, phVal); + mPulseHeight2DperSM[sector]->Fill(tb, phVal); + } + } + } + } else { + tbmax = tbsum[d][r][c]; + tbhi = tbsum[d][r][c + 1]; + tblo = tbsum[d][r][c - 1]; + auto adcMax = dataMap.find(std::make_tuple(d, r, c)); + auto adcHi = dataMap.find(std::make_tuple(d, r, c + 1)); + auto adcLo = dataMap.find(std::make_tuple(d, r, c - 1)); + if (dataMap.find(std::make_tuple(d, r, c + 2)) == dataMap.end()) { + + if (tblo > 400) { + //std::cout << "updatec " << d << " " << r << " " << c-1 << "("<second)[tb] + (adcHi->second)[tb] + (adcLo->second)[tb]); + mPulseHeight->Fill(tb, phVal); + mTotalPulseHeight2D->Fill(tb, phVal); + mPulseHeight2DperSM[sector]->Fill(tb, phVal); + } + } + } else { + auto adcHiNeighbour = dataMap.find(std::make_tuple(d, r, c + 2)); + if (tblo > 400) { + //std::cout << "updated " << d << " " << r << " " << c-1 << "("<second)[tb] + (adcHi->second)[tb] + (adcLo->second)[tb]); + mPulseHeight->Fill(tb, phVal); + mTotalPulseHeight2D->Fill(tb, phVal); + mPulseHeight2DperSM[sector]->Fill(tb, phVal); + } + } + } + } //end else + } // end if (tbsum[d][r][c]>tbsum[d][r][c-1] && tbsum[d][r][c]>tbsum[d][r][c+1]) + } // end for c + } //end for r + } // end for d + //std::cout << "finishedupdating....." << std::endl; + dataMap.clear(); + } //end trigger event + + auto end = std::chrono::steady_clock::now(); + std::chrono::duration pulseheightduration = end - start; + LOG(info) << "Digits into pulsheight spectrum: " << digitcount; + triggercount = 0; + //alternate formulation: + auto start1 = std::chrono::steady_clock::now(); + std::vector digitv(digits.begin(), digits.end()); + std::vector digitsIndex(digitv.size()); + std::iota(digitsIndex.begin(), digitsIndex.end(), 0); + + for (auto& trigger : triggerrecords) { + uint64_t numtracklets = trigger.getNumberOfTracklets(); + uint64_t numdigits = trigger.getNumberOfDigits(); + + int tbmax = 0; + int tbhi = 0; + int tblo = 0; + + int det = 0; + int row = 0; + int pad = 0; + int channel = 0; + + memset(tbsum, 0, sizeof(tbsum)); + + if (digitv.size() == 0) + continue; + + if (trigger.getNumberOfDigits() == 0) + continue; //bail if we have no digits in this trigger + //now sort digits to det,row,pad + std::sort(std::begin(digitsIndex) + trigger.getFirstDigit(), std::begin(digitsIndex) + trigger.getFirstDigit() + trigger.getNumberOfDigits(), + [&digitv](unsigned int i, unsigned int j) { return pulseheightdigitindexcompare(i, j, digitv); }); + //std::cout << " staring updating second ... for trigger:" << triggercount++ << " with " << trigger.getNumberOfDigits() << " digits" << std::endl; + for (int currentdigit = trigger.getFirstDigit() + 1; currentdigit < trigger.getFirstDigit() + trigger.getNumberOfDigits() - 1; ++currentdigit) { // -1 and +1 as we are looking for consecutive digits pre and post the current one indexed. + int detector = digits[digitsIndex[currentdigit]].getDetector(); + auto adcs = digits[digitsIndex[currentdigit]].getADC(); + det = digits[digitsIndex[currentdigit]].getDetector(); + row = digits[digitsIndex[currentdigit]].getPadRow(); + pad = digits[digitsIndex[currentdigit]].getPadCol(); + int supermod = detector / 30; + int sector = detector / 30; + int detLoc = detector % 30; + int layer = detector % 6; + int istack = detLoc / 6; + int iChamber = supermod * 30 + istack * o2::trd::constants::NLAYER + layer; + int nADChigh = 0; + //do we have 3 digits next to each other: + std::tuple aa, ba, ca; + aa = std::make_tuple(digits[digitsIndex[currentdigit - 1]].getDetector(), digits[digitsIndex[currentdigit - 1]].getPadRow(), digits[digitsIndex[currentdigit - 1]].getPadCol()); + ba = std::make_tuple(digits[digitsIndex[currentdigit]].getDetector(), digits[digitsIndex[currentdigit]].getPadRow(), digits[digitsIndex[currentdigit]].getPadCol()); + ca = std::make_tuple(digits[digitsIndex[currentdigit + 1]].getDetector(), digits[digitsIndex[currentdigit + 1]].getPadRow(), digits[digitsIndex[currentdigit + 1]].getPadCol()); + auto [det1, row1, col1] = aa; + auto [det2, row2, col2] = ba; + auto [det3, row3, col3] = ca; + // check we have 3 consecutive adc + // if (det1 == det2 && det2 == det3 && row1 == row2 && row2 == row3 && col1 + 1 == col2 && col2 + 1 == col3) { + + const o2::trd::Digit* b = &digits[digitsIndex[currentdigit]]; + const o2::trd::Digit* a = &digits[digitsIndex[currentdigit - 1]]; + const o2::trd::Digit* c = &digits[digitsIndex[currentdigit + 1]]; + uint32_t suma = a->getADCsum(); + uint32_t sumb = b->getADCsum(); + uint32_t sumc = c->getADCsum(); + if (det2 == 50) { + //std::cout << "on detector 50 " << det1 << " " << row1 << " " << col1 << "("< suma && sumb > sumc) { + if (suma > sumc) { + tbmax = sumb; + tbhi = suma; + tblo = sumc; + if (tblo > 400) { + int phVal = 0; + //std::cout << "updating2a " << det1 << " " << row1 << " " << col1 << " -- " << det2 << " " << row2 << " " << col2 << " -- " << det3 << " " << row3 << " " << col3 << std::endl; + for (int tb = 0; tb < 30; tb++) { + phVal = (b->getADC()[tb] + a->getADC()[tb] + c->getADC()[tb]); + //TODO do we have a corresponding tracklet? + mPulseHeight2->Fill(tb, phVal); + mTotalPulseHeight2D2->Fill(tb, phVal); + mPulseHeight2DperSM2[sector]->Fill(tb, phVal); + } + } + } else { + tbmax = sumb; + tblo = suma; + tbhi = sumc; + if (tblo > 400) { + int phVal = 0; + //std::cout << "updating2b " << det1 << " " << row1 << " " << col1 << " -- " << det2 << " " << row2 << " " << col2 << " -- " << det3 << " " << row3 << " " << col3 << std::endl; + for (int tb = 0; tb < 30; tb++) { + phVal = (b->getADC()[tb] + a->getADC()[tb] + c->getADC()[tb]); + mPulseHeight2->Fill(tb, phVal); + mTotalPulseHeight2D2->Fill(tb, phVal); + mPulseHeight2DperSM2[sector]->Fill(tb, phVal); + } + } + } //end else + } // end if (tbsum[d][r][c]>tbsum[d][r][c-1] && tbsum[d][r][c]>tbsum[d][r][c+1]) + } // end for c + } //end for r + //std::cout << " finished updating second ... " << std::endl; + } // end for d + auto end1 = std::chrono::steady_clock::now(); + std::chrono::duration pulseheightduration1 = end1 - start1; + + //plot the 2 pulseheight durations and the difference. + mPulseHeightDuration->Fill(pulseheightduration.count()); + mPulseHeightDuration1->Fill(pulseheightduration1.count()); + mPulseHeightDurationDiff->Fill(pulseheightduration.count() - pulseheightduration1.count()); +} + +void PulseHeight::endOfCycle() +{ + ILOG(Info, Support) << "endOfCycle" << ENDM; + double scale = mPulseHeight->GetEntries() / 30; + for (int i = 0; i < 30; ++i) + mPulseHeightScaled->SetBinContent(i, mPulseHeight->GetBinContent(i)); + mPulseHeightScaled->Scale(1 / scale); +} + +void PulseHeight::endOfActivity(Activity& /*activity*/) +{ + ILOG(Info, Support) << "endOfActivity" << ENDM; +} + +void PulseHeight::reset() +{ + // clean all the monitor objects here + mTotalPulseHeight2D->Reset(); + mPulseHeight->Reset(); + + ILOG(Info, Support) << "Resetting the histogram" << ENDM; +} + +} // namespace o2::quality_control_modules::trd diff --git a/Modules/TRD/src/PulseHeightCheck.cxx b/Modules/TRD/src/PulseHeightCheck.cxx new file mode 100644 index 0000000000..5cf7e18af6 --- /dev/null +++ b/Modules/TRD/src/PulseHeightCheck.cxx @@ -0,0 +1,107 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// 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 PulseHeightCheck.cxx +/// \author My Name +/// + +#include "QualityControl/MonitorObject.h" +#include "QualityControl/Quality.h" +#include "QualityControl/QcInfoLogger.h" +// ROOT +#include +#include +#include +#include +#include + +#include + +#include "TRD/PulseHeightCheck.h" + +using namespace std; +using namespace o2::quality_control; + +namespace o2::quality_control_modules::trd +{ + +void PulseHeightCheck::configure(std::string) +{ +} + +Quality PulseHeightCheck::check(std::map>* moMap) +{ + Quality result = Quality::Null; + + for (auto& [moName, mo] : *moMap) { + + (void)moName; + if (mo->getName() == "pulseheightscaled") { + auto* h = dynamic_cast(mo->getObject()); + + result = Quality::Good; + + for (int i = 2; i < 6; ++i) { + if (i > 0 && i < 8 && h->GetBinContent(i) < 50 && h->GetSum() > 100) { + result = Quality::Bad; + result.addReason(FlagReasonFactory::Unknown(), + "Peak missing " + std::to_string(i)); + break; + } else if (i > 0 && i < 8 && h->GetBinContent(i) < 100 && h->GetSum() > 100) { + result = Quality::Medium; + result.addReason(FlagReasonFactory::Unknown(), + "Peak rather low " + std::to_string(i) + " is not empty"); + result.addReason(FlagReasonFactory::ProcessingError(), + "This is to demonstrate that we can assign more than one Reason to a Quality"); + } + } + } + } + return result; +} + +std::string PulseHeightCheck::getAcceptedType() { return "TH1"; } + +void PulseHeightCheck::beautify(std::shared_ptr mo, Quality checkResult) +{ + if (mo->getName() == "pulseheightscaled") { + auto* h = dynamic_cast(mo->getObject()); + TPaveText* msg = new TPaveText(0.3, 0.9, 0.7, 0.95, "NDC"); + h->GetListOfFunctions()->Add(msg); + //std::string message = fmt::format("Pulseheight message"); + std::string message = "Pulseheight message"; + msg->SetName(message.c_str()); + TLine* lmin = new TLine(1, 0, 1, 1100); + TLine* lmax = new TLine(7, 0, 7, 1100); + + h->GetListOfFunctions()->Add(lmin); + h->GetListOfFunctions()->Add(lmax); + lmin->SetLineColor(kBlue); + lmin->Draw(); + lmax->SetLineColor(kBlue); + lmax->Draw(); + + if (checkResult == Quality::Good) { + h->SetFillColor(kGreen); + } else if (checkResult == Quality::Bad) { + ILOG(Info, Support) << "Quality::Bad, setting to red" << ENDM; + h->SetFillColor(kRed); + } else if (checkResult == Quality::Medium) { + ILOG(Info, Support) << "Quality::medium, setting to orange" << ENDM; + h->SetFillColor(kOrange); + } + h->SetLineColor(kBlack); + h->Draw(); + } +} + +} // namespace o2::quality_control_modules::trd diff --git a/Modules/TRD/src/RawData.cxx b/Modules/TRD/src/RawData.cxx new file mode 100644 index 0000000000..785f57f2bb --- /dev/null +++ b/Modules/TRD/src/RawData.cxx @@ -0,0 +1,377 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// 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 RawData.cxx +/// \author Sean Murray +/// + +#include "TCanvas.h" +#include "TH1F.h" +#include "TH2F.h" + +#include "QualityControl/QcInfoLogger.h" +#include "TRD/RawData.h" +#include "DataFormatsTRD/RawDataStats.h" +#include "DataFormatsTRD/Digit.h" +#include "DataFormatsTRD/Tracklet64.h" +#include "DataFormatsTRD/TriggerRecord.h" +#include +#include +#include + +using namespace o2::trd; + +namespace o2::quality_control_modules::trd +{ + +RawData::~RawData() +{ +} + +void RawData::buildHistograms() +{ + std::array linkerrortitles = { "Count of Link had no errors during tf", + "Count of # times Linkerrors 0x1 seen per tf", + "Count of # time Linkerrors 0x2 seen per tf", + "Count of any Linkerror seen during tf", + "Link was seen with no data (empty) in a tf", + "Link was seen with data during a tf", + "Links seen with corrupted data during tf", + "Links seen with out corrupted data during tf", + "Accepted Data volume on link", + "Rejected Data volume on link" }; + std::array parsingerrortitle = { + "TRDParsingNoError", + "TRDParsingUnrecognisedVersion", + "TRDParsingBadDigt", + "TRDParsingBadTracklet", + "TRDParsingDigitEndMarkerWrongState", + "TRDParsingDigitMCMHeaderSanityCheckFailure", + "TRDParsingDigitROBDecreasing", + "TRDParsingDigitMCMNotIncreasing", + "TRDParsingDigitADCMaskMismatch", + "TRDParsingDigitADCMaskAdvanceToEnd", + "TRDParsingDigitMCMHeaderBypassButStateMCMHeader", + "TRDParsingDigitEndMarkerStateButReadingMCMADCData", + "TRDParsingDigitADCChannel21", + "TRDParsingDigitADCChannelGT22", + "TRDParsingDigitGT10ADCs", + "TRDParsingDigitSanityCheck", + "TRDParsingDigitExcessTimeBins", + "TRDParsingDigitParsingExitInWrongState", + "TRDParsingDigitStackMismatch", + "TRDParsingDigitLayerMismatch", + "TRDParsingDigitSectorMismatch", + "TRDParsingTrackletCRUPaddingWhileParsingTracklets", + "TRDParsingTrackletBit11NotSetInTrackletHCHeader", + "TRDParsingTrackletHCHeaderSanityCheckFailure", + "TRDParsingTrackletMCMHeaderSanityCheckFailure", + "TRDParsingTrackletMCMHeaderButParsingMCMData", + "TRDParsingTrackletStateMCMHeaderButParsingMCMData", + "TRDParsingTrackletTrackletCountGTThatDeclaredInMCMHeader", + "TRDParsingTrackletInvalidTrackletCount", + "TRDParsingTrackletPadRowIncreaseError", + "TRDParsingTrackletColIncreaseError", + "TRDParsingTrackletNoTrackletEndMarker", + "TRDParsingTrackletExitingNoTrackletEndMarker", + "TRDParsingDigitHeaderCountGT3", + "TRDParsingDigitHeaderWrong1", + "TRDParsingDigitHeaderWrong2", + "TRDParsingDigitHeaderWrong3", + "TRDParsingDigitHeaderWrong4", + "TRDParsingDigitDataStillOnLink", + "TRDParsingTrackletIgnoringDataTillEndMarker", + "TRDLastParsingError" + }; + + mDataAcceptance = new TH1F("dataacceptance", "Data Accepted and Rejected", 2, 0, 2); + getObjectsManager()->startPublishing(mDataAcceptance); + mTimeFrameTime = new TH1F("timeframetime", "Time taken per time frame", 10000, 0, 10000); + getObjectsManager()->startPublishing(mTimeFrameTime); + mTrackletParsingTime = new TH1F("tracklettime", "Time taken per tracklet block", 1000, 0, 1000); + getObjectsManager()->startPublishing(mTrackletParsingTime); + mDigitParsingTime = new TH1F("digittime", "Time taken per digit block", 1000, 0, 1000); + getObjectsManager()->startPublishing(mDigitParsingTime); + mDataVersions = new TH1F("dataversions", "Data versions major.minor seen in data (half chamber header required)", 65000, 0, 65000); + getObjectsManager()->startPublishing(mDataVersions); + mDataVersionsMajor = new TH1F("dataversionsmajor", "Data versions major seen in the data (half chamber header required)", 256, 0, 256); + getObjectsManager()->startPublishing(mDataVersionsMajor); + mParsingErrors = new TH1F("parseerrors", "Parsing Errors seen in data", 256, 0, 256); + getObjectsManager()->startPublishing(mParsingErrors); + + mTotalChargevsTimeBin = new TH1F("totalchargevstimebin", "Total Charge vs Timebin", 30, 0, 30); + getObjectsManager()->startPublishing(mTotalChargevsTimeBin); + mDataVolumePerHalfSector = new TH2F("datavolumeperhalfsector", "Event size per half chamber, from parsing", 1080, 0, 1080, 1000, 0, 1000); + getObjectsManager()->startPublishing(mDataVolumePerHalfSector); + getObjectsManager()->setDefaultDrawOptions("datavolumeperhalfsector", "COLZ"); + mDataVolumePerHalfSectorCru = new TH2F("datavolumeperhalfsectorcru", "Event size per half chamber, from cru header", 1080, 0, 1080, 1000, 0, 1000); + getObjectsManager()->startPublishing(mDataVolumePerHalfSectorCru); + getObjectsManager()->setDefaultDrawOptions("datavolumeperhalfsectorcru", "COLZ"); + int count = 0; + for (int count = 0; count < o2::trd::TRDLastParsingError; ++count) { + std::string label = fmt::format("parsingerrors_{0}", count); + std::string title = parsingerrortitle[count]; + TH2F* h = new TH2F(label.c_str(), title.c_str(), 36, 0, 36, 30, 0, 30); + mParsingErrors2d[count] = h; + getObjectsManager()->startPublishing(h); + getObjectsManager()->setDefaultDrawOptions(h->GetName(), "COLZ"); + } + count = 0; + for (int count = 0; count < 10; ++count) { + std::string label = fmt::format("linkstatus_{0}", count); + std::string title = linkerrortitles[count]; + TH2F* h = new TH2F(label.c_str(), title.c_str(), 36, 0, 36, 30, 0, 30); + mLinkErrors[count] = h; + getObjectsManager()->startPublishing(h); + getObjectsManager()->setDefaultDrawOptions(h->GetName(), "COLZ"); + } + mDataAcceptance->GetXaxis()->SetTitle("Type"); + mDataAcceptance->GetYaxis()->SetTitle("MBytes"); + mTimeFrameTime->GetXaxis()->SetTitle("Time taken in ms"); + mTimeFrameTime->GetYaxis()->SetTitle("Counts"); + + mTrackletParsingTime->GetXaxis()->SetTitle("Time taken in #mus"); + mTrackletParsingTime->GetYaxis()->SetTitle("Counts"); + mDigitParsingTime->GetXaxis()->SetTitle("Time taken in #mus"); + mDigitParsingTime->GetYaxis()->SetTitle("Counts"); + mDigitParsingTime->GetYaxis()->SetTitle("Counts"); + mDataVersions->GetXaxis()->SetTitle("Version"); + mDataVersions->GetYaxis()->SetTitle("Counts"); + mDataVersionsMajor->GetYaxis()->SetTitle("Counts"); + mDataVersionsMajor->GetXaxis()->SetTitle("Version major"); + mParsingErrors->GetYaxis()->SetTitle("Counts"); + mParsingErrors->GetXaxis()->SetTitle("Error Types"); + mDataVolumePerHalfSector->GetXaxis()->SetTitle("half chamber"); + mDataVolumePerHalfSectorCru->GetXaxis()->SetTitle("half chamber"); + mDataVolumePerHalfSector->GetYaxis()->SetTitle("Data Volume [kB/event]"); + mDataVolumePerHalfSectorCru->GetYaxis()->SetTitle("Data Volume [kB/event]"); + for (int i = 0; i < o2::trd::TRDLastParsingError; ++i) { + std::string label = fmt::format("{0}_{1}", parsingerrortitle[i], i); + mParsingErrors->GetXaxis()->SetBinLabel(i + 1, label.c_str()); + } + for (int count = 0; count < o2::trd::TRDLastParsingError; ++count) { + TH2F* h = mParsingErrors2d[count]; + h->GetXaxis()->SetTitle("Sector*2 + side"); + h->GetXaxis()->CenterTitle(kTRUE); + h->GetYaxis()->SetTitle("Stack_Layer"); + h->GetYaxis()->CenterTitle(kTRUE); + for (int s = 0; s < o2::trd::constants::NSTACK; ++s) { + for (int l = 0; l < o2::trd::constants::NLAYER; ++l) { + std::string label = fmt::format("{0}_{1}", s, l); + int pos = s * o2::trd::constants::NLAYER + l + 1; + h->GetYaxis()->SetBinLabel(pos, label.c_str()); + } + } + for (int sm = 0; sm < o2::trd::constants::NSECTOR; ++sm) { + for (int side = 0; side < 2; ++side) { + std::string label = fmt::format("{0}.{1}", sm, side); + int pos = sm * 2 + side + 1; + h->GetXaxis()->SetBinLabel(pos, label.c_str()); + } + } + } + for (int count = 0; count < 10; ++count) { + TH2F* h = mLinkErrors[count]; + h->GetXaxis()->SetTitle("Sector*2 + side"); + h->GetXaxis()->CenterTitle(kTRUE); + h->GetYaxis()->SetTitle("Stack_Layer"); + h->GetYaxis()->CenterTitle(kTRUE); + for (int s = 0; s < o2::trd::constants::NSTACK; ++s) { + for (int l = 0; l < o2::trd::constants::NLAYER; ++l) { + std::string label = fmt::format("{0}.{1}", s, l); + int pos = s * o2::trd::constants::NLAYER + l + 1; + h->GetYaxis()->SetBinLabel(pos, label.c_str()); + } + } + for (int sm = 0; sm < o2::trd::constants::NSECTOR; ++sm) { + for (int side = 0; side < 2; ++side) { + std::string label = fmt::format("{0}_{1}", sm, side); + int pos = sm * 2 + side + 1; + h->GetXaxis()->SetBinLabel(pos, label.c_str()); + } + } + } + + for (Int_t det = 0; det < 540; ++det) { + fClusterChamberAmplitude[det] = new TH1F(Form("clustramplitude_%d", det), "", 300, -0.5, 299.5); + } +} + +void RawData::initialize(o2::framework::InitContext& /*ctx*/) +{ + + ILOG(Info, Support) << "initialize TRD RawData QC " << ENDM; + + // this is how to get access to custom parameters defined in the config file at qc.tasks..taskParameters + //if (auto param = mCustomParameters.find("myOwnKey"); param != mCustomParameters.end()) { + // ILOG(Info, Devel) << "Custom parameter - myOwnKey: " << param->second << ENDM; + // } commented out for now I will come back to this. + + buildHistograms(); + ILOG(Info, Support) << "TRD RawData QC histograms built" << ENDM; + + // ILOG(Warning, Support) << "Metadata could not be added to " << mHistogram->GetName() << ENDM; +} + +void RawData::startOfActivity(Activity& activity) +{ + ILOG(Info, Support) << "startOfActivity " << activity.mId << ENDM; + resetHistograms(); +} + +void RawData::startOfCycle() +{ + ILOG(Info, Support) << "startOfCycle" << ENDM; +} + +void RawData::monitorData(o2::framework::ProcessingContext& ctx) +{ + + auto rawdatastats = ctx.inputs().get("rawstats"); + auto digits = ctx.inputs().get>("digits"); + auto tracklets = ctx.inputs().get>("tracklets"); + auto triggerrecords = ctx.inputs().get>("triggers"); + + //RAWSTATS is the RawDataStats class which is essentially histograms. + //loop through all the bits and pieces and fill the histograms + + std::array eventsize{}; + // triggerrecords is a span of our triggers in the respective time frame + int lastmcm = 0; + uint64_t digitcount = 0; + uint64_t trackletcount = 0; + for (auto& trigger : triggerrecords) { + uint64_t numtracklets = trigger.getNumberOfTracklets(); + uint64_t numdigits = trigger.getNumberOfDigits(); + uint64_t digitend = trigger.getFirstDigit() + trigger.getNumberOfDigits(); + uint64_t trackletend = trigger.getFirstTracklet() + trigger.getNumberOfTracklets(); + for (int tracklet = trigger.getFirstTracklet(); tracklet < trackletend; ++tracklet) { + eventsize[tracklets[tracklet].getHCID()]++; + if (lastmcm != tracklets[tracklet].getMCM()) { + eventsize[tracklets[tracklet].getHCID()]++; // include the mcmheader in the data volume calculation + lastmcm = tracklets[tracklet].getMCM(); + } + } + for (int digit = trigger.getFirstDigit(); digit < digitend; ++digit) { + eventsize[digits[digit].getHCId()] += 12; + } + } + //data per event per link. + for (int hcid = 0; hcid < 1080; ++hcid) { + if (eventsize[hcid] > 0) { + mDataVolumePerHalfSector->Fill(hcid, eventsize[hcid] / 256); // conver 32bit words to bytes then to kb *4/1024=/256 + } + } + for (int error; error < o2::trd::TRDLastParsingError; ++error) { + mParsingErrors->AddBinContent(error, rawdatastats.mParsingErrors[error]); + //std::cout << "Increment parsing errors 1d at : " << rawdatastats.mParsingErrors[error] << " for error:" << error<< std::endl; + for (int sm = 0; sm < o2::trd::constants::NSECTOR * 2 - 1; ++sm) { + for (int stacklayer = 0; stacklayer < 30; ++stacklayer) { + int specoffset = sm * 30 * o2::trd::TRDLastParsingError + stacklayer * o2::trd::TRDLastParsingError + error; + if (rawdatastats.mParsingErrorsByLink[specoffset] > 0) { + mParsingErrors2d[error]->SetBinContent(sm, stacklayer, mParsingErrors2d[error]->GetBinContent(sm, stacklayer) + rawdatastats.mParsingErrorsByLink[specoffset]); + } + } + } + } + //linkstatus: + for (int sm = 0; sm < o2::trd::constants::NSECTOR * 2 - 1; ++sm) { + for (int stacklayer = 0; stacklayer < 30; ++stacklayer) { + int specoffset = sm * 30 + stacklayer; + if (rawdatastats.mLinkErrorFlag[specoffset] == 0) { //"Count of Link had no errors during tf", + mLinkErrors[0]->Fill(sm, stacklayer); + } + if (rawdatastats.mLinkErrorFlag[specoffset] & 0x1) { //"Count of # times Linkerrors 0x1 seen per tf", + mLinkErrors[1]->Fill(sm, stacklayer, rawdatastats.mLinkErrorFlag[specoffset]); + } + if (rawdatastats.mLinkErrorFlag[specoffset] & 0x2) { //"Count of # time Linkerrors 0x2 seen per tf", + mLinkErrors[2]->Fill(sm, stacklayer, rawdatastats.mLinkErrorFlag[specoffset]); + } + if (rawdatastats.mLinkErrorFlag[specoffset] != 0) { //"Count of any Linkerror seen during tf", + mLinkErrors[3]->Fill(sm, stacklayer, rawdatastats.mLinkErrorFlag[specoffset]); + } + if (rawdatastats.mLinkWordsRejected[specoffset] + rawdatastats.mLinkWordsRead[specoffset] == 0) { //LinkWords[specoffset]==0){//"Link was seen with no data (empty) in a tf", + mLinkErrors[4]->Fill(sm, stacklayer); //,rawdatastats.mLinkNoData[specoffset]); + } else { + if (rawdatastats.mLinkWords[specoffset] > 0) { + mDataVolumePerHalfSectorCru->Fill(sm, stacklayer, rawdatastats.mLinkWords[specoffset] / 32); // 32 because convert 256bit into bytes and then to kb, *32/1024 == /32 + } + } + if (rawdatastats.mLinkWordsRead[specoffset] > 0) { //"Link was seen with data during a tf", + mLinkErrors[5]->Fill(sm, stacklayer, rawdatastats.mLinkWordsRead[specoffset]); + } + if (rawdatastats.mLinkWordsRejected[specoffset] > 0) { //"Links seen with corrupted data during tf" + mLinkErrors[6]->Fill(sm, stacklayer); + } + if (rawdatastats.mLinkWordsRejected[specoffset] < 20) { //"Links seen with out corrupted data during tf", "", + mLinkErrors[7]->Fill(sm, stacklayer); + } + if (rawdatastats.mLinkWordsRead[specoffset] != 0) { //"Accepted Data volume on link", + mLinkErrors[8]->Fill(sm, stacklayer, rawdatastats.mLinkWordsRead[specoffset]); + mDataAcceptance->AddAt(((float)rawdatastats.mLinkWordsRead[specoffset]) / 1024.0 / 256.0, 0); + } + if (rawdatastats.mLinkWordsRejected[specoffset] != 0) { // "Rejected Data volume on link"}; + mLinkErrors[9]->Fill(sm, stacklayer, rawdatastats.mLinkWordsRejected[specoffset]); + mDataAcceptance->AddAt(((float)rawdatastats.mLinkWordsRead[specoffset]) / 1024.0 / 256.0, 1); + } + } + } + //time graphs + mTimeFrameTime->Fill(rawdatastats.mTimeTaken); + mDigitParsingTime->Fill(rawdatastats.mTimeTakenForDigits); + mTrackletParsingTime->Fill(rawdatastats.mTimeTakenForTracklets); + + for (int i = 0; i < rawdatastats.mDataFormatRead.size(); ++i) { + mDataVersionsMajor->Fill(i, rawdatastats.mDataFormatRead[i]); + } + LOG(info) << "for TF : D:" << digitcount << " T:" << trackletcount; +} + +void RawData::endOfCycle() +{ + ILOG(Info, Support) << "endOfCycle" << ENDM; +} + +void RawData::endOfActivity(Activity& /*activity*/) +{ + ILOG(Info, Support) << "endOfActivity" << ENDM; +} + +void RawData::reset() +{ + // clean all the monitor objects here + + ILOG(Info, Support) << "Resetting the histogram" << ENDM; + resetHistograms(); +} +void RawData::resetHistograms() +{ + ILOG(Info, Support) << "Resetting the histogram " << ENDM; + for (auto hist : mLinkErrors) { + hist->Reset(); + } + for (auto hist : mParsingErrors2d) { + hist->Reset(); + } + mTimeFrameTime->Reset(); + mTrackletParsingTime->Reset(); + mDigitParsingTime->Reset(); + mDataVersions->Reset(); + mDataVersionsMajor->Reset(); + mParsingErrors->Reset(); + mDataVolumePerHalfSector->Reset(); + mDataVolumePerHalfSectorCru->Reset(); + mTotalChargevsTimeBin->Reset(); + //TODO come back and change the drawing of these and labels +} + +} // namespace o2::quality_control_modules::trd diff --git a/Modules/TRD/src/TrackletsCheck.cxx b/Modules/TRD/src/TrackletsCheck.cxx new file mode 100644 index 0000000000..a66f7ad7ef --- /dev/null +++ b/Modules/TRD/src/TrackletsCheck.cxx @@ -0,0 +1,85 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// 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 TrackletsCheck.cxx +/// \author My Name +/// + +#include "TRD/TrackletsCheck.h" +#include "QualityControl/MonitorObject.h" +#include "QualityControl/Quality.h" +#include "QualityControl/QcInfoLogger.h" +// ROOT +#include + +#include + +using namespace std; +using namespace o2::quality_control; + +namespace o2::quality_control_modules::trd +{ + +void TrackletsCheck::configure(std::string) {} + +Quality TrackletsCheck::check(std::map>* moMap) +{ + Quality result = Quality::Null; + + for (auto& [moName, mo] : *moMap) { + + (void)moName; + if (mo->getName() == "example") { + auto* h = dynamic_cast(mo->getObject()); + + result = Quality::Good; + + for (int i = 0; i < h->GetNbinsX(); i++) { + if (i > 0 && i < 8 && h->GetBinContent(i) == 0) { + result = Quality::Bad; + result.addReason(FlagReasonFactory::Unknown(), + "It is bad because there is nothing in bin " + std::to_string(i)); + break; + } else if ((i == 0 || i > 7) && h->GetBinContent(i) > 0) { + result = Quality::Medium; + result.addReason(FlagReasonFactory::Unknown(), + "It is medium because bin " + std::to_string(i) + " is not empty"); + result.addReason(FlagReasonFactory::ProcessingError(), + "This is to demonstrate that we can assign more than one Reason to a Quality"); + } + } + } + } + return result; +} + +std::string TrackletsCheck::getAcceptedType() { return "TH1"; } + +void TrackletsCheck::beautify(std::shared_ptr mo, Quality checkResult) +{ + if (mo->getName() == "example") { + auto* h = dynamic_cast(mo->getObject()); + + if (checkResult == Quality::Good) { + h->SetFillColor(kGreen); + } else if (checkResult == Quality::Bad) { + ILOG(Info, Support) << "Quality::Bad, setting to red" << ENDM; + h->SetFillColor(kRed); + } else if (checkResult == Quality::Medium) { + ILOG(Info, Support) << "Quality::medium, setting to orange" << ENDM; + h->SetFillColor(kOrange); + } + h->SetLineColor(kBlack); + } +} + +} // namespace o2::quality_control_modules::trd diff --git a/Modules/TRD/src/TrackletsTask.cxx b/Modules/TRD/src/TrackletsTask.cxx new file mode 100644 index 0000000000..1d17227294 --- /dev/null +++ b/Modules/TRD/src/TrackletsTask.cxx @@ -0,0 +1,181 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// 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 TrackletsTask.cxx +/// \author My Name +/// + +#include +#include +#include +#include + +#include "QualityControl/QcInfoLogger.h" +#include "TRD/TrackletsTask.h" +#include +#include +#include "DataFormatsTRD/Tracklet64.h" +#include "DataFormatsTRD/Digit.h" +#include "DataFormatsTRD/TriggerRecord.h" + +namespace o2::quality_control_modules::trd +{ + +TrackletsTask::~TrackletsTask() +{ +} + +void TrackletsTask::drawLinesMCM(TH2F* histo) +{ + + TLine* l; + Int_t nPos[o2::trd::constants::NSTACK - 1] = { 16, 32, 44, 60 }; + + for (Int_t iStack = 0; iStack < o2::trd::constants::NSTACK - 1; ++iStack) { + l = new TLine(nPos[iStack] - 0.5, -0.5, nPos[iStack] - 0.5, 47.5); + l->SetLineStyle(2); + //std::cout << " adding vertical line to histo" << std::endl; + //std::cout << " l = new TLine(" << nPos[iStack] - 0.5 << "," << -0.5 << "," << nPos[iStack] - 0.5 << "," << 47.5 << ");" << std::endl; + histo->GetListOfFunctions()->Add(l); + } + + for (Int_t iLayer = 0; iLayer < o2::trd::constants::NLAYER; ++iLayer) { + l = new TLine(-0.5, iLayer * 8 - 0.5, 75.5, iLayer * 8 - 0.5); + l = new TLine(0.5, iLayer * 8 - 0.5, 75.5, iLayer * 8 - 0.5); + l->SetLineStyle(2); + //std::cout << " adding horizontal line to histo" << std::endl; + //std::cout << " l = new TLine(" << -0.5 <<","<< iLayer * 8 - 0.5 << "," << 75.5 << "," << iLayer * 8 - 0.5 << ");" << std::endl; + histo->GetListOfFunctions()->Add(l); + } +} + +void TrackletsTask::buildHistograms() +{ + for (Int_t sm = 0; sm < o2::trd::constants::NSECTOR; ++sm) { + std::string label = fmt::format("TrackletHCMCM_{0}", sm); + std::string title = fmt::format("MCM in Tracklets data stream for sector {0}", sm); + moHCMCM[sm].reset(new TH2F(label.c_str(), title.c_str(), 76, -0.5, 75.5, 8 * 5, -0.5, 8 * 5 - 0.5)); + moHCMCM[sm]->GetYaxis()->SetTitle("ROB in stack"); + moHCMCM[sm]->GetXaxis()->SetTitle("mcm in rob in layer"); + getObjectsManager()->startPublishing(moHCMCM[sm].get()); + getObjectsManager()->setDefaultDrawOptions(moHCMCM[sm]->GetName(), "COLZ"); + drawLinesMCM(moHCMCM[sm].get()); + } + mTrackletSlope.reset(new TH1F("trackletslope", "uncalibrated Slope of tracklets", 1024, -6.0, 6.0)); // slope is 8 bits in the tracklet + getObjectsManager()->startPublishing(mTrackletSlope.get()); + mTrackletSlopeRaw.reset(new TH1F("trackletsloperaw", "Raw Slope of tracklets", 256, 0, 256)); // slope is 8 bits in the tracklet + getObjectsManager()->startPublishing(mTrackletSlopeRaw.get()); + mTrackletHCID.reset(new TH1F("tracklethcid", "Tracklet distribution over Halfchambers", 1080, 0, 1080)); + getObjectsManager()->startPublishing(mTrackletHCID.get()); + mTrackletPosition.reset(new TH1F("trackletpos", "Uncalibrated Position of Tracklets", 1400, -70, 70)); + getObjectsManager()->startPublishing(mTrackletPosition.get()); + mTrackletPositionRaw.reset(new TH1F("trackletposraw", "Raw Position of Tracklets", 2048, 0, 2048)); + getObjectsManager()->startPublishing(mTrackletPositionRaw.get()); + mTrackletsPerEvent.reset(new TH1F("trackletsperevent", "Number of Tracklets per event", 2500, 0, 250000)); + getObjectsManager()->startPublishing(mTrackletsPerEvent.get()); +} + +void TrackletsTask::initialize(o2::framework::InitContext& /*ctx*/) +{ + ILOG(Info, Support) << "initialize TrackletsTask" << ENDM; // QcInfoLogger is used. FairMQ logs will go to there as well. + + buildHistograms(); +} + +void TrackletsTask::startOfActivity(Activity& activity) +{ + ILOG(Info, Support) << "startOfActivity " << activity.mId << ENDM; + for (Int_t sm = 0; sm < o2::trd::constants::NSECTOR; ++sm) { + moHCMCM[sm]->Reset(); + } +} + +void TrackletsTask::startOfCycle() +{ + ILOG(Info, Support) << "startOfCycle" << ENDM; +} + +void TrackletsTask::monitorData(o2::framework::ProcessingContext& ctx) +{ + for (auto&& input : ctx.inputs()) { + if (input.header != nullptr && input.payload != nullptr) { + + auto digits = ctx.inputs().get>("digits"); + auto tracklets = ctx.inputs().get>("tracklets"); + auto triggerrecords = ctx.inputs().get>("triggers"); + for (auto& trigger : triggerrecords) { + if (trigger.getNumberOfTracklets() == 0) + continue; //bail if we have no digits in this trigger + //now sort digits to det,row,pad + mTrackletsPerEvent->Fill(trigger.getNumberOfTracklets()); + for (int currenttracklet = trigger.getFirstTracklet(); currenttracklet < trigger.getFirstTracklet() + trigger.getNumberOfTracklets() - 1; ++currenttracklet) { + int detector = tracklets[currenttracklet].getDetector(); + int sm = detector / 30; + int detLoc = detector % 30; + int layer = detector % 6; + int istack = detLoc / 6; + int iChamber = sm * 30 + istack * o2::trd::constants::NLAYER + layer; + int stackoffset = istack * o2::trd::constants::NSTACK * o2::trd::constants::NROBC1; + if (istack >= 2) { + stackoffset -= 2; // only 12in stack 2 + } + //8 rob x 16 mcm each per chamber + // 5 stack(y), 6 layers(x) + // y=stack_rob, x=layer_mcm + int x = o2::trd::constants::NMCMROB * layer + tracklets[currenttracklet].getMCM(); + int y = o2::trd::constants::NROBC1 * istack + tracklets[currenttracklet].getROB(); + moHCMCM[sm]->Fill(x, y); + mTrackletSlope->Fill(tracklets[currenttracklet].getUncalibratedDy()); + mTrackletSlopeRaw->Fill(tracklets[currenttracklet].getSlope()); + mTrackletPosition->Fill(tracklets[currenttracklet].getUncalibratedY()); + mTrackletPositionRaw->Fill(tracklets[currenttracklet].getPosition()); + mTrackletHCID->Fill(tracklets[currenttracklet].getHCID()); + } + } + } + } +} + +void TrackletsTask::endOfCycle() +{ + ILOG(Info, Support) << "endOfCycle" << ENDM; + //scale 2d mHCMCM plots so they all have the same max height. + int max = 0; + for (auto& hist : moHCMCM) { + if (hist->GetMaximum() > max) { + max = hist->GetMaximum(); + } + } + for (auto& hist : moHCMCM) { + hist->SetMaximum(max); + } +} + +void TrackletsTask::endOfActivity(Activity& /*activity*/) +{ + ILOG(Info, Support) << "endOfActivity" << ENDM; +} + +void TrackletsTask::reset() +{ + // clean all the monitor objects here + + ILOG(Info, Support) << "Resetting the histogram" << ENDM; + mTrackletPosition.get()->Reset(); + mTrackletPositionRaw.get()->Reset(); + mTrackletSlope.get()->Reset(); + mTrackletSlopeRaw.get()->Reset(); + mTrackletHCID.get()->Reset(); + mTrackletsPerEvent.get()->Reset(); +} + +} // namespace o2::quality_control_modules::trd