diff --git a/macro/run/run_qa.C b/macro/run/run_qa.C index 17e1d89a247e49c78c22860e148427a06d60c1f7..2292beed81c6f4a8fe73c7250a1c4532abc38b1e 100644 --- a/macro/run/run_qa.C +++ b/macro/run/run_qa.C @@ -90,8 +90,8 @@ void run_qa(TString dataTra = "data/sis100_muon_jpsi_test", TString dataRaw = "d // - MUCH digitisation parameters TString muchParFile {}; if (CbmSetup::Instance()->GetGeoTag(ECbmModuleId::kMuch, geoTag)) { - bool mcbmFlag = geoTag.Contains("mcbm", TString::kIgnoreCase); - muchParFile = srcDir + "/parameters/much/much_"; + bool mcbmFlag = geoTag.Contains("mcbm", TString::kIgnoreCase); + muchParFile = srcDir + "/parameters/much/much_"; muchParFile += (mcbmFlag) ? geoTag : geoTag(0, 4); muchParFile += "_digi_sector.root"; { // init geometry from the file @@ -187,6 +187,12 @@ void run_qa(TString dataTra = "data/sis100_muon_jpsi_test", TString dataRaw = "d } // ------------------------------------------------------------------------ + // ----- TRD QA --------------------------------- + if (CbmSetup::Instance()->IsActive(ECbmModuleId::kTof)) { + run->AddTask(new CbmTrackerInputQaTof()); // Tracker requirements to TRD + } + // ------------------------------------------------------------------------ + // ----- STS QA --------------------------------- if (CbmSetup::Instance()->IsActive(ECbmModuleId::kSts)) { //run->AddTask(new CbmStsDigitizeQa()); //opens lots of windows diff --git a/reco/L1/CMakeLists.txt b/reco/L1/CMakeLists.txt index 5f18556f4cdb3c72a73f5358bc2370c2d62228b8..4eda3f60ecda8dd6fb5b241c19a559238a692824 100644 --- a/reco/L1/CMakeLists.txt +++ b/reco/L1/CMakeLists.txt @@ -157,6 +157,7 @@ set(SRCS ParticleFinder/CbmL1PFMCParticle.cxx qa/CbmTrackerInputQaTrd.cxx + qa/CbmTrackerInputQaTof.cxx qa/CbmTrackingInputQaSts.cxx ) @@ -196,6 +197,7 @@ set(HEADERS L1Algo/L1Def.h L1Algo/L1Vector.h qa/CbmTrackerInputQaTrd.h + qa/CbmTrackerInputQaTof.h qa/CbmTrackingInputQaSts.h ) diff --git a/reco/L1/L1LinkDef.h b/reco/L1/L1LinkDef.h index 7bb7c97cbfe465b6484b00edcb11fbb72d61b0bd..2ac5e1fdf2a4ba27d71fe971361a397143d2af08 100644 --- a/reco/L1/L1LinkDef.h +++ b/reco/L1/L1LinkDef.h @@ -31,6 +31,6 @@ //#pragma link C++ class CbmL1SttTrack+; #pragma link C++ class CbmTrackingInputQaSts + ; #pragma link C++ class CbmTrackerInputQaTrd + ; - +#pragma link C++ class CbmTrackerInputQaTof + ; #endif diff --git a/reco/L1/qa/CbmTrackerInputQaTof.cxx b/reco/L1/qa/CbmTrackerInputQaTof.cxx new file mode 100644 index 0000000000000000000000000000000000000000..7679387ac69312345e68f8aa746ddec92fd90ae1 --- /dev/null +++ b/reco/L1/qa/CbmTrackerInputQaTof.cxx @@ -0,0 +1,763 @@ +/* Copyright (C) 2021 Facility for Antiproton and Ion Research in Europe, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergey Gorbunov[committer]*/ + +/// @file CbmTrackerInputQaTof.cxx +/// @author Sergey Gorbunov +/// @date 02.11.2021 + +#include "CbmTrackerInputQaTof.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 "CbmTofCell.h" +#include "CbmTofDigiPar.h" // in tof/TofParam +#include "CbmTofTrackingInterface.h" +//#include "CbmSetup.h" +#include "CbmTimeSlice.h" +//#include "CbmTofCluster.h" +#include "CbmTofDigi.h" +#include "CbmTofHit.h" +//#include "CbmTofParModDigi.h" // for CbmTofModule +//#include "CbmTofParModGeo.h" +//#include "CbmTofParSetDigi.h" // for CbmTofParSetDigi +//#include "CbmTofParSetGeo.h" +#include "CbmTofPoint.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(CbmTrackerInputQaTof); + +// ------------------------------------------------------------------------- + +CbmTrackerInputQaTof::CbmTrackerInputQaTof(const char* name, Int_t verbose) : FairTask(name, verbose) +{ + // Constructor + + // Create a list of histogramms + + fHistList.clear(); + fHistList.reserve(20); + fHistList.push_back(&fhResidualX); + fHistList.push_back(&fhResidualY); + fHistList.push_back(&fhResidualT); + //fHistList.push_back(&fhResidualZ); + fHistList.push_back(&fhPullX); + fHistList.push_back(&fhPullY); + fHistList.push_back(&fhPullT); + + + // Keep the ownership on the histograms to avoid double deletion + for (unsigned int i = 0; i < fHistList.size(); i++) { + fHistList[i]->SetDirectory(0); + } +} + +// ------------------------------------------------------------------------- +CbmTrackerInputQaTof::~CbmTrackerInputQaTof() +{ /// Destructor + DeInit(); +} + + +// ------------------------------------------------------------------------- +void CbmTrackerInputQaTof::DeInit() +{ + fIsTofInSetup = 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 CbmTrackerInputQaTof::SetParContainers() +{ + fTofDigiPar = nullptr; + fTofGeoPar = 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"; } + + // fTofDigiPar = dynamic_cast<CbmTofDigiPar*>(rtdb->getContainer("CbmTofDigiPar")); + // fTofGeoPar = dynamic_cast<CbmTofParSetGeo*>(rtdb->getContainer("CbmTofParSetGeo")); + + fTofDigiPar = (CbmTofDigiPar*) rtdb->getContainer("CbmTofDigiPar"); + // fTofGeoPar = rtdb->getContainer("CbmGeoTofPar"); +} + +// ------------------------------------------------------------------------- +InitStatus CbmTrackerInputQaTof::ReInit() +{ + // Initialize and check the setup + + DeInit(); + + // Check if Tof is present in the CBM setup + // A straightforward way to do so is CbmSetup::Instance()->IsActive(ECbmModuleId::kTof), + // but unfortunatelly CbmSetup class is unaccesible. + // ( CbmSimSteer library requires libfreetype that has linking problems on MacOsX ) + // Therefore let's simply check if any Tof material is present in the geometry. + // + fIsTofInSetup = 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("tof")) { + fIsTofInSetup = 1; + break; + } + } + } + + if (!fIsTofInSetup) { + LOG(info) << "Tof is not in the setup, do nothing"; + return kSUCCESS; + } + + // check parameter containers + + if (!fTofDigiPar) { + LOG(error) << "No CbmTofParSetDigi container in FairRuntimeDb"; + return kERROR; + } + + // if (!fTofGeoPar) { + // LOG(error) << "No CbmTofParSetGeo 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("TofHit")); + + if (!fHits) { + LOG(error) << "No Tof hit array found"; + return kERROR; + } + + // fClusters = dynamic_cast<TClonesArray*>(manager->GetObject("TofCluster")); + // + // if (!fClusters) { + // LOG(error) << "No Tof 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("TofPoint"); + fHitMatches = dynamic_cast<TClonesArray*>(manager->GetObject("TofHitMatch")); + + fDigiManager = CbmDigiManager::Instance(); + fDigiManager->Init(); + + fHitDigiMatches = dynamic_cast<TClonesArray*>(manager->GetObject("TofDigiMatch")); + fDigis = dynamic_cast<TClonesArray*>(manager->GetObject("TofCalDigi")); + if (nullptr == fDigis) { + LOG(info) << "No calibrated tof digi vector in the input files => trying original vector"; + fDigis = dynamic_cast<TClonesArray*>(manager->GetObject("TofDigi")); + if (nullptr == fDigis) { LOG(info) << "No original tof digi vector in the input files! Ignore TOF!"; } + } + fTofCalDigiMatch = dynamic_cast<TClonesArray*>(manager->GetObject("TofCalDigiMatch")); + + + // we assume that when Tof is in the setup and the MC manager is present, + // the Tof 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 Tof hit matches found"; + return kERROR; + } + + // if (!fDigis) { + // LOG(error) << "No Tof digis found"; + // return kERROR; + // } + + if (!fDigiManager->IsMatchPresent(ECbmModuleId::kTof)) { + LOG(error) << "No Tof digi matches found"; + return kERROR; + } + } + + GeometryQa(); + + if (fNtrackingStations <= 0) { + LOG(error) << "can not count Tof 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 CbmTrackerInputQaTof::Exec(Option_t*) +{ + if (!fIsTofInSetup) { return; } + + // update number of processed events + fNevents.SetVal(fNevents.GetVal() + 1); + LOG(debug) << "Event: " << fNevents.GetVal(); + ResolutionQa(); +} + + +// ------------------------------------------------------------------------- +void CbmTrackerInputQaTof::Finish() +{ + FairSink* sink = FairRootManager::Instance()->GetSink(); + if (sink) { sink->WriteObject(&GetQa(), nullptr); } +} + +int CbmTrackerInputQaTof::GetStationIndex(CbmTofPoint* p) +{ + // if (p->GetZ() > 800) return -1; + float dist = 1000; + int iSta = -1; + auto tofInterface = CbmTofTrackingInterface::Instance(); + for (int iSt = 0; iSt < fNtrackingStations; iSt++) { + if (fabs(p->GetZ() - tofInterface->GetZ(iSt)) < dist) { + dist = fabs(p->GetZ() - tofInterface->GetZ(iSt)); + iSta = iSt; + } + } + return iSta; +} + + +// ------------------------------------------------------------------------- +InitStatus CbmTrackerInputQaTof::GeometryQa() +{ + // check geometry of the Tof modules + + auto tofInterface = CbmTofTrackingInterface::Instance(); + + fNtrackingStations = tofInterface->GetNtrackingStations(); + + double lastZ = -1; + for (int iStation = 0; iStation < fNtrackingStations; iStation++) { + + // tofInterface->GetZ(iStation); + // tofInterface->GetTimeResolution(iStation); + // tofInterface->IsTimeInfoProvided(iStation); + + double staZ = tofInterface->GetZ(iStation); + + // check that the stations are properly ordered in Z + if (((iStation > 0) && (staZ <= lastZ)) || ((staZ != staZ))) { + LOG(error) << "Tof trackig stations are not properly ordered in Z"; + return kERROR; + } + lastZ = staZ; + } + + return kSUCCESS; +} + +// ------------------------------------------------------------------------- +void CbmTrackerInputQaTof::ResolutionQa() +{ + if (!fDigiManager->IsMatchPresent(ECbmModuleId::kTof)) return; + + if (!(fHitMatches && fHits)) return; + + // if (!fDigiManager) return; + + Int_t nHits = fHits->GetEntriesFast(); + Int_t nHitMatches = fHitMatches->GetEntriesFast(); + // Int_t nClusters = fClusters->GetEntriesFast(); + //Int_t nDigis = fDigiManager->GetNofDigis(ECbmModuleId::kTof); + // Int_t nTofDigis = fDigis->GetEntriesFast(); + + auto tofInterface = CbmTofTrackingInterface::Instance(); + + fNtrackingStations = tofInterface->GetNtrackingStations(); + + GeometryQa(); + + 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); + } + + std::vector<int> TofSinglePoints[nMcEvents]; // choose only one MC point per plane per MC track + + for (int iE = 0; iE < nMcEvents; iE++) { + + int fileId = fMcEventList->GetFileIdByIndex(iE); + int eventId = fMcEventList->GetEventIdByIndex(iE); + + int nMcTracks = fMcTracks->Size(fileId, eventId); + int nHitPoints = fMcPoints->Size(fileId, eventId); + + std::vector<char> singleMcPointPerStation[fNtrackingStations]; + + for (Int_t iS = 0; iS < fNtrackingStations; iS++) + singleMcPointPerStation[iS].resize(nMcTracks, 0); + + std::vector<char> isTofPointMatched; + isTofPointMatched.resize(nHitPoints, 0); + // check whether point was matched + for (Int_t iHit = 0; iHit < nHits; iHit++) { + CbmMatch* pHitMatch = static_cast<CbmMatch*>(fHitMatches->At(iHit)); + for (int iLink = 0; iLink < pHitMatch->GetNofLinks(); iLink++) { + Int_t iMC = pHitMatch->GetLink(iLink).GetIndex(); + int iEvent = pHitMatch->GetLink(iLink).GetEntry(); + if (eventId != iEvent) continue; + isTofPointMatched[iMC] = 1; + } + } + // store matched points first + for (Int_t iMC = 0; iMC < fMcPoints->Size(fileId, eventId); iMC++) { + if (isTofPointMatched[iMC] == 0) continue; + + CbmTofPoint* p = dynamic_cast<CbmTofPoint*>(fMcPoints->Get(fileId, eventId, iMC)); + + int PointStation = GetStationIndex(p); + + if (PointStation < 0) continue; + + if (!singleMcPointPerStation[PointStation][p->GetTrackID()]) TofSinglePoints[eventId].push_back(iMC); + singleMcPointPerStation[PointStation][p->GetTrackID()] = 1; + } + + // store unmatched points if quota not exceeded (one point per mc track per tof plane) + for (Int_t iMC = 0; iMC < fMcPoints->Size(fileId, eventId); iMC++) { + + CbmTofPoint* p = dynamic_cast<CbmTofPoint*>(fMcPoints->Get(fileId, eventId, iMC)); + + int PointStation = GetStationIndex(p); + + if (PointStation < 0) continue; + + if (!singleMcPointPerStation[PointStation][p->GetTrackID()]) TofSinglePoints[eventId].push_back(iMC); + singleMcPointPerStation[PointStation][p->GetTrackID()] = 1; + } + } + + + if (nHits != nHitMatches) + LOG(fatal) << "CbmTrackerInputQaTof::ResolutionQa => Nb hits in vector not matching nb matches in vector: " << nHits + << " VS " << nHitMatches; + + for (Int_t iHit = 0; iHit < nHits; iHit++) { + + CbmTofHit* hit = dynamic_cast<CbmTofHit*>(fHits->At(iHit)); + if (!hit) { + LOG(error) << "Tof hit N " << iHit << " doesn't exist"; + // return; + } + const int address = hit->GetAddress(); + + if (0x00202806 == address || 0x00002806 == address) continue; //should ignore these hits for some reason + + int StationIndex = tofInterface->GetTrackingStationIndex(hit); + + if (StationIndex < 0 || StationIndex >= fNtrackingStations) { + LOG(fatal) << "Tof hit layer id " << StationIndex << " is out of range"; + // return; + } + + CbmMatch* myHitMatch = dynamic_cast<CbmMatch*>(fHitMatches->At(iHit)); + + // take corresponding MC point + + const CbmLink& bestLink = myHitMatch->GetMatchedLink(); + + // skip hits from the noise digis + if (bestLink.GetIndex() < 0) { continue; } + + CbmTofPoint* p = dynamic_cast<CbmTofPoint*>(fMcPoints->Get(bestLink)); + // if (p == nullptr) { + // LOG(error) << /*ADD ALERT HERE;*/ "link points to a non-existing MC point"; + // // return; + // } + // + // if (p->GetDetectorID() != (int) hit->GetAddress()) { + // LOG(error) << /*ADD ALERT HERE;*/ "mc point module address differs from the hit module address"; + // // return; + // } + + // 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) { + + int iE = fMcEventList->GetEventIndex(link); + int indexPoint = link.GetIndex(); + + bool mcPointValid = 0; + + int nPointsEv = TofSinglePoints[iE].size(); + for (int ip = 0; ip < nPointsEv; ip++) { + if (TofSinglePoints[iE][ip] == indexPoint) mcPointValid = 1; + } + + if (!mcPointValid) continue; + + nHitPoints++; + + + 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); + + // if (p == nullptr) { + // LOG(error) << "link points to a non-existing MC point"; + // return; + // } + // + // if (p->GetDetectorID() != (int) hit->GetAddress()) { + // 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 + // + // const CbmTofParModGeo* pGeo = dynamic_cast<const CbmTofParModGeo*>(fTofGeoPar->GetModulePar(ModuleId)); + // if (!pGeo) { + // LOG(fatal) << " No Geo params for station " << StationIndex << " module " << ModuleId << " found "; + // return; + // } + + // double staZ = tofInterface->GetZ(StationIndex); // module->GetZ(); //+ 410; + // if ((staZ < p->GetZ() - 1.) || (staZ > p->GetZ() + 1.)) { + // LOG(error) << "Tof station " << StationIndex << ": active material Z[" << p->GetZ() << " cm," << p->GetZ() + // << " 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) << "Tof 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 Tof 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->GetPz()) < 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; + + fhResidualZ.Fill(dz); + + // 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 dx = hit->GetX() - x; + double dy = hit->GetY() - y; + double dt = hit->GetTime() - t; + double sx = hit->GetDx(); + double sy = hit->GetDy(); + double st = hit->GetTimeError(); + + // residuals + fhResidualX.Fill(dx); + fhResidualY.Fill(dy); + fhResidualT.Fill(dt); + + // pulls + if ((sx < 1.e-5) || (sy < 1.e-5) || (st < 1.e-5)) { + LOG(error) << "Tof hit errors are not properly set: errX " << hit->GetDx() << " errY " << hit->GetDy() << " errT " + << st; + // return; + } + + fhPullX.Fill(dx / sx); + fhPullY.Fill(dy / sy); + fhPullT.Fill(dt / st); + + + } // iHit // for (Int_t iHit = 0; iHit < nofHits; 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 nPointsEv = TofSinglePoints[eventId].size(); + for (int ip = 0; ip < nPointsEv; ip++) { + + int iPoint = TofSinglePoints[eventId][ip]; + CbmTofPoint* p = dynamic_cast<CbmTofPoint*>(fMcPoints->Get(fileId, eventId, iPoint)); + if (p == nullptr) { + LOG(error) << "MC point doesn't exist"; + return; + } + + int iSt = GetStationIndex(p); + if (iSt < 0) continue; + + int StationIndex = iSt; //CbmTofTrackingInterface::Instance()->GetTrackingStationIndex(p); + if (StationIndex < 0 || StationIndex >= fNtrackingStations) { + LOG(fatal) << "Tof point layer id " << StationIndex << " is out of range, z of point = " << p->GetZ(); + return; + } + fhHitsPerPoint[StationIndex].Fill(pointNhits[iE][iPoint]); + fhEfficiencyR[StationIndex].Fill(sqrt(p->GetX() * p->GetX() + p->GetY() * p->GetY()), + (pointNhits[iE][iPoint] > 0)); + fhEfficiencyXY[StationIndex].Fill(p->GetX(), p->GetY(), (pointNhits[iE][iPoint] > 0)); + } + } +} + + +// ------------------------------------------------------------------------- +TFolder& CbmTrackerInputQaTof::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 < 3; i++) { + fCanvResidual.cd(i + 1); + fHistList[i]->DrawCopy("", ""); + fCanvPull.cd(i + 1); + fHistList[i + 3]->DrawCopy("", ""); + } + + return fOutFolder; +} diff --git a/reco/L1/qa/CbmTrackerInputQaTof.h b/reco/L1/qa/CbmTrackerInputQaTof.h new file mode 100644 index 0000000000000000000000000000000000000000..577584c043931b3ad75ae16b2ca447499d9a193e --- /dev/null +++ b/reco/L1/qa/CbmTrackerInputQaTof.h @@ -0,0 +1,167 @@ +/* Copyright (C) 2021 Facility for Antiproton and Ion Research in Europe, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergey Gorbunov[committer]*/ + +/// @file CbmTrackerInputQaTof.h +/// @author Sergey Gorbunov +/// @date 02.11.2021 + +#ifndef CbmTrackerInputQaTof_H +#define CbmTrackerInputQaTof_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 CbmTofDigiPar; +class CbmTofParSetGeo; +class CbmTofPoint; + +class TClonesArray; + +/// +/// CbmTrackerInputQaTof class represents all the CA tracker requirements for the Tof 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 Tof geometry and interfaces +/// and checks the quality of all tracking-related aspects of the Tof data. +/// +class CbmTrackerInputQaTof : public FairTask { + +public: + /// Constructor + CbmTrackerInputQaTof(const char* name = "TrackerInputQaTof", Int_t verbose = 1); + + /// Destructor + ~CbmTrackerInputQaTof(); + + /// 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(); + + int GetStationIndex(CbmTofPoint* p); + + + // Setup + + Bool_t fIsTofInSetup {false}; + Bool_t fIsMcPresent {false}; + + Int_t fNtrackingStations {0}; + + CbmTimeSlice* fTimeSlice {nullptr}; + CbmTofParSetGeo* fTofGeoPar {nullptr}; + CbmTofDigiPar* fTofDigiPar {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}; + + TClonesArray* fHitDigiMatches {nullptr}; + TClonesArray* fDigis {nullptr}; + TClonesArray* fTofCalDigiMatch {nullptr}; + + /// Output + + + TFolder fOutFolder {"TrackerInputQaTof", "TrackerInputQaTof"}; /// 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> fhResidualX {"hResidualX", "Tof: Residual X;(X_{reco} - X_{MC})(cm)", 100, -10, 10}; + CbmQaHist<TH1D> fhResidualY {"hRresidualY", "Tof: Residual Y;(Y_{reco} - Y_{MC})(cm)", 100, -10, 10}; + CbmQaHist<TH1D> fhResidualT {"hRresidualT", "Tof: Residual T;(T_{reco} - T_{MC})(ns)", 100, -50, 50}; + CbmQaHist<TH1D> fhResidualZ {"hRresidualZ", "Tof: Residual Z;(Z_{reco} - Z_{MC})(cm)", 100, -100, 100}; + + /// Histogram for PULL Distribution + CbmQaHist<TH1D> fhPullX {"hPullX", "Tof: Pull X;(X_{reco} - X_{MC}) / #sigmaU_{reco}", 100, -5, 5}; + CbmQaHist<TH1D> fhPullY {"hPullX", "Tof: Pull Y;(Y_{reco} - Y_{MC}) / #sigmaV_{reco}", 100, -5, 5}; + CbmQaHist<TH1D> fhPullT {"hPullT", "Tof: 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 + CbmTrackerInputQaTof(const CbmTrackerInputQaTof&) = delete; + + /// Suppressed assignment operator + CbmTrackerInputQaTof& operator=(const CbmTrackerInputQaTof&) = delete; + + ClassDef(CbmTrackerInputQaTof, 0); +}; + +#endif