diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt index 5f807e9cbf5b63df71177908170fb289c2a18aea..6e80b666e2d6cb2d0f2b5ad9c39529ba29ca2aa8 100644 --- a/algo/CMakeLists.txt +++ b/algo/CMakeLists.txt @@ -10,6 +10,7 @@ set(SRCS detectors/sts/UnpackSts.cxx detectors/much/MuchReadoutConfig.cxx detectors/much/UnpackMuch.cxx + detectors/tof/HitFinderTof.cxx ) add_library(Algo SHARED ${SRCS}) @@ -24,7 +25,7 @@ target_include_directories(Algo ${CMAKE_CURRENT_SOURCE_DIR}/detectors/tof ) -target_link_libraries(Algo PUBLIC OnlineData INTERFACE FairLogger::FairLogger external::fles_ipc) +target_link_libraries(Algo PUBLIC OnlineData ROOT::GenVector INTERFACE FairLogger::FairLogger external::fles_ipc) target_compile_definitions(Algo PUBLIC NO_ROOT) install(TARGETS Algo DESTINATION lib) diff --git a/algo/detectors/tof/HitFinderTof.cxx b/algo/detectors/tof/HitFinderTof.cxx new file mode 100644 index 0000000000000000000000000000000000000000..dfa2b493710280223d8d2c0dc1dda9d052ad8674 --- /dev/null +++ b/algo/detectors/tof/HitFinderTof.cxx @@ -0,0 +1,186 @@ +/* Copyright (C) 2022 Facility for Antiproton and Ion Research in Europe, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dominik Smith [committer], Pierre-Alain Loizeau */ + +#include "HitFinderTof.h" + +// TOF Classes and includes +#include "CbmTofDigi.h" + +// C++ Classes and includes +#include <iomanip> +#include <iostream> + +namespace cbm::algo +{ + HitFinderTof::resultType HitFinderTof::operator()(std::vector<CbmTofDigi> digisIn, + const std::vector<int32_t>& digiIndexIn) + { + // Intermediate storage variables + std::vector<std::vector<CbmTofDigi*>> digisExp; //[nbCh][nDigis] + std::vector<std::vector<int32_t>> digisInd; //[nbCh][nDigis] + + digisExp.resize(fParams.fChanPar.size()); + digisInd.resize(fParams.fChanPar.size()); + + { // digi calibration + const int32_t numClWalkBinX = fParams.numClWalkBinX; + const double TOTMax = fParams.TOTMax; + const double TOTMin = fParams.TOTMin; + + for (size_t iDigi = 0; iDigi < digisIn.size(); iDigi++) { + CbmTofDigi* pDigi = &digisIn[iDigi]; + assert(pDigi); + + // These are doubles in the digi class + const double chan = pDigi->GetChannel(); + const double side = pDigi->GetSide(); + const double charge = pDigi->GetTot(); + const double time = pDigi->GetTime(); + + digisExp[chan].push_back(pDigi); + digisInd[chan].push_back(digiIndexIn[iDigi]); + + // calibrate Digi Time + pDigi->SetTime(time - fParams.fChanPar[chan].fvCPTOff[side]); + + // calibrate Digi ToT + pDigi->SetTot(charge * fParams.fChanPar[chan].fvCPTotGain[side]); + + { // walk correction + const double totBinSize = (TOTMax - TOTMin) / 2. / numClWalkBinX; + int32_t iWx = (int32_t)((charge - TOTMin / 2.) / totBinSize); + if (0 > iWx) iWx = 0; + if (iWx > (numClWalkBinX - 1)) iWx = numClWalkBinX - 1; + + std::vector<double>& cpWalk = fParams.fChanPar[chan].fvCPWalk[side]; + double dWT = cpWalk[iWx]; + const double dDTot = (charge - TOTMin / 2.) / totBinSize - (double) iWx - 0.5; + + if (dDTot > 0) { + if (iWx < numClWalkBinX - 1) { // linear interpolation to next bin + dWT += dDTot * (cpWalk[iWx + 1] - cpWalk[iWx]); + } + } + else { + if (0 < iWx) { // linear interpolation to next bin + dWT -= dDTot * (cpWalk[iWx - 1] - cpWalk[iWx]); + } + } + pDigi->SetTime(time - dWT); + } + } + } // digi calibration + + // Hit variables + TofCluster cluster; + std::vector<TofCluster> clustersOut; + + // Reference cell of a cluster + TofCell* trafoCell = nullptr; + int32_t iTrafoCell = -1; + + // Last Channel Temp variables + int32_t lastChan = -1; + double lastPosY = 0.0; + double lastTime = 0.0; + + //reset counter + numSameSide = 0; + + cluster.reset(); // Don't spread clusters over RPCs + + for (int32_t chan = 0; chan < fParams.fChanPar.size(); chan++) { + + std::vector<CbmTofDigi*>& storDigiExp = digisExp[chan]; + std::vector<int32_t>& storDigiInd = digisInd[chan]; + HitFinderTofChanPar& chanPar = fParams.fChanPar[chan]; + + while (1 < storDigiExp.size()) { + while ((storDigiExp[0])->GetSide() == (storDigiExp[1])->GetSide()) { + // Not one Digi of each end! + numSameSide++; + storDigiExp.erase(storDigiExp.begin()); + storDigiInd.erase(storDigiInd.begin()); + if (2 > storDigiExp.size()) break; + } + if (2 > storDigiExp.size()) break; // 2 Digis = both sides present + + TofCell* channelInfo = &chanPar.cell; + CbmTofDigi* xDigiA = storDigiExp[0]; + CbmTofDigi* xDigiB = storDigiExp[1]; + + // The "Strip" time is the mean time between each end + const double time = 0.5 * (xDigiA->GetTime() + xDigiB->GetTime()); + + // Weight is the total charge => sum of both ends ToT + const double totSum = xDigiA->GetTot() + xDigiB->GetTot(); + + if (nullptr == trafoCell) { + trafoCell = channelInfo; + iTrafoCell = chan; + } + + // switch to local coordinates, (0,0,0) is in the center of counter ? + // It is assumed that the cell size is the same for all channels within one rpc! + ROOT::Math::XYZVector pos(((double) (-iTrafoCell + chan)) * trafoCell->sizeX, 0., 0.); + + if (1 == xDigiA->GetSide()) { + // 0 is the top side, 1 is the bottom side + pos.SetY(fParams.CPSigPropSpeed * (xDigiA->GetTime() - xDigiB->GetTime()) / 2.0); + } + else { + // 0 is the bottom side, 1 is the top side + pos.SetY(fParams.CPSigPropSpeed * (xDigiB->GetTime() - xDigiA->GetTime()) / 2.0); + } + + if (channelInfo->sizeY / 2.0 < pos.Y() || -1 * channelInfo->sizeY / 2.0 > pos.Y()) { + // if outside of strip limits, the pair is bad => try to remove one end and check the next pair + // (if another possibility exist) + storDigiExp.erase(storDigiExp.begin()); + storDigiInd.erase(storDigiInd.begin()); + continue; + } // Pair leads to hit oustide of strip limits + + // Now check if a hit/cluster is already started + if (0 < cluster.numChan()) { + // a cluster is already started => check distance in space/time + // For simplicity, just check along strip direction for now + // and break cluster when a not fired strip is found + if (!(std::abs(time - lastTime) < fParams.maxTimeDist && lastChan == chan - 1 + && std::abs(pos.Y() - lastPosY) < fParams.maxSpaceDist)) { + // simple error scaling with TOT + // weightedTimeErr = TMath::Sqrt( weightedTimeErr ) * SysTimeRes / weightsSum; + // OR harcoded value: weightedTimeErr = SysTimeRes; + cluster.normalize(fParams.SysTimeRes); + + // Save Hit and start a new one + cluster.finalize(*trafoCell, iTrafoCell, fParams); + clustersOut.push_back(cluster); + cluster.reset(); + } + } + cluster.add(pos, time, totSum, totSum, storDigiInd[0], storDigiInd[1]); + storDigiExp.erase(storDigiExp.begin()); + storDigiExp.erase(storDigiExp.begin()); + storDigiInd.erase(storDigiInd.begin()); + storDigiInd.erase(storDigiInd.begin()); + + lastChan = chan; + lastPosY = pos.Y(); + lastTime = time; + } // while (1 < storDigiExp.size()) + storDigiExp.clear(); + storDigiInd.clear(); + } // for( int32_t chan = 0; chan < iNbCh; chan++ ) + + // Save last cluster if it exists + if (0 < cluster.numChan()) { + cluster.normalize(fParams.SysTimeRes); + cluster.finalize(*trafoCell, iTrafoCell, fParams); + clustersOut.push_back(cluster); + } + + return clustersOut; + } +} /* namespace cbm::algo */ diff --git a/algo/detectors/tof/HitFinderTof.h b/algo/detectors/tof/HitFinderTof.h new file mode 100644 index 0000000000000000000000000000000000000000..cb21482196601d10006cd63a76fdf36ac38024bc --- /dev/null +++ b/algo/detectors/tof/HitFinderTof.h @@ -0,0 +1,164 @@ +/* Copyright (C) 2022 Facility for Antiproton and Ion Research in Europe, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dominik Smith [committer], Pierre-Alain Loizeau */ + +#ifndef HITFINDERTOF_H +#define HITFINDERTOF_H + +// TOF Classes and includes +class CbmTofDigi; + +// ROOT Classes and includes +#include "Math/Rotation3D.h" +#include "Math/Vector3Dfwd.h" + +// C++ Classes and includes +#include <vector> + +#include <cmath> + +namespace cbm::algo +{ + struct TofCell { + double sizeX, sizeY; + ROOT::Math::XYZVector pos; + ROOT::Math::Rotation3D rotation; + }; + + struct HitFinderTofChanPar { + std::vector<double> fvCPTOff; //[nbSide] + std::vector<double> fvCPTotGain; //[nbSide] + std::vector<std::vector<double>> fvCPWalk; //[nbSide][nbWalkBins] + int32_t address; //unique address + TofCell cell; + }; + + struct HitFinderTofRpcPar { + double FeeTimeRes; + double SysTimeRes; + double CPSigPropSpeed; + double outTimeFactor; + double TOTMax; + double TOTMin; + double maxTimeDist; + double maxSpaceDist; + double gapSize; + int32_t numGaps; + int32_t numClWalkBinX; + std::vector<HitFinderTofChanPar> fChanPar = {}; + }; + + struct TofCluster { + //temporary values + std::vector<int32_t> vDigiIndRef; + ROOT::Math::XYZVector weightedPos = ROOT::Math::XYZVector(0.0, 0.0, 0.0); + double weightedTime = 0.0; + double weightedTimeErr = 0.0; + double weightsSum = 0.0; + + //after finalization + ROOT::Math::XYZVector globalPos = ROOT::Math::XYZVector(0.0, 0.0, 0.0); + ROOT::Math::XYZVector globalErr = ROOT::Math::XYZVector(0.0, 0.0, 0.0); + int32_t channel = 0; + int32_t detId = 0; + + int32_t numChan() { return vDigiIndRef.size() / 2; } + + void reset() + { + vDigiIndRef.clear(); + weightedPos = ROOT::Math::XYZVector(0.0, 0.0, 0.0); + globalPos = ROOT::Math::XYZVector(0.0, 0.0, 0.0); + globalErr = ROOT::Math::XYZVector(0.0, 0.0, 0.0); + weightedTime = 0.0; + weightedTimeErr = 0.0; + weightsSum = 0.0; + channel = 0; + detId = 0; + } + + void add(ROOT::Math::XYZVector pos, double time, double timeErr, double weight, int32_t digiIndA, int32_t digiIndB) + { + vDigiIndRef.push_back(digiIndA); + vDigiIndRef.push_back(digiIndB); + weightedPos += pos * weight; + weightedTime += time * weight; + weightedTimeErr += timeErr * weight; + weightsSum += weight; + } + + void normalize(double timeErr) + { + // a/=b is translated to a := a*(1/b) in the ROOT::Math::XYZVector class, which has a different + // rounding behavior than a := (a/b). In rare cases this leads to 1.000... becoming 0.999... inside + // the floor() operation in the finalize() function of this class, and results in a different + // channel being associated with the cluster. To reproduce the output of the old hit finder, we + // divide element-wise instead. Perhaps floor() should be replaced by round(). + //////// weightedPos /= weightsSum; + + weightedPos.SetXYZ(weightedPos.X() / weightsSum, weightedPos.Y() / weightsSum, weightedPos.Z() / weightsSum); + weightedTime /= weightsSum; + weightedTimeErr = timeErr; + } + + void finalize(const TofCell& trafoCell, const int32_t iTrafoCell, const HitFinderTofRpcPar& par) + { + // prepare local->global trafo + ROOT::Math::Rotation3D rotMatrix = trafoCell.rotation; + + // get offset from weighted cluster position by rotation to master frame + ROOT::Math::XYZVector hitposOffset = rotMatrix(weightedPos); + + //get hit position by adding offset to cell coordinates + globalPos = trafoCell.pos + hitposOffset; + + // Simple errors, not properly done at all for now + // Right way of doing it should take into account the weight distribution + // and real system time resolution + ROOT::Math::XYZVector hitErrLocal; + hitErrLocal.SetX(trafoCell.sizeX / std::sqrt(12.0)); // Single strips approximation + hitErrLocal.SetY(par.FeeTimeRes * par.CPSigPropSpeed); // Use the electronics resolution + hitErrLocal.SetZ(par.numGaps * par.gapSize / 10.0 // Change gap size in cm + / std::sqrt(12.0)); // Use full RPC thickness as "Channel" Z size + + //store results + globalErr = rotMatrix(hitErrLocal); + channel = iTrafoCell + floor(weightedPos.X() / trafoCell.sizeX); + detId = par.fChanPar[channel].address; + weightedTime *= par.outTimeFactor; + } + }; + + class HitFinderTof { + public: + typedef std::vector<TofCluster> resultType; + + /** + ** @brief Constructor. + **/ + HitFinderTof() {}; + + /** + ** @brief Destructor. + **/ + ~HitFinderTof() {}; + + /** + ** @brief Build clusters out of ToF Digis and store the resulting info in a TofHit. + **/ + resultType operator()(std::vector<CbmTofDigi> digisIn, const std::vector<int32_t>& digiIndexIn); + + /** @brief Set the parameter container + ** @param params Vectorer to parameter container + **/ + void SetParams(std::unique_ptr<HitFinderTofRpcPar> params) { fParams = *(std::move(params)); } + + private: + HitFinderTofRpcPar fParams = {}; ///< Parameter container + + int32_t numSameSide; // Digis quality + }; + +} /* namespace cbm::algo */ + +#endif // HITFINDERTOF_H diff --git a/reco/tasks/CMakeLists.txt b/reco/tasks/CMakeLists.txt index 49ea82ea7382560ac1a17e08a032511a1bc2a6ab..6c7302a21cc694f3fe6713dff4e69a2af41d8f03 100644 --- a/reco/tasks/CMakeLists.txt +++ b/reco/tasks/CMakeLists.txt @@ -3,7 +3,6 @@ set(INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_SOURCE_DIR}/algo/evbuild ) set(SRCS @@ -14,6 +13,7 @@ set(SRCS CbmTaskEventsCloneInToOut.cxx CbmTaskMakeRecoEvents.cxx CbmTaskTriggerDigi.cxx + CbmTaskTofHitFinder.cxx CbmTaskUnpack.cxx ) @@ -27,6 +27,7 @@ set(LIBRARY_NAME CbmRecoTasks) set(LINKDEF ${LIBRARY_NAME}LinkDef.h) set(PUBLIC_DEPENDENCIES CbmData + CbmTofBase FairRoot::Base Algo ROOT::Core diff --git a/reco/tasks/CbmRecoTasksLinkDef.h b/reco/tasks/CbmRecoTasksLinkDef.h index 32def2aa29305cd57e458a56ab5b3526ac2d3850..980fb920edc0e77f102f7470cd8bbe2ff346d3d9 100644 --- a/reco/tasks/CbmRecoTasksLinkDef.h +++ b/reco/tasks/CbmRecoTasksLinkDef.h @@ -18,6 +18,7 @@ #pragma link C++ class CbmTaskDigiEventQa + ; #pragma link C++ class CbmTaskEventsCloneInToOut + ; #pragma link C++ class CbmTaskMakeRecoEvents + ; +#pragma link C++ class CbmTaskTofHitFinder + ; #pragma link C++ class CbmTaskTriggerDigi + ; #pragma link C++ class CbmTaskUnpack + ; diff --git a/reco/tasks/CbmTaskTofHitFinder.cxx b/reco/tasks/CbmTaskTofHitFinder.cxx new file mode 100644 index 0000000000000000000000000000000000000000..079c9ae224fe2a3e77738324f3f857bf796e5906 --- /dev/null +++ b/reco/tasks/CbmTaskTofHitFinder.cxx @@ -0,0 +1,472 @@ +/* Copyright (C) 2022 Facility for Antiproton and Ion Research in Europe, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dominik Smith [committer], Pierre-Alain Loizeau, Norbert Herrmann */ + +#include "CbmTaskTofHitFinder.h" + +// TOF Classes and includes +#include "CbmTofAddress.h" // in cbmdata/tof +#include "CbmTofCell.h" // in tof/TofData +#include "CbmTofCreateDigiPar.h" +#include "CbmTofDetectorId_v14a.h" // in cbmdata/tof +#include "CbmTofDetectorId_v21a.h" // in cbmdata/tof +#include "CbmTofDigi.h" // in cbmdata/tof +#include "CbmTofDigiBdfPar.h" // in tof/TofParam +#include "CbmTofDigiPar.h" // in tof/TofParam +#include "CbmTofGeoHandler.h" // in tof/TofTools +#include "CbmTofHit.h" // in cbmdata/tof + +// CBMroot classes and includes +#include "CbmDigiManager.h" +#include "CbmEvent.h" +#include "CbmMatch.h" + +// FAIR classes and includes +#include "FairEventHeader.h" // from CbmStsDigitize, for GetEventInfo +#include "FairMCEventHeader.h" // from CbmStsDigitize, for GetEventInfo +#include "FairRootManager.h" +#include "FairRunAna.h" +#include "FairRunSim.h" // from CbmStsDigitize, for GetEventInfo +#include "FairRuntimeDb.h" +#include <Logger.h> + +// ROOT Classes and includes +#include "TClonesArray.h" + +// C++ Classes and includes +#include <iomanip> +#include <iostream> + +using std::fixed; +using std::left; +using std::pair; +using std::right; +using std::setprecision; +using std::setw; +using std::stringstream; + +using cbm::algo::HitFinderTofRpcPar; +using cbm::algo::TofCell; +using cbm::algo::TofCluster; + +const int32_t numClWalkBinX = 20; +const double TTotMean = 2.E4; + +/************************************************************************************/ +CbmTaskTofHitFinder::CbmTaskTofHitFinder() : FairTask("CbmTaskTofHitFinder"), fGeoHandler(new CbmTofGeoHandler()) {} + +CbmTaskTofHitFinder::CbmTaskTofHitFinder(const char* name, int32_t verbose) + : FairTask(TString(name), verbose) + , fGeoHandler(new CbmTofGeoHandler()) +{ +} + +CbmTaskTofHitFinder::~CbmTaskTofHitFinder() +{ + if (fGeoHandler) delete fGeoHandler; +} + +InitStatus CbmTaskTofHitFinder::Init() +{ + fDigiMan = CbmDigiManager::Instance(), fDigiMan->Init(); + if (false == RegisterInputs()) return kFATAL; + if (false == RegisterOutputs()) return kFATAL; + if (false == InitParameters()) return kFATAL; + if (false == InitCalibParameter()) return kFATAL; + if (false == InitAlgos()) return kFATAL; + return kSUCCESS; +} + +void CbmTaskTofHitFinder::SetParContainers() +{ + LOG(info) << " CbmTaskTofHitFinder => Get the digi parameters for tof"; + + // Get Base Container + FairRunAna* ana = FairRunAna::Instance(); + FairRuntimeDb* rtdb = ana->GetRuntimeDb(); + + fDigiPar = (CbmTofDigiPar*) (rtdb->getContainer("CbmTofDigiPar")); + LOG(info) << " CbmTaskTofHitFinder::SetParContainers found " << fDigiPar->GetNrOfModules() << " cells "; + + fDigiBdfPar = (CbmTofDigiBdfPar*) (rtdb->getContainer("CbmTofDigiBdfPar")); +} + +void CbmTaskTofHitFinder::Exec(Option_t* /*option*/) +{ + // Start timer counter + fTimer.Start(); + + fTofHitsColl->Clear("C"); + fTofDigiMatchColl->Delete(); + + fStart.Set(); + + // --- Local variables + int32_t nDigisAll = CbmDigiManager::GetNofDigis(ECbmModuleId::kTof); + int32_t nEvents = 0; + int32_t nDigis = 0; + int32_t nHits = 0; + CbmEvent* event = nullptr; + pair<int32_t, int32_t> result; + + // --- Time-slice mode: process entire digi array + if (!fEvents) { + result = BuildClusters(nullptr); + nDigis = result.first; + nHits = result.second; + } + + // --- Event-based mode: read and process event after event + else { + nEvents = fEvents->GetEntriesFast(); + for (int32_t iEvent = 0; iEvent < nEvents; iEvent++) { + event = dynamic_cast<CbmEvent*>(fEvents->At(iEvent)); + assert(event); + result = BuildClusters(event); + nDigis += result.first; + nHits += result.second; + } //# events + } //? event mode + + fStop.Set(); + fTimer.Stop(); + + // --- Timeslice log + stringstream logOut; + logOut << setw(20) << left << GetName() << " ["; + logOut << fixed << setw(8) << setprecision(1) << right << fTimer.RealTime() * 1000. << " ms] "; + logOut << "TS " << fiNofTs; + if (fEvents) logOut << ", events " << nEvents; + logOut << ", digis " << nDigis << " / " << nDigisAll; + logOut << ", hits " << nHits; + LOG(info) << logOut.str(); + + // --- Update Counters + fiNofTs++; + fiNofEvents += nEvents; + fNofDigisAll += nDigisAll; + fNofDigisUsed += nDigis; + fdNofHitsTot += nHits; + fdTimeTot += fTimer.RealTime(); +} + +void CbmTaskTofHitFinder::Finish() +{ + // Screen log + std::cout << std::endl; + LOG(info) << "====================================="; + LOG(info) << GetName() << ": Run summary"; + LOG(info) << "Time slices : " << fiNofTs; + LOG(info) << "Digis / TS : " << fixed << setprecision(2) << fNofDigisAll / double(fiNofTs); + LOG(info) << "Hits / TS : " << fixed << setprecision(2) << fdNofHitsTot / double(fiNofTs); + LOG(info) << "Time / TS : " << fixed << setprecision(2) << 1000. * fdTimeTot / double(fiNofTs) << " ms"; + if (fEvents) { + double unusedFrac = 100. * (1. - fNofDigisUsed / fNofDigisAll); + LOG(info) << "Digis outside events : " << fNofDigisAll - fNofDigisUsed << " = " << unusedFrac << " %"; + LOG(info) << "Events : " << fiNofEvents; + LOG(info) << "Events / TS : " << fixed << setprecision(2) << double(fiNofEvents) / double(fiNofTs); + LOG(info) << "Digis / event : " << fixed << setprecision(2) << fNofDigisUsed / double(fiNofEvents); + LOG(info) << "Hits / event : " << fixed << setprecision(2) << fdNofHitsTot / double(fiNofEvents); + } + LOG(info) << "=====================================\n"; +} + +/************************************************************************************/ +// Functions common for all clusters approximations +bool CbmTaskTofHitFinder::RegisterInputs() +{ + FairRootManager* fManager = FairRootManager::Instance(); + + // --- Check input branch (TofDigiExp). If not present, set task inactive. + if (!fDigiMan->IsPresent(ECbmModuleId::kTof)) { + LOG(error) << GetName() << ": No TofDigi input array present; " + << "task will be inactive."; + return kERROR; + } + + // --- Look for event branch + fEvents = dynamic_cast<TClonesArray*>(fManager->GetObject("Event")); + if (fEvents) LOG(info) << GetName() << ": Found Event branch; run event-based"; + else { + fEvents = dynamic_cast<TClonesArray*>(fManager->GetObject("CbmEvent")); + if (fEvents) LOG(info) << GetName() << ": Found CbmEvent branch; run event-based"; + else + LOG(info) << GetName() << ": No event branch found; run time-based"; + } + return true; +} + +bool CbmTaskTofHitFinder::RegisterOutputs() +{ + FairRootManager* rootMgr = FairRootManager::Instance(); + fTofHitsColl = new TClonesArray("CbmTofHit"); + + // Flag check to control whether digis are written in output root file + rootMgr->Register("TofHit", "Tof", fTofHitsColl, IsOutputBranchPersistent("TofHit")); + + fTofDigiMatchColl = new TClonesArray("CbmMatch", 100); + rootMgr->Register("TofHitDigiMatch", "Tof", fTofDigiMatchColl, IsOutputBranchPersistent("TofHitDigiMatch")); + + return true; +} + +bool CbmTaskTofHitFinder::InitParameters() +{ + if (fDigiBdfPar->UseExpandedDigi() == false) { + LOG(fatal) << " Compressed Digis not implemented ... "; + return false; + } + + // Initialize the TOF GeoHandler + bool isSimulation = false; + int32_t iGeoVersion = fGeoHandler->Init(isSimulation); + LOG(info) << "CbmTaskTofHitFinder::InitParameters with GeoVersion " << iGeoVersion; + + if (k14a > iGeoVersion) { + LOG(error) << "CbmTaskTofHitFinder::InitParameters => Only compatible " + "with geometries after v12b !!!"; + return false; + } + + if (nullptr != fTofId) + LOG(info) << "CbmTaskTofHitFinder::InitParameters with GeoVersion " << fGeoHandler->GetGeoVersion(); + else { + switch (iGeoVersion) { + case k14a: fTofId = new CbmTofDetectorId_v14a(); break; + case k21a: fTofId = new CbmTofDetectorId_v21a(); break; + default: LOG(error) << "CbmTaskTofHitFinder::InitParameters => Invalid geometry!!!" << iGeoVersion; return false; + } + } + + LOG(info) << "=> Get the digi parameters for tof"; + FairRunAna* ana = FairRunAna::Instance(); + FairRuntimeDb* rtdb = ana->GetRuntimeDb(); + + // create digitization parameters from geometry file + CbmTofCreateDigiPar* tofDigiPar = new CbmTofCreateDigiPar("TOF Digi Producer", "TOF task"); + LOG(info) << "Create DigiPar "; + tofDigiPar->Init(); + + fDigiPar = (CbmTofDigiPar*) (rtdb->getContainer("CbmTofDigiPar")); + if (0 == fDigiPar) { + LOG(error) << "CbmTofSimpleClusterizer::InitParameters => Could not obtain " + "the CbmTofDigiPar "; + return false; + } + return true; +} + +/************************************************************************************/ +bool CbmTaskTofHitFinder::InitCalibParameter() +{ + // dimension and initialize calib parameter + int32_t iNbSmTypes = fDigiBdfPar->GetNbSmTypes(); + + fvCPSigPropSpeed.resize(iNbSmTypes); + for (int32_t iT = 0; iT < iNbSmTypes; iT++) { + int32_t iNbRpc = fDigiBdfPar->GetNbRpc(iT); + fvCPSigPropSpeed[iT].resize(iNbRpc); + for (int32_t iRpc = 0; iRpc < iNbRpc; iRpc++) + if (0.0 < fDigiBdfPar->GetSigVel(iT, 0, iRpc)) fvCPSigPropSpeed[iT][iRpc] = fDigiBdfPar->GetSigVel(iT, 0, iRpc); + else + fvCPSigPropSpeed[iT][iRpc] = fDigiBdfPar->GetSignalSpeed(); + } + + // Other calibration constants can be set here. Currently set to default values + fvCPTOff.resize(iNbSmTypes); + fvCPTotGain.resize(iNbSmTypes); + fvCPWalk.resize(iNbSmTypes); + for (int32_t iSmType = 0; iSmType < iNbSmTypes; iSmType++) { + int32_t iNbSm = fDigiBdfPar->GetNbSm(iSmType); + int32_t iNbRpc = fDigiBdfPar->GetNbRpc(iSmType); + fvCPTOff[iSmType].resize(iNbSm * iNbRpc); + fvCPTotGain[iSmType].resize(iNbSm * iNbRpc); + fvCPWalk[iSmType].resize(iNbSm * iNbRpc); + for (int32_t iSm = 0; iSm < iNbSm; iSm++) { + for (int32_t iRpc = 0; iRpc < iNbRpc; iRpc++) { + int32_t iNbChan = fDigiBdfPar->GetNbChan(iSmType, iRpc); + fvCPTOff[iSmType][iSm * iNbRpc + iRpc].resize(iNbChan); + fvCPTotGain[iSmType][iSm * iNbRpc + iRpc].resize(iNbChan); + fvCPWalk[iSmType][iSm * iNbRpc + iRpc].resize(iNbChan); + int32_t nbSide = 2 - fDigiBdfPar->GetChanType(iSmType, iRpc); + for (int32_t iCh = 0; iCh < iNbChan; iCh++) { + fvCPTOff[iSmType][iSm * iNbRpc + iRpc][iCh].resize(nbSide); + fvCPTotGain[iSmType][iSm * iNbRpc + iRpc][iCh].resize(nbSide); + fvCPWalk[iSmType][iSm * iNbRpc + iRpc][iCh].resize(nbSide); + for (int32_t iSide = 0; iSide < nbSide; iSide++) { + fvCPTOff[iSmType][iSm * iNbRpc + iRpc][iCh][iSide] = 0.; + fvCPTotGain[iSmType][iSm * iNbRpc + iRpc][iCh][iSide] = 1.; + fvCPWalk[iSmType][iSm * iNbRpc + iRpc][iCh][iSide].resize(numClWalkBinX); + for (int32_t iWx = 0; iWx < numClWalkBinX; iWx++) { + fvCPWalk[iSmType][iSm * iNbRpc + iRpc][iCh][iSide][iWx] = 0.; + } + } + } + } + } + } + LOG(info) << "CbmTaskTofHitFinder::InitCalibParameter: defaults set"; + LOG(info) << "CbmTaskTofHitFinder::InitCalibParameter: initialization done"; + return true; +} + +bool CbmTaskTofHitFinder::InitAlgos() +{ + /// Go to Top volume of the geometry in the GeoManager to make sure our nodes are found + gGeoManager->CdTop(); + + //Prepare storage vectors + int32_t iNbSmTypes = fDigiBdfPar->GetNbSmTypes(); + fStorDigiExp.resize(iNbSmTypes); + fStorDigiInd.resize(iNbSmTypes); + + for (int32_t iSmType = 0; iSmType < iNbSmTypes; iSmType++) { + int32_t iNbSm = fDigiBdfPar->GetNbSm(iSmType); + int32_t iNbRpc = fDigiBdfPar->GetNbRpc(iSmType); + fStorDigiExp[iSmType].resize(iNbSm * iNbRpc); + fStorDigiInd[iSmType].resize(iNbSm * iNbRpc); + } + + // Create one algorithm per RPC and configure it with parameters + for (int32_t iSmType = 0; iSmType < iNbSmTypes; iSmType++) { + int32_t iNbSm = fDigiBdfPar->GetNbSm(iSmType); + int32_t iNbRpc = fDigiBdfPar->GetNbRpc(iSmType); + for (int32_t iSm = 0; iSm < iNbSm; iSm++) { + for (int32_t iRpc = 0; iRpc < iNbRpc; iRpc++) { + std::unique_ptr<HitFinderTofRpcPar> par(new HitFinderTofRpcPar()); + par->FeeTimeRes = fDigiBdfPar->GetFeeTimeRes(); + par->SysTimeRes = 0.080; + par->CPSigPropSpeed = fvCPSigPropSpeed[iSmType][iRpc]; + par->outTimeFactor = 1.0; + par->numClWalkBinX = numClWalkBinX; + par->TOTMax = 5.E4; + par->TOTMin = 2.E4; + par->maxTimeDist = fDigiBdfPar->GetMaxTimeDist(); + par->maxSpaceDist = fDigiBdfPar->GetMaxDistAlongCh(); + par->numGaps = fDigiBdfPar->GetNbGaps(iSmType, iRpc); + par->gapSize = fDigiBdfPar->GetGapSize(iSmType, iRpc); + int32_t iNbChan = fDigiBdfPar->GetNbChan(iSmType, iRpc); + par->fChanPar.resize(iNbChan); + for (int32_t iCh = 0; iCh < iNbChan; iCh++) { + + //get channel info from iSmType, iSm, iRpc, iCh + CbmTofDetectorInfo xDetInfo(ECbmModuleId::kTof, iSmType, iSm, iRpc, 0, iCh); + const int32_t iChId = fTofId->SetDetectorInfo(xDetInfo); + CbmTofCell* channelInfo = fDigiPar->GetCell(iChId); + + //init Tof cell + TofCell& cell = par->fChanPar[iCh].cell; + cell.pos.SetX(channelInfo->GetX()); + cell.pos.SetY(channelInfo->GetY()); + cell.pos.SetZ(channelInfo->GetZ()); + cell.sizeX = channelInfo->GetSizex(); + cell.sizeY = channelInfo->GetSizey(); + + // prepare local->global trafo + gGeoManager->FindNode(channelInfo->GetX(), channelInfo->GetY(), channelInfo->GetZ()); + double* rot_ptr = gGeoManager->GetCurrentMatrix()->GetRotationMatrix(); + cell.rotation = ROOT::Math::Rotation3D(&rot_ptr[0], &rot_ptr[9]); + + par->fChanPar[iCh].fvCPTOff = std::move(fvCPTOff[iSmType][iSm * iNbRpc + iRpc][iCh]); + par->fChanPar[iCh].fvCPTotGain = std::move(fvCPTotGain[iSmType][iSm * iNbRpc + iRpc][iCh]); + par->fChanPar[iCh].fvCPWalk = std::move(fvCPWalk[iSmType][iSm * iNbRpc + iRpc][iCh]); + par->fChanPar[iCh].address = CbmTofAddress::GetUniqueAddress(iSm, iRpc, iCh, 0, iSmType); + } + fAlgo[iSmType][iSm * iNbRpc + iRpc].SetParams(std::move(par)); + } + } + } + return true; +} + +pair<int32_t, int32_t> CbmTaskTofHitFinder::BuildClusters(CbmEvent* event) +{ + // --- MC Event info (input file, entry number, start time) + int32_t iInputNr = 0; + int32_t iEventNr = 0; + double dEventTime = 0.; + GetEventInfo(iInputNr, iEventNr, dEventTime); + + // Local variables + int32_t nDigis = 0; + int32_t nHits = 0; + + nDigis = (event ? event->GetNofData(ECbmDataType::kTofDigi) : fDigiMan->GetNofDigis(ECbmModuleId::kTof)); + LOG(debug) << "Number of TOF digis: " << nDigis; + + // Loop over the digis array and store the Digis in separate vectors for each RPC modules + for (int32_t iDigi = 0; iDigi < nDigis; iDigi++) { + const uint32_t digiIndex = (event ? event->GetIndex(ECbmDataType::kTofDigi, iDigi) : iDigi); + assert(fDigiMan->Get<CbmTofDigi>(digiIndex)); + CbmTofDigi* pDigi = new CbmTofDigi(*(fDigiMan->Get<CbmTofDigi>(digiIndex))); + assert(pDigi); + + // These are doubles in the digi class + const double smType = pDigi->GetType(); + const double smId = pDigi->GetSm(); + const double rpcId = pDigi->GetRpc(); + const int32_t numRpc = fDigiBdfPar->GetNbRpc(smType); + fStorDigiExp[smType][smId * numRpc + rpcId].push_back(*pDigi); + fStorDigiInd[smType][smId * numRpc + rpcId].push_back(digiIndex); + } + + const uint32_t iNbSmTypes = fDigiBdfPar->GetNbSmTypes(); + + for (uint32_t iSmType = 0; iSmType < iNbSmTypes; iSmType++) { + const uint32_t iNbSm = fDigiBdfPar->GetNbSm(iSmType); + const uint32_t iNbRpc = fDigiBdfPar->GetNbRpc(iSmType); + for (uint32_t iSm = 0; iSm < iNbSm; iSm++) { + for (uint32_t iRpc = 0; iRpc < iNbRpc; iRpc++) { + //read digis + const uint32_t rpcIdx = iSm * iNbRpc + iRpc; + std::vector<CbmTofDigi>& digiExp = fStorDigiExp[iSmType][rpcIdx]; + std::vector<int32_t>& digiInd = fStorDigiInd[iSmType][rpcIdx]; + + //call cluster finder + std::vector<TofCluster> clusters = fAlgo[iSmType][rpcIdx](digiExp, digiInd); + + //Store hits and match + for (auto const& cluster : clusters) { + const int32_t hitIndex = fTofHitsColl->GetEntriesFast(); + TVector3 hitpos = TVector3(cluster.globalPos.X(), cluster.globalPos.Y(), cluster.globalPos.Z()); + TVector3 hiterr = TVector3(cluster.globalErr.X(), cluster.globalErr.Y(), cluster.globalErr.Z()); + new ((*fTofHitsColl)[hitIndex]) + CbmTofHit(cluster.detId, hitpos, hiterr, nHits, cluster.weightedTime, cluster.weightedTimeErr, 0, 0); + nHits++; + if (event) event->AddData(ECbmDataType::kTofHit, hitIndex); + CbmMatch* digiMatch = new CbmMatch(); + for (uint32_t i = 0; i < cluster.vDigiIndRef.size(); i++) { + digiMatch->AddLink(CbmLink(0., cluster.vDigiIndRef.at(i), iEventNr, iInputNr)); + } + new ((*fTofDigiMatchColl)[hitIndex]) CbmMatch(*digiMatch); + delete digiMatch; + } + } + } + } + + return std::make_pair(nDigis, nHits); +} + +void CbmTaskTofHitFinder::GetEventInfo(int32_t& inputNr, int32_t& eventNr, double& eventTime) +{ + // --- In a FairRunAna, take the information from FairEventHeader + if (FairRunAna::Instance()) { + FairEventHeader* event = FairRunAna::Instance()->GetEventHeader(); + inputNr = event->GetInputFileId(); + eventNr = event->GetMCEntryNumber(); + eventTime = event->GetEventTime(); + } + + // --- In a FairRunSim, the input number and event time are always zero; + // --- only the event number is retrieved. + else { + if (!FairRunSim::Instance()) LOG(fatal) << GetName() << ": neither SIM nor ANA run."; + FairMCEventHeader* event = FairRunSim::Instance()->GetMCEventHeader(); + inputNr = 0; + eventNr = event->GetEventID(); + eventTime = 0.; + } +} + +ClassImp(CbmTaskTofHitFinder) diff --git a/reco/tasks/CbmTaskTofHitFinder.h b/reco/tasks/CbmTaskTofHitFinder.h new file mode 100644 index 0000000000000000000000000000000000000000..e192587d494f351552416af7527d772ec7b55f94 --- /dev/null +++ b/reco/tasks/CbmTaskTofHitFinder.h @@ -0,0 +1,163 @@ +/* Copyright (C) 2022 Facility for Antiproton and Ion Research in Europe, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dominik Smith [committer], Pierre-Alain Loizeau, Norbert Herrmann */ + +/** @class CbmTaskTofHitFinder + ** @brief Simple Cluster building and hit producing for CBM ToF using Digis as input + ** @author Pierre-Alain Loizeau <loizeau@physi.uni-heidelberg.de> + ** @version 1.0 + **/ +#ifndef CBMTOFHITFINDER_H +#define CBMTOFHITFINDER_H 1 + +// TOF Classes and includes +// Input/Output +class CbmTofDigi; +class CbmMatch; +// Geometry +class CbmTofGeoHandler; +class CbmTofDetectorId; +class CbmTofDigiPar; +class CbmTofDigiBdfPar; +class CbmTofCell; +class CbmDigiManager; +class CbmEvent; + +#include "HitFinderTof.h" + +// FAIR classes and includes +#include "FairTask.h" + +// ROOT Classes and includes +class TClonesArray; +#include "TStopwatch.h" +#include "TTimeStamp.h" + +// C++ Classes and includes +#include <vector> + +class CbmTaskTofHitFinder : public FairTask { +public: + /** + ** @brief Constructor. + **/ + CbmTaskTofHitFinder(); + + /** + ** @brief Constructor. + **/ + CbmTaskTofHitFinder(const char* name, int32_t verbose = 1); + /** + ** @brief Destructor. + **/ + virtual ~CbmTaskTofHitFinder(); + + /** + ** @brief Inherited from FairTask. + **/ + virtual InitStatus Init(); + + /** + ** @brief Inherited from FairTask. + **/ + virtual void SetParContainers(); + + /** + ** @brief Inherited from FairTask. + **/ + virtual void Exec(Option_t* option); + + /** + ** @brief Inherited from FairTask. + **/ + virtual void Finish(); + +protected: +private: + /** + ** @brief Copy constructor. + **/ + CbmTaskTofHitFinder(const CbmTaskTofHitFinder&); + /** + ** @brief Copy operator. + **/ + CbmTaskTofHitFinder& operator=(const CbmTaskTofHitFinder&); + + // Functions common for all clusters approximations + /** + ** @brief Recover pointer on input TClonesArray: TofPoints, TofDigis... + **/ + bool RegisterInputs(); + /** + ** @brief Create and register output TClonesArray of Tof Hits. + **/ + bool RegisterOutputs(); + /** + ** @brief Initialize other parameters not included in parameter classes. + **/ + bool InitParameters(); + /** + ** @brief Initialize other parameters not included in parameter classes. + **/ + bool InitCalibParameter(); + + /** + ** @brief Create one algo object for each RPC + **/ + bool InitAlgos(); + + /** + ** @brief Build clusters out of ToF Digis and store the resulting info in a TofHit. + **/ + std::pair<int32_t, int32_t> BuildClusters(CbmEvent* event); + + /** + ** @brief Retrieve event info from run manager to properly fill the CbmLink objects. + **/ + void GetEventInfo(int32_t& inputNr, int32_t& eventNr, double& eventTime); + + // ToF geometry variables + CbmTofGeoHandler* fGeoHandler; + CbmTofDetectorId* fTofId = nullptr; + CbmTofDigiPar* fDigiPar = nullptr; + CbmTofDigiBdfPar* fDigiBdfPar = nullptr; + + // Input variables + CbmDigiManager* fDigiMan = nullptr; + TClonesArray* fEvents = nullptr; + + // Output variables + TClonesArray* fTofHitsColl = nullptr; // TOF hits + TClonesArray* fTofDigiMatchColl = nullptr; // TOF Digis + + // Hit finder algo + std::map<uint32_t, std::map<uint32_t, cbm::algo::HitFinderTof>> fAlgo = {}; //[nbType][nbSm*nbRpc] + + // Intermediate storage variables + std::vector<std::vector<std::vector<CbmTofDigi>>> fStorDigiExp; //[nbType][nbSm*nbRpc][nbCh][nDigis] + std::vector<std::vector<std::vector<int32_t>>> fStorDigiInd; //[nbType][nbSm*nbRpc][nbCh][nDigis] + + //Calibration parameters + std::vector<std::vector<double>> fvCPSigPropSpeed; //[nSMT][nRpc] + std::vector<std::vector<std::vector<std::vector<double>>>> fvCPTOff; //[nSMT][nRpc][nCh][nbSide] + std::vector<std::vector<std::vector<std::vector<double>>>> fvCPTotGain; //[nSMT][nRpc][nCh][nbSide] + std::vector<std::vector<std::vector<std::vector<std::vector<double>>>>> + fvCPWalk; //[nSMT][nRpc][nCh][nbSide][nbWalkBins] + + // Control + TTimeStamp fStart; + TTimeStamp fStop; + + // --- Run counters + TStopwatch fTimer; ///< ROOT timer + int32_t fiNofTs = 0; ///< Number of processed timeslices + int32_t fiNofEvents = 0; ///< Total number of events processed + double fNofDigisAll = 0.; ///< Total number of TOF digis in input + double fNofDigisUsed = 0.; ///< Total number of Tof Digis processed + double fdNofHitsTot = 0.; ///< Total number of hits produced + double fdTimeTot = 0.; ///< Total execution time + + ClassDef(CbmTaskTofHitFinder, 1); +}; + +#endif // CBMTOFHITFINDER_H