From a8601a15398943a1f934fdc6ac3347c8784389ea Mon Sep 17 00:00:00 2001 From: "se.gorbunov" <se.gorbunov@gsi.de> Date: Thu, 25 Nov 2021 12:34:12 +0000 Subject: [PATCH] tracker input QA for TRD --- core/data/CbmLink.h | 2 + core/data/CbmMCEventList.cxx | 11 + core/data/CbmMCEventList.h | 18 + core/qa/CMakeLists.txt | 6 + core/qa/CbmQaBaseLinkDef.h | 12 +- core/qa/CbmQaHist.cxx | 5 +- core/qa/CbmQaHist.h | 94 ++-- macro/run/run_qa.C | 5 +- reco/L1/CMakeLists.txt | 8 +- reco/L1/L1LinkDef.h | 2 + reco/L1/qa/CbmTrackerInputQaTrd.cxx | 739 ++++++++++++++++++++++++++++ reco/L1/qa/CbmTrackerInputQaTrd.h | 166 +++++++ 12 files changed, 1024 insertions(+), 44 deletions(-) create mode 100644 reco/L1/qa/CbmTrackerInputQaTrd.cxx create mode 100644 reco/L1/qa/CbmTrackerInputQaTrd.h diff --git a/core/data/CbmLink.h b/core/data/CbmLink.h index 2823928c4c..1ca2563482 100644 --- a/core/data/CbmLink.h +++ b/core/data/CbmLink.h @@ -61,6 +61,8 @@ public: return (lhs.GetFile() == rhs.GetFile() && lhs.GetEntry() == rhs.GetEntry() && lhs.GetIndex() == rhs.GetIndex()); } + friend Bool_t operator!=(const CbmLink& lhs, const CbmLink& rhs) { return !(lhs == rhs); } + /** Comparison operators by //Dr.Sys **/ friend Bool_t operator<(const CbmLink& l, const CbmLink& r) { diff --git a/core/data/CbmMCEventList.cxx b/core/data/CbmMCEventList.cxx index 054fa8dc7a..8dd8e851ae 100644 --- a/core/data/CbmMCEventList.cxx +++ b/core/data/CbmMCEventList.cxx @@ -97,6 +97,17 @@ Double_t CbmMCEventList::GetEventTime(UInt_t eventId, UInt_t fileId) // ---------------------------------------------------------------------------- +// ----- Get event index in the list ------------------------------------- +Int_t CbmMCEventList::GetEventIndex(UInt_t eventId, UInt_t fileId) +{ + if (!fIsSorted) Sort(); + auto it = Find(fileId, eventId); + if (it == fEvents.end()) return -1.; + return (it - fEvents.begin()); +} +// ---------------------------------------------------------------------------- + + // ----- Get event time for event at index in list ------------------------ Double_t CbmMCEventList::GetEventTimeByIndex(UInt_t index) { diff --git a/core/data/CbmMCEventList.h b/core/data/CbmMCEventList.h index 82e4d99267..ae5ef757aa 100644 --- a/core/data/CbmMCEventList.h +++ b/core/data/CbmMCEventList.h @@ -10,6 +10,7 @@ #ifndef CBMMCEVENTLIST_H #define CBMMCEVENTLIST_H 1 +#include "CbmLink.h" #include "CbmMCEventInfo.h" // for CbmMCEventInfo #include <Rtypes.h> // for THashConsistencyHolder, ClassDef @@ -72,6 +73,10 @@ public: **/ Double_t GetEventTime(UInt_t event, UInt_t file); + /** @brief Event start time + * * @param link link to the MC event + **/ + Double_t GetEventTime(const CbmLink& link) { return GetEventTime(link.GetEntry(), link.GetFile()); } /** @brief Event time by index ** @value Event time for event at given index in list @@ -88,6 +93,19 @@ public: **/ Int_t GetFileIdByIndex(UInt_t index); + /** @brief Event index + ** @param event MC event number + ** @param file MC input file number + ** @value eventindex in list + ** + ** Returns -1. if the event is not present in the list. + **/ + Int_t GetEventIndex(UInt_t event, UInt_t file); + + /** @brief Event index + **/ + Int_t GetEventIndex(const CbmLink& link) { return GetEventIndex(link.GetEntry(), link.GetFile()); } + /** @brief Number of events in the list ** @value Number of events diff --git a/core/qa/CMakeLists.txt b/core/qa/CMakeLists.txt index 80589aa5a5..aaa8e0a0f2 100644 --- a/core/qa/CMakeLists.txt +++ b/core/qa/CMakeLists.txt @@ -24,6 +24,12 @@ CbmQaPie.cxx CbmQaHist.cxx ) +set(HEADERS +CbmQaCanvas.h +CbmQaPie.h +CbmQaHist.h +) + set(LINKDEF CbmQaBaseLinkDef.h) Set(LIBRARY_NAME CbmQaBase) Set(DEPENDENCIES Hist Gpad) diff --git a/core/qa/CbmQaBaseLinkDef.h b/core/qa/CbmQaBaseLinkDef.h index 06d82d7b0e..c17d1514d6 100644 --- a/core/qa/CbmQaBaseLinkDef.h +++ b/core/qa/CbmQaBaseLinkDef.h @@ -11,10 +11,16 @@ #pragma link C++ nestedclasses; #pragma link C++ nestedtypedef; +// custom streamers are already implemented in the class body #pragma link C++ class CbmQaCanvas - ; -#pragma link C++ class CbmQaPieSlice + ; #pragma link C++ class CbmQaPie - ; -#pragma link C++ class CbmQaH1F + ; -#pragma link C++ class CbmQaH1D + ; + +// create streamers automatically +#pragma link C++ class CbmQaPieSlice + ; +#pragma link C++ class CbmQaHist < TH1F> + ; +#pragma link C++ class CbmQaHist < TH1D> + ; +#pragma link C++ class CbmQaHist < TH1I> + ; +#pragma link C++ class CbmQaHist < TProfile> + ; +#pragma link C++ class CbmQaHist < TProfile2D> + ; #endif diff --git a/core/qa/CbmQaHist.cxx b/core/qa/CbmQaHist.cxx index 0651387e7f..5e46f88cf0 100644 --- a/core/qa/CbmQaHist.cxx +++ b/core/qa/CbmQaHist.cxx @@ -13,6 +13,9 @@ templateClassImp(CbmQaHist); // create definitions of specific CbmQaHist classes, // otherwise they will not be linked by the ROOT linker -// + template class CbmQaHist<TH1F>; template class CbmQaHist<TH1D>; +template class CbmQaHist<TH1I>; +template class CbmQaHist<TProfile>; +template class CbmQaHist<TProfile2D>; diff --git a/core/qa/CbmQaHist.h b/core/qa/CbmQaHist.h index e0e609f053..860e9d1e8b 100644 --- a/core/qa/CbmQaHist.h +++ b/core/qa/CbmQaHist.h @@ -12,13 +12,24 @@ #include "CbmQaCanvas.h" -#include "TFitResultPtr.h" -#include "TH1D.h" -#include "TH1F.h" -#include "TStyle.h" -#include "TVirtualPad.h" -/// A modification of TH* classes that keeps statistics & fit drawing options -/// and resizes the stat window accordingly without actual drawing. +#include <FairLogger.h> + +#include <TFitResultPtr.h> +#include <TPaveStats.h> +#include <TStyle.h> +#include <TVirtualPad.h> + +// The TH* headers are needed here for the ROOT linker + +#include <TF1.h> +#include <TH1D.h> +#include <TH1F.h> +#include <TH1I.h> +#include <TProfile.h> +#include <TProfile2D.h> + +/// A modification of TH* and TProfile* classes that keeps statistics & fit drawing options +/// and resizes the stat window accordingly without the actual drawing. /// In the original classes, hist->Draw() & canv->Update() must be called /// for resetting Stat/Fit window. /// @@ -34,6 +45,7 @@ public: fOptStat = gStyle->GetOptStat(); fOptFit = gStyle->GetOptFit(); } + this->SetLineWidth(2); } /// Copy constructor @@ -45,6 +57,7 @@ public: CbmQaHist(Types... args) : HistTypeT(args...) { if (gStyle) { SetOptStatFit(gStyle->GetOptStat(), gStyle->GetOptFit()); } + this->SetLineWidth(2); } /// Destructor @@ -57,54 +70,63 @@ public: TFitResultPtr Fit(Types... args) { TVirtualPad* padsav = gPad; - GetCanvas().cd(); + GetDummyCanvas().cd(); + this->Sumw2(); auto ret = HistTypeT::Fit(args...); - GetCanvas().Clear(); + GetDummyCanvas().Clear(); + + // make the output look nice + if (padsav) padsav->cd(); auto* f = this->GetFunction("gaus"); if (f) { - f->SetParName(0, "Peak"); - f->SetParName(1, "#mu"); - f->SetParName(2, "#sigma"); + f->SetParNames("Peak", "#mu", "#sigma"); f->SetLineColor(kRed); f->SetLineWidth(3); + TPaveStats* st = (TPaveStats*) this->FindObject("stats"); + if (!st) { LOG(fatal) << "CbmQaHist: can not access histogram statistics"; } + else { + st->SetX1NDC(0.6); + st->SetX2NDC(0.940); + st->SetY1NDC(0.5); + st->SetY2NDC(0.930); + st->SetOptStat(111110); + st->SetOptFit(10001); + } } return ret; } - /// Set stat drawing options and resize the stat window + /// Set stat drawing options and autoresize the stat window void SetOptStat(Int_t stat = 1) { SetOptStatFit(stat, fOptFit); } - /// Set fit drawing options and resize the stat window + /// Set fit drawing options and autoresize the stat window void SetOptFit(Int_t fit = 1) { SetOptStatFit(fOptStat, fit); } - /// Set stat & fit drawing options and resize the stat window + /// Set stat & fit drawing options and autoresize the stat window void SetOptStatFit(int stat, int fit) { + // the only way to create and auto-size the stat window is to draw the histogram + fOptStat = stat; fOptFit = fit; - if (!gStyle) { return; } + if (!gStyle) { return; } // should not happen + TVirtualPad* savePad = gPad; int saveStat = gStyle->GetOptStat(); int saveFit = gStyle->GetOptFit(); - this->SetStats(0); // remove the old stat window - this->SetStats(1); // set the flag to create thes stat window during Draw() - CbmQaHist* tmp = (CbmQaHist*) this->Clone("myClone"); - GetCanvas().cd(); + GetDummyCanvas().cd(); gStyle->SetOptStat(fOptStat); gStyle->SetOptFit(fOptFit); - // the only way to create and auto-size the stat window - tmp->Draw(); - GetCanvas().Update(); - // move the stat window to *this - TObject* obj = tmp->FindObject("stats"); - // the list of functions seems always to exist, but let's check it - if (obj && this->GetListOfFunctions()) { - tmp->GetListOfFunctions()->Remove(obj); - this->GetListOfFunctions()->Add(obj); - } - GetCanvas().Clear(); + + this->SetStats(0); // remove the old stat window + this->SetStats(1); // set the flag to create the stat window during Draw() + this->Draw(); + GetDummyCanvas().Update(); + GetDummyCanvas().Clear(); + + // restore the environment gStyle->SetOptStat(saveStat); gStyle->SetOptFit(saveFit); if (savePad) savePad->cd(); @@ -112,12 +134,12 @@ public: private: /// a static canvas for temporary drawing - static CbmQaCanvas& GetCanvas() + static CbmQaCanvas& GetDummyCanvas() { /// the static variable will be initialised at the first call; /// deleted at the application end (c++11) - static CbmQaCanvas tmp("CbmQaTempCanvas", "CbmQaTempCanvas", 1, 1); - return tmp; + static CbmQaCanvas dummy("CbmQaTempCanvas", "CbmQaTempCanvas", 1, 1); + return dummy; } int fOptStat = 1; @@ -126,8 +148,4 @@ private: ClassDefNV(CbmQaHist, 1); }; -// shortcuts -typedef CbmQaHist<TH1F> CbmQaH1F; -typedef CbmQaHist<TH1D> CbmQaH1D; - #endif diff --git a/macro/run/run_qa.C b/macro/run/run_qa.C index b56a8a2007..1754454a3c 100644 --- a/macro/run/run_qa.C +++ b/macro/run/run_qa.C @@ -48,7 +48,9 @@ void run_qa(TString dataTra = "data/sis100_muon_jpsi_test", TString dataRaw = "d // ----- Logger settings ---------------------------------------------- FairLogger::GetLogger()->SetLogScreenLevel("INFO"); - FairLogger::GetLogger()->SetLogVerbosityLevel("LOW"); + fair::Logger::DefineVerbosity( + "user1", fair::VerbositySpec::Make(fair::VerbositySpec::Info::severity, fair::VerbositySpec::Info::file_line)); + FairLogger::GetLogger()->SetLogVerbosityLevel("user1"); // ------------------------------------------------------------------------ // ----- Environment -------------------------------------------------- @@ -173,6 +175,7 @@ void run_qa(TString dataTra = "data/sis100_muon_jpsi_test", TString dataRaw = "d //run->AddTask(new CbmTrdDigitizerPRFQa()); //works put currently doesn't do anything //run->AddTask(new CbmTrdHitRateFastQa()); //opens lots of windows run->AddTask(new CbmTrdHitProducerQa()); + run->AddTask(new CbmTrackerInputQaTrd()); // Tracker requirements to TRD } // ------------------------------------------------------------------------ diff --git a/reco/L1/CMakeLists.txt b/reco/L1/CMakeLists.txt index 6c4fb45a51..5c76eaf2a1 100644 --- a/reco/L1/CMakeLists.txt +++ b/reco/L1/CMakeLists.txt @@ -41,6 +41,7 @@ ${CBMROOT_SOURCE_DIR}/reco/L1 ${CBMROOT_SOURCE_DIR}/reco/L1/L1Algo ${CBMROOT_SOURCE_DIR}/reco/L1/OffLineInterface ${CBMROOT_SOURCE_DIR}/reco/L1/ParticleFinder +${CBMROOT_SOURCE_DIR}/reco/L1/qa ${CBMROOT_SOURCE_DIR}/reco/KF ${CBMROOT_SOURCE_DIR}/reco/KF/KFQA @@ -52,7 +53,7 @@ ${CBMROOT_SOURCE_DIR}/sim/transport/geosetup ${CBMDATA_DIR} ${CBMDATA_DIR}/base - ${CBMDETECTORBASE_DIR}/sts +${CBMDETECTORBASE_DIR}/sts ${CBMROOT_SOURCE_DIR}/mvd ${CBMDETECTORBASE_DIR}/trd @@ -144,6 +145,8 @@ L1Algo/utils/L1AlgoPulls.cxx ParticleFinder/CbmL1PFFitter.cxx ParticleFinder/CbmL1PFMCParticle.cxx + +qa/CbmTrackerInputQaTrd.cxx ) set(HEADERS @@ -175,6 +178,8 @@ OffLineInterface/CbmL1GlobalFindTracksEvents.h #OffLineInterface / CbmL1SttTrack.h L1Algo/L1Def.h L1Algo/L1Vector.h + +qa/CbmTrackerInputQaTrd.h ) @@ -231,6 +236,7 @@ Set(DEPENDENCIES CbmStsBase CbmRecoBase CbmRecoSts + CbmQaBase boost_regex ) diff --git a/reco/L1/L1LinkDef.h b/reco/L1/L1LinkDef.h index 4ddd908135..d7b233e93c 100644 --- a/reco/L1/L1LinkDef.h +++ b/reco/L1/L1LinkDef.h @@ -29,5 +29,7 @@ //#pragma link C++ class CbmL1SttHit+; //#pragma link C++ class CbmL1SttTrackFinder+; //#pragma link C++ class CbmL1SttTrack+; +#pragma link C++ class CbmTrackerInputQaTrd + ; + #endif diff --git a/reco/L1/qa/CbmTrackerInputQaTrd.cxx b/reco/L1/qa/CbmTrackerInputQaTrd.cxx new file mode 100644 index 0000000000..5ebe1fae51 --- /dev/null +++ b/reco/L1/qa/CbmTrackerInputQaTrd.cxx @@ -0,0 +1,739 @@ +/* Copyright (C) 2021 Facility for Antiproton and Ion Research in Europe, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergey Gorbunov[committer]*/ + +/// @file CbmTrackerInputQaTrd.cxx +/// @author Sergey Gorbunov +/// @date 02.11.2021 + +#include "CbmTrackerInputQaTrd.h" + +#include "CbmDefs.h" +#include "CbmDigiManager.h" +#include "CbmLink.h" +#include "CbmMCDataArray.h" +#include "CbmMCDataManager.h" +#include "CbmMCEventList.h" +#include "CbmMCTrack.h" +#include "CbmMatch.h" +#include "CbmQaCanvas.h" +//#include "CbmSetup.h" +#include "CbmTimeSlice.h" +#include "CbmTrdCluster.h" +#include "CbmTrdDigi.h" +#include "CbmTrdHit.h" +#include "CbmTrdParModDigi.h" // for CbmTrdModule +#include "CbmTrdParModGeo.h" +#include "CbmTrdParSetDigi.h" // for CbmTrdParSetDigi +#include "CbmTrdParSetGeo.h" +#include "CbmTrdPoint.h" + +#include <FairRootManager.h> +#include <FairRunAna.h> +#include <FairRuntimeDb.h> +#include <FairSink.h> +#include <FairTask.h> +#include <Logger.h> + +#include <TClonesArray.h> +#include <TDatabasePDG.h> +#include <TGenericClassInfo.h> +#include <TGeoManager.h> +#include <TGeoNode.h> +#include <TMathBase.h> +#include <TObjArray.h> +#include <TParameter.h> +#include <TParticlePDG.h> +#include <TString.h> +#include <TStyle.h> + +#include <algorithm> +#include <iostream> +#include <map> +#include <vector> + +#include <stdio.h> + +ClassImp(CbmTrackerInputQaTrd); + +// ------------------------------------------------------------------------- + +CbmTrackerInputQaTrd::CbmTrackerInputQaTrd(const char* name, Int_t verbose) : FairTask(name, verbose) +{ + // Constructor + + // Create a list of histogramms + + fHistList.clear(); + fHistList.reserve(20); + fHistList.push_back(&fh1DresidualU); + fHistList.push_back(&fh1DresidualV); + fHistList.push_back(&fh1DresidualT); + fHistList.push_back(&fh2DresidualX); + fHistList.push_back(&fh2DresidualY); + fHistList.push_back(&fh2DresidualT); + fHistList.push_back(&fh1DpullU); + fHistList.push_back(&fh1DpullV); + fHistList.push_back(&fh1DpullT); + fHistList.push_back(&fh2DpullX); + fHistList.push_back(&fh2DpullY); + fHistList.push_back(&fh2DpullT); + + // Keep the ownership on the histograms to avoid double deletion + for (unsigned int i = 0; i < fHistList.size(); i++) { + fHistList[i]->SetDirectory(0); + } +} + +// ------------------------------------------------------------------------- +CbmTrackerInputQaTrd::~CbmTrackerInputQaTrd() +{ /// Destructor + DeInit(); +} + + +// ------------------------------------------------------------------------- +void CbmTrackerInputQaTrd::DeInit() +{ + fIsTrdInSetup = 0; + fIsMcPresent = false; + fNtrackingStations = 0; + + fTimeSlice = nullptr; + fDigiManager = nullptr; + + fMcManager = nullptr; + fMcTracks = nullptr; + fMcPoints = nullptr; + + fClusters = nullptr; + fHits = nullptr; + fHitMatches = nullptr; + + fOutFolder.Clear(); + + fHistFolder = nullptr; + + fNevents.SetVal(0); + + fhPointsPerHit.clear(); + fhHitsPerPoint.clear(); +} +// ------------------------------------------------------------------------- + +void CbmTrackerInputQaTrd::SetParContainers() +{ + fTrdDigiPar = nullptr; + fTrdGeoPar = nullptr; + + // Get run and runtime database + FairRunAna* ana = FairRunAna::Instance(); + if (!ana) { LOG(fatal) << "No FairRunAna instance"; } + + FairRuntimeDb* rtdb = ana->GetRuntimeDb(); + if (!rtdb) { LOG(fatal) << "No FairRuntimeDb instance"; } + + fTrdDigiPar = dynamic_cast<CbmTrdParSetDigi*>(rtdb->getContainer("CbmTrdParSetDigi")); + fTrdGeoPar = dynamic_cast<CbmTrdParSetGeo*>(rtdb->getContainer("CbmTrdParSetGeo")); +} + +// ------------------------------------------------------------------------- +InitStatus CbmTrackerInputQaTrd::ReInit() +{ + // Initialize and check the setup + + DeInit(); + + // Check if TRD is present in the CBM setup + // A straightforward way to do so is CbmSetup::Instance()->IsActive(ECbmModuleId::kTrd), + // but unfortunatelly CbmSetup class is unaccesible. + // ( CbmSimSteer library requires libfreetype that has linking problems on MacOsX ) + // Therefore let's simply check if any TRD material is present in the geometry. + // + fIsTrdInSetup = 0; + { + TObjArray* topNodes = gGeoManager->GetTopNode()->GetNodes(); + for (Int_t iTopNode = 0; iTopNode < topNodes->GetEntriesFast(); iTopNode++) { + TGeoNode* topNode = static_cast<TGeoNode*>(topNodes->At(iTopNode)); + if (TString(topNode->GetName()).Contains("trd")) { + fIsTrdInSetup = 1; + break; + } + } + } + + if (!fIsTrdInSetup) { + LOG(info) << "TRD is not in the setup, do nothing"; + return kSUCCESS; + } + + // check parameter containers + + if (!fTrdDigiPar) { + LOG(error) << "No CbmTrdParSetDigi container in FairRuntimeDb"; + return kERROR; + } + + if (!fTrdGeoPar) { + LOG(error) << "No CbmTrdParSetGeo container in FairRuntimeDb"; + return kERROR; + } + + FairRootManager* manager = FairRootManager::Instance(); + fDigiManager = CbmDigiManager::Instance(); + fDigiManager->Init(); + + fTimeSlice = dynamic_cast<CbmTimeSlice*>(manager->GetObject("TimeSlice.")); + if (!fTimeSlice) { + LOG(error) << "No time slice found"; + return kERROR; + } + + fHits = dynamic_cast<TClonesArray*>(manager->GetObject("TrdHit")); + + if (!fHits) { + LOG(error) << "No TRD hit array found"; + return kERROR; + } + + fClusters = dynamic_cast<TClonesArray*>(manager->GetObject("TrdCluster")); + + if (!fClusters) { + LOG(error) << "No TRD cluster array found"; + return kERROR; + } + + fMcManager = dynamic_cast<CbmMCDataManager*>(manager->GetObject("MCDataManager")); + + fIsMcPresent = (fMcManager != nullptr); + + if (fIsMcPresent) { + + fMcEventList = dynamic_cast<CbmMCEventList*>(manager->GetObject("MCEventList.")); + fMcTracks = fMcManager->InitBranch("MCTrack"); + fMcPoints = fMcManager->InitBranch("TrdPoint"); + fHitMatches = dynamic_cast<TClonesArray*>(manager->GetObject("TrdHitMatch")); + + // we assume that when TRD is in the setup and the MC manager is present, + // the TRD MC data must be present too + + if (!fMcEventList) { + LOG(error) << ": No MCEventList data!"; + return kERROR; + } + + if (!fMcTracks) { + LOG(error) << "No MC tracks found"; + return kERROR; + } + + if (!fMcPoints) { + LOG(error) << "No MC points found"; + return kERROR; + } + + if (!fHitMatches) { + LOG(error) << "No TRD hit matches found"; + return kERROR; + } + + if (!fDigiManager->IsMatchPresent(ECbmModuleId::kTrd)) { + LOG(error) << "No TRD digi matches found"; + return kERROR; + } + } + + // count TRD stations + // TODO: put the code below to some TRD geometry interface + + fNtrackingStations = 0; + { + Int_t layerCounter = 0; + TObjArray* topNodes = gGeoManager->GetTopNode()->GetNodes(); + for (Int_t iTopNode = 0; iTopNode < topNodes->GetEntriesFast(); iTopNode++) { + TGeoNode* topNode = static_cast<TGeoNode*>(topNodes->At(iTopNode)); + if (TString(topNode->GetName()).Contains("trd")) { + TObjArray* layers = topNode->GetNodes(); + for (Int_t iLayer = 0; iLayer < layers->GetEntriesFast(); iLayer++) { + TGeoNode* layer = static_cast<TGeoNode*>(layers->At(iLayer)); + if (TString(layer->GetName()).Contains("layer")) { layerCounter++; } + } + } + } + fNtrackingStations = layerCounter; + } + + LOG(debug) << "Init(): fNtrackingStations = " << fNtrackingStations; + + if (fNtrackingStations <= 0) { + LOG(error) << "can not count TRD tracking stations"; + return kERROR; + } + + // initialise histograms + fOutFolder.SetOwner(false); + fHistFolder = fOutFolder.AddFolder("rawHist", "Raw Histograms"); + gStyle->SetOptStat(0); + + fNevents.SetVal(0); + fHistFolder->Add(&fNevents); + + for (unsigned int i = 0; i < fHistList.size(); i++) { + fHistList[i]->Reset(); + fHistFolder->Add(fHistList[i]); + } + + fhPointsPerHit.clear(); + fhHitsPerPoint.clear(); + fhEfficiencyR.clear(); + fhEfficiencyXY.clear(); + + fhPointsPerHit.reserve(fNtrackingStations); + fhHitsPerPoint.reserve(fNtrackingStations); + fhEfficiencyR.reserve(fNtrackingStations); + fhEfficiencyXY.reserve(fNtrackingStations); + + for (Int_t i = 0; i < fNtrackingStations; i++) { + fhPointsPerHit.emplace_back(Form("hMcPointsPerHit%i", i), + Form("MC Points per Hit: Station %i;N mc points / hit", i), 10, -0.5, 9.5); + fhPointsPerHit[i].SetDirectory(0); + fhPointsPerHit[i].SetLineWidth(2); + fhPointsPerHit[i].SetOptStat(110); + fHistFolder->Add(&fhPointsPerHit[i]); + } + + for (Int_t i = 0; i < fNtrackingStations; i++) { + fhHitsPerPoint.emplace_back(Form("hHitsPerMcPoint%i", i), + Form("Hits per MC Point: Station %i; N hits / mc point", i), 10, -0.5, 9.5); + fhHitsPerPoint[i].SetDirectory(0); + fhHitsPerPoint[i].SetLineWidth(2); + fhHitsPerPoint[i].SetOptStat(110); + fHistFolder->Add(&fhHitsPerPoint[i]); + } + + for (Int_t i = 0; i < fNtrackingStations; i++) { + + double dx = 350; + double dy = 350; + double dr = sqrt(dx * dx + dy * dy); + + fhEfficiencyR.emplace_back(Form("hEfficiencyR%i", i), Form("Efficiency R: Station %i;R [cm]", i), 100, 0, dr); + fhEfficiencyR[i].SetDirectory(0); + fhEfficiencyR[i].SetOptStat(1110); + fHistFolder->Add(&fhEfficiencyR[i]); + + fhEfficiencyXY.emplace_back(Form("hEfficiencyXY%i", i), Form("Efficiency XY: Station %i;X [cm];Y [cm]", i), 50, -dx, + dx, 50, -dy, dy); + fhEfficiencyXY[i].SetDirectory(0); + fhEfficiencyXY[i].SetOptStat(10); + fhEfficiencyXY[i].GetYaxis()->SetTitleOffset(1.4); + fHistFolder->Add(&fhEfficiencyXY[i]); + } + + fCanvResidual.Clear(); + fCanvResidual.Divide2D(6); + + fCanvPull.Clear(); + fCanvPull.Divide2D(6); + + fCanvEfficiencyR.Clear(); + fCanvEfficiencyR.Divide2D(fNtrackingStations); + + fCanvEfficiencyXY.Clear(); + fCanvEfficiencyXY.Divide2D(fNtrackingStations); + + fCanvPointsPerHit.Clear(); + fCanvPointsPerHit.Divide2D(fNtrackingStations); + + fCanvHitsPerPoint.Clear(); + fCanvHitsPerPoint.Divide2D(fNtrackingStations); + + fOutFolder.Add(&fCanvResidual); + fOutFolder.Add(&fCanvPull); + fOutFolder.Add(&fCanvEfficiencyR); + fOutFolder.Add(&fCanvEfficiencyXY); + fOutFolder.Add(&fCanvPointsPerHit); + fOutFolder.Add(&fCanvHitsPerPoint); + + // analyse the geometry setup + return GeometryQa(); +} + + +// ------------------------------------------------------------------------- +void CbmTrackerInputQaTrd::Exec(Option_t*) +{ + if (!fIsTrdInSetup) { return; } + + // update number of processed events + fNevents.SetVal(fNevents.GetVal() + 1); + LOG(debug) << "Event: " << fNevents.GetVal(); + ResolutionQa(); +} + + +// ------------------------------------------------------------------------- +void CbmTrackerInputQaTrd::Finish() +{ + FairSink* sink = FairRootManager::Instance()->GetSink(); + if (sink) { sink->WriteObject(&GetQa(), nullptr); } +} + + +// ------------------------------------------------------------------------- +InitStatus CbmTrackerInputQaTrd::GeometryQa() +{ + // check geometry of the TRD modules + + double lastZ = -1; + for (int iStation = 0; iStation < fNtrackingStations; iStation++) { + + int ModuleId = fTrdDigiPar->GetModuleId(iStation); + + const CbmTrdParModGeo* pGeo = dynamic_cast<const CbmTrdParModGeo*>(fTrdGeoPar->GetModulePar(ModuleId)); + if (!pGeo) { + LOG(fatal) << " No Geo params for station " << iStation << " module " << ModuleId << " found "; + return kERROR; + } + + double staZ = pGeo->GetZ(); + + // check that the stations are properly ordered in Z + if ((iStation > 0) && (staZ <= lastZ)) { + LOG(error) << "TRD trackig stations are not properly ordered in Z"; + return kERROR; + } + lastZ = staZ; + + //TODO: what are these 3 and 6 here in L1 code? Why are they hardcoed? + // if (num == 0 || num == 2 || num == 4) geo.push_back(3); + // if (num == 1 || num == 3) geo.push_back(6); + } + + return kSUCCESS; +} + +// ------------------------------------------------------------------------- +void CbmTrackerInputQaTrd::ResolutionQa() +{ + if (!fDigiManager->IsMatchPresent(ECbmModuleId::kTrd)) return; + + if (!fIsMcPresent) return; + + Int_t nHits = fHits->GetEntriesFast(); + Int_t nClusters = fClusters->GetEntriesFast(); + Int_t nDigis = fDigiManager->GetNofDigis(ECbmModuleId::kTrd); + + int nMcEvents = fMcEventList->GetNofEvents(); + + // Vector of integers parallel to mc points + + std::vector<std::vector<int>> pointNhits; + pointNhits.resize(nMcEvents); + for (int iE = 0; iE < nMcEvents; iE++) { + int fileId = fMcEventList->GetFileIdByIndex(iE); + int eventId = fMcEventList->GetEventIdByIndex(iE); + int nPoints = fMcPoints->Size(fileId, eventId); + pointNhits[iE].resize(nPoints, 0); + } + + for (Int_t iHit = 0; iHit < nHits; iHit++) { + + CbmTrdHit* hit = dynamic_cast<CbmTrdHit*>(fHits->At(iHit)); + if (!hit) { + LOG(error) << "TRD hit N " << iHit << " doesn't exist"; + return; + } + + if ((int) hit->GetClassType() > 1) { + // class type 0: trd-1D + // class type 1: trd-2D + // the return type is (currently) boolean, so this error should never happen + LOG(error) << "Unknown detector class type " << hit->GetClassType() << " for TRD hit N " << iHit; + return; + } + + const int address = hit->GetAddress(); + int StationIndex = CbmTrdAddress::GetLayerId(address); + + if (StationIndex < 0 || StationIndex >= fNtrackingStations) { + LOG(fatal) << "TRD hit layer id " << StationIndex << " is out of range"; + return; + } + + Int_t clusterId = hit->GetRefId(); + if (clusterId < 0 || clusterId >= nClusters) { + LOG(error) << "TRD cluster id " << clusterId << " is out of range"; + return; + } + + CbmTrdCluster* cluster = dynamic_cast<CbmTrdCluster*>(fClusters->At(clusterId)); + if (!cluster) { + LOG(error) << "TRD cluster N " << clusterId << " doesn't exist"; + return; + } + + if (cluster->GetAddress() != address) { + LOG(error) << "TRD hit address " << address << " differs from its cluster address " << cluster->GetAddress(); + return; + } + + Int_t nClusterDigis = cluster->GetNofDigis(); + + if (nClusterDigis <= 0) { + LOG(error) << "hit match for TRD cluster " << clusterId << " has no digis "; + return; + } + + // custom finder of the digi matches + + CbmMatch myHitMatch; + for (Int_t iDigi = 0; iDigi < nClusterDigis; iDigi++) { + Int_t digiIdx = cluster->GetDigi(iDigi); + if (digiIdx < 0 || digiIdx >= nDigis) { + LOG(error) << "TRD cluster: digi index " << digiIdx << " out of range "; + return; + } + const CbmTrdDigi* digi = CbmDigiManager::Instance()->Get<CbmTrdDigi>(digiIdx); + if (!digi) { + LOG(fatal) << "TRD digi " << iDigi << " not found"; + return; + } + + if (digi->GetAddressModule() != address) { + LOG(error) << "TRD hit address " << address << " differs from its digi address " << digi->GetAddressModule(); + return; + } + const CbmMatch* match = dynamic_cast<const CbmMatch*>(fDigiManager->GetMatch(ECbmModuleId::kTrd, digiIdx)); + if (!match) { + LOG(fatal) << "TRD digi match " << digiIdx << " not found"; + return; + } + myHitMatch.AddLinks(*match); + } + if (myHitMatch.GetNofLinks() == 0) { continue; } + + const CbmLink& bestLink = myHitMatch.GetMatchedLink(); + + { // check if the hit match is correct + CbmMatch* hitMatch = dynamic_cast<CbmMatch*>(fHitMatches->At(iHit)); + if (hitMatch == nullptr) { LOG(error) << "hit match for TRD hit " << iHit << " doesn't exist"; } + else { + const CbmLink& link = hitMatch->GetMatchedLink(); + if ((link != bestLink) && (link.GetWeight() != bestLink.GetWeight())) { + LOG(error) << "hit match for TRD hit " << iHit << " doesn't correspond to digi matches"; + } + } + } + + // how many MC points? fill N hits per mc point + + int nHitPoints = 0; + for (int i = 0; i < myHitMatch.GetNofLinks(); i++) { + const CbmLink& link = myHitMatch.GetLink(i); + if (link.GetIndex() >= 0) { // not a noise digi + nHitPoints++; + int iE = fMcEventList->GetEventIndex(link); + if (iE < 0 || iE >= nMcEvents) { + LOG(error) << "link points to a non-existing MC event"; + return; + } + if (link.GetIndex() >= (int) pointNhits[iE].size()) { + LOG(error) << "link points to a non-existing MC point"; + return; + } + pointNhits[iE][link.GetIndex()]++; + } + } + + fhPointsPerHit[StationIndex].Fill(nHitPoints); + + // take corresponding MC point + + // skip hits from the noise digis + if (bestLink.GetIndex() < 0) { continue; } + + CbmTrdPoint* p = dynamic_cast<CbmTrdPoint*>(fMcPoints->Get(bestLink)); + if (p == nullptr) { + LOG(error) << "link points to a non-existing MC point"; + return; + } + + if (p->GetModuleAddress() != (int) CbmTrdAddress::GetModuleAddress(address)) { + LOG(error) << "mc point module address differs from the hit module address"; + return; + } + + // check that the nominal station Z is not far from the active material + { + // the cut of 1 cm is choosen arbitrary and can be changed + + int ModuleId = fTrdDigiPar->GetModuleId(StationIndex); + + const CbmTrdParModGeo* pGeo = dynamic_cast<const CbmTrdParModGeo*>(fTrdGeoPar->GetModulePar(ModuleId)); + if (!pGeo) { + LOG(fatal) << " No Geo params for station " << StationIndex << " module " << ModuleId << " found "; + return; + } + + double staZ = pGeo->GetZ(); // module->GetZ(); //+ 410; + if ((staZ < p->GetZIn() - 1.) || (staZ > p->GetZOut() + 1.)) { + LOG(error) << "TRD station " << StationIndex << ": active material Z[" << p->GetZIn() << " cm," << p->GetZOut() + << " cm] is too far from the nominal station Z " << staZ << " cm"; + return; + } + // the cut of 1 cm is choosen arbitrary and can be changed + if (fabs(hit->GetZ() - staZ) > 1.) { + LOG(error) << "TRD station " << StationIndex << ": hit Z " << hit->GetZ() + << " cm, is too far from the nominal station Z " << staZ << " cm"; + return; + } + } + + // residual and pull + + if (nHitPoints != 1) continue; // only check residual for non-mixed hits + + double t0 = fMcEventList->GetEventTime(bestLink); + if (t0 < 0) { + LOG(error) << "MC event time doesn't exist for a TRD link"; + return; + } + + double x = p->GetX(); // take the "In" point since the time is stored only for this point + double y = p->GetY(); + double z = p->GetZ(); + double t = t0 + p->GetTime(); + double px = p->GetPx(); + double py = p->GetPy(); + double pz = p->GetPz(); + + // skip very slow particles + if (fabs(p->GetPzOut()) < 0.01) continue; + + // transport the particle from the MC point to the hit Z + double dz = hit->GetZ() - z; + x += dz * px / pz; + y += dz * py / pz; + + // get particle mass + double mass = 0; + { + CbmLink mcTrackLink = bestLink; + mcTrackLink.SetIndex(p->GetTrackID()); + CbmMCTrack* mcTrack = dynamic_cast<CbmMCTrack*>(fMcTracks->Get(mcTrackLink)); + if (!mcTrack) { + LOG(error) << "MC track " << p->GetTrackID() << " doesn't exist"; + return; + } + Int_t pdg = mcTrack->GetPdgCode(); + if (pdg < 9999999 && ((TParticlePDG*) TDatabasePDG::Instance()->GetParticle(pdg))) { + mass = TDatabasePDG::Instance()->GetParticle(pdg)->Mass(); + } + } + + constexpr double speedOfLight = 29.979246; // cm/ns + TVector3 mom3; + p->Momentum(mom3); + t += dz / (pz * speedOfLight) * sqrt(mass * mass + mom3.Mag2()); + + double du = hit->GetX() - x; + double dv = hit->GetY() - y; + double dt = hit->GetTime() - t; + double su = hit->GetDx(); + double sv = hit->GetDy(); + double st = hit->GetTimeError(); + + // residuals + if (hit->GetClassType() == 0) { + if (StationIndex % 2) { + std::swap(du, dv); + std::swap(su, sv); + } + fh1DresidualU.Fill(du); + fh1DresidualV.Fill(dv); + fh1DresidualT.Fill(hit->GetTime() - t); + } + else { + fh2DresidualX.Fill(du); + fh2DresidualY.Fill(dv); + fh2DresidualT.Fill(hit->GetTime() - t); + } + + // pulls + if ((su < 1.e-5) || (sv < 1.e-5) || (st < 1.e-5)) { + LOG(error) << "TRD hit errors are not properly set: errX " << hit->GetDx() << " errY " << hit->GetDy() << " errT " + << st; + return; + } + + if (hit->GetClassType() == 0) { + fh1DpullU.Fill(du / su); + fh1DpullV.Fill(dv / sv); + fh1DpullT.Fill(dt / st); + } + else { + fh2DpullX.Fill(du / su); + fh2DpullY.Fill(dv / sv); + fh2DpullT.Fill(dt / st); + } + + } // iHit + + // fill efficiency: N hits per MC point + + for (int iE = 0; iE < nMcEvents; iE++) { + int fileId = fMcEventList->GetFileIdByIndex(iE); + int eventId = fMcEventList->GetEventIdByIndex(iE); + int nPoints = fMcPoints->Size(fileId, eventId); + for (int ip = 0; ip < nPoints; ip++) { + CbmTrdPoint* p = dynamic_cast<CbmTrdPoint*>(fMcPoints->Get(fileId, eventId, ip)); + if (p == nullptr) { + LOG(error) << "MC point doesn't exist"; + return; + } + auto address = p->GetModuleAddress(); + int StationIndex = CbmTrdAddress::GetLayerId(address); + if (StationIndex < 0 || StationIndex >= fNtrackingStations) { + LOG(fatal) << "TRD hit layer id " << StationIndex << " for module " << address << " is out of range"; + return; + } + fhHitsPerPoint[StationIndex].Fill(pointNhits[iE][ip]); + fhEfficiencyR[StationIndex].Fill(sqrt(p->GetX() * p->GetX() + p->GetY() * p->GetY()), (pointNhits[iE][ip] > 0)); + fhEfficiencyXY[StationIndex].Fill(p->GetX(), p->GetY(), (pointNhits[iE][ip] > 0)); + } + } +} + + +// ------------------------------------------------------------------------- +TFolder& CbmTrackerInputQaTrd::GetQa() +{ + gStyle->SetPaperSize(20, 20); + + for (Int_t i = 0; i < fNtrackingStations; i++) { + fCanvHitsPerPoint.cd(i + 1); + fhHitsPerPoint[i].DrawCopy("", ""); + fCanvPointsPerHit.cd(i + 1); + fhPointsPerHit[i].DrawCopy("", ""); + + fCanvEfficiencyR.cd(i + 1); + fhEfficiencyR[i].DrawCopy("colz", ""); + + fCanvEfficiencyXY.cd(i + 1); + fhEfficiencyXY[i].DrawCopy("colz", ""); + } + + for (UInt_t i = 0; i < fHistList.size(); i++) { + fHistList[i]->Fit("gaus", "Q"); // Q for the quiet mode + } + + for (UInt_t i = 0; i < 6; i++) { + fCanvResidual.cd(i + 1); + fHistList[i]->DrawCopy("", ""); + fCanvPull.cd(i + 1); + fHistList[6 + i]->DrawCopy("", ""); + } + + return fOutFolder; +} diff --git a/reco/L1/qa/CbmTrackerInputQaTrd.h b/reco/L1/qa/CbmTrackerInputQaTrd.h new file mode 100644 index 0000000000..c7c6f5aad8 --- /dev/null +++ b/reco/L1/qa/CbmTrackerInputQaTrd.h @@ -0,0 +1,166 @@ +/* Copyright (C) 2021 Facility for Antiproton and Ion Research in Europe, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergey Gorbunov[committer]*/ + +/// @file CbmTrackerInputQaTrd.h +/// @author Sergey Gorbunov +/// @date 02.11.2021 + +#ifndef CbmTrackerInputQaTrd_H +#define CbmTrackerInputQaTrd_H + +#include "CbmQaCanvas.h" +#include "CbmQaHist.h" + +#include <FairTask.h> + +#include <TFolder.h> +#include <TH1D.h> +#include <TH1F.h> +#include <TH1I.h> +#include <TH2F.h> +#include <TParameter.h> +#include <TProfile.h> +#include <TProfile2D.h> + +class CbmDigiManager; +class CbmMCDataManager; +class CbmMCEventList; +class CbmMCDataArray; +class CbmTimeSlice; +class CbmTrdParSetGeo; +class CbmTrdParSetDigi; + +class TClonesArray; + +/// +/// CbmTrackerInputQaTrd class represents all the CA tracker requirements for the TRD detector. +/// When this QA test is passed, the tracker must work (at least that is the idea). +/// +/// The class ensures that the tracker has the correct understanding of the TRD geometry and interfaces +/// and checks the quality of all tracking-related aspects of the TRD data. +/// +class CbmTrackerInputQaTrd : public FairTask { + +public: + /// Constructor + CbmTrackerInputQaTrd(const char* name = "TrackerInputQaTrd", Int_t verbose = 1); + + /// Destructor + ~CbmTrackerInputQaTrd(); + + /// FairTask: Intialisation at begin of run. + InitStatus Init() { return ReInit(); } + + /// FairTask: Reinitialisation. + InitStatus ReInit(); + + /// FairTask: Intialise parameter containers. + void SetParContainers(); + + /// FairTask: Action at end of run. For this task and all of the subtasks. + void Finish(); + + /// TTask: Clear all data structures created by a previous execution of a task. + void Clear(Option_t* /*option*/ = "") {} + + /// TTask: Process a timeslice + void Exec(Option_t*); + + /// Prepare the Qa output and return it as a reference to an internal TFolder. + /// The reference is non-const, because FairSink can not write const objects + TFolder& GetQa(); + +private: + /// Check the geometry + InitStatus GeometryQa(); + + /// Analysis of hit uncertainty (pull) distributions + void ResolutionQa(); + + /// Free the memory etc. + void DeInit(); + + + // Setup + + Bool_t fIsTrdInSetup {false}; + Bool_t fIsMcPresent {false}; + + Int_t fNtrackingStations {0}; + + CbmTimeSlice* fTimeSlice {nullptr}; + CbmTrdParSetGeo* fTrdGeoPar {nullptr}; + CbmTrdParSetDigi* fTrdDigiPar {nullptr}; + CbmDigiManager* fDigiManager {nullptr}; + + /// MC data + CbmMCEventList* fMcEventList {nullptr}; // list of MC events connected to the current time slice + CbmMCDataManager* fMcManager {nullptr}; + CbmMCDataArray* fMcTracks {nullptr}; + CbmMCDataArray* fMcPoints {nullptr}; + + /// Data + TClonesArray* fClusters {nullptr}; + TClonesArray* fHits {nullptr}; + TClonesArray* fHitMatches {nullptr}; + + /// Output + + + TFolder fOutFolder {"TrackerInputQaTrd", "TrackerInputQaTrd"}; /// output folder with histos and canvases + TFolder* fHistFolder {nullptr}; /// subfolder for histograms + + TParameter<int> fNevents {"nEvents", 0}; /// number of processed events + + /// Histogram for Residual Distribution + CbmQaHist<TH1D> fh1DresidualU {"h1DresidualU", "Trd1D: Residual U;(U_{reco} - U_{MC})(cm)", 100, -.5, .5}; + CbmQaHist<TH1D> fh1DresidualV {"h1DresidualV", "Trd1D: Residual V;(V_{reco} - V_{MC})(cm)", 100, -10, 10}; + CbmQaHist<TH1D> fh1DresidualT {"h1DresidualT", "Trd1D: Residual T;(T_{reco} - T_{MC})(ns)", 100, -50, 50}; + + CbmQaHist<TH1D> fh2DresidualX {"h2DresidualX", "Trd2D: Residual X;(X_{reco} - X_{MC})(cm)", 100, -5, 5}; + CbmQaHist<TH1D> fh2DresidualY {"h2DresidualY", "Trd2D: Residual Y;(Y_{reco} - Y_{MC})(cm)", 100, -5, 5}; + CbmQaHist<TH1D> fh2DresidualT {"h2DresidualT", "Trd2D: Residual T;(T_{reco} - T_{MC})(ns)", 100, -1000, 1000}; + + /// Histogram for PULL Distribution + CbmQaHist<TH1D> fh1DpullU {"h1DpullU", "Trd1D: Pull U;(U_{reco} - U_{MC}) / #sigmaU_{reco}", 100, -5, 5}; + CbmQaHist<TH1D> fh1DpullV {"h1DpullV", "Trd1D: Pull V;(V_{reco} - V_{MC}) / #sigmaV_{reco}", 100, -5, 5}; + CbmQaHist<TH1D> fh1DpullT {"h1DpullT", "Trd1D: Pull T;(T_{reco} - T_{MC}) / #sigmaT_{reco}", 100, -5, 5}; + + CbmQaHist<TH1D> fh2DpullX {"h2DpullX", "Trd2D: Pull X;(X_{reco} - X_{MC}) / #sigmaX_{reco}", 100, -5, 5}; + CbmQaHist<TH1D> fh2DpullY {"h2DpullY", "Trd2D: Pull Y;(Y_{reco} - Y_{MC}) / #sigmaY_{reco}", 100, -5, 5}; + CbmQaHist<TH1D> fh2DpullT {"h2DpullT", "Trd2D: Pull T;(T_{reco} - T_{MC}) / #sigmaT_{reco}", 100, -5, 5}; + + /// List of the above histogramms + std::vector<CbmQaHist<TH1D>*> fHistList; + + /// hits purity + std::vector<CbmQaHist<TH1I>> fhPointsPerHit; + + /// hits efficiency + std::vector<CbmQaHist<TH1I>> fhHitsPerPoint; + + /// hits efficiency + std::vector<CbmQaHist<TProfile2D>> fhEfficiencyXY; + std::vector<CbmQaHist<TProfile>> fhEfficiencyR; + + /// Canvaces: collection of histogramms + + CbmQaCanvas fCanvResidual {"cResidual", "Residual Distribution", 3 * 500, 2 * 500}; + CbmQaCanvas fCanvPull {"cPull", "Pull Distribution", 3 * 500, 2 * 500}; + CbmQaCanvas fCanvEfficiencyXY {"cEfficiencyXY", "Efficiency XY: % reconstructed McPoint", 2 * 500, 2 * 500}; + CbmQaCanvas fCanvEfficiencyR {"cEfficiencyR", "Efficiency R: % reconstructed McPoint", 2 * 500, 2 * 500}; + CbmQaCanvas fCanvHitsPerPoint {"cHitsPerMcPoint", "Efficiency: Hits Per McPoint", 2 * 500, 2 * 500}; + CbmQaCanvas fCanvPointsPerHit {"cMcPointsPerHit", "Purity: McPoints per Hit", 2 * 500, 2 * 500}; + +private: + /// Suppressed copy constructor + CbmTrackerInputQaTrd(const CbmTrackerInputQaTrd&) = delete; + + /// Suppressed assignment operator + CbmTrackerInputQaTrd& operator=(const CbmTrackerInputQaTrd&) = delete; + + ClassDef(CbmTrackerInputQaTrd, 0); +}; + +#endif -- GitLab