diff --git a/analysis/detectors/sts/CMakeLists.txt b/analysis/detectors/sts/CMakeLists.txt index 6b4a0ac3ee0192e1da1e1aa94c4cc2aaac055a34..ecb7c91bb259de480f64f7729a8b260a607c43df 100644 --- a/analysis/detectors/sts/CMakeLists.txt +++ b/analysis/detectors/sts/CMakeLists.txt @@ -7,12 +7,29 @@ set(INCLUDE_DIRECTORIES set(SRCS CbmStsWkn.cxx + CbmCutId.cxx + CbmCutMap.cxx + CbmStsUtils.cxx + CbmStsAnaBase.cxx + CbmStsAnalysis.cxx + CbmSpillCheck.cxx + CbmStsChannelQA.cxx + CbmStsTimeCal.cxx + CbmStsHitAna.cxx + CbmStsCorrelation.cxx + CbmStsRecoBeamSpot.cxx + CbmDcaVertexFinder.cxx + CbmStsEfficiency.cxx + CbmStsResolution.cxx + CbmEventVertexDca.cxx ) - set(LIBRARY_NAME CbmStsAna) set(LINKDEF ${LIBRARY_NAME}LinkDef.h) set(PUBLIC_DEPENDENCIES + Algo + CbmSimBase + CbmStsBase ROOT::Core ) @@ -22,4 +39,11 @@ set(PRIVATE_DEPENDENCIES ROOT::MathCore ) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/CbmCut.h + DESTINATION include) + generate_cbm_library() + +If(GTEST_FOUND) + add_subdirectory(tests) +EndIf() diff --git a/analysis/detectors/sts/CbmCut.h b/analysis/detectors/sts/CbmCut.h new file mode 100644 index 0000000000000000000000000000000000000000..fac886230e776742e86bce9ffaed7f4f1f5e89f6 --- /dev/null +++ b/analysis/detectors/sts/CbmCut.h @@ -0,0 +1,90 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#ifndef CBMCUT_H +#define CBMCUT_H + +#include "CbmCutId.h" + +#include <type_traits> + +template<typename T> +class CbmCut { + public: + /** + * @brief Default constructor + * Any check done to a non-configured object CbmCut will pass + */ + CbmCut() = default; + + /** + * @brief Add a CbmTarget object to the list of targets with a key as trg_name. + * @param min Minimum value for the check + * @param max Maximum value for the check + * When this constructor is created, check will be done for each boundary + */ + CbmCut(T min, T max) + { + SetMin(min); + SetMax(max); + } + + /** + * @brief Check if the T is inside the configured ranges [min, max]. + * @param a Object for which operators <= >= == is implemented + * @return true or false depending on two possible general cases + * Case 1 + * -----------------########## TRUE ##########----------------- + * @MIN @MAX + * + * Case 2 + * #### TRUE ####--------------------------------#### TRUE #### + * @MAX @MIN + */ + bool Check(T a) const + { + bool check_min_status = fMinState ? a >= min_ : true; + bool check_max_status = fMaxState ? a <= max_ : true; + if (fMinState && fMaxState && (max_ < min_)) return check_min_status || check_max_status; + return check_min_status && check_max_status; + } + + void SetMin(T val) + { + min_ = val; + fMinState = true; + } + void SetMax(T val) + { + max_ = val; + fMaxState = true; + } + void SetRange(T min_val, T max_val) + { + SetMin(min_val); + SetMax(max_val); + } + + friend std::ostream& operator<<(std::ostream& out, const CbmCut<T>& obj) + { + std::string min_msg = obj.fMinState ? std::to_string(obj.min_) : "none"; + std::string max_msg = obj.fMaxState ? std::to_string(obj.max_) : "none"; + out << "[Min: " << min_msg << " , Max: " << max_msg << "]"; + return out; + } + + T GetMin() const { return min_; } + T GetMax() const { return max_; } + bool GetMinState() const { return fMinState; } + bool GetMaxState() const { return fMaxState; } + + + private: + bool fMinState{false}; + bool fMaxState{false}; + T min_; + T max_; +}; + +#endif diff --git a/analysis/detectors/sts/CbmCutId.cxx b/analysis/detectors/sts/CbmCutId.cxx new file mode 100644 index 0000000000000000000000000000000000000000..e39fb3eacb1a6b125fcd12ffae0e7705f80dfecf --- /dev/null +++ b/analysis/detectors/sts/CbmCutId.cxx @@ -0,0 +1,20 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#include "CbmCutId.h" + +std::string ToString(CbmCutId id) +{ + auto result = cut_id_to_str_map.find(id); + if (result == cut_id_to_str_map.end()) { + return "NotExist"; + } + return result->second; +}; + +std::ostream& operator<<(std::ostream& strm, const CbmCutId& cut_id) +{ + strm << ToString(cut_id); + return strm; +} diff --git a/analysis/detectors/sts/CbmCutId.h b/analysis/detectors/sts/CbmCutId.h new file mode 100644 index 0000000000000000000000000000000000000000..747b982f8ab931fc7b122c9303cef232341a8147 --- /dev/null +++ b/analysis/detectors/sts/CbmCutId.h @@ -0,0 +1,257 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#ifndef CBMCUTID_H +#define CBMCUTID_H + +#include <iostream> +#include <unordered_map> + +/** \enum CbmCutId + * \brief Enumeration of cut identifiers for various observables. + */ +enum class CbmCutId : ushort +{ + // Digi + kBmonDigiChannel, + kBmonDigiSide, + + kMvdDigiChannel, + kMvdDigiCharge, + kMvdDigiTime, + + kStsDigiChannel, + kStsDigiCharge, + kStsDigiTime, + + kRichDigiChannel, + kRichDigiCharge, + kRichDigiTime, + + kMuchigiChannel, + kMuchigiCharge, + kMuchigiTime, + + kTrdDigiChannel, + kTrdDigiCharge, + kTrdDigiTime, + + kTofDigiChannel, + kTofDigiCharge, + kTofDigiTime, + + // Cluster + kStsClusterAddress, + kStsClusterTime, + kStsClusterCharge, + kStsClusterSize, + kStsClusterPosition, + + // Hit + kMvdHitAddress, + kMvdHitTime, + kMvdHitCharge, + kMvdHitX, + kMvdHitY, + kMvdHitZ, + + kStsHitAddress, + kStsHitTime, + kStsHitCharge, + kStsHitQasym, + kStsHitX, + kStsHitY, + kStsHitZ, + + kRichHitAddress, + kRichHitTime, + kRichHitCharge, + kRichHitX, + kRichHitY, + kRichHitZ, + + kMuchHitAddress, + kMuchHitTime, + kMuchHitCharge, + kMuchHitX, + kMuchHitY, + kMuchHitZ, + + kTrdHitAddress, + kTrdHitTime, + kTrdHitCharge, + kTrdHitX, + kTrdHitY, + kTrdHitZ, + + kTofHitAddress, + kTofHitTime, + kTofHitCharge, + kTofHitX, + kTofHitY, + kTofHitZ, + + // Event + kEventNofMvdHit, + kEventNofStsHit, + kEventNofRichHit, + kEventNofRichRing, + kEventNofMuchPixelHit, + kEventNofMuchStrawHit, + kEventNofTrdHit, + kEventNofTofHit, + kEventNofGlobalTrack, + + // Detector tracks + kMvdTrackChi2, + kMvdTrackSize, + + kStsTrackChi2, + kStsTrackSize, + + kRichTrackChi2, + kRichTrackSize, + + kMuchTrackChi2, + kMuchTrackSize, + + kTrdTrackChi2, + kTrdTrackSize, + + kTofTrackChi2, + kTofTrackSize, + + // Global tracks + kGlobalTrackChi2, + kGlobalTrackPval, + kGlobalTrackSize, + kGlobalTrackLength, + + kGlobalTrackMvdChi2, + kGlobalTrackMvdSize, + + kGlobalTrackStsChi2, + kGlobalTrackStsSize, + + kGlobalTrackRichChi2, + kGlobalTrackRichSize, + + kGlobalTrackMuchChi2, + kGlobalTrackMuchSize, + + kGlobalTrackTrdChi2, + kGlobalTrackTrdSize, + + kGlobalTrackTofChi2, + kGlobalTrackTofSize +}; + +static const std::unordered_map<CbmCutId, std::string> cut_id_to_str_map = { + {CbmCutId::kBmonDigiChannel, "kBmonDigiChannel"}, + {CbmCutId::kBmonDigiSide, "kBmonDigiSide"}, + {CbmCutId::kMvdDigiChannel, "kMvdDigiChannel"}, + {CbmCutId::kMvdDigiCharge, "kMvdDigiCharge"}, + {CbmCutId::kMvdDigiTime, "kMvdDigiTime"}, + {CbmCutId::kStsDigiChannel, "kStsDigiChannel"}, + {CbmCutId::kStsDigiCharge, "kStsDigiCharge"}, + {CbmCutId::kStsDigiTime, "kStsDigiTime"}, + {CbmCutId::kRichDigiChannel, "kRichDigiChannel"}, + {CbmCutId::kRichDigiCharge, "kRichDigiCharge"}, + {CbmCutId::kRichDigiTime, "kRichDigiTime"}, + {CbmCutId::kMuchigiChannel, "kMuchigiChannel"}, + {CbmCutId::kMuchigiCharge, "kMuchigiCharge"}, + {CbmCutId::kMuchigiTime, "kMuchigiTime"}, + {CbmCutId::kTrdDigiChannel, "kTrdDigiChannel"}, + {CbmCutId::kTrdDigiCharge, "kTrdDigiCharge"}, + {CbmCutId::kTrdDigiTime, "kTrdDigiTime"}, + {CbmCutId::kTofDigiChannel, "kTofDigiChannel"}, + {CbmCutId::kTofDigiCharge, "kTofDigiCharge"}, + {CbmCutId::kTofDigiTime, "kTofDigiTime"}, + {CbmCutId::kStsClusterAddress, "kStsClusterAddress"}, + {CbmCutId::kStsClusterTime, "kStsClusterTime"}, + {CbmCutId::kStsClusterCharge, "kStsClusterCharge"}, + {CbmCutId::kStsClusterSize, "kStsClusterSize"}, + {CbmCutId::kStsClusterPosition, "kStsClusterPosition"}, + {CbmCutId::kMvdHitAddress, "kMvdHitAddress"}, + {CbmCutId::kMvdHitTime, "kMvdHitTime"}, + {CbmCutId::kMvdHitCharge, "kMvdHitCharge"}, + {CbmCutId::kMvdHitX, "kMvdHitX"}, + {CbmCutId::kMvdHitY, "kMvdHitY"}, + {CbmCutId::kMvdHitZ, "kMvdHitZ"}, + {CbmCutId::kStsHitAddress, "kStsHitAddress"}, + {CbmCutId::kStsHitTime, "kStsHitTime"}, + {CbmCutId::kStsHitCharge, "kStsHitCharge"}, + {CbmCutId::kStsHitQasym, "kStsHitQasym"}, + {CbmCutId::kStsHitX, "kStsHitX"}, + {CbmCutId::kStsHitY, "kStsHitY"}, + {CbmCutId::kStsHitZ, "kStsHitZ"}, + {CbmCutId::kRichHitAddress, "kRichHitAddress"}, + {CbmCutId::kRichHitTime, "kRichHitTime"}, + {CbmCutId::kRichHitCharge, "kRichHitCharge"}, + {CbmCutId::kRichHitX, "kRichHitX"}, + {CbmCutId::kRichHitY, "kRichHitY"}, + {CbmCutId::kRichHitZ, "kRichHitZ"}, + {CbmCutId::kMuchHitAddress, "kMuchHitAddress"}, + {CbmCutId::kMuchHitTime, "kMuchHitTime"}, + {CbmCutId::kMuchHitCharge, "kMuchHitCharge"}, + {CbmCutId::kMuchHitX, "kMuchHitX"}, + {CbmCutId::kMuchHitY, "kMuchHitY"}, + {CbmCutId::kMuchHitZ, "kMuchHitZ"}, + {CbmCutId::kTrdHitAddress, "kTrdHitAddress"}, + {CbmCutId::kTrdHitTime, "kTrdHitTime"}, + {CbmCutId::kTrdHitCharge, "kTrdHitCharge"}, + {CbmCutId::kTrdHitX, "kTrdHitX"}, + {CbmCutId::kTrdHitY, "kTrdHitY"}, + {CbmCutId::kTrdHitZ, "kTrdHitZ"}, + {CbmCutId::kTofHitAddress, "kTofHitAddress"}, + {CbmCutId::kTofHitTime, "kTofHitTime"}, + {CbmCutId::kTofHitCharge, "kTofHitCharge"}, + {CbmCutId::kTofHitX, "kTofHitX"}, + {CbmCutId::kTofHitY, "kTofHitY"}, + {CbmCutId::kTofHitZ, "kTofHitZ"}, + {CbmCutId::kEventNofMvdHit, "kEventNofMvdHit"}, + {CbmCutId::kEventNofStsHit, "kEventNofStsHit"}, + {CbmCutId::kEventNofRichHit, "kEventNofRichHit"}, + {CbmCutId::kEventNofRichRing, "kEventNofRichRing"}, + {CbmCutId::kEventNofMuchPixelHit, "kEventNofMuchPixelHit"}, + {CbmCutId::kEventNofMuchStrawHit, "kEventNofMuchStrawHit"}, + {CbmCutId::kEventNofTrdHit, "kEventNofTrdHit"}, + {CbmCutId::kEventNofTofHit, "kEventNofTofHit"}, + {CbmCutId::kEventNofGlobalTrack, "kEventNofGlobalTrack"}, + {CbmCutId::kMvdTrackChi2, "kMvdTrackChi2"}, + {CbmCutId::kMvdTrackSize, "kMvdTrackSize"}, + {CbmCutId::kStsTrackChi2, "kStsTrackChi2"}, + {CbmCutId::kStsTrackSize, "kStsTrackSize"}, + {CbmCutId::kRichTrackChi2, "kRichTrackChi2"}, + {CbmCutId::kRichTrackSize, "kRichTrackSize"}, + {CbmCutId::kMuchTrackChi2, "kMuchTrackChi2"}, + {CbmCutId::kMuchTrackSize, "kMuchTrackSize"}, + {CbmCutId::kTrdTrackChi2, "kTrdTrackChi2"}, + {CbmCutId::kTrdTrackSize, "kTrdTrackSize"}, + {CbmCutId::kTofTrackChi2, "kTofTrackChi2"}, + {CbmCutId::kTofTrackSize, "kTofTrackSize"}, + {CbmCutId::kGlobalTrackChi2, "kGlobalTrackChi2"}, + {CbmCutId::kGlobalTrackPval, "kGlobalTrackPval"}, + {CbmCutId::kGlobalTrackSize, "kGlobalTrackSize"}, + {CbmCutId::kGlobalTrackLength, "kGlobalTrackLength"}, + {CbmCutId::kGlobalTrackMvdChi2, "kGlobalTrackMvdChi2"}, + {CbmCutId::kGlobalTrackMvdSize, "kGlobalTrackMvdSize"}, + {CbmCutId::kGlobalTrackStsChi2, "kGlobalTrackStsChi2"}, + {CbmCutId::kGlobalTrackStsSize, "kGlobalTrackStsSize"}, + {CbmCutId::kGlobalTrackRichChi2, "kGlobalTrackRichChi2"}, + {CbmCutId::kGlobalTrackRichSize, "kGlobalTrackRichSize"}, + {CbmCutId::kGlobalTrackMuchChi2, "kGlobalTrackMuchChi2"}, + {CbmCutId::kGlobalTrackMuchSize, "kGlobalTrackMuchSize"}, + {CbmCutId::kGlobalTrackTrdChi2, "kGlobalTrackTrdChi2"}, + {CbmCutId::kGlobalTrackTrdSize, "kGlobalTrackTrdSize"}, + {CbmCutId::kGlobalTrackTofChi2, "kGlobalTrackTofChi2"}, + {CbmCutId::kGlobalTrackTofSize, "kGlobalTrackTofSize"}}; + +/** \brief Convert CbmCutId to a string representation. + * + * \param cutId The CbmCutId to convert. + * \return String representation of the CbmCutId. + */ +std::string ToString(CbmCutId); +#endif diff --git a/analysis/detectors/sts/CbmCutMap.cxx b/analysis/detectors/sts/CbmCutMap.cxx new file mode 100644 index 0000000000000000000000000000000000000000..b055814bee61f166301554f7c2c881f02f551eca --- /dev/null +++ b/analysis/detectors/sts/CbmCutMap.cxx @@ -0,0 +1,49 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#include "CbmCutMap.h" + +CbmCut<float>* CbmCutMap::AddCbmCut(CbmCutId id) { return &fCbmCuts[id]; } + +bool CbmCutMap::Check(CbmCutId id, double value) +{ + if (!fCbmCuts.count(id)) { + fFailedCounter[id] = 0; + return true; + } + bool check_result = fCbmCuts[id].Check(value); + fFailedCounter[id] = check_result ? fFailedCounter[id] + 1 : fFailedCounter[id]; + return check_result; +} + +bool CbmCutMap::CheckStsHit(CbmStsHit* hit, TClonesArray* clu_array = nullptr) +{ + if (hit == nullptr) return false; + + if (clu_array != nullptr) { + if (!Check(CbmCutId::kStsHitCharge, cbm_sts_utils::GetHitCharge(hit, clu_array))) { + return false; + } + if (!Check(CbmCutId::kStsHitQasym, cbm_sts_utils::GetHitChargeAsy(hit, clu_array))) { + return false; + } + } + return Check(CbmCutId::kStsHitTime, hit->GetTime()) && Check(CbmCutId::kStsHitX, hit->GetX()) + && Check(CbmCutId::kStsHitY, hit->GetY()) && Check(CbmCutId::kStsHitZ, hit->GetZ()); +} + +bool CbmCutMap::CheckEvent(CbmEvent* evt) +{ + if (evt == nullptr) return false; + + return Check(CbmCutId::kEventNofMvdHit, evt->GetNofData(ECbmDataType::kMvdHit)) + && Check(CbmCutId::kEventNofStsHit, evt->GetNofData(ECbmDataType::kStsHit)) + && Check(CbmCutId::kEventNofRichHit, evt->GetNofData(ECbmDataType::kRichHit)) + && Check(CbmCutId::kEventNofRichRing, evt->GetNofData(ECbmDataType::kRichRing)) + && Check(CbmCutId::kEventNofMuchPixelHit, evt->GetNofData(ECbmDataType::kMuchPixelHit)) + && Check(CbmCutId::kEventNofMuchStrawHit, evt->GetNofData(ECbmDataType::kMuchStrawHit)) + && Check(CbmCutId::kEventNofTrdHit, evt->GetNofData(ECbmDataType::kTrdHit)) + && Check(CbmCutId::kEventNofTofHit, evt->GetNofData(ECbmDataType::kTofHit)) + && Check(CbmCutId::kEventNofGlobalTrack, evt->GetNofData(ECbmDataType::kGlobalTrack)); +} diff --git a/analysis/detectors/sts/CbmCutMap.h b/analysis/detectors/sts/CbmCutMap.h new file mode 100644 index 0000000000000000000000000000000000000000..513e9b9e98bf73a8c77470176cb3fbb566571542 --- /dev/null +++ b/analysis/detectors/sts/CbmCutMap.h @@ -0,0 +1,89 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#ifndef CBMCUTMAP_H +#define CBMCUTMAP_H + +#include "CbmCut.h" +#include "CbmEvent.h" +#include "CbmStsHit.h" +#include "CbmStsUtils.h" + +#include <TClonesArray.h> + +/** + * @class CbmCutMap + * @brief Manages a map of cuts associated with their IDs. + */ +class CbmCutMap { + public: + CbmCutMap() = default; + ~CbmCutMap() = default; + + /** + * @brief Get the map of cuts. + * @return The map of cuts. + */ + std::unordered_map<CbmCutId, CbmCut<float>> GetMap() const { return fCbmCuts; } + + /** + * @brief Add a new cut to the map. + * @param id The ID of the cut. + * @return Pointer to the added CbmCut. + */ + CbmCut<float>* AddCbmCut(CbmCutId id); + + /** + * @brief Check if a value passes the cut with the given ID. + * @param id The ID of the cut to check. + * @param value The value to check against the cut. + * @return True if the value passes the cut, false otherwise. + */ + bool Check(CbmCutId id, double value); + + /** + * @brief Check if a CbmStsHit passes the cuts. + * @param hit Pointer to the CbmStsHit to check. + * @param array Pointer to the TClonesArray containing CbmStsCluster (for charge retrieving). + * @return True if the hit passes the cuts, false otherwise. + */ + bool CheckStsHit(CbmStsHit*, TClonesArray*); + + /** + * @brief Check if a CbmEvent passes the cuts. + * @param evt Pointer to the CbmEvent to check. + * @return True if the event passes the cuts, false otherwise. + */ + bool CheckEvent(CbmEvent* evt); + + /** + * @brief Overloaded stream insertion operator for CbmCutMap. + * @param out Output stream. + * @param obj CbmCutMap object to output. + * @return Reference to the output stream. + */ + friend std::ostream& operator<<(std::ostream& out, const CbmCutMap& obj) + { + for (auto& [id, cut] : obj.GetMap()) { + out << ToString(id) << "\t" << cut << std::endl; + } + return out; + } + + /** + * @brief Print the cuts and failed pass counters. + */ + void Print() + { + for (auto& [id, cut] : fCbmCuts) { + std::cout << int(id) << ": " << cut << "\tFailed: " << fFailedCounter[id] << std::endl; + } + } + + private: + std::unordered_map<CbmCutId, CbmCut<float>> fCbmCuts; + std::unordered_map<CbmCutId, unsigned long int> fFailedCounter; +}; + +#endif diff --git a/analysis/detectors/sts/CbmDcaVertexFinder.cxx b/analysis/detectors/sts/CbmDcaVertexFinder.cxx new file mode 100644 index 0000000000000000000000000000000000000000..bd394f0466e00ca2f162adb82b0dbb4d8ca5ce81 --- /dev/null +++ b/analysis/detectors/sts/CbmDcaVertexFinder.cxx @@ -0,0 +1,127 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#include "CbmDcaVertexFinder.h" + +#include "CbmGlobalTrack.h" +#include "CbmVertex.h" + +#include <Logger.h> + +CbmDcaVertexFinder::CbmDcaVertexFinder() + : fMaxDca(0.1) + , fInputTracks(std::vector<CbmGlobalTrack*>()) + , fNbPairs(0) + , fQA(std::nullopt) +{ + LOG(debug) << "Creating CbmDcaVertexFinder ..."; + fCovMatrix.ResizeTo(3, 3); +} + +CbmDcaVertexFinder::CbmDcaVertexFinder(double max_dca) + : fMaxDca(max_dca) + , fInputTracks(std::vector<CbmGlobalTrack*>()) + , fNbPairs(0) + , fQA(std::nullopt) +{ + LOG(debug) << "Creating CbmDcaVertexFinder ..."; + fCovMatrix.ResizeTo(3, 3); +} + +/** \brief Default constructor + * \param tracks +*/ +CbmDcaVertexFinder::CbmDcaVertexFinder(const std::vector<CbmGlobalTrack*> tracks) + : fMaxDca(0.1) + , fInputTracks(tracks) + , fNbPairs(0) + , fQA(std::nullopt) +{ + LOG(debug) << "Creating CbmDcaVertexFinder ..."; + fCovMatrix.ResizeTo(3, 3); +} + +/** \brief Default constructor + * \param tracks +*/ +CbmDcaVertexFinder::CbmDcaVertexFinder(const std::vector<CbmGlobalTrack*> tracks, const double max_dca) + : fMaxDca(max_dca) + , fInputTracks(tracks) + , fNbPairs(0) + , fQA(std::nullopt) +{ + LOG(debug) << "Creating CbmDcaVertexFinder ..."; + fCovMatrix.ResizeTo(3, 3); +} + +void CbmDcaVertexFinder::SetTracks(const std::vector<CbmGlobalTrack*> tracks) +{ + fInputTracks.clear(); + fInputTracks = tracks; +} + +/** \brief find vertex using input tracks + */ +std::optional<CbmVertex> CbmDcaVertexFinder::FindVertex() +{ + // Reset the number of track pair used + fNbPairs = 0; + int n_of_trk = fInputTracks.size(); + LOG(debug) << "- PCA - Find event vertex using CbmGlobalTracks: " << n_of_trk; + TVector3 vtx; + for (int trk_i_idx = 0; trk_i_idx < n_of_trk - 1; trk_i_idx++) { + for (int trk_j_idx = trk_i_idx + 1; trk_j_idx < n_of_trk; trk_j_idx++) { + auto pca = FindPca(fInputTracks[trk_i_idx], fInputTracks[trk_j_idx]); + if (pca.has_value() && pca->d_trk < fMaxDca) { + TVector3 pca_i_j = pca->point; + vtx += pca_i_j; + fNbPairs++; + + if (fQA.has_value()) { + fQA->pca_y_vs_x->Fill(pca_i_j[0], pca_i_j[1]); + fQA->pca_x_vs_z->Fill(pca_i_j[2], pca_i_j[0]); + fQA->pca_y_vs_z->Fill(pca_i_j[2], pca_i_j[1]); + } + } + } + } + if (!fNbPairs) return std::nullopt; + + vtx *= 1. / fNbPairs; + + // WARNING LINE + return CbmVertex("EventVertex", "EventVertex", vtx[0], vtx[1], vtx[2], 0, 0, fNbPairs, fCovMatrix); +} + +std::optional<CbmDcaVertexFinder::PCA> CbmDcaVertexFinder::FindPca(CbmGlobalTrack* trk_i, CbmGlobalTrack* trk_j) +{ + TVector3 r1(trk_i->GetParamFirst()->GetX(), trk_i->GetParamFirst()->GetY(), trk_i->GetParamFirst()->GetZ()); + TVector3 e1(trk_i->GetParamFirst()->GetTx(), trk_i->GetParamFirst()->GetTy(), 1); + + TVector3 r2(trk_j->GetParamFirst()->GetX(), trk_j->GetParamFirst()->GetY(), trk_j->GetParamFirst()->GetZ()); + TVector3 e2(trk_j->GetParamFirst()->GetTx(), trk_j->GetParamFirst()->GetTy(), 1); + + TVector3 n = e1.Cross(e2); // Directional vector for segment of the pca + + float nn = n.Dot(n); + if (nn != 0) { // Non-Parallel tracks + + TVector3 e1n = e1.Cross(n); + TVector3 e2n = e1.Cross(n); + TVector3 r21 = r2 - r1; + + float t1 = e2n.Dot(r21) / nn; + float t2 = e1n.Dot(r21) / nn; + + TVector3 p1 = r1 + t1 * e1; // Closest point on track 1 + TVector3 p2 = r2 + t2 * e2; // Closest point on track 2 + TVector3 p21 = p2 - p1; + + TVector3 point = 0.5 * (p1 + p2); + double d_trk = 0.5 * p21.Mag(); + + return std::make_optional<PCA>({point, d_trk}); + } + return std::nullopt; +} diff --git a/analysis/detectors/sts/CbmDcaVertexFinder.h b/analysis/detectors/sts/CbmDcaVertexFinder.h new file mode 100644 index 0000000000000000000000000000000000000000..f3830f17e315bfd58e2d98ddd05cb64fdb7326fe --- /dev/null +++ b/analysis/detectors/sts/CbmDcaVertexFinder.h @@ -0,0 +1,75 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#ifndef CBMDCAVERTEXFINDER_H +#define CBMDCAVERTEXFINDER_H + +#include "TH2.h" +#include "TMatrixFSym.h" +#include "TVector3.h" + +#include <optional> +class CbmVertex; +class CbmGlobalTrack; + +/** \class CbmDcaVertexFinder + * \brief Estimates a common vertex from multiple straight GlobalTracks + * + * The algorithm consist in averaging the PCA of all valid pair of tracks. + * A valid pair is defined but a cut in the maximum value for DCA + */ +class CbmDcaVertexFinder { + public: + struct Qa { + std::shared_ptr<TH2> pca_y_vs_x; + std::shared_ptr<TH2> pca_x_vs_z; + std::shared_ptr<TH2> pca_y_vs_z; + }; + + /** Holds PCA analysis for a pair of straight tracks */ + struct PCA { + TVector3 point; + double d_trk; + }; + + CbmDcaVertexFinder(); + + /** \brief Constructor setting maximum DCA for track pair */ + CbmDcaVertexFinder(double max_dca); + + /** \brief Constructor using input set of track */ + CbmDcaVertexFinder(const std::vector<CbmGlobalTrack*> tracks); + + /** \brief Constructor using input set of track, and maximum DCA for track pair */ + CbmDcaVertexFinder(const std::vector<CbmGlobalTrack*> tracks, const double max_dca); + + /** \brief Set input track */ + void SetTracks(const std::vector<CbmGlobalTrack*> tracks); + + ~CbmDcaVertexFinder() = default; + + /** \brief Calculate the Point of Closest Approach of two straight tracks + * if tracks are parallel it return a nullopt + * \param trk_i, trk_j pointer to CbmGlobal tracks + */ + std::optional<CbmDcaVertexFinder::PCA> FindPca(CbmGlobalTrack* trk_i, CbmGlobalTrack* trk_j); + + /** \brief Run algorithm to find vertex + * \return std::optional<CbmVertex>: + * - std::nullopt if algorithm fails or no vertex was found under present configuration + */ + std::optional<CbmVertex> FindVertex(); + + void EnableQa(Qa qa) { fQA = std::make_optional<Qa>(qa); } + + private: + const double fMaxDca; + std::vector<CbmGlobalTrack*> fInputTracks; + double fNbPairs; + std::optional<Qa> fQA; + + TMatrixFSym fCovMatrix; +}; + +#endif diff --git a/analysis/detectors/sts/CbmEventVertexDca.cxx b/analysis/detectors/sts/CbmEventVertexDca.cxx new file mode 100644 index 0000000000000000000000000000000000000000..f5cc0bb53aadda5ec371a3a40ee74c55b548c09a --- /dev/null +++ b/analysis/detectors/sts/CbmEventVertexDca.cxx @@ -0,0 +1,354 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#include "CbmEventVertexDca.h" + +#include "CbmStsAddress.h" + + +void CbmEventVertexDca::BookHistograms() +{ + std::string h_name; + + // ---- Binning configuration ---- + // 3D Vertex + double vertex_dx = 0.01; + double vertex_dy = 0.01; + double vertex_dz = 0.01; + double vertex_x_min = -35 + 0.5 * vertex_dx; + double vertex_x_max = +35 + 0.5 * vertex_dx; + double vertex_y_min = -35 + 0.5 * vertex_dy; + double vertex_y_max = +35 + 0.5 * vertex_dy; + double vertex_z_min = -65 + 0.5 * vertex_dz; + double vertex_z_max = +65 + 0.5 * vertex_dz; + + auto x_vtx_binning = + cbm_sts_utils::HBinning{uint32_t((vertex_x_max - vertex_x_min) / vertex_dx), vertex_x_min, vertex_x_max}; + auto y_vtx_binning = + cbm_sts_utils::HBinning{uint32_t((vertex_y_max - vertex_y_min) / vertex_dy), vertex_y_min, vertex_y_max}; + auto z_vtx_binning = + cbm_sts_utils::HBinning{uint32_t((vertex_z_max - vertex_z_min) / vertex_dz), vertex_z_min, vertex_z_max}; + + // DCA + double dca_dx = .001; // 10 um + double dca_min = -2.5 - 0.5 * dca_dx; + double dca_max = +2.5 + 0.5 * dca_dx; + auto r_dca_binning = cbm_sts_utils::HBinning{uint32_t((dca_max - dca_min) / dca_dx), dca_min, dca_max}; + // ---- --------------------- ---- + + // ---- 3D Vertex 2D projections ---- + h_name = "vertex_y_vs_x"; + fH2D[h_name] = + std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), x_vtx_binning.n_of_bins, x_vtx_binning.x_min, + x_vtx_binning.x_max, y_vtx_binning.n_of_bins, y_vtx_binning.x_min, y_vtx_binning.x_max); + fH2D[h_name]->GetXaxis()->SetTitle("Vertex_{X} [cm]"); + fH2D[h_name]->GetYaxis()->SetTitle("Vertex_{Y} [cm]"); + + h_name = "vertex_x_vs_z"; + fH2D[h_name] = + std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), z_vtx_binning.n_of_bins, z_vtx_binning.x_min, + z_vtx_binning.x_max, x_vtx_binning.n_of_bins, x_vtx_binning.x_min, x_vtx_binning.x_max); + fH2D[h_name]->GetXaxis()->SetTitle("Vertex_{Z} [cm]"); + fH2D[h_name]->GetYaxis()->SetTitle("Vertex_{X} [cm]"); + + h_name = "vertex_y_vs_z"; + fH2D[h_name] = + std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), z_vtx_binning.n_of_bins, z_vtx_binning.x_min, + z_vtx_binning.x_max, y_vtx_binning.n_of_bins, y_vtx_binning.x_min, y_vtx_binning.x_max); + fH2D[h_name]->GetXaxis()->SetTitle("Vertex_{Z} [cm]"); + fH2D[h_name]->GetYaxis()->SetTitle("Vertex_{Y} [cm]"); + // ---- ------------------------ ---- + + // ---- PCA 2D projections ---- + if (fVertexFinder) { + h_name = "pca_y_vs_x"; + fH2DShared[h_name] = + std::make_shared<TH2D>(h_name.c_str(), h_name.c_str(), x_vtx_binning.n_of_bins, x_vtx_binning.x_min, + x_vtx_binning.x_max, y_vtx_binning.n_of_bins, y_vtx_binning.x_min, y_vtx_binning.x_max); + fH2DShared[h_name]->GetXaxis()->SetTitle("Vertex_{X} [cm]"); + fH2DShared[h_name]->GetYaxis()->SetTitle("Vertex_{Y} [cm]"); + + h_name = "pca_x_vs_z"; + fH2DShared[h_name] = + std::make_shared<TH2D>(h_name.c_str(), h_name.c_str(), z_vtx_binning.n_of_bins, z_vtx_binning.x_min, + z_vtx_binning.x_max, x_vtx_binning.n_of_bins, x_vtx_binning.x_min, x_vtx_binning.x_max); + fH2DShared[h_name]->GetXaxis()->SetTitle("Vertex_{Z} [cm]"); + fH2DShared[h_name]->GetYaxis()->SetTitle("Vertex_{X} [cm]"); + + h_name = "pca_y_vs_z"; + fH2DShared[h_name] = + std::make_shared<TH2D>(h_name.c_str(), h_name.c_str(), z_vtx_binning.n_of_bins, z_vtx_binning.x_min, + z_vtx_binning.x_max, y_vtx_binning.n_of_bins, y_vtx_binning.x_min, y_vtx_binning.x_max); + fH2DShared[h_name]->GetXaxis()->SetTitle("Vertex_{Z} [cm]"); + fH2DShared[h_name]->GetYaxis()->SetTitle("Vertex_{Y} [cm]"); + + std::cout << " - DcaVertexFinder - Enabling QA" << std::endl; + fVertexFinder->EnableQa({fH2DShared["pca_y_vs_x"], fH2DShared["pca_x_vs_z"], fH2DShared["pca_y_vs_z"]}); + } + // ---- ------------------------ ---- + + // ---- DCA ---- + for (const char* di : {"x", "y", "z"}) { + h_name = Form("dca_d%s_vs_x", di); + fH2D[h_name] = + std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), r_dca_binning.n_of_bins, r_dca_binning.x_min, + r_dca_binning.x_max, x_vtx_binning.n_of_bins, x_vtx_binning.x_min, x_vtx_binning.x_max); + fH2D[h_name]->GetYaxis()->SetTitle("X [cm]"); + fH2D[h_name]->GetXaxis()->SetTitle(Form("DCA_{%s} [cm]", di)); + + h_name = Form("dca_d%s_vs_y", di); + fH2D[h_name] = + std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), r_dca_binning.n_of_bins, r_dca_binning.x_min, + r_dca_binning.x_max, y_vtx_binning.n_of_bins, y_vtx_binning.x_min, y_vtx_binning.x_max); + fH2D[h_name]->GetYaxis()->SetTitle("Y [cm]"); + fH2D[h_name]->GetXaxis()->SetTitle(Form("DCA_{%s} [cm]", di)); + + h_name = Form("dca_d%s_vs_z", di); + fH2D[h_name] = + std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), r_dca_binning.n_of_bins, r_dca_binning.x_min, + r_dca_binning.x_max, z_vtx_binning.n_of_bins, z_vtx_binning.x_min, z_vtx_binning.x_max); + fH2D[h_name]->GetYaxis()->SetTitle(Form("Z [cm]")); + fH2D[h_name]->GetXaxis()->SetTitle(Form("DCA_{%s} [cm]", di)); + } + // ---- ------------------ ---- + + // ---- Residual with MC ---- + if (input_has_mc) { + LOG(info) << " - INFO - Enabling MC comparison"; + for (const char* di : {"x", "y", "z"}) { + h_name = Form("res_%s_vs_x", di); + fH2D[h_name] = + std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), r_dca_binning.n_of_bins, r_dca_binning.x_min, + r_dca_binning.x_max, x_vtx_binning.n_of_bins, x_vtx_binning.x_min, x_vtx_binning.x_max); + fH2D[h_name]->GetYaxis()->SetTitle("X [cm]"); + fH2D[h_name]->GetXaxis()->SetTitle(Form("%s_{rec} - %s_{MC} [cm]", di, di)); + + h_name = Form("res_%s_vs_y", di); + fH2D[h_name] = + std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), r_dca_binning.n_of_bins, r_dca_binning.x_min, + r_dca_binning.x_max, y_vtx_binning.n_of_bins, y_vtx_binning.x_min, y_vtx_binning.x_max); + fH2D[h_name]->GetYaxis()->SetTitle("Y [cm]"); + fH2D[h_name]->GetXaxis()->SetTitle(Form("%s_{rec} - %s_{MC} [cm]", di, di)); + + h_name = Form("res_%s_vs_z", di); + fH2D[h_name] = + std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), r_dca_binning.n_of_bins, r_dca_binning.x_min, + r_dca_binning.x_max, z_vtx_binning.n_of_bins, z_vtx_binning.x_min, z_vtx_binning.x_max); + fH2D[h_name]->GetYaxis()->SetTitle(Form("Z [cm]")); + fH2D[h_name]->GetXaxis()->SetTitle(Form("%s_{rec} - %s_{MC} [cm]", di, di)); + } + } + // ---- ------------------------- ---- + + // Track multiplicity + h_name = "ca_track_multiplicity_all"; + fH1D[h_name] = std::make_unique<TH1D>(h_name.c_str(), h_name.c_str(), 50, 0, 50); + fH1D[h_name]->GetXaxis()->SetTitle("N_{CATrack}"); + fH1D[h_name]->GetYaxis()->SetTitle("Entries"); + + h_name = "ca_track_multiplicity_sel"; + fH1D[h_name] = std::make_unique<TH1D>(h_name.c_str(), h_name.c_str(), 50, 0, 50); + fH1D[h_name]->GetXaxis()->SetTitle("N_{CATrack}"); + fH1D[h_name]->GetYaxis()->SetTitle("Entries"); +} + + +void CbmEventVertexDca::FinishTask() { SaveToFile(); } + +void CbmEventVertexDca::CheckVertex() +{ + fH1D["ca_track_multiplicity_sel"]->Fill(fGlbTrks.size()); + + fVertexFinder->SetTracks(fGlbTrks); + const std::optional<CbmVertex> vertex = fVertexFinder->FindVertex(); + + if (vertex.has_value()) { + + TVector3 vtx(vertex->GetX(), vertex->GetY(), vertex->GetZ()); + + fH2D["vertex_y_vs_x"]->Fill(vtx[0], vtx[1]); + fH2D["vertex_x_vs_z"]->Fill(vtx[2], vtx[0]); + fH2D["vertex_y_vs_z"]->Fill(vtx[2], vtx[1]); + + for (const CbmGlobalTrack* trk : fGlbTrks) { + float trk_tx = trk->GetParamFirst()->GetTx(); + float trk_ty = trk->GetParamFirst()->GetTy(); + float trk_x0 = trk->GetParamFirst()->GetX(); + float trk_y0 = trk->GetParamFirst()->GetY(); + float trk_z0 = trk->GetParamFirst()->GetZ(); + + TVector3 p(trk_x0, trk_y0, trk_z0); + TVector3 e(trk_tx, trk_ty, 1); + TVector3 trk_to_vtx = ((vtx - p).Cross(e)) * (1. / e.Mag()); + + fH2D["dca_dx_vs_x"]->Fill(trk_to_vtx.Px(), vtx.Px()); + fH2D["dca_dx_vs_y"]->Fill(trk_to_vtx.Px(), vtx.Py()); + fH2D["dca_dx_vs_z"]->Fill(trk_to_vtx.Px(), vtx.Pz()); + + fH2D["dca_dy_vs_x"]->Fill(trk_to_vtx.Py(), vtx.Px()); + fH2D["dca_dy_vs_y"]->Fill(trk_to_vtx.Py(), vtx.Py()); + fH2D["dca_dy_vs_z"]->Fill(trk_to_vtx.Py(), vtx.Pz()); + + fH2D["dca_dz_vs_x"]->Fill(trk_to_vtx.Pz(), vtx.Px()); + fH2D["dca_dz_vs_y"]->Fill(trk_to_vtx.Pz(), vtx.Py()); + fH2D["dca_dz_vs_z"]->Fill(trk_to_vtx.Pz(), vtx.Pz()); + } + + if (input_has_mc) { + TVector3 mc_vertex; + int n_of_mc_trks = fMCTrkArray->GetEntriesFast(); + for (int mc_trk_idx = 0; mc_trk_idx < n_of_mc_trks; mc_trk_idx++) { + auto mc_trk = (CbmMCTrack*) fMCTrkArray->UncheckedAt(mc_trk_idx); + if (mc_trk->GetMotherId() == -1) { + mc_trk->GetStartVertex(mc_vertex); + break; + } + } + + fH2D["res_x_vs_x"]->Fill(vtx[0] - mc_vertex[0], vtx[0]); + fH2D["res_x_vs_y"]->Fill(vtx[0] - mc_vertex[0], vtx[1]); + fH2D["res_x_vs_z"]->Fill(vtx[0] - mc_vertex[0], vtx[2]); + + fH2D["res_y_vs_x"]->Fill(vtx[1] - mc_vertex[1], vtx[0]); + fH2D["res_y_vs_y"]->Fill(vtx[1] - mc_vertex[1], vtx[1]); + fH2D["res_y_vs_z"]->Fill(vtx[1] - mc_vertex[1], vtx[2]); + + fH2D["res_z_vs_x"]->Fill(vtx[2] - mc_vertex[2], vtx[0]); + fH2D["res_z_vs_y"]->Fill(vtx[2] - mc_vertex[2], vtx[1]); + fH2D["res_z_vs_z"]->Fill(vtx[2] - mc_vertex[2], vtx[2]); + } + } + + // Clear track containers + fGlbTrks.clear(); +} + +void CbmEventVertexDca::ProcessEvent(CbmEvent* evt) +{ + int nb_of_glob_trk = evt->GetNofData(ECbmDataType::kGlobalTrack); + fH1D["ca_track_multiplicity_all"]->Fill(nb_of_glob_trk); + + if (fAnalysisCuts != nullptr && !fAnalysisCuts->CheckEvent(evt)) return; + + + for (int evt_glob_trk_idx = 0; evt_glob_trk_idx < nb_of_glob_trk; evt_glob_trk_idx++) { + int glob_trk_idx = evt->GetIndex(ECbmDataType::kGlobalTrack, evt_glob_trk_idx); + ProcessGlobalTrack((CbmGlobalTrack*) fGlbTrkArray->UncheckedAt(glob_trk_idx)); + } + + CheckVertex(); +} + +void CbmEventVertexDca::ProcessGlobalTrack(CbmGlobalTrack* trk) +{ + auto sts_trk_idx = trk->GetStsTrackIndex(); + auto rich_trk_idx = trk->GetRichRingIndex(); + auto much_trk_idx = trk->GetMuchTrackIndex(); + auto trd_trk_idx = trk->GetTrdTrackIndex(); + auto tof_trk_idx = trk->GetTofTrackIndex(); + float trk_p_val = TMath::Prob(trk->GetChi2(), trk->GetNDF()); + + // Apply GlobalTracks cuts + int32_t mvd_trk_size = sts_trk_idx != -1 && fStsTrkArray != nullptr + ? ((CbmStsTrack*) fStsTrkArray->UncheckedAt(sts_trk_idx))->GetNofMvdHits() + : 0; + + int32_t sts_trk_size = sts_trk_idx != -1 && fStsTrkArray != nullptr + ? ((CbmStsTrack*) fStsTrkArray->UncheckedAt(sts_trk_idx))->GetNofStsHits() + : 0; + + int32_t rich_trk_size = rich_trk_idx != -1 && fRchTrkArray != nullptr + ? ((CbmTrack*) fRchTrkArray->UncheckedAt(rich_trk_idx))->GetNofHits() + : 0; + + int32_t much_trk_size = much_trk_idx != -1 && fMchTrkArray != nullptr + ? ((CbmTrack*) fMchTrkArray->UncheckedAt(much_trk_idx))->GetNofHits() + : 0; + + int32_t trd_trk_size = trd_trk_idx != -1 && fTrdTrkArray != nullptr + ? ((CbmTrack*) fTrdTrkArray->UncheckedAt(trd_trk_idx))->GetNofHits() + : 0; + + int32_t tof_trk_size = tof_trk_idx != -1 && fTofTrkArray != nullptr + ? ((CbmTrack*) fTofTrkArray->UncheckedAt(tof_trk_idx))->GetNofHits() + : 0; + + if (fAnalysisCuts != nullptr + && (!fAnalysisCuts->Check(CbmCutId::kGlobalTrackMvdSize, mvd_trk_size) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackStsSize, sts_trk_size) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackRichSize, rich_trk_size) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackMuchSize, much_trk_size) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackTrdSize, trd_trk_size) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackTofSize, tof_trk_size) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackChi2, trk->GetChi2()) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackPval, trk_p_val))) { + return; + } + + fGlbTrks.push_back(trk); +} + +void CbmEventVertexDca::Exec(Option_t*) +{ + LOG(info) << "Running CbmEventVertexDca;\t" + << "Entry: " << entry_; + + // Check if CbmEvent + if (fCbmEvtArray != nullptr) { + uint32_t nb_events = fCbmEvtArray->GetEntriesFast(); + LOG(info) << Form(" ... number of CbmEvent: %u\n", nb_events); + for (uint32_t evt_idx = 0; evt_idx < nb_events; evt_idx++) { + ProcessEvent((CbmEvent*) fCbmEvtArray->UncheckedAt(evt_idx)); + } + } + else { + + LOG(debug) << " ... running time-like*** Using all tracks in TS - risk of high combinatorial"; + + uint32_t nb_of_glob_trk = fGlbTrkArray->GetEntriesFast(); + LOG(info) << Form("Number of GlobalTracks: %u\n", nb_of_glob_trk); + for (uint32_t glob_trk_idx = 0; glob_trk_idx < nb_of_glob_trk; glob_trk_idx++) { + CbmGlobalTrack* glob_trk = (CbmGlobalTrack*) fGlbTrkArray->UncheckedAt(glob_trk_idx); + ProcessGlobalTrack(glob_trk); + } + + CheckVertex(); + } + entry_++; +} + +InitStatus CbmEventVertexDca::Init() +{ + LOG(debug) << "Init CbmEventVertexDca ..."; + + FairRootManager* ioman = FairRootManager::Instance(); + if (ioman != nullptr) { + fCbmEvtArray = (TClonesArray*) ioman->GetObject("CbmEvent"); + + fGlbTrkArray = (TClonesArray*) ioman->GetObject("GlobalTrack"); + + fStsTrkArray = (TClonesArray*) ioman->GetObject("StsTrack"); + fRchTrkArray = (TClonesArray*) ioman->GetObject("RichTrack"); + fMchTrkArray = (TClonesArray*) ioman->GetObject("MuchTrack"); + fTrdTrkArray = (TClonesArray*) ioman->GetObject("TrdTrack"); + fTofTrkArray = (TClonesArray*) ioman->GetObject("TofTrack"); + + fStsHitArray = (TClonesArray*) ioman->GetObject("StsHit"); + + fStsCluArray = (TClonesArray*) ioman->GetObject("StsCluster"); + + fMCTrkArray = (TClonesArray*) ioman->GetObject("MCTrack"); + input_has_mc = fMCTrkArray != nullptr; + } + + LoadSetup(); + + BookHistograms(); + + fGlbTrks.reserve(100); + + return kSUCCESS; +} + +void CbmEventVertexDca::SetVertexFinder(std::shared_ptr<CbmDcaVertexFinder> vtx_finder) { fVertexFinder = vtx_finder; } diff --git a/analysis/detectors/sts/CbmEventVertexDca.h b/analysis/detectors/sts/CbmEventVertexDca.h new file mode 100644 index 0000000000000000000000000000000000000000..7ff35da8e935f248268b3374d6f0e99fc2141265 --- /dev/null +++ b/analysis/detectors/sts/CbmEventVertexDca.h @@ -0,0 +1,80 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#ifndef CBMEVENTVERTEXDCA_H +#define CBMEVENTVERTEXDCA_H + +#include "CbmDcaVertexFinder.h" +#include "CbmEvent.h" +#include "CbmGlobalTrack.h" +#include "CbmMCTrack.h" +#include "CbmStsAnaBase.h" +#include "CbmStsCluster.h" +#include "CbmStsHit.h" +#include "CbmStsTrack.h" +#include "CbmStsUtils.h" +#include "CbmTrack.h" + +#include <FairTask.h> + +#include <TClonesArray.h> +#include <TVector3.h> + +#include <cstring> +#include <map> +#include <unordered_map> +#include <unordered_set> + +/** \class CbmEventVertexDca + * \brief Task for Event based vertex reconstruction. + * + * The current approach is to average the PCA of multiple pair of tracks. + * Therfore the vertex found, is a primary vertex approximation. + */ +class CbmEventVertexDca : public FairTask, public CbmStsAnaBase { + public: + CbmEventVertexDca() = default; + ~CbmEventVertexDca() = default; + + InitStatus Init(); + void Exec(Option_t*); + void FinishTask(); + + void SetVertexFinder(std::shared_ptr<CbmDcaVertexFinder>); + + private: + std::shared_ptr<CbmDcaVertexFinder> fVertexFinder; + + bool input_has_mc{false}; + + std::vector<CbmGlobalTrack*> fGlbTrks; + + TClonesArray* fMCTrkArray{nullptr}; + TClonesArray* fCbmEvtArray{nullptr}; + TClonesArray* fGlbTrkArray{nullptr}; + TClonesArray* fStsTrkArray{nullptr}; + TClonesArray* fRchTrkArray{nullptr}; + TClonesArray* fMchTrkArray{nullptr}; + TClonesArray* fTrdTrkArray{nullptr}; + TClonesArray* fTofTrkArray{nullptr}; + TClonesArray* fStsHitArray{nullptr}; + TClonesArray* fStsCluArray{nullptr}; + + void BookHistograms(); + + void CheckVertex(); + + /** \brief Process an Cbm events + * It filters event based on the provided CbmCutMap + */ + void ProcessEvent(CbmEvent*); + + /** \brief Process an Global tracks + * It filters the tracks based on the provided CbmCutMap + */ + void ProcessGlobalTrack(CbmGlobalTrack*); + + ClassDef(CbmEventVertexDca, 1); +}; +#endif diff --git a/analysis/detectors/sts/CbmSpillCheck.cxx b/analysis/detectors/sts/CbmSpillCheck.cxx new file mode 100644 index 0000000000000000000000000000000000000000..ca6f883e51522a55c5d2bf4c5bab7147a86dec61 --- /dev/null +++ b/analysis/detectors/sts/CbmSpillCheck.cxx @@ -0,0 +1,89 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#include "CbmSpillCheck.h" + +#include <TFitResult.h> + +#include <ctime> +#include <iostream> +#include <typeinfo> + +CbmSpillCheck::CbmSpillCheck() : fRefSystem{ECbmModuleId::kBmon}, fRateMinPercent{0.2}, fRateMaxPercent{0.5} +{ + LOG(debug) << "Creating an instance of CbmSpillCheck ..."; +} + +CbmSpillCheck::CbmSpillCheck(ECbmModuleId ref, double lvl_min, double lvl_max) + : fRefSystem{ref} + , fRateMinPercent{lvl_min} + , fRateMaxPercent{lvl_max} +{ + LOG(debug) << "Creating an instance of CbmSpillCheck ..."; +} + +void CbmSpillCheck::BookHistograms(ECbmModuleId system) +{ + std::string h_name = Form("%s Digi Rate", ToString(system).c_str()); + LOG(debug) << "Booking rate for : " << h_name; + fG1D[h_name] = std::make_unique<TGraphErrors>(); +} + +InitStatus CbmSpillCheck::Init() +{ + FairRootManager* ioman = FairRootManager::Instance(); + if (ioman != nullptr) { + fDigiManager = CbmDigiManager::Instance(); + fDigiManager->Init(); + + if (!fDigiManager->IsPresent(fRefSystem)) { + LOG(fatal) << GetName() << ": No " << ToString(fRefSystem) << " branch in input!"; + } + + for (ECbmModuleId system = ECbmModuleId::kRef; system != ECbmModuleId::kLastModule; ++system) { + if (fDigiManager->IsPresent(system)) BookHistograms(system); + } + + return kSUCCESS; + } + return kERROR; +} + +void CbmSpillCheck::Exec(Option_t*) +{ + LOG(info) << "Running CbmSpillCheck ..."; + + size_t nb_ref_digis = fDigiManager->GetNofDigis(fRefSystem); + fRateMin = fRateMin == -1 ? nb_ref_digis : std::min(nb_ref_digis, size_t(fRateMin)); + fRateMax = fRateMax == -1 ? nb_ref_digis : std::max(nb_ref_digis, size_t(fRateMax)); + fRefRate.push_back(nb_ref_digis); + + for (ECbmModuleId system = ECbmModuleId::kRef; system != ECbmModuleId::kLastModule; ++system) { + if (fDigiManager->IsPresent(system)) { + size_t n_of_digis = fDigiManager->GetNofDigis(system); + std::string h_name = Form("%s Digi Rate", ToString(system).c_str()); + fG1D[h_name]->SetPoint(fNbPoints, fNbPoints, n_of_digis); + } + } + fNbPoints++; +} + +void CbmSpillCheck::Finish() +{ + double spill_lvl_off = fRateMin + fRateMinPercent * (fRateMax - fRateMin); + double spill_lvl_on = fRateMin + fRateMaxPercent * (fRateMax - fRateMin); + fSpillStatus = std::vector<int>(fRefRate.size(), 0); + for (size_t ts_idx = 0; ts_idx < fRefRate.size(); ts_idx++) { + fSpillStatus[ts_idx] = fRefRate[ts_idx] <= spill_lvl_off ? -1 : (fRefRate[ts_idx] <= spill_lvl_on ? 0 : 1); + std::cout << fSpillStatus[ts_idx] << std::endl; + } + + std::cout << spill_lvl_off << std::endl; + std::cout << spill_lvl_on << std::endl; + + SaveToFile(); + fG1D.clear(); + fH1D.clear(); + fH2D.clear(); +} diff --git a/analysis/detectors/sts/CbmSpillCheck.h b/analysis/detectors/sts/CbmSpillCheck.h new file mode 100644 index 0000000000000000000000000000000000000000..3a93596befeadff88a63b5a997dceb03fc4e9ffe --- /dev/null +++ b/analysis/detectors/sts/CbmSpillCheck.h @@ -0,0 +1,61 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#ifndef CBMSPILLCHECK_H +#define CBMSPILLCHECK_H + +#include "CbmDefs.h" +#include "CbmDigiManager.h" +#include "CbmStsAnaBase.h" + +#include <FairTask.h> + +#include <unordered_set> + +/** \class CbmSpillCheck + * \brief Task for checking the spill status based on a reference module. + * + * This class inherits from FairTask and CbmStsAnaBase. + * It provides functionality for checking the spill status based on the Digis rate of a reference module. + */ +class CbmSpillCheck : public FairTask, public CbmStsAnaBase { + public: + /** \brief Default constructor */ + CbmSpillCheck(); + + /** \brief Parameterized constructor + * + * \param ref The ECbmModuleId representing the reference module for spill checking. + * \param spill_off_prcnt The percentage threshold for spill-off detection (default: 0.2). + * \param spill_on_prcnt The percentage threshold for spill-on detection (default: 0.5). + */ + CbmSpillCheck(ECbmModuleId ref = ECbmModuleId::kBmon, double spill_off_prcnt = 0.2, double spill_on_prcnt = 0.5); + + ~CbmSpillCheck() = default; + + InitStatus Init(); + + void Exec(Option_t*); + + void Finish(); + + private: + ECbmModuleId fRefSystem; + double fRateMinPercent; + double fRateMaxPercent; + int fRateMax{-1}; + int fRateMin{-1}; + int fNbPoints{0}; + std::vector<int> fRefRate; + std::vector<int> fSpillStatus; + + + CbmDigiManager* fDigiManager{nullptr}; + + /** \brief Book histograms for a specific module */ + void BookHistograms(ECbmModuleId); + + ClassDef(CbmSpillCheck, 1); +}; +#endif diff --git a/analysis/detectors/sts/CbmStsAnaBase.cxx b/analysis/detectors/sts/CbmStsAnaBase.cxx new file mode 100644 index 0000000000000000000000000000000000000000..3076f5af47bdf5953681d378027e0fe7ed3d2253 --- /dev/null +++ b/analysis/detectors/sts/CbmStsAnaBase.cxx @@ -0,0 +1,123 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#include "CbmStsAnaBase.h" + +#include "CbmStsModule.h" +#include "CbmStsStation.h" +#include "FairRootManager.h" + +#include <TGeoBBox.h> // for Geometry loading from file +#include <TGeoManager.h> // for Geometry loading from file +#include <TGeoMatrix.h> // for Geometry loading from file +#include <TGeoNode.h> // for Geometry loading from file +#include <TGeoPhysicalNode.h> // for Geometry loading from file +#include <TGeoVolume.h> // for Geometry loading from file + +#include <cassert> + + +void CbmStsAnaBase::SetCutMap(CbmCutMap* cuts_map) +{ + assert(cuts_map != nullptr); + fAnalysisCuts = cuts_map; +} + +void CbmStsAnaBase::LoadSetup() +{ + CbmStsSetup* sts_setup = CbmStsSetup::Instance(); + if (!sts_setup->IsInit()) { + sts_setup->Init(nullptr); + } + + if (sts_setup == nullptr) { + LOG(warning) << "Something is wrong with the setup ..."; + return; + } + + nb_sts_station_ = sts_setup->GetNofStations(); + assert(nb_sts_station_ != 0); + + for (unsigned short unit_idx = 0; unit_idx < nb_sts_station_; unit_idx++) { + CbmStsStation* sts_unit = sts_setup->GetStation(unit_idx); + fStsGeoInfo[unit_idx] = std::vector<double>(5, 0); + fStsGeoInfo[unit_idx][0] = sts_unit->GetXmin(); + fStsGeoInfo[unit_idx][1] = sts_unit->GetXmax(); + fStsGeoInfo[unit_idx][2] = sts_unit->GetYmin(); + fStsGeoInfo[unit_idx][3] = sts_unit->GetYmax(); + fStsGeoInfo[unit_idx][4] = sts_unit->GetZ(); + } + + for (unsigned short module_idx = 0; module_idx < sts_setup->GetNofModules(); module_idx++) { // Loop over modules + CbmStsModule* sts_module = sts_setup->GetModule(module_idx); + assert(sts_module); + int32_t address = sts_module->GetAddress(); + assert(sts_module->GetNofDaughters() == 1); + + CbmStsElement* sts_sensor = sts_module->GetDaughter(0); + TGeoPhysicalNode* sensor_node = sts_sensor->GetPnode(); + TGeoVolume* sensor_volume = sensor_node->GetVolume(); + TGeoBBox* sensor_shape = (TGeoBBox*) sensor_volume->GetShape(); + TGeoMatrix* sensor_matrix = sensor_node->GetMatrix(); + + // const double* local = sensor_shape->GetOrigin(); + const double* trans = sensor_matrix->GetTranslation(); + fStsGeoInfo[address] = std::vector<double>(6, 0); + fStsGeoInfo[address][0] = trans[0] - sensor_shape->GetDX(); // Xmin + fStsGeoInfo[address][1] = trans[0] + sensor_shape->GetDX(); // Xmax + fStsGeoInfo[address][2] = trans[1] - sensor_shape->GetDY(); // Ymin + fStsGeoInfo[address][3] = trans[1] + sensor_shape->GetDY(); // Ymax + fStsGeoInfo[address][4] = trans[2] - sensor_shape->GetDZ(); // Zmin + fStsGeoInfo[address][5] = trans[2] + sensor_shape->GetDZ(); // Zmax + + if (address > 8) { + switch (2 * (int) sensor_shape->GetDY()) { + case 2: fFirstZStrip[address] = 2048 - 32; break; + case 4: fFirstZStrip[address] = 2048 - 88; break; + case 6: fFirstZStrip[address] = 2048 - 134; break; + case 12: fFirstZStrip[address] = 2048 - 274; break; + default: + LOG(warning) << Form("Unknown sensor shape 0x%x: [%0.3f , %0.3f , %0.3f]", address, sensor_shape->GetDX(), + sensor_shape->GetDY(), sensor_shape->GetDZ()); + break; + } + } + } + + LOG(debug) << "Loaded setup information:\n"; + for (auto& [address, geo] : fStsGeoInfo) { + LOG(debug) << Form("0x%x: [%+0.3f, %+0.3f] ^ [%+0.3f, %+0.3f] ^ [%+0.3f, %+0.3f]", address, geo[0], geo[1], geo[2], + geo[3], geo[4], geo[5]); + } +} + +void CbmStsAnaBase::SaveToFile() +{ + FairRootManager* ioman = FairRootManager::Instance(); + auto sink = ioman->GetSink(); + + if (!sink) { + LOG(fatal) << "Check the configuration: sink is invalid!"; + return; + } + + LOG(info) << Form("Task histograms will be saved to %s ...", sink->GetFileName().Data()); + + for (auto& [name, gr] : fG1D) { + if (gr == nullptr) continue; + sink->WriteObject(gr.get(), name.c_str()); + } + for (auto& [name, h] : fH1D) { + if (h == nullptr) continue; + sink->WriteObject(h.get(), name.c_str()); + } + for (auto& [name, h] : fH2D) { + if (h == nullptr) continue; + sink->WriteObject(h.get(), name.c_str()); + } + for (auto& [name, h] : fH2DShared) { + if (h == nullptr) continue; + sink->WriteObject(h.get(), name.c_str()); + } +} diff --git a/analysis/detectors/sts/CbmStsAnaBase.h b/analysis/detectors/sts/CbmStsAnaBase.h new file mode 100644 index 0000000000000000000000000000000000000000..1dfd3941fc97780efc98d66bd6bdd205da7f3c95 --- /dev/null +++ b/analysis/detectors/sts/CbmStsAnaBase.h @@ -0,0 +1,101 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#ifndef CBMSTSANABASE_H +#define CBMSTSANABASE_H + +#include "CbmCutMap.h" +#include "CbmDefs.h" +#include "CbmStsSetup.h" +#include "CbmStsUtils.h" + +#include <FairRootManager.h> +#include <Logger.h> + +#include <TCanvas.h> +#include <TClonesArray.h> +#include <TFile.h> +#include <TGraphErrors.h> +#include <TH1D.h> +#include <TH2D.h> +#include <TSystem.h> + +#include <cstring> +#include <map> +#include <optional> +#include <unordered_set> + + +enum InputType +{ + kMCEventMode, + kMCTimeMode, + kEventMode, + kTimeMode +}; + +/** + * @class CbmStsAnaBase + * @brief Base class for STS analysis. + * + * This class provides a base for STS (Silicon Tracking System) analysis. It contains + * common functionalities and data members used by derived analysis classes. + */ +class CbmStsAnaBase { + public: + CbmStsAnaBase() = default; + ~CbmStsAnaBase() = default; + + /** + * @brief Set the cut map for analysis. + * @param cutMap Pointer to the CbmCutMap object. + */ + void SetCutMap(CbmCutMap* map); + + /** + * @brief User defined sensor translations. + * @param user_mat Input translations. + */ + void UserAlignment(const std::map<int32_t, std::vector<double>>& user_mat) { fUserAlignment = user_mat; } + + /** + * @brief Virtual function to draw analysis results. + */ + virtual void DrawResults() {} + + protected: + uint entry_{0}; + std::unique_ptr<TFile> fReportFile; + std::unordered_set<int32_t> fAddressBook; + std::map<std::string, std::unique_ptr<TGraphErrors>> fG1D; // Map of TGraphErrors objects. + std::map<std::string, std::unique_ptr<TH1D>> fH1D; // Map of TH1D objects. + std::map<std::string, std::unique_ptr<TH2D>> fH2D; // Map of TH2D objects. + std::map<std::string, std::shared_ptr<TH2D>> fH2DShared; // Map of TH2D objects. + std::map<std::string, std::unique_ptr<TCanvas>> fCanvas; // Map of TH2D objects. + + int nb_sts_station_{8}; // Number of STS stations. + std::unordered_map<int32_t, std::vector<double>> fStsGeoInfo; // Map of STS geometry information. + + std::map<int32_t, std::vector<double>> fUserAlignment; // Stores the user-defined alignment information. + + std::unordered_map<int32_t, int> fFirstZStrip; + + CbmCutMap* fAnalysisCuts{nullptr}; + + int fRunId{-1}; + + /** + * @brief Load the STS setup and fill the map with XYZ boundaries for each STS setup element. + * It maps the first z strip for each module depending on the size of the sensor. + */ + void LoadSetup(); + + /** + * @brief It write all mapped objects to the FairRunAna sink file + */ + void SaveToFile(); + + ClassDef(CbmStsAnaBase, 1); +}; +#endif diff --git a/analysis/detectors/sts/CbmStsAnaLinkDef.h b/analysis/detectors/sts/CbmStsAnaLinkDef.h index 6b334db83021bf24d9e82831896d7088df2a815e..8f7a841e294a4c45eca0e17b9eba9634d1f06471 100644 --- a/analysis/detectors/sts/CbmStsAnaLinkDef.h +++ b/analysis/detectors/sts/CbmStsAnaLinkDef.h @@ -10,5 +10,20 @@ #pragma link C++ class CbmStsWkn + ; +#pragma link C++ class CbmCutMap + ; +#pragma link C++ class CbmStsAnaBase + ; +#pragma link C++ namespace CbmStsUtils + ; +#pragma link C++ class CbmStsAnalysis + ; +#pragma link C++ class CbmSpillCheck + ; +#pragma link C++ class CbmStsChannelQA + ; +#pragma link C++ class CbmStsTimeCal + ; +#pragma link C++ class CbmStsHitAna + ; +#pragma link C++ class CbmStsCorrelation + ; +#pragma link C++ class CbmStsRecoBeamSpot + ; +#pragma link C++ class CbmDcaVertexFinder + ; +#pragma link C++ class CbmStsEfficiency + ; +#pragma link C++ class CbmStsResolution + ; +#pragma link C++ class CbmEventVertexDca + ; + #endif /* __CINT__ */ diff --git a/analysis/detectors/sts/CbmStsAnalysis.cxx b/analysis/detectors/sts/CbmStsAnalysis.cxx new file mode 100644 index 0000000000000000000000000000000000000000..497afb8ee6ba0bf3160efd17560b47dbaf3379d1 --- /dev/null +++ b/analysis/detectors/sts/CbmStsAnalysis.cxx @@ -0,0 +1,12 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#include "CbmStsAnalysis.h" + +#include "TList.h" + +#include <cassert> +#include <iomanip> + +void CbmStsAnalysis::SetAnalysisCuts(CbmCutMap* cuts_map) { fAnaCutsMap = cuts_map; } diff --git a/analysis/detectors/sts/CbmStsAnalysis.h b/analysis/detectors/sts/CbmStsAnalysis.h new file mode 100644 index 0000000000000000000000000000000000000000..f5d7b2e259fef1bd676025d2307235209f885d8b --- /dev/null +++ b/analysis/detectors/sts/CbmStsAnalysis.h @@ -0,0 +1,39 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#ifndef CBMSTSANALYSIS_H +#define CBMSTSANALYSIS_H + +// Includes from C/C++ +#include <unordered_map> +#include <unordered_set> + +// Includes from FairRoot +#include <FairRunAna.h> +#include <Logger.h> + +// Includes from CbmRoot +#include "CbmStsAnaBase.h" +#include "CbmStsDigi.h" + +// Includes from ROOT +#include <TClonesArray.h> +#include <TFile.h> +#include <TSystem.h> +#include <TTree.h> + +class CbmStsAnalysis : public FairRunAna { + public: + CbmStsAnalysis() = default; + ~CbmStsAnalysis() = default; + + void SetAnalysisCuts(CbmCutMap*); + + private: + CbmCutMap* fAnaCutsMap{nullptr}; + + ClassDef(CbmStsAnalysis, 1); +}; + +#endif diff --git a/analysis/detectors/sts/CbmStsChannelQA.cxx b/analysis/detectors/sts/CbmStsChannelQA.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d2734a216db6210090e9ec8e713d0f8a20601f89 --- /dev/null +++ b/analysis/detectors/sts/CbmStsChannelQA.cxx @@ -0,0 +1,315 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#include "CbmStsChannelQA.h" + +#include "CbmStsDigi.h" +#include "TArrow.h" +#include "TBox.h" +#include "TPaveLabel.h" +#include "TPaveText.h" +#include "TText.h" + +#include <TParameter.h> + +#include <ctime> +#include <iostream> +#include <typeinfo> + +CbmStsChannelQA::CbmStsChannelQA(int dead_threshold) : fActiveMinEntries(dead_threshold) {} +CbmStsChannelQA::CbmStsChannelQA(int dead_threshold, double noise_threshold, + std::optional<std::pair<size_t, size_t>> spill_on_off_threshold) + : fActiveMinEntries(dead_threshold) + , fSBThreshold(noise_threshold) + , fSpillThresholds(spill_on_off_threshold) +{ + assert(noise_threshold > 0); + if (fSpillThresholds.has_value()) { + assert(fSpillThresholds->first <= fSpillThresholds->second); + fSpillSections = {":ramp", ":spill_on", ":spill_off"}; + } +} + +InitStatus CbmStsChannelQA::Init() +{ + FairRootManager* ioman = FairRootManager::Instance(); + if (ioman != nullptr) { + fDigiManager = CbmDigiManager::Instance(); + fDigiManager->Init(); + + if (!fDigiManager->IsPresent(ECbmModuleId::kSts)) LOG(fatal) << GetName() << ": No StsDigi branch in input!"; + + LOG(info) << "CbmStsChannelQA configuration:"; + LOG(info) << "Report lvl: " << fReportLvl; + LOG(info) << "Active channel minimum entries: " << fActiveMinEntries; + LOG(info) << "Noisy channel S/B thresholds: " << fSBThreshold; + if (fSpillThresholds.has_value()) { + LOG(info) << Form("Spill OFF: RefDet_DigiRate < %lu", fSpillThresholds->first); + LOG(info) << Form("Spill ON: RefDet_DigiRate > %lu", fSpillThresholds->second); + } + else { + LOG(info) << "No Spill section defined. Using all time slices"; + } + + return kSUCCESS; + } + return kERROR; +} + + +void CbmStsChannelQA::BookHistograms(int32_t address) +{ + LOG(debug) << Form("Booking histograms for module_addr: 0x%x", address); + + double dt_max = 120; // HARDCODE + auto t_binning = cbm_sts_utils::HBinning{uint32_t(dt_max / 3.125), 0, dt_max}; + + std::string h_name; + for (auto modifier : fSpillSections) { + h_name = Form("0x%x_charge_vs_channel%s", address, modifier); + LOG(debug) << h_name; + fH2D[h_name] = std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), 2048, 0, 2048, 31, 1, 32); + fH2D[h_name]->GetXaxis()->SetTitle("Channel_{StsDigi}"); + fH2D[h_name]->GetYaxis()->SetTitle("Charge_{StsDigi} [ADC]"); + + LOG(debug) << h_name; + h_name = Form("0x%x_dt_vs_charge%s", address, modifier); + fH2D[h_name] = std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), 31, 1, 32, t_binning.n_of_bins, + t_binning.x_min, t_binning.x_max); + fH2D[h_name]->GetYaxis()->SetTitle("Charge_{StsDigi} [ADC]"); + fH2D[h_name]->GetYaxis()->SetTitle("Time difference [ns]"); + + LOG(debug) << h_name; + h_name = Form("0x%x_dt_vs_channel%s", address, modifier); + fH2D[h_name] = std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), 2048, 0, 2048, t_binning.n_of_bins, + t_binning.x_min, t_binning.x_max); + fH2D[h_name]->GetXaxis()->SetTitle("Channel_{StsDigi}"); + fH2D[h_name]->GetYaxis()->SetTitle("Time difference [ns]"); + } + + fAddressBook.insert(address); +} + +void CbmStsChannelQA::ResetLastTime() +{ + for (auto& [address, time] : fLastDigiTime) { + for (int chn = 0; chn < 2048; chn++) { + time[chn] = -1; + } + } +} + +void CbmStsChannelQA::Exec(Option_t*) +{ + LOG(info) << "Running CbmStsChannelQA ..."; + + // Get Spill status + size_t nb_ref_digis = fDigiManager->GetNofDigis(ECbmModuleId::kBmon); + + /** + * -1 Off spill + * 0 Spill ramp + * +1 On spill + **/ + int fSpillStatus = 0; + if (fSpillThresholds.has_value()) { + fSpillStatus = nb_ref_digis <= fSpillThresholds->first ? -1 : (nb_ref_digis <= fSpillThresholds->second ? 0 : 1); + } + + std::string str_spill; + switch (fSpillStatus) { + case +1: { + fNbTsSpillOn++; + str_spill = fSpillSections[1]; + break; + } + case -1: { + fNbTsSpillOff++; + str_spill = fSpillSections[2]; + break; + } + default: { + str_spill = fSpillSections[0]; + break; + } + } + + LOG(info) << Form("TS %d\t-\t Spill section: %s", entry_, str_spill.c_str()); + + + auto sts_digis_ = fDigiManager->GetArray<CbmStsDigi>(); + size_t nb_sts_digis = fDigiManager->GetNofDigis(ECbmModuleId::kSts); + for (size_t sts_digi_idx = 0; sts_digi_idx < nb_sts_digis; sts_digi_idx++) { // sts digi loop + const CbmStsDigi* sts_digi = &sts_digis_[sts_digi_idx]; + int32_t sts_digi_addr = sts_digi->GetAddress(); + int32_t sts_digi_chan = sts_digi->GetChannel(); + int32_t sts_digi_char = sts_digi->GetCharge(); + double sts_digi_time = sts_digi->GetTime(); + + if (!fAddressBook.count(sts_digi_addr)) { + BookHistograms(sts_digi_addr); + + for (int chn = 0; chn < 2048; chn++) { + fLastDigiTime[sts_digi_addr][chn] = -1; + } + } + + fH2D[Form("0x%x_charge_vs_channel%s", sts_digi_addr, str_spill.c_str())]->Fill(sts_digi_chan, sts_digi_char); + + if (fLastDigiTime[sts_digi_addr][sts_digi_chan] > 0) { + fH2D[Form("0x%x_dt_vs_charge%s", sts_digi_addr, str_spill.c_str())]->Fill( + sts_digi_char, sts_digi_time - fLastDigiTime[sts_digi_addr][sts_digi_chan]); + fH2D[Form("0x%x_dt_vs_channel%s", sts_digi_addr, str_spill.c_str())]->Fill( + sts_digi_chan, sts_digi_time - fLastDigiTime[sts_digi_addr][sts_digi_chan]); + } + + fLastDigiTime[sts_digi_addr][sts_digi_chan] = sts_digi_time; + + } // end sts digi loop + + ResetLastTime(); + entry_++; +} + +void CbmStsChannelQA::CheckDeadChannels() +{ + const char* mod = fSpillThresholds.has_value() ? fSpillSections[1] : fSpillSections[0]; + for (auto& module_addr : fAddressBook) { + auto h = (TH1D*) fH2D[Form("0x%x_charge_vs_channel%s", module_addr, mod)]->ProjectionX(); + for (int chn = 0; chn < 2048; chn++) { + double entries = h->GetBinContent(chn + 1); + + if (entries < fActiveMinEntries) { + fDeadChannelList[module_addr].push_back(chn); + } + } + } +} + +void CbmStsChannelQA::CheckNoisyChannels() +{ + for (auto& module_addr : fAddressBook) { + TH1D* h_sgn = fH2D[Form("0x%x_charge_vs_channel:spill_on", module_addr)]->ProjectionX(); + TH1D* h_bkg = fH2D[Form("0x%x_charge_vs_channel:spill_off", module_addr)]->ProjectionX(); + + h_sgn->Scale(1. / fNbTsSpillOn); + h_bkg->Scale(1. / fNbTsSpillOff); + h_sgn->Add(h_bkg, -1); + + for (int chn = 0; chn < 2048; chn++) { + double sgn = h_sgn->GetBinContent(chn + 1); + double bkg = h_bkg->GetBinContent(chn + 1); + + if (bkg == 0) continue; + + double sb_ratio = sgn / bkg; + + if (sb_ratio < fSBThreshold) { + LOG(debug) << Form("Noisy channel 0x%x: %d, \tS/B: %0.4f", module_addr, chn, sb_ratio); + fNoisyChannelList[module_addr].push_back(chn); + } + } + } +} + +void CbmStsChannelQA::GenerateReport() +{ + std::string f_name = "cbm_sts_channel_qa_report.root"; + std::unique_ptr<TFile> o_root_file = std::make_unique<TFile>(f_name.c_str(), "RECREATE"); + int pad_size_x = 1000; + int pad_size_y = 1000; + for (auto& module_addr : fAddressBook) { + std::string c_name = Form("0x%x_channel", module_addr); + std::unique_ptr<TCanvas> c = std::make_unique<TCanvas>(c_name.c_str(), c_name.c_str(), pad_size_x, pad_size_y); + TH1D* h_sgn = fH2D[Form("0x%x_charge_vs_channel:spill_on", module_addr)]->ProjectionX(); + TH1D* h_bkg = fH2D[Form("0x%x_charge_vs_channel:spill_off", module_addr)]->ProjectionX(); + + LOG(debug) << Form("Building report for 0x%x", module_addr); + h_sgn->Scale(1. / fNbTsSpillOn); + h_bkg->Scale(1. / fNbTsSpillOff); + + h_sgn->SetLineColor(kRed); + h_bkg->SetLineColor(kBlack); + + h_sgn->SetTitle(""); + h_bkg->SetTitle(""); + + h_sgn->SetFillColorAlpha(kRed, 0.2); + h_bkg->SetFillColorAlpha(kBlack, 0.2); + + double h_sgn_max = h_sgn->GetMaximum(); + double h_bkg_max = h_bkg->GetMaximum(); + double max = std::max(h_sgn_max, h_bkg_max); + + h_sgn->SetMaximum(1.05 * max); + h_bkg->SetMaximum(1.05 * max); + + c->cd(1); + gPad->SetLogy(1); + gPad->SetLeftMargin(0.15); + gPad->SetBottomMargin(0.12); + gPad->SetTopMargin(0.10); + gPad->SetRightMargin(0.12); + + h_sgn->Draw("histo"); + h_bkg->Draw("histo same"); + + // Draw a line for bad channels + if (fReportLvl > 1) { + LOG(debug) << Form("Drawing %d noisy channel markers: %lu", module_addr, fNoisyChannelList[module_addr].size()); + for (auto& chn : fNoisyChannelList[module_addr]) { + TBox chn_box = TBox(chn + 0.2, 0.99 * max, chn + 0.8, 1.01 * max); + chn_box.SetLineColor(kBlue); + chn_box.SetFillColor(kBlue); + chn_box.DrawClone(); + } + } + + c->Write(Form("0x%x_channel:spill_on_vs_off.png", module_addr)); + } +} + +void CbmStsChannelQA::Finish() +{ + CheckDeadChannels(); + if (fSpillThresholds.has_value()) CheckNoisyChannels(); + + if (fReportLvl) GenerateReport(); + + SaveToFile(); + + fH1D.clear(); + fH2D.clear(); + + // Write normalization factor to file + std::unique_ptr<TFile> out_file = std::make_unique<TFile>("cbm_sts_channel_qa.root", "UPDATE"); + TParameter<int>("fNbTsSpillOn", fNbTsSpillOn).Write(); + TParameter<int>("fNbTsSpillOff", fNbTsSpillOff).Write(); + + std::ofstream dead_info("sts_dead_channels.info"); + std::ofstream dead_par("sts_dead_channels.par"); + dead_info << Form("# Module\t Number of dead channels\n"); + int n_of_dead_chn = 0; + for (auto& [module_addr, list] : fDeadChannelList) { + dead_info << Form("0x%x\t%lu\n", module_addr, list.size()); + for (int& dead_chn : list) { + dead_par << module_addr << "\t" << dead_chn << std::endl; + } + n_of_dead_chn += list.size(); + } + dead_info << Form("# Total Number of dead channels: %d\n", n_of_dead_chn); + + std::ofstream noisy_info("sts_noisy_channels.info"); + std::ofstream noisy_par("sts_noisy_channels.par"); + noisy_info << Form("# Module\t Number of noisy channels\n"); + int n_of_noisy_chn = 0; + for (auto& [module_addr, list] : fNoisyChannelList) { + noisy_info << Form("0x%x\t%lu\n", module_addr, list.size()); + for (int& noisy_chn : list) { + noisy_par << module_addr << "\t" << noisy_chn << std::endl; + } + n_of_noisy_chn += list.size(); + } + noisy_info << Form("# Total Number of noisy channels: %d\n", n_of_noisy_chn); +} diff --git a/analysis/detectors/sts/CbmStsChannelQA.h b/analysis/detectors/sts/CbmStsChannelQA.h new file mode 100644 index 0000000000000000000000000000000000000000..d947d5fb567894d4c018beac174940b0b3716c21 --- /dev/null +++ b/analysis/detectors/sts/CbmStsChannelQA.h @@ -0,0 +1,136 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + + +/** + * @class CbmStsChannelQA + * @brief FairTask for StsChannelQa. + * + * It processes StsDigis to produce two sets of output files: + * - sts_dead_channels.info: Summary of the amount of dead channels for each STS module (.par file with two columns <module_address> <dead_channel>). + * - sts_noisy_channels.info: Summary of the amount of noisy channels for each STS module (.par file with two columns <module_address> <noisy_channel>). + * + * Moreover, all the histograms/graphs generated will be dumped to: cbm_sts_channel_qa.root. + */ + +#ifndef CBMSTSCHANNELQA_H +#define CBMSTSCHANNELQA_H + +#include "CbmDigiManager.h" +#include "CbmStsAnaBase.h" + +#include <FairTask.h> + +#include <TClonesArray.h> + +#include <cstring> +#include <map> +#include <unordered_set> + +/** + * @class CbmStsChannelQA + * @brief Quality assurance task for STS channel monitoring. + * + * This class inherits from FairTask and CbmStsAnaBase, and provides tools + * to analyze and monitor the behavior of STS channels during data processing. + * + * The main functions of this class include: + * + * - **Dead Channel Detection**: + * Identifies channels with an entry count below a minimum threshold, + * classifying them as non-functional or "dead". + * + * - **Noisy Channel Detection**: + * Evaluates the signal-to-background (S/B) ratio using the ratio of + * entries in-spill versus off-spill. Channels with an user-defined + * S/B ratio are marked as noisy. + * + * - **Charge vs. Channel Histograms**: + * Generates 2D histograms of charge versus channel, split by spill sections (see CbmSpillCheck documentation): + * - All events : if not spill information is provided + * - Ramp section: between the lower and upper thresholds + * - In-spill section : bellow the lower threshold + * - Off-spill section : above the upper threshold + * + * If spill information is provided the number of in|off spill TS analyzed are save to enable normalization + * + * Output files are produced: + * - **Summary files**: + * - sts_dead_channels.info: Summary of the amount of dead channels for each STS module (.par file with two columns <module_address> <dead_channel>). + * - sts_noisy_channels.info: Summary of the amount of noisy channels for each STS module (.par file with two columns <module_address> <noisy_channel>). + * - **Extendend information**: + * - cbm_sts_channel_qa.root: Contains all the histograms/graphs generated. + * - sts_dead_channels.par: Contains the list of dead channels for each module. + * - sts_noisy_channels.par: Contains the list of noisy channels for each module. + */ +class CbmStsChannelQA : public FairTask, public CbmStsAnaBase { + public: + CbmStsChannelQA() = default; + + /** + * @brief Constructor with threshold for inactive channels. + * @param dead_threshold Minimum amount of entries to consider a channel active. + * Useful to generate the channel masking map for MC input + */ + CbmStsChannelQA(int dead_threshold); + + /** + * @brief Constructor with thresholds for dead and noisy channels. Additionally set thresholds + * to define OFF | Rise-Fall | ON spill status base on RefDet digi rate + * @param dead_threshold Minimum amount of entries to consider a channel alive. + * @param noise_threshold Minimum value for Signal/Background ratio to consider a channel to be noisy + * @param spill_on_off_threshold (Minimum,Maximum) value for RefDet digi rate to consider spill (ON,OFF) [optional analysis branching] + */ + CbmStsChannelQA(int dead_threshold, double noise_threshold, const std::optional<std::pair<size_t, size_t>>); + + ~CbmStsChannelQA() = default; + + InitStatus Init(); + void Exec(Option_t*); + void Finish(); + + void SetReportLevel(int lvl) { fReportLvl = lvl; } + + private: + int fReportLvl{0}; + const int fActiveMinEntries{1}; + const double fSBThreshold{0.5}; + const std::optional<std::pair<size_t, size_t>> fSpillThresholds{std::nullopt}; + + std::vector<const char*> fSpillSections = {":all"}; + + int fNbTsSpillOn{0}; + int fNbTsSpillOff{0}; + + std::map<int32_t, std::vector<int>> fDeadChannelList; + std::map<int32_t, std::vector<int>> fNoisyChannelList; + + std::map<int32_t, double[2048]> fLastDigiTime; + void ResetLastTime(); + + CbmDigiManager* fDigiManager{nullptr}; + + /** + * @brief Book histograms. + * @param address The module address for which histograms will be booked. + */ + void BookHistograms(int32_t); + + /** + * @brief Check for dead channels. + * Fills fDeadChannelList map + */ + void CheckDeadChannels(); + + /** + * @brief Check for dead channels. + * Fills fNoisyChannelList map + */ + void CheckNoisyChannels(); + + void GenerateReport(); + + ClassDef(CbmStsChannelQA, 1); +}; +#endif diff --git a/analysis/detectors/sts/CbmStsCorrelation.cxx b/analysis/detectors/sts/CbmStsCorrelation.cxx new file mode 100644 index 0000000000000000000000000000000000000000..9f0e34f32525d46f245753193da8fe7a10689cdd --- /dev/null +++ b/analysis/detectors/sts/CbmStsCorrelation.cxx @@ -0,0 +1,205 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#include "CbmStsCorrelation.h" + +#include "CbmPixelHit.h" +#include "CbmStsAddress.h" +#include "TPaveStats.h" +#include "TStyle.h" + +void CbmStsCorrelation::BookHistograms() +{ + + for (auto& [element_i, geo_i] : fStsGeoInfo) { + for (auto& [element_j, geo_j] : fStsGeoInfo) { + + std::string h_name_base = ""; + std::string obj_x, obj_y; + if (element_i < 8 && element_j < 8) { + if (element_i >= element_j) continue; + obj_x = Form("Sts%d", element_i); + obj_y = Form("Sts%d", element_j); + h_name_base = Form("Sts%d:Sts%d", element_i, element_j); + } + else if (element_i > 8 && element_j > 8) { + int32_t unit_i = CbmStsAddress::GetElementId(element_i, kStsUnit); + int32_t unit_j = CbmStsAddress::GetElementId(element_j, kStsUnit); + + if (unit_i >= unit_j) continue; + + obj_x = Form("Sts0x%x", element_i); + obj_y = Form("Sts0x%x", element_j); + h_name_base = Form("Sts0x%x:Sts0x%x", element_i, element_j); + } + + if (!h_name_base.length()) continue; + LOG(debug) << Form("Booking for %s", h_name_base.c_str()); + + double x_min_i = geo_i[0]; + double x_max_i = geo_i[1]; + double y_min_i = geo_i[2]; + double y_max_i = geo_i[3]; + + unsigned int nb_bins_x_i = (x_max_i - x_min_i) / cbm_sts_utils::kStsDx; + unsigned int nb_bins_y_i = (y_max_i - y_min_i) / cbm_sts_utils::kStsDy; + + double x_min_j = geo_j[0]; + double x_max_j = geo_j[1]; + double y_min_j = geo_j[2]; + double y_max_j = geo_j[3]; + + LOG(debug) << Form("XX_[%0.3f, %0.3f]:[%0.3f, %0.3f]", x_min_i, x_max_i, x_min_j, x_max_j); + LOG(debug) << Form("YY_[%0.3f, %0.3f]:[%0.3f, %0.3f]", y_min_i, y_max_i, y_min_j, y_max_j); + + unsigned int nb_bins_x_j = (x_max_j - x_min_j) / cbm_sts_utils::kStsDx; + unsigned int nb_bins_y_j = (y_max_j - y_min_j) / cbm_sts_utils::kStsDy; + + std::string h_name; + h_name = Form("%s_XX", h_name_base.c_str()); + fH2D[h_name] = + std::make_unique<TH2D>(h_name.c_str(), "", nb_bins_x_i, x_min_i, x_max_i, nb_bins_x_j, x_min_j, x_max_j); + fH2D[h_name]->GetXaxis()->SetTitle(Form("X_{%s} [cm]", obj_x.c_str())); + fH2D[h_name]->GetYaxis()->SetTitle(Form("X_{%s} [cm]", obj_y.c_str())); + fH2D[h_name]->GetXaxis()->CenterTitle(true); + fH2D[h_name]->GetYaxis()->CenterTitle(true); + + h_name = Form("%s_YY", h_name_base.c_str()); + fH2D[h_name] = + std::make_unique<TH2D>(h_name.c_str(), "", nb_bins_y_i, y_min_i, y_max_i, nb_bins_y_j, y_min_j, y_max_j); + fH2D[h_name]->GetXaxis()->SetTitle(Form("Y_{%s} [cm]", obj_x.c_str())); + fH2D[h_name]->GetYaxis()->SetTitle(Form("Y_{%s} [cm]", obj_y.c_str())); + fH2D[h_name]->GetXaxis()->CenterTitle(true); + fH2D[h_name]->GetYaxis()->CenterTitle(true); + } + } +} + +void CbmStsCorrelation::BuildCorrelation() +{ + bool is_mc_data = false; + if (!fStsHits.size()) return; + for (int sts_unit_i = 0; sts_unit_i < nb_sts_station_ - 1; sts_unit_i++) { + for (int sts_unit_j = sts_unit_i + 1; sts_unit_j < nb_sts_station_; sts_unit_j++) { + LOG(debug) << Form("Building correlations for STS%d:STS%d: %ld\t%ld", sts_unit_i, sts_unit_j, + fStsHits[sts_unit_i].size(), fStsHits[sts_unit_j].size()); + for (auto sts_hit_i : fStsHits[sts_unit_i]) { + int32_t sts_address_i = sts_hit_i->GetAddress(); + double sts_i_x = sts_hit_i->GetX(); + double sts_i_y = sts_hit_i->GetY(); + + CbmStsHit* closest_hit = nullptr; + double abs_time_diff = 999; + for (auto sts_hit_j : fStsHits[sts_unit_j]) { + + if (is_mc_data) { + int32_t sts_address_j = sts_hit_j->GetAddress(); + double sts_j_x = sts_hit_j->GetX(); + double sts_j_y = sts_hit_j->GetY(); + + fH2D[Form("Sts%d:Sts%d_YY", sts_unit_i, sts_unit_j)]->Fill(sts_i_y, sts_j_y); + fH2D[Form("Sts%d:Sts%d_XX", sts_unit_i, sts_unit_j)]->Fill(sts_i_x, sts_j_x); + fH2D[Form("Sts0x%x:Sts0x%x_YY", sts_address_i, sts_address_j)]->Fill(sts_i_y, sts_j_y); + fH2D[Form("Sts0x%x:Sts0x%x_XX", sts_address_i, sts_address_j)]->Fill(sts_i_x, sts_j_x); + } + else { + + double dt = std::abs(sts_hit_i->GetTime() - sts_hit_j->GetTime()); + if (abs_time_diff > dt) { + abs_time_diff = dt; + closest_hit = sts_hit_j; + } + } + } + if (!is_mc_data && closest_hit != nullptr) { + int32_t sts_address_j = closest_hit->GetAddress(); + double sts_j_x = closest_hit->GetX(); + double sts_j_y = closest_hit->GetY(); + + fH2D[Form("Sts%d:Sts%d_YY", sts_unit_i, sts_unit_j)]->Fill(sts_i_y, sts_j_y); + fH2D[Form("Sts%d:Sts%d_XX", sts_unit_i, sts_unit_j)]->Fill(sts_i_x, sts_j_x); + fH2D[Form("Sts0x%x:Sts0x%x_YY", sts_address_i, sts_address_j)]->Fill(sts_i_y, sts_j_y); + fH2D[Form("Sts0x%x:Sts0x%x_XX", sts_address_i, sts_address_j)]->Fill(sts_i_x, sts_j_x); + } + } + } + } + fStsHits.clear(); +} + +void CbmStsCorrelation::ProcessEvent(CbmEvent* evt) +{ + if (fAnalysisCuts != nullptr && !fAnalysisCuts->CheckEvent(evt)) return; + + int nb_sts_hits = evt->GetNofData(ECbmDataType::kStsHit); + LOG(debug) << Form("Processing event with %d StsHit", nb_sts_hits); + for (int hit_evt_idx = 0; hit_evt_idx < nb_sts_hits; hit_evt_idx++) { + int sts_hit_idx = evt->GetIndex(ECbmDataType::kStsHit, hit_evt_idx); + CbmStsHit* hit = (CbmStsHit*) fStsHitArray->UncheckedAt(sts_hit_idx); + ProcessHit(hit); + } +} + +void CbmStsCorrelation::ProcessHit(CbmStsHit* hit) +{ + if (hit == nullptr) return; + + if (fAnalysisCuts != nullptr && !fAnalysisCuts->CheckStsHit(hit, fStsCluArray)) return; + + int32_t address = hit->GetAddress(); + int32_t unit = CbmStsAddress::GetElementId(address, kStsUnit); + + fStsHits[unit].push_back(hit); +} + +void CbmStsCorrelation::Exec(Option_t*) +{ + // Check if CbmEvent + if (fCbmEvtArray != nullptr) { + uint32_t nb_events = fCbmEvtArray->GetEntriesFast(); + LOG(info) << Form("Running event-like Entry: %d\t%d CbmEvents", entry_, nb_events); + for (uint32_t evt_idx = 0; evt_idx < nb_events; evt_idx++) { + CbmEvent* event = (CbmEvent*) fCbmEvtArray->UncheckedAt(evt_idx); + ProcessEvent(event); + BuildCorrelation(); + } + } + else { + + LOG(debug) << "Running time-like ..."; + uint32_t n_of_hits = fStsHitArray->GetEntriesFast(); + for (uint32_t hit_idx = 0; hit_idx < n_of_hits; hit_idx++) { + CbmStsHit* sts_hit = (CbmStsHit*) fStsHitArray->UncheckedAt(hit_idx); + ProcessHit(sts_hit); + } + BuildCorrelation(); + } + entry_++; +} + +InitStatus CbmStsCorrelation::Init() +{ + LOG(debug) << "Init CbmStsCorrelation ..."; + + FairRootManager* ioman = FairRootManager::Instance(); + if (ioman != nullptr) { + fCbmEvtArray = (TClonesArray*) ioman->GetObject("CbmEvent"); + fStsHitArray = (TClonesArray*) ioman->GetObject("StsHit"); + fStsCluArray = (TClonesArray*) ioman->GetObject("StsCluster"); + } + + LoadSetup(); + + BookHistograms(); + + return kSUCCESS; +} + + +void CbmStsCorrelation::Finish() +{ + SaveToFile(); + fH1D.clear(); + fH2D.clear(); +} diff --git a/analysis/detectors/sts/CbmStsCorrelation.h b/analysis/detectors/sts/CbmStsCorrelation.h new file mode 100644 index 0000000000000000000000000000000000000000..34cbf428cb28ac1a580bd36af7a5633a16da6d8e --- /dev/null +++ b/analysis/detectors/sts/CbmStsCorrelation.h @@ -0,0 +1,65 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#ifndef CBMSTSCORRELATION_H +#define CBMSTSCORRELATION_H + +#include "CbmEvent.h" +#include "CbmStsAnaBase.h" +#include "CbmStsCluster.h" +#include "CbmStsHit.h" +#include "CbmStsUtils.h" + +#include <FairTask.h> + +#include <TClonesArray.h> + +#include <cstring> +#include <map> +#include <unordered_map> +#include <unordered_set> + +/** \class CbmStsCorrelation + * \brief Task for correlation analysis of STS hits. + * + * This class inherits from FairTask and CbmStsAnaBase. + * It provides functionality for analyzing correlations + * among STS hits. + */ +class CbmStsCorrelation : public FairTask, public CbmStsAnaBase { + public: + CbmStsCorrelation() = default; + ~CbmStsCorrelation() = default; + + InitStatus Init(); + void Exec(Option_t*); + void Finish(); + + private: + std::map<int32_t, std::vector<CbmStsHit*>> fStsHits; + + TClonesArray* fCbmEvtArray; + TClonesArray* fStsHitArray; + TClonesArray* fStsCluArray; + + void BookHistograms(); + + /** \brief Process an Cbm events + * It filters event based on the provided CbmCutMap + */ + void ProcessEvent(CbmEvent*); + + /** \brief Process an STS hit + * It filters hits based on the provided CbmCutMap + */ + void ProcessHit(CbmStsHit*); + + /** \brief Build the correlation + * Using filtered hit build the combinatorial among different station sensors + */ + void BuildCorrelation(); + + ClassDef(CbmStsCorrelation, 1); +}; +#endif diff --git a/analysis/detectors/sts/CbmStsEfficiency.cxx b/analysis/detectors/sts/CbmStsEfficiency.cxx new file mode 100644 index 0000000000000000000000000000000000000000..7823ea455b0ebe49a9272ad3f84ade8459a37cb5 --- /dev/null +++ b/analysis/detectors/sts/CbmStsEfficiency.cxx @@ -0,0 +1,583 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#include "CbmStsEfficiency.h" + +#include "CbmStsAddress.h" +#include "CbmStsUtils.h" + +CbmStsEfficiency::CbmStsEfficiency(uint32_t layer_idx) : fTestLayer(layer_idx) +{ + LOG(debug) << "Creating an instance of CbmStsEfficiency ..."; +} +CbmStsEfficiency::CbmStsEfficiency(uint32_t test_layer, double max_dis_trg_vtx, double max_dca_trk_vtx, + double default_residual) + : fTestLayer(test_layer) + , fMaxDisTrgVtx(max_dis_trg_vtx) + , fMaxDCATrkVtx(max_dca_trk_vtx) + , fDefaultResidual(default_residual) +{ + LOG(debug) << "Creating an instance of CbmStsEfficiency ..."; +} + +void CbmStsEfficiency::BookHistograms() +{ + // ---- Binning configuration ---- + // Vertex + double vertex_x_min = -15; + double vertex_x_max = +15; + double vertex_y_min = -15; + double vertex_y_max = +15; + double vertex_z_min = -15; + double vertex_z_max = +15; + double vertex_dx = 0.1; + double vertex_dy = 0.1; + double vertex_dz = 0.1; + + auto x_vtx_binning = + cbm_sts_utils::HBinning{uint32_t((vertex_x_max - vertex_x_min) / vertex_dx), vertex_x_min, vertex_x_max}; + auto y_vtx_binning = + cbm_sts_utils::HBinning{uint32_t((vertex_y_max - vertex_y_min) / vertex_dy), vertex_y_min, vertex_y_max}; + auto z_vtx_binning = + cbm_sts_utils::HBinning{uint32_t((vertex_z_max - vertex_z_min) / vertex_dz), vertex_z_min, vertex_z_max}; + + // DCA + double dca_dx = .001; // 10 um + double dca_min = -2.5 - 0.5 * dca_dx; + double dca_max = +2.5 + 0.5 * dca_dx; + auto r_dca_binning = cbm_sts_utils::HBinning{uint32_t((dca_max - dca_min) / dca_dx), dca_min, dca_max}; + + // Residuals track - hit + double res_dx = +0.001; // 10 um + double res_min = -2.50 - 0.5 * res_dx; + double res_max = +2.50 + 0.5 * res_dx; + auto r_sen_binning = cbm_sts_utils::HBinning{uint32_t((res_max - res_min) / res_dx), res_min, res_max}; + // ---- --------------------- ---- + + std::string h_name; + for (auto& [element, geo] : fStsGeoInfo) { + std::string h_name_base = ""; + if (element == fTestLayer) { + h_name_base = Form("Sts%d", element); + } + else if (element > 8) { + if (CbmStsAddress::GetElementId(element, kStsUnit) != fTestLayer) continue; + fDUTList.push_back(element); + h_name_base = Form("Sts0x%x", element); + } + + if (!h_name_base.length()) continue; + + auto x_sen_binning = cbm_sts_utils::HBinning{uint32_t((geo[1] - geo[0]) / cbm_sts_utils::kStsDx), geo[0], geo[1]}; + auto y_sen_binning = cbm_sts_utils::HBinning{uint32_t((geo[3] - geo[2]) / cbm_sts_utils::kStsDy), geo[2], geo[3]}; + + // ---- efficiency ---- + h_name = Form("%s_found", h_name_base.c_str()); + fH2D[h_name] = + std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), x_sen_binning.n_of_bins, x_sen_binning.x_min, + x_sen_binning.x_max, y_sen_binning.n_of_bins, y_sen_binning.x_min, y_sen_binning.x_max); + fH2D[h_name]->GetXaxis()->SetTitle("StsHit^{rec}_{X} [cm]"); + fH2D[h_name]->GetYaxis()->SetTitle("StsHit^{rec}_{Y} [cm]"); + + h_name = Form("%s_track", h_name_base.c_str()); + fH2D[h_name] = + std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), x_sen_binning.n_of_bins, x_sen_binning.x_min, + x_sen_binning.x_max, y_sen_binning.n_of_bins, y_sen_binning.x_min, y_sen_binning.x_max); + fH2D[h_name]->GetXaxis()->SetTitle("StsHit^{ext}_{X} [cm]"); + fH2D[h_name]->GetYaxis()->SetTitle("StsHit^{ext}_{Y} [cm]"); + + h_name = Form("%s_hit_map", h_name_base.c_str()); + fH2D[h_name] = + std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), x_sen_binning.n_of_bins, x_sen_binning.x_min, + x_sen_binning.x_max, y_sen_binning.n_of_bins, y_sen_binning.x_min, y_sen_binning.x_max); + fH2D[h_name]->GetXaxis()->SetTitle("StsHit_{X} [cm]"); + fH2D[h_name]->GetYaxis()->SetTitle("StsHit_{Y} [cm]"); + // ---- ---------- ---- + + // ---- residuals ---- + for (const char* di : {"x", "y"}) { + h_name = Form("%s_d%s_vs_x", h_name_base.c_str(), di); + fH2D[h_name] = + std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), r_sen_binning.n_of_bins, r_sen_binning.x_min, + r_sen_binning.x_max, x_sen_binning.n_of_bins, x_sen_binning.x_min, x_sen_binning.x_max); + fH2D[h_name]->GetYaxis()->SetTitle("X [cm]"); + fH2D[h_name]->GetXaxis()->SetTitle(Form("\\rho_{%s} [cm]", di)); + + h_name = Form("%s_d%s_vs_y", h_name_base.c_str(), di); + fH2D[h_name] = + std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), r_sen_binning.n_of_bins, r_sen_binning.x_min, + r_sen_binning.x_max, y_sen_binning.n_of_bins, y_sen_binning.x_min, y_sen_binning.x_max); + fH2D[h_name]->GetYaxis()->SetTitle("Y [cm]"); + fH2D[h_name]->GetXaxis()->SetTitle(Form("\\rho_{%s} [cm]", di)); + } + // ---- --------- ---- + } // end geo loop + + // ---- 3D vertex ---- + h_name = "vertex_y_vs_x"; + fH2D[h_name] = + std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), x_vtx_binning.n_of_bins, x_vtx_binning.x_min, + x_vtx_binning.x_max, y_vtx_binning.n_of_bins, y_vtx_binning.x_min, y_vtx_binning.x_max); + fH2D[h_name]->GetXaxis()->SetTitle("Vertex_{X} [cm]"); + fH2D[h_name]->GetYaxis()->SetTitle("Vertex_{Y} [cm]"); + + h_name = "vertex_x_vs_z"; + fH2D[h_name] = + std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), z_vtx_binning.n_of_bins, z_vtx_binning.x_min, + z_vtx_binning.x_max, x_vtx_binning.n_of_bins, x_vtx_binning.x_min, x_vtx_binning.x_max); + fH2D[h_name]->GetXaxis()->SetTitle("Vertex_{Z} [cm]"); + fH2D[h_name]->GetYaxis()->SetTitle("Vertex_{X} [cm]"); + + h_name = "vertex_y_vs_z"; + fH2D[h_name] = + std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), z_vtx_binning.n_of_bins, z_vtx_binning.x_min, + z_vtx_binning.x_max, y_vtx_binning.n_of_bins, y_vtx_binning.x_min, y_vtx_binning.x_max); + fH2D[h_name]->GetXaxis()->SetTitle("Vertex_{Z} [cm]"); + fH2D[h_name]->GetYaxis()->SetTitle("Vertex_{Y} [cm]"); + // ---- ------------------ ---- + + // ---- DCA ---- + for (const char* di : {"x", "y", "z"}) { + h_name = Form("dca_d%s_vs_x", di); + fH2D[h_name] = + std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), r_dca_binning.n_of_bins, r_dca_binning.x_min, + r_dca_binning.x_max, x_vtx_binning.n_of_bins, x_vtx_binning.x_min, x_vtx_binning.x_max); + fH2D[h_name]->GetYaxis()->SetTitle("X [cm]"); + fH2D[h_name]->GetXaxis()->SetTitle(Form("DCA_{%s} [cm]", di)); + + h_name = Form("dca_d%s_vs_y", di); + fH2D[h_name] = + std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), r_dca_binning.n_of_bins, r_dca_binning.x_min, + r_dca_binning.x_max, y_vtx_binning.n_of_bins, y_vtx_binning.x_min, y_vtx_binning.x_max); + fH2D[h_name]->GetYaxis()->SetTitle("Y [cm]"); + fH2D[h_name]->GetXaxis()->SetTitle(Form("DCA_{%s} [cm]", di)); + + h_name = Form("dca_d%s_vs_z", di); + fH2D[h_name] = + std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), r_dca_binning.n_of_bins, r_dca_binning.x_min, + r_dca_binning.x_max, z_vtx_binning.n_of_bins, z_vtx_binning.x_min, z_vtx_binning.x_max); + fH2D[h_name]->GetYaxis()->SetTitle(Form("Z [cm]")); + fH2D[h_name]->GetXaxis()->SetTitle(Form("DCA_{%s} [cm]", di)); + } + // ---- ------------------ ---- + + // ---- Track multiplicity ---- + for (const char* mod : {":all", ":sel"}) { + h_name = Form("ca_track_multiplicity%s", mod); + fH1D[h_name] = std::make_unique<TH1D>(h_name.c_str(), h_name.c_str(), 50, 0, 50); // HARDCODE + fH1D[h_name]->GetXaxis()->SetTitle("N_{CATrack}"); + fH1D[h_name]->GetYaxis()->SetTitle("Entries"); + + h_name = Form("ca_track_chi2%s", mod); + fH1D[h_name] = std::make_unique<TH1D>(h_name.c_str(), h_name.c_str(), 10000, 0, 10); // HARDCODE + fH1D[h_name]->GetXaxis()->SetTitle("Chi2"); + fH1D[h_name]->GetYaxis()->SetTitle("Entries"); + } + // ---- ------------------ ---- +} + +void CbmStsEfficiency::CheckEfficiency() +{ + LOG(debug) << "Checking efficiency of STS station " << fTestLayer << " using " << fGlbTrks.size() + << " CbmGlobalTracks"; + fVertexFinder->SetTracks(fGlbTrks); + const std::optional<CbmVertex> vertex = fVertexFinder->FindVertex(); + + if (!vertex.has_value()) return; + + if (std::abs(vertex->GetZ()) > fMaxDisTrgVtx) return; + + fH2D["vertex_y_vs_x"]->Fill(vertex->GetX(), vertex->GetY()); + fH2D["vertex_x_vs_z"]->Fill(vertex->GetZ(), vertex->GetX()); + fH2D["vertex_y_vs_z"]->Fill(vertex->GetZ(), vertex->GetY()); + + TVector3 vtx(vertex->GetX(), vertex->GetY(), vertex->GetZ()); + std::map<int32_t, std::vector<bool>> hit_used; + for (auto trk : fGlbTrks) { + double trk_tx = trk->GetParamFirst()->GetTx(); + double trk_ty = trk->GetParamFirst()->GetTy(); + double trk_x0 = trk->GetParamFirst()->GetX(); + double trk_y0 = trk->GetParamFirst()->GetY(); + double trk_z0 = trk->GetParamFirst()->GetZ(); + + // Check if tack comes from vertex + TVector3 p(trk_x0, trk_y0, trk_z0); + TVector3 e(trk_tx, trk_ty, 1); + TVector3 trk_to_vtx = ((vtx - p).Cross(e)) * (1. / e.Mag()); + + fH2D["dca_dx_vs_x"]->Fill(trk_to_vtx.Px(), vtx.Px()); + fH2D["dca_dx_vs_y"]->Fill(trk_to_vtx.Px(), vtx.Py()); + fH2D["dca_dx_vs_z"]->Fill(trk_to_vtx.Px(), vtx.Pz()); + + fH2D["dca_dy_vs_x"]->Fill(trk_to_vtx.Py(), vtx.Px()); + fH2D["dca_dy_vs_y"]->Fill(trk_to_vtx.Py(), vtx.Py()); + fH2D["dca_dy_vs_z"]->Fill(trk_to_vtx.Py(), vtx.Pz()); + + fH2D["dca_dz_vs_x"]->Fill(trk_to_vtx.Pz(), vtx.Px()); + fH2D["dca_dz_vs_y"]->Fill(trk_to_vtx.Pz(), vtx.Py()); + fH2D["dca_dz_vs_z"]->Fill(trk_to_vtx.Pz(), vtx.Pz()); + + if (trk_to_vtx.Mag() > fMaxDCATrkVtx) continue; + + for (int32_t& sensor : fDUTList) { + auto hits = fStsHits[sensor]; + size_t n_of_hits = hits.size(); + + // Extrapolate to the current sensor z-plane + double sensor_z = 0.5 * (fStsGeoInfo[sensor][4] + fStsGeoInfo[sensor][5]); + double predicted_x = trk_x0 + trk_tx * (sensor_z - trk_z0); + double predicted_y = trk_y0 + trk_ty * (sensor_z - trk_z0); + + // Check if the sensor contain the space point + if (predicted_x < fStsGeoInfo[sensor][0] || predicted_x > fStsGeoInfo[sensor][1] + || predicted_y < fStsGeoInfo[sensor][2] || predicted_y > fStsGeoInfo[sensor][3]) { + continue; + } + + int unit = CbmStsAddress::GetElementId(sensor, kStsUnit); + fH2D[Form("Sts%d_track", unit)]->Fill(predicted_x, predicted_y); + fH2D[Form("Sts0x%x_track", sensor)]->Fill(predicted_x, predicted_y); + + if (n_of_hits) { + /* Block to search in available hits */ + double closest_id = 0; + double dx = hits[0]->GetX() - predicted_x - fResiduals[sensor].mean.Px(); + double dy = hits[0]->GetY() - predicted_y - fResiduals[sensor].mean.Py(); + double closest_rho = dx * dx + dy * dy; + + for (size_t idx = 1; idx < n_of_hits; idx++) { + dx = hits[idx]->GetX() - predicted_x - fResiduals[sensor].mean.Px(); + dy = hits[idx]->GetY() - predicted_y - fResiduals[sensor].mean.Py(); + double rho = dx * dx + dy * dy; + + if (rho < closest_rho) { + closest_id = idx; + closest_rho = rho; + } + } + + dx = hits[closest_id]->GetX() - predicted_x - fResiduals[sensor].mean.Px(); + dy = hits[closest_id]->GetY() - predicted_y - fResiduals[sensor].mean.Py(); + + fH2D[Form("Sts%d_dx_vs_x", unit)]->Fill(dx, hits[closest_id]->GetX()); + fH2D[Form("Sts%d_dy_vs_x", unit)]->Fill(dy, hits[closest_id]->GetX()); + fH2D[Form("Sts%d_dx_vs_y", unit)]->Fill(dx, hits[closest_id]->GetY()); + fH2D[Form("Sts%d_dy_vs_y", unit)]->Fill(dy, hits[closest_id]->GetY()); + + fH2D[Form("Sts0x%x_dx_vs_x", sensor)]->Fill(dx, hits[closest_id]->GetX()); + fH2D[Form("Sts0x%x_dy_vs_x", sensor)]->Fill(dy, hits[closest_id]->GetX()); + fH2D[Form("Sts0x%x_dx_vs_y", sensor)]->Fill(dx, hits[closest_id]->GetY()); + fH2D[Form("Sts0x%x_dy_vs_y", sensor)]->Fill(dy, hits[closest_id]->GetY()); + + double sensor_resolution = + fResiduals[sensor].sigma.Mag() != 0 ? fResiduals[sensor].sigma.Mag() : fDefaultResidual; + if (closest_rho <= 3.0 * sensor_resolution) { + fH2D[Form("Sts%d_found", unit)]->Fill(predicted_x, predicted_y); + fH2D[Form("Sts0x%x_found", sensor)]->Fill(predicted_x, predicted_y); + } + } + + + /* Block to search in avaliable hit */ + + } // end sensor under test lopp + } // end track loop +} + +TH2D* FindInactiveArea(TH2D* h, int dead_size_x = 10, int dead_size_y = 10) +{ + TH2D* dead = (TH2D*) h->Clone(); + dead->Reset(); + + for (int bin_y = 1; bin_y < dead->GetNbinsY(); bin_y++) { + for (int bin_x = dead_size_x; bin_x < dead->GetNbinsX() - dead_size_x; bin_x++) { + + bool all_dead = true; + for (int test_bin = -dead_size_x; test_bin <= +dead_size_x; test_bin++) { + if (h->GetBinContent(bin_x + test_bin, bin_y)) { + all_dead = false; + break; + } + } + if (all_dead) { + for (int test_bin = -dead_size_x; test_bin <= +dead_size_x; test_bin++) + dead->SetBinContent(bin_x + test_bin, bin_y, 1); + } + } + } + + for (int bin_x = 1; bin_x < dead->GetNbinsX(); bin_x++) { + for (int bin_y = dead_size_y; bin_y < dead->GetNbinsY() - dead_size_y; bin_y++) { + + bool all_dead = true; + for (int test_bin = -dead_size_y; test_bin <= +dead_size_y; test_bin++) { + if (h->GetBinContent(bin_x, bin_y + test_bin)) { + all_dead = false; + break; + } + } + if (all_dead) { + for (int test_bin = -dead_size_y; test_bin <= +dead_size_y; test_bin++) + dead->SetBinContent(bin_x, bin_y + test_bin, 1); + } + } + } + + return dead; +} + +void CbmStsEfficiency::Efficiency(uint32_t sensor) +{ + std::string h_name_base = ""; + if (sensor == fTestLayer) { + h_name_base = Form("Sts%d", sensor); + } + else if (sensor > 8) { + if (CbmStsAddress::GetElementId(sensor, kStsUnit) != fTestLayer) return; + h_name_base = Form("Sts0x%x", sensor); + } + auto found = fH2D[Form("%s_found", h_name_base.c_str())].get(); + auto track = fH2D[Form("%s_track", h_name_base.c_str())].get(); + + if (found == nullptr || track == nullptr) return; + + TH2D* inactive_area = FindInactiveArea(fH2D[Form("%s_hit_map", h_name_base.c_str())].get(), 50, 50); + + // Manual integration + int n_of_found = 0; + int n_of_track = 0; + std::vector<double> samples; + + for (int bin_y = 50; bin_y < inactive_area->GetNbinsY() - 50; bin_y++) { + for (int bin_x = 50; bin_x < inactive_area->GetNbinsX() - 50; bin_x++) { + if (!inactive_area->GetBinContent(bin_x, bin_y)) { + n_of_found += found->GetBinContent(bin_x, bin_y); + n_of_track += track->GetBinContent(bin_x, bin_y); + } + } + } + LOG(debug) << n_of_found << "\t" << n_of_track; + double efficiency = n_of_track != 0 ? double(n_of_found) / n_of_track : -1; + if (efficiency != -1) { + samples.push_back(efficiency); + } + + double mean = 0; + double sigma = 0; + int n = samples.size(); + if (n == 0) { + return; + } + + // Take efficiency as mean value + for (double& val : samples) { + mean += val; + } + mean = double(mean / n); + + // Take error as RMS of the values + for (double& val : samples) { + sigma += (val - mean) * (val - mean); + } + sigma = sqrt(double(sigma / n)); + + std::cout << Form("0x%x:\t%0.4f\t%0.4f\n", sensor, mean, sigma); +} + +void CbmStsEfficiency::FinishTask() +{ + for (auto& [element, geo] : fStsGeoInfo) { + Efficiency(element); + } + + SaveToFile(); +} + + +void CbmStsEfficiency::ProcessEvent(CbmEvent* evt) +{ + int nb_sts_hits = evt->GetNofData(ECbmDataType::kStsHit); + + for (int hit_evt_idx = 0; hit_evt_idx < nb_sts_hits; hit_evt_idx++) { + int sts_hit_idx = evt->GetIndex(ECbmDataType::kStsHit, hit_evt_idx); + CbmStsHit* hit = (CbmStsHit*) fStsHitArray->UncheckedAt(sts_hit_idx); + ProcessHit(hit); + } + + int nb_of_glob_trk = evt->GetNofData(ECbmDataType::kGlobalTrack); + for (int evt_glob_trk_idx = 0; evt_glob_trk_idx < nb_of_glob_trk; evt_glob_trk_idx++) { + int glob_trk_idx = evt->GetIndex(ECbmDataType::kGlobalTrack, evt_glob_trk_idx); + CbmGlobalTrack* glob_trk = (CbmGlobalTrack*) fGlbTrkArray->UncheckedAt(glob_trk_idx); + ProcessGlobalTrack(glob_trk); + } + + fH1D["ca_track_multiplicity:all"]->Fill(nb_of_glob_trk); + fH1D["ca_track_multiplicity:sel"]->Fill(fGlbTrks.size()); + + if (fGlbTrks.size()) CheckEfficiency(); + + fGlbTrks.clear(); + fStsHits.clear(); +} + +void CbmStsEfficiency::ProcessGlobalTrack(CbmGlobalTrack* trk) +{ + auto sts_trk_idx = trk->GetStsTrackIndex(); + auto rich_trk_idx = trk->GetRichRingIndex(); + auto much_trk_idx = trk->GetMuchTrackIndex(); + auto trd_trk_idx = trk->GetTrdTrackIndex(); + auto tof_trk_idx = trk->GetTofTrackIndex(); + double trk_p_val = TMath::Prob(trk->GetChi2(), trk->GetNDF()); + fH1D["ca_track_chi2:all"]->Fill(trk->GetChi2()); + + // Apply GlobalTracks cuts + int32_t mvd_trk_size = sts_trk_idx != -1 && fStsTrkArray != nullptr + ? ((CbmStsTrack*) fStsTrkArray->UncheckedAt(sts_trk_idx))->GetNofMvdHits() + : 0; + + int32_t sts_trk_size = sts_trk_idx != -1 && fStsTrkArray != nullptr + ? ((CbmStsTrack*) fStsTrkArray->UncheckedAt(sts_trk_idx))->GetNofStsHits() + : 0; + + int32_t rich_trk_size = rich_trk_idx != -1 && fRchTrkArray != nullptr + ? ((CbmTrack*) fRchTrkArray->UncheckedAt(rich_trk_idx))->GetNofHits() + : 0; + + int32_t much_trk_size = much_trk_idx != -1 && fMchTrkArray != nullptr + ? ((CbmTrack*) fMchTrkArray->UncheckedAt(much_trk_idx))->GetNofHits() + : 0; + + int32_t trd_trk_size = trd_trk_idx != -1 && fTrdTrkArray != nullptr + ? ((CbmTrack*) fTrdTrkArray->UncheckedAt(trd_trk_idx))->GetNofHits() + : 0; + + int32_t tof_trk_size = tof_trk_idx != -1 && fTofTrkArray != nullptr + ? ((CbmTrack*) fTofTrkArray->UncheckedAt(tof_trk_idx))->GetNofHits() + : 0; + + if (fAnalysisCuts != nullptr + && (!fAnalysisCuts->Check(CbmCutId::kGlobalTrackMvdSize, mvd_trk_size) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackStsSize, sts_trk_size) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackRichSize, rich_trk_size) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackMuchSize, much_trk_size) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackTrdSize, trd_trk_size) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackTofSize, tof_trk_size) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackChi2, trk->GetChi2()) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackPval, trk_p_val))) { + return; + } + + fH1D["ca_track_chi2:sel"]->Fill(trk->GetChi2()); + fGlbTrks.push_back(trk); +} + +void CbmStsEfficiency::ProcessHit(CbmStsHit* hit) +{ + if (hit == nullptr) return; + uint32_t address = hit->GetAddress(); + uint32_t unit = CbmStsAddress::GetElementId(address, kStsUnit); + + if (unit != fTestLayer) return; + + if (fAnalysisCuts != nullptr && !fAnalysisCuts->CheckStsHit(hit, fStsCluArray)) return; + + fStsHits[address].push_back(hit); + fH2D[Form("Sts%u_hit_map", unit)]->Fill(hit->GetX(), hit->GetY()); + fH2D[Form("Sts0x%x_hit_map", address)]->Fill(hit->GetX(), hit->GetY()); +} + +void CbmStsEfficiency::Exec(Option_t*) +{ + // Check if CbmEvent + if (fCbmEvtArray != nullptr) { + LOG(info) << "Running CbmStsEfficiency - Event-like - " << entry_; + + uint32_t nb_events = fCbmEvtArray->GetEntriesFast(); + for (uint32_t evt_idx = 0; evt_idx < nb_events; evt_idx++) { + CbmEvent* event = (CbmEvent*) fCbmEvtArray->UncheckedAt(evt_idx); + if (fAnalysisCuts != nullptr && !fAnalysisCuts->CheckEvent(event)) continue; + LOG(debug) << Form("Process CbmEvent %d / %d", evt_idx, nb_events); + ProcessEvent(event); + } + } + else { + + LOG(info) << "Running CbmStsEfficiency - TS-like - "; + uint32_t n_of_hits = fStsHitArray->GetEntriesFast(); + for (uint32_t hit_idx = 0; hit_idx < n_of_hits; hit_idx++) { + CbmStsHit* sts_hit = (CbmStsHit*) fStsHitArray->UncheckedAt(hit_idx); + ProcessHit(sts_hit); + } + uint32_t nb_of_glob_trk = fGlbTrkArray->GetEntriesFast(); + LOG(debug) << Form("Number of GlobalTracks: %u", nb_of_glob_trk); + for (uint32_t glob_trk_idx = 0; glob_trk_idx < nb_of_glob_trk; glob_trk_idx++) { + CbmGlobalTrack* glob_trk = (CbmGlobalTrack*) fGlbTrkArray->UncheckedAt(glob_trk_idx); + ProcessGlobalTrack(glob_trk); + } + + if (fGlbTrks.size()) CheckEfficiency(); + + fGlbTrks.clear(); + fStsHits.clear(); + } + entry_++; +} + +InitStatus CbmStsEfficiency::Init() +{ + LOG(debug) << "Init CbmStsEfficiency ..."; + + FairRootManager* ioman = FairRootManager::Instance(); + if (ioman != nullptr) { + fCbmEvtArray = (TClonesArray*) ioman->GetObject("CbmEvent"); + + fGlbTrkArray = (TClonesArray*) ioman->GetObject("GlobalTrack"); + + fStsTrkArray = (TClonesArray*) ioman->GetObject("StsTrack"); + fRchTrkArray = (TClonesArray*) ioman->GetObject("RichTrack"); + fMchTrkArray = (TClonesArray*) ioman->GetObject("MuchTrack"); + fTrdTrkArray = (TClonesArray*) ioman->GetObject("TrdTrack"); + fTofTrkArray = (TClonesArray*) ioman->GetObject("TofTrack"); + + fStsHitArray = (TClonesArray*) ioman->GetObject("StsHit"); + + fStsCluArray = (TClonesArray*) ioman->GetObject("StsCluster"); + } + + LoadSetup(); + + BookHistograms(); + + std::stringstream ss; + for (auto s : fDUTList) { + ss << Form("\t\t0x%x - U%d L%d M%d\n", s, CbmStsAddress::GetElementId(s, kStsUnit), + CbmStsAddress::GetElementId(s, kStsLadder), CbmStsAddress::GetElementId(s, kStsModule)); + } + + LOG(info) << "Max vtx target dz: " << fMaxDisTrgVtx; + LOG(info) << "Max trk vtx DCA: " << fMaxDCATrkVtx; + LOG(info) << "Default residual: " << fDefaultResidual; + LOG(info) << "Test layer: " << fTestLayer; + LOG(info) << "Layer modules:\n" << ss.str(); + + return kSUCCESS; +} + +void CbmStsEfficiency::SetSensorResidual(int32_t address, TVector3 m, TVector3 s) +{ + fResiduals[address] = Residual(m, s); +} + +void CbmStsEfficiency::SetResidual(std::string file_name) +{ + std::ifstream in(file_name); + int32_t sensor; + double mean_x, mean_y, sigma_x, sigma_y; + while (in >> std::hex >> sensor >> std::dec >> mean_x >> mean_y >> sigma_x >> sigma_y) { + SetSensorResidual(sensor, TVector3(mean_x, mean_y, 0), TVector3(sigma_x, sigma_y, 0)); + } +} + + +void CbmStsEfficiency::SetVertexFinder(CbmDcaVertexFinder* vtx_finder) { fVertexFinder = vtx_finder; } diff --git a/analysis/detectors/sts/CbmStsEfficiency.h b/analysis/detectors/sts/CbmStsEfficiency.h new file mode 100644 index 0000000000000000000000000000000000000000..29469fb0627748841808130e911e3b62ab82f7e8 --- /dev/null +++ b/analysis/detectors/sts/CbmStsEfficiency.h @@ -0,0 +1,115 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#ifndef CBMSTSEFFICIENCY_H +#define CBMSTSEFFICIENCY_H + +#include "CbmDcaVertexFinder.h" +#include "CbmEvent.h" +#include "CbmGlobalTrack.h" +#include "CbmStsAnaBase.h" +#include "CbmStsCluster.h" +#include "CbmStsHit.h" +#include "CbmStsTrack.h" +#include "CbmStsUtils.h" +#include "CbmTrack.h" + +#include <FairTask.h> + +#include <TClonesArray.h> +#include <TVector3.h> + +#include <cstring> +#include <map> +#include <unordered_map> +#include <unordered_set> + +struct Residual { + TVector3 mean; + TVector3 sigma; + Residual() {} + Residual(TVector3 m, TVector3 s) + { + mean = m; + sigma = s; + } +}; + +/** \class CbmStsEfficiency + * \brief Task for Hit Reconstruction Efficiency (HRE) analysis of STS hits. + * + * This class inherits from FairTask and CbmStsAnaBase. + * It provides functionality for analyzing the CA Tracking based HRE + * of STS hits. + */ +class CbmStsEfficiency : public FairTask, public CbmStsAnaBase { + public: + CbmStsEfficiency() = default; + ~CbmStsEfficiency() = default; + + /** \brief Parameterized constructor */ + CbmStsEfficiency(uint32_t); + + /** \brief Parameterized constructor */ + CbmStsEfficiency(uint32_t, double, double, double); + + InitStatus Init(); + void Exec(Option_t*); + void FinishTask(); + + void SetSensorResidual(int32_t, TVector3, TVector3); + void SetResidual(std::string); + + void SetVertexFinder(CbmDcaVertexFinder*); + + bool fDrawOpt{true}; + + private: + uint32_t fTestLayer{0}; + std::vector<int32_t> fDUTList; + double fMaxDisTrgVtx{0.25}; + double fMaxDCATrkVtx{0.25}; + double fDefaultResidual{0.12}; + std::map<int32_t, Residual> fResiduals; + + + CbmDcaVertexFinder* fVertexFinder; + + std::vector<CbmGlobalTrack*> fGlbTrks; + std::map<int32_t, std::vector<CbmStsHit*>> fStsHits; + + TClonesArray* fCbmEvtArray{nullptr}; + TClonesArray* fGlbTrkArray{nullptr}; + TClonesArray* fStsTrkArray{nullptr}; + TClonesArray* fRchTrkArray{nullptr}; + TClonesArray* fMchTrkArray{nullptr}; + TClonesArray* fTrdTrkArray{nullptr}; + TClonesArray* fTofTrkArray{nullptr}; + TClonesArray* fStsHitArray{nullptr}; + TClonesArray* fStsCluArray{nullptr}; + + void BookHistograms(); + + void Efficiency(uint32_t); + + /** \brief Process an Cbm events + * It filters event based on the provided CbmCutMap + */ + void ProcessEvent(CbmEvent*); + + /** \brief Process an Global tracks + * It filters the tracks based on the provided CbmCutMap + */ + void ProcessGlobalTrack(CbmGlobalTrack*); + + /** \brief Process an STS hit + * It filters hits based on the provided CbmCutMap + */ + void ProcessHit(CbmStsHit*); + + void CheckEfficiency(); + + ClassDef(CbmStsEfficiency, 1); +}; +#endif diff --git a/analysis/detectors/sts/CbmStsHitAna.cxx b/analysis/detectors/sts/CbmStsHitAna.cxx new file mode 100644 index 0000000000000000000000000000000000000000..15785ec3d65274833296df70c9d1ff68a5b8b6e0 --- /dev/null +++ b/analysis/detectors/sts/CbmStsHitAna.cxx @@ -0,0 +1,241 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#include "CbmStsHitAna.h" + +#include <CbmStsTrack.h> + +#include <ctime> +#include <iostream> +#include <typeinfo> + +CbmStsHitAna::CbmStsHitAna() : fHitModifier({":all"}) {} + +CbmStsHitAna::CbmStsHitAna(std::string cal_par_file) : fHitModifier({":all"}), fCalibrationFile(cal_par_file) {} + +InitStatus CbmStsHitAna::Init() +{ + if (fModuleParSet == nullptr) { + LOG(debug) << "Loading charge calibration ..."; + fModuleParSet = std::make_unique<CbmStsParSetModule>("CbmParSetModule", "STS parameters", "Default"); + fModuleParSet->LoadParASCII(fCalibrationFile); + } + + FairRootManager* ioman = FairRootManager::Instance(); + if (ioman == nullptr) return kERROR; + + fCbmEvtArray = (TClonesArray*) ioman->GetObject("CbmEvent"); + + fGlbTrkArray = (TClonesArray*) ioman->GetObject("GlobalTrack"); + fStsTrkArray = (TClonesArray*) ioman->GetObject("StsTrack"); + fRchTrkArray = (TClonesArray*) ioman->GetObject("RichTrack"); + fMchTrkArray = (TClonesArray*) ioman->GetObject("MuchTrack"); + fTrdTrkArray = (TClonesArray*) ioman->GetObject("TrdTrack"); + fTofTrkArray = (TClonesArray*) ioman->GetObject("TofTrack"); + + fStsHitArray = (TClonesArray*) ioman->GetObject("StsHit"); + fStsCluArray = (TClonesArray*) ioman->GetObject("StsCluster"); + + if (fGlbTrkArray) fHitModifier.push_back(":trk"); + + LoadSetup(); + + return kSUCCESS; +} + +void CbmStsHitAna::BookHistograms(int32_t address) +{ + LOG(debug) << Form("Booking histograms for module: 0x%x", address); + + const CbmStsParModule& par_module = fModuleParSet->GetParModule(address); + const auto [n_side_binning, p_side_binning] = cbm_sts_utils::ChargeBinning(par_module, fMaxCluSize); + + std::string h_name; + + double x_min = fStsGeoInfo.count(address) ? fStsGeoInfo[address][0] : -15; + double x_max = fStsGeoInfo.count(address) ? fStsGeoInfo[address][1] : +15; + double y_min = fStsGeoInfo.count(address) ? fStsGeoInfo[address][2] : -15; + double y_max = fStsGeoInfo.count(address) ? fStsGeoInfo[address][3] : +15; + + double t_min = -150 - 0.5 * cbm_sts_utils::kStsClock; + double t_max = +150 + 0.5 * cbm_sts_utils::kStsClock; + + auto x_binning = cbm_sts_utils::HBinning{uint32_t((x_max - x_min) / cbm_sts_utils::kStsDx), x_min, x_max}; + auto y_binning = cbm_sts_utils::HBinning{uint32_t((y_max - y_min) / cbm_sts_utils::kStsDy), y_min, y_max}; + auto t_binning = cbm_sts_utils::HBinning{uint32_t((t_max - t_min) / cbm_sts_utils::kStsClock), t_min, t_max}; + + for (const char* mod : fHitModifier) { + h_name = Form("0x%x_Q_asymmetry%s", address, mod); + fH1D[h_name] = std::make_unique<TH1D>(h_name.c_str(), h_name.c_str(), 1000, -1.01, +1.01); + + h_name = Form("0x%x_Qp_vs_Qn%s", address, mod); + fH2D[h_name] = std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), n_side_binning.n_of_bins, + n_side_binning.x_min, n_side_binning.x_max, p_side_binning.n_of_bins, + p_side_binning.x_min, p_side_binning.x_max); + + h_name = Form("0x%x_Qp_vs_size%s", address, mod); + fH2D[h_name] = std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), p_side_binning.n_of_bins, + p_side_binning.x_min, p_side_binning.x_max, fMaxCluSize, 1, fMaxCluSize + 1); + + h_name = Form("0x%x_Qn_vs_size%s", address, mod); + fH2D[h_name] = std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), n_side_binning.n_of_bins, + n_side_binning.x_min, n_side_binning.x_max, fMaxCluSize, 1, fMaxCluSize + 1); + + h_name = Form("0x%x_psize_vs_nsize%s", address, mod); + fH2D[h_name] = std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), fMaxCluSize, 1, fMaxCluSize + 1, fMaxCluSize, + 1, fMaxCluSize + 1); + + h_name = Form("0x%x_y_vs_x%s", address, mod); + fH2D[h_name] = std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), x_binning.n_of_bins, x_binning.x_min, + x_binning.x_max, y_binning.n_of_bins, y_binning.x_min, y_binning.x_max); + + h_name = Form("0x%x_cluster_dt%s", address, mod); + fH1D[h_name] = + std::make_unique<TH1D>(h_name.c_str(), h_name.c_str(), t_binning.n_of_bins, t_binning.x_min, t_binning.x_max); + } +} + +void CbmStsHitAna::ProcessEvent(CbmEvent* evt) +{ + if (fAnalysisCuts != nullptr && !fAnalysisCuts->CheckEvent(evt)) return; + + int nb_sts_hits = evt->GetNofData(ECbmDataType::kStsHit); + for (int hit_evt_idx = 0; hit_evt_idx < nb_sts_hits; hit_evt_idx++) { + int sts_hit_idx = evt->GetIndex(ECbmDataType::kStsHit, hit_evt_idx); + CbmStsHit* hit = (CbmStsHit*) fStsHitArray->UncheckedAt(sts_hit_idx); + ProcessHit(hit, false); + } + + int nb_of_glob_trk = evt->GetNofData(ECbmDataType::kGlobalTrack); + for (int evt_glob_trk_idx = 0; evt_glob_trk_idx < nb_of_glob_trk; evt_glob_trk_idx++) { + int glob_trk_idx = evt->GetIndex(ECbmDataType::kGlobalTrack, evt_glob_trk_idx); + CbmGlobalTrack* glob_trk = (CbmGlobalTrack*) fGlbTrkArray->UncheckedAt(glob_trk_idx); + ProcessGlobalTrack(glob_trk); + } +} + +void CbmStsHitAna::ProcessHit(CbmStsHit* hit, bool belong_to_trk) +{ + if (hit == nullptr) return; + int32_t address = hit->GetAddress(); + + if (fAnalysisCuts != nullptr && !fAnalysisCuts->CheckStsHit(hit, fStsCluArray)) { + return; + } + + if (!fAddressBook.count(address)) { + fAddressBook.insert(address); + BookHistograms(address); + } + + double q_n = cbm_sts_utils::GetHitChargeF(hit, fStsCluArray); + double q_p = cbm_sts_utils::GetHitChargeB(hit, fStsCluArray); + + int32_t size_n = cbm_sts_utils::GetHitCluSizeF(hit, fStsCluArray); + int32_t size_p = cbm_sts_utils::GetHitCluSizeB(hit, fStsCluArray); + + const char* mod = fHitModifier[belong_to_trk]; + + fH1D[Form("0x%x_Q_asymmetry%s", address, mod)]->Fill(cbm_sts_utils::GetHitChargeAsy(hit, fStsCluArray)); + fH2D[Form("0x%x_Qp_vs_Qn%s", address, mod)]->Fill(q_n, q_p); + fH2D[Form("0x%x_Qp_vs_size%s", address, mod)]->Fill(q_p, size_p); + fH2D[Form("0x%x_Qn_vs_size%s", address, mod)]->Fill(q_n, size_n); + fH2D[Form("0x%x_psize_vs_nsize%s", address, mod)]->Fill(size_p, size_n); + fH2D[Form("0x%x_y_vs_x%s", address, mod)]->Fill(hit->GetX(), hit->GetY()); + fH1D[Form("0x%x_cluster_dt%s", address, mod)]->Fill(cbm_sts_utils::GetHitTimeF(hit, fStsCluArray) + - cbm_sts_utils::GetHitTimeB(hit, fStsCluArray)); +} + +void CbmStsHitAna::ProcessGlobalTrack(CbmGlobalTrack* trk) +{ + auto sts_trk_idx = trk->GetStsTrackIndex(); + auto rich_trk_idx = trk->GetRichRingIndex(); + auto much_trk_idx = trk->GetMuchTrackIndex(); + auto trd_trk_idx = trk->GetTrdTrackIndex(); + auto tof_trk_idx = trk->GetTofTrackIndex(); + float trk_p_val = TMath::Prob(trk->GetChi2(), trk->GetNDF()); + + // Apply GlobalTracks cuts + int32_t mvd_trk_size = sts_trk_idx != -1 && fStsTrkArray != nullptr + ? ((CbmStsTrack*) fStsTrkArray->UncheckedAt(sts_trk_idx))->GetNofMvdHits() + : 0; + + int32_t sts_trk_size = sts_trk_idx != -1 && fStsTrkArray != nullptr + ? ((CbmStsTrack*) fStsTrkArray->UncheckedAt(sts_trk_idx))->GetNofStsHits() + : 0; + + int32_t rich_trk_size = rich_trk_idx != -1 && fRchTrkArray != nullptr + ? ((CbmTrack*) fRchTrkArray->UncheckedAt(rich_trk_idx))->GetNofHits() + : 0; + + int32_t much_trk_size = much_trk_idx != -1 && fMchTrkArray != nullptr + ? ((CbmTrack*) fMchTrkArray->UncheckedAt(much_trk_idx))->GetNofHits() + : 0; + + int32_t trd_trk_size = trd_trk_idx != -1 && fTrdTrkArray != nullptr + ? ((CbmTrack*) fTrdTrkArray->UncheckedAt(trd_trk_idx))->GetNofHits() + : 0; + + int32_t tof_trk_size = tof_trk_idx != -1 && fTofTrkArray != nullptr + ? ((CbmTrack*) fTofTrkArray->UncheckedAt(tof_trk_idx))->GetNofHits() + : 0; + + if (fAnalysisCuts != nullptr + && (!fAnalysisCuts->Check(CbmCutId::kGlobalTrackMvdSize, mvd_trk_size) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackStsSize, sts_trk_size) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackRichSize, rich_trk_size) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackMuchSize, much_trk_size) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackTrdSize, trd_trk_size) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackTofSize, tof_trk_size) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackChi2, trk->GetChi2()) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackPval, trk_p_val))) { + return; + } + LOG(debug) << Form("Processing %d StsHit that were attached to a CATrack", sts_trk_size); + CbmStsTrack* sts_track = (CbmStsTrack*) fStsTrkArray->UncheckedAt(sts_trk_idx); + + // ------------------------------------ + // Process StsHit attached to a CATrack + // ------------------------------------ + for (int hit_idx = 0; hit_idx < sts_trk_size; hit_idx++) { + uint32_t sts_hit_idx = sts_track->GetStsHitIndex(hit_idx); + CbmStsHit* sts_hit = (CbmStsHit*) fStsHitArray->UncheckedAt(sts_hit_idx); + ProcessHit(sts_hit, true); + } + // ------------------------------------ +} + +void CbmStsHitAna::Exec(Option_t*) +{ + if (fCbmEvtArray != nullptr) { + uint32_t nb_events = fCbmEvtArray->GetEntriesFast(); + double avg_nb_sts_hits = 0; + double avg_nb_of_glob_trk = 0; + for (uint32_t evt_idx = 0; evt_idx < nb_events; evt_idx++) { + ProcessEvent((CbmEvent*) fCbmEvtArray->UncheckedAt(evt_idx)); + avg_nb_sts_hits += ((CbmEvent*) fCbmEvtArray->UncheckedAt(evt_idx))->GetNofData(ECbmDataType::kStsHit); + avg_nb_of_glob_trk += ((CbmEvent*) fCbmEvtArray->UncheckedAt(evt_idx))->GetNofData(ECbmDataType::kGlobalTrack); + } + LOG_IF(info, nb_events) << "Running CbmStsHitAna - Event-like - Entry: " << entry_ << "\tEvents: " << nb_events + << "\tAvg StsHit/Events: " << avg_nb_sts_hits / nb_events + << "\tAvg GlobalTrk/Events: " << avg_nb_of_glob_trk / nb_events; + LOG_IF(info, !nb_events) << "Running CbmStsHitAna - Event-like - Entry: " << entry_ << "\tEvents: " << nb_events; + } + else { + uint32_t n_of_hits = fStsHitArray->GetEntriesFast(); + LOG(info) << "Running CbmStsHitAna - Time-like - Entry: " << entry_ << "\tStsHits: " << n_of_hits; + for (uint32_t hit_idx = 0; hit_idx < n_of_hits; hit_idx++) { + CbmStsHit* sts_hit = (CbmStsHit*) fStsHitArray->UncheckedAt(hit_idx); + ProcessHit(sts_hit, false); + } + } + entry_++; +} + +void CbmStsHitAna::Finish() +{ + SaveToFile(); + fH1D.clear(); + fH2D.clear(); +} diff --git a/analysis/detectors/sts/CbmStsHitAna.h b/analysis/detectors/sts/CbmStsHitAna.h new file mode 100644 index 0000000000000000000000000000000000000000..fcefecd4e4c761a94325302f90e03e794411f0c6 --- /dev/null +++ b/analysis/detectors/sts/CbmStsHitAna.h @@ -0,0 +1,106 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#ifndef CBMSTSHITANA_H +#define CBMSTSHITANA_H + +#include "CbmEvent.h" +#include "CbmGlobalTrack.h" +#include "CbmStsAnaBase.h" +#include "CbmStsCluster.h" +#include "CbmStsHit.h" +#include "CbmStsParSetModule.h" +#include "CbmStsUtils.h" + +#include <FairTask.h> + +#include <TClonesArray.h> + +#include <cstring> +#include <map> +#include <unordered_set> + +/** + * @class CbmStsHitAna + * @brief Task for detailed charge-size analysis of STS hits. + * + * This class inherits from FairTask and CbmStsAnaBase and is designed to + * perform detailed analysis of charge and size correlations in STS (Silicon Tracking System) hits. + * + * Key functionalities include: + * + * - **Charge Correlation**: + * Analyzes the correlation of charge between the p-side and n-side of STS sensors. + * + * - **Time Correlation**: + * Analyzes the correlation of time between the p-side and n-side cluster forming a hit. + * + * - **Cluster Size Correlation**: + * Evaluates the size correlation between p-side and n-side clusters. + * + * - **Charge vs. Size Analysis**: + * Performs charge versus cluster size studies separately for each sensor side. + * + * - **Hit Type Separation**: + * All analyses are performed separately for: + * - **All hits** (all) - includes all reconstructed hits. + * - **Track hits** (trk) - includes only hits associated with reconstructed tracks. + */ + +class CbmStsHitAna : public FairTask, public CbmStsAnaBase { + public: + CbmStsHitAna(); + ~CbmStsHitAna() = default; + + /** \brief Constructor + * \param cal_par_file charge calibration file + */ + CbmStsHitAna(std::string cal_par_file); + + InitStatus Init(); + void Exec(Option_t*); + void Finish(); + + private: + uint32_t fMaxCluSize{10}; + std::vector<const char*> fHitModifier; + + std::unique_ptr<CbmStsParSetModule> fModuleParSet; + std::string fCalibrationFile; + + TClonesArray* fCbmEvtArray{nullptr}; + + TClonesArray* fGlbTrkArray{nullptr}; + TClonesArray* fStsTrkArray{nullptr}; + TClonesArray* fRchTrkArray{nullptr}; + TClonesArray* fMchTrkArray{nullptr}; + TClonesArray* fTrdTrkArray{nullptr}; + TClonesArray* fTofTrkArray{nullptr}; + + TClonesArray* fStsHitArray{nullptr}; + TClonesArray* fStsCluArray{nullptr}; + + void BookHistograms(int32_t); + + /** \brief Process an Cbm events + * It filters event based on the provided CbmCutMap + */ + void ProcessEvent(CbmEvent*); + + /** \brief Process an STS hit + * It filters hits based on the provided CbmCutMap and fill the corresponding histogram + * \param hit StsHit to proccess + * \param belong_to_trk Specify that the hit belongs to a track, branching the histogram filling + */ + void ProcessHit(CbmStsHit* hit, bool belong_to_trk = false); + + /** \brief Process an STS hit attached to a Track + * It filters hits based on the provided CbmCutMap + * and check the track filters + */ + void ProcessGlobalTrack(CbmGlobalTrack*); + + ClassDef(CbmStsHitAna, 1); +}; +#endif diff --git a/analysis/detectors/sts/CbmStsRecoBeamSpot.cxx b/analysis/detectors/sts/CbmStsRecoBeamSpot.cxx new file mode 100644 index 0000000000000000000000000000000000000000..16ed149009b6a87a30ed66b1a7d69cacca87561f --- /dev/null +++ b/analysis/detectors/sts/CbmStsRecoBeamSpot.cxx @@ -0,0 +1,205 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#include "CbmStsRecoBeamSpot.h" + +#include "CbmStsAddress.h" + +void CbmStsRecoBeamSpot::AddTarget(CbmTarget* trg) { AddTarget("", trg); } +void CbmStsRecoBeamSpot::AddTarget(std::string trg_name, CbmTarget* trg) +{ + if (trg_name.length() == 0) { + trg_name = Form("target_%ld", fTargets.size()); + } + fTargets[trg_name] = trg; +} + +void CbmStsRecoBeamSpot::BookHistograms() +{ + std::string h_name; + std::string h_name_base = ""; + for (auto& [element_i, geo_i] : fStsGeoInfo) { + if (element_i < 8) continue; + + for (auto& [element_j, geo_j] : fStsGeoInfo) { + if (element_j < 8) continue; + int32_t unit_i = CbmStsAddress::GetElementId(element_i, kStsUnit); + int32_t unit_j = CbmStsAddress::GetElementId(element_j, kStsUnit); + + if (unit_i >= unit_j) continue; + + for (auto& [trg_name, trg] : fTargets) { + h_name_base = Form("0x%x:0x%x_%s", element_i, element_j, trg_name.c_str()); + + fSampleRangeXmin = trg->GetPosition().Px() - 10.; // HARDCODE + fSampleRangeXmax = trg->GetPosition().Px() + 10.; // HARDCODE + fSampleRangeYmin = trg->GetPosition().Py() - 10.; // HARDCODE + fSampleRangeYmax = trg->GetPosition().Py() + 10.; // HARDCODE + fSampleRangeZmin = trg->GetPosition().Pz() - 10.; // HARDCODE + fSampleRangeZmax = trg->GetPosition().Pz() + 10.; // HARDCODE + + + unsigned int nb_bins_x = (fSampleRangeXmax - fSampleRangeXmin) / fSampleBinSizeX; + unsigned int nb_bins_y = (fSampleRangeYmax - fSampleRangeYmin) / fSampleBinSizeY; + unsigned int nb_bins_z = (fSampleRangeZmax - fSampleRangeZmin) / fSampleBinSizeZ; + + h_name = Form("%s_Y_vs_X", h_name_base.c_str()); + fH2D[h_name] = std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), nb_bins_x, fSampleRangeXmin, + fSampleRangeXmax, nb_bins_y, fSampleRangeYmin, fSampleRangeYmax); + fH2D[h_name]->GetXaxis()->SetTitle("X [cm]"); + fH2D[h_name]->GetYaxis()->SetTitle("Y [cm]"); + + h_name = Form("%s_X_vs_Z", h_name_base.c_str()); + fH2D[h_name] = std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), nb_bins_z, fSampleRangeZmin, + fSampleRangeZmax, nb_bins_x, fSampleRangeXmin, fSampleRangeXmax); + fH2D[h_name]->GetXaxis()->SetTitle("Z [cm]"); + fH2D[h_name]->GetYaxis()->SetTitle("X [cm]"); + + h_name = Form("%s_Y_vs_Z", h_name_base.c_str()); + fH2D[h_name] = std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), nb_bins_z, fSampleRangeZmin, + fSampleRangeZmax, nb_bins_y, fSampleRangeYmin, fSampleRangeYmax); + fH2D[h_name]->GetXaxis()->SetTitle("Z [cm]"); + fH2D[h_name]->GetYaxis()->SetTitle("Y [cm]"); + + h_name = Form("%s_Y_beam_vs_X_beam", h_name_base.c_str()); + fH2D[h_name] = std::make_unique<TH2D>(h_name.c_str(), h_name.c_str(), nb_bins_x, fSampleRangeXmin, + fSampleRangeXmax, nb_bins_y, fSampleRangeYmin, fSampleRangeYmax); + fH2D[h_name]->GetXaxis()->SetTitle("X_beam [cm]"); + fH2D[h_name]->GetYaxis()->SetTitle("Y_beam [cm]"); + + } // end targets loop + } // end sts_i modules loop + } // end sts_j modules loop +} + +TVector3 CbmStsRecoBeamSpot::ExtrapolateTrackTo(CbmPixelHit* hit_0, CbmPixelHit* hit_1, CbmTarget* trg) +{ + TVector3 n0 = trg->GetPosition(); + TVector3 n1(.0, .0, 1.); + n1.RotateY(trg->GetRotation()); + + TVector3 l0(hit_0->GetX(), hit_0->GetY(), hit_0->GetZ()); + TVector3 l1(hit_1->GetX() - hit_0->GetX(), hit_1->GetY() - hit_0->GetY(), hit_1->GetZ() - hit_0->GetZ()); + + double l1_dot_n1 = l1.Dot(n1); + if (l1_dot_n1 <= 1e-6) { // tracklet is parallel (or close to it) to target plane + throw std::invalid_argument("Track-let parallel to target plane"); + } + + double t = (n0 - l0).Dot(n1) / l1.Dot(n1); + return l0 + t * l1; +} + +void CbmStsRecoBeamSpot::BeamSpotReco() +{ + if (!fStsHits.size()) return; + for (int sts_unit_i = 0; sts_unit_i < nb_sts_station_ - 1; sts_unit_i++) { + for (int sts_unit_j = sts_unit_i + 1; sts_unit_j < nb_sts_station_; sts_unit_j++) { + for (auto sts_hit_i : fStsHits[sts_unit_i]) { + for (auto sts_hit_j : fStsHits[sts_unit_j]) { + int32_t sts_address_i = sts_hit_i->GetAddress(); + int32_t sts_address_j = sts_hit_j->GetAddress(); + + for (auto& [trg_name, trg] : fTargets) { + TVector3 sol = ExtrapolateTrackTo(sts_hit_i, sts_hit_j, trg); + std::string h_name_base = Form("0x%x:0x%x_%s", sts_address_i, sts_address_j, trg_name.c_str()); + + std::string h_name = Form("%s_X_vs_Z", h_name_base.c_str()); + fH2D[h_name]->Fill(sol.Pz(), sol.Px()); + + h_name = Form("%s_Y_vs_Z", h_name_base.c_str()); + fH2D[h_name]->Fill(sol.Pz(), sol.Py()); + + h_name = Form("%s_Y_vs_X", h_name_base.c_str()); + fH2D[h_name]->Fill(sol.Px(), sol.Py()); + + TVector3 sol_beam = sol - trg->GetPosition(); + + double x_beam = sol_beam.Px() / cos(trg->GetRotation()); + double y_beam = sol_beam.Py(); + h_name = Form("%s_Y_beam_vs_X_beam", h_name_base.c_str()); + fH2D[h_name]->Fill(x_beam, y_beam); + } + } + } + } + } + fStsHits.clear(); +} + + +void CbmStsRecoBeamSpot::FinishTask() { SaveToFile(); } + + +void CbmStsRecoBeamSpot::ProcessEvent(CbmEvent* evt) +{ + int nb_sts_hits = evt->GetNofData(ECbmDataType::kStsHit); + if (nb_sts_hits > 0) + for (int hit_evt_idx = 0; hit_evt_idx < nb_sts_hits; hit_evt_idx++) { + int sts_hit_idx = evt->GetIndex(ECbmDataType::kStsHit, hit_evt_idx); + CbmStsHit* hit = (CbmStsHit*) fStsHitArray->UncheckedAt(sts_hit_idx); + ProcessHit(hit); + } +} + +void CbmStsRecoBeamSpot::ProcessHit(CbmStsHit* hit) +{ + if (hit == nullptr) return; + int32_t address = hit->GetAddress(); + int32_t unit = CbmStsAddress::GetElementId(address, kStsUnit); + + if (fAnalysisCuts != nullptr && fAnalysisCuts->CheckStsHit(hit, fStsCluArray)) { + fStsHits[unit].push_back(hit); + } +} + +void CbmStsRecoBeamSpot::Exec(Option_t*) +{ + + // Check if CbmEvent + if (fCbmEvtArray != nullptr) { + + uint32_t nb_events = fCbmEvtArray->GetEntriesFast(); + LOG(info) << "Running CbmStsRecoBeamSpot - Event like - Entry:" << entry_ << "\tEvents: " << nb_events; + for (uint32_t evt_idx = 0; evt_idx < nb_events; evt_idx++) { + CbmEvent* event = (CbmEvent*) fCbmEvtArray->UncheckedAt(evt_idx); + if (fAnalysisCuts != nullptr && fAnalysisCuts->CheckEvent(event)) { + ProcessEvent(event); + BeamSpotReco(); + } + } + } + else { + + uint32_t n_of_hits = fStsHitArray->GetEntriesFast(); + LOG(info) << "Running CbmStsRecoBeamSpot - Time like - Entry:" << entry_ << "\tStsHit: " << n_of_hits; + for (uint32_t hit_idx = 0; hit_idx < n_of_hits; hit_idx++) { + CbmStsHit* sts_hit = (CbmStsHit*) fStsHitArray->UncheckedAt(hit_idx); + ProcessHit(sts_hit); + } + BeamSpotReco(); + } + entry_++; +} + +InitStatus CbmStsRecoBeamSpot::Init() +{ + LOG(debug) << "Init CbmStsRecoBeamSpot ..."; + + FairRootManager* ioman = FairRootManager::Instance(); + if (ioman == nullptr) return kERROR; + + fCbmEvtArray = (TClonesArray*) ioman->GetObject("CbmEvent"); + fStsHitArray = (TClonesArray*) ioman->GetObject("StsHit"); + fStsCluArray = (TClonesArray*) ioman->GetObject("StsCluster"); + + for (auto& [trg_name, trg] : fTargets) { + LOG(debug) << trg_name << ": " << trg->ToString(); + } + + LoadSetup(); + BookHistograms(); + + return kSUCCESS; +} diff --git a/analysis/detectors/sts/CbmStsRecoBeamSpot.h b/analysis/detectors/sts/CbmStsRecoBeamSpot.h new file mode 100644 index 0000000000000000000000000000000000000000..8ea1172e795931794beb0e8af197e16d01a08ab0 --- /dev/null +++ b/analysis/detectors/sts/CbmStsRecoBeamSpot.h @@ -0,0 +1,113 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#ifndef CBMSTSRECOBEAMSPOT_H +#define CBMSTSRECOBEAMSPOT_H + +#include "CbmEvent.h" +#include "CbmGlobalTrack.h" +#include "CbmStsAnaBase.h" +#include "CbmStsCluster.h" +#include "CbmStsHit.h" +#include "CbmStsTrack.h" +#include "CbmStsUtils.h" +#include "CbmTarget.h" +#include "CbmTrack.h" + +#include <FairTask.h> + +#include <TClonesArray.h> +#include <TVector3.h> + +#include <cstring> +#include <map> +#include <unordered_map> +#include <unordered_set> + +/** \class CbmStsRecoBeamSpot + * \brief Task for reconstructing the beam spot using STS hits. + * + * This class inherits from FairTask and CbmStsAnaBase. + * It provides functionality for reconstructing the beam spot at + * different planes defined by CbmTarget + * using STS hits. + */ +class CbmStsRecoBeamSpot : public FairTask, public CbmStsAnaBase { + public: + CbmStsRecoBeamSpot() = default; + ~CbmStsRecoBeamSpot() = default; + + /** + * @brief Add a CbmTarget object to the list of targets. + * + * Position and rotation are the only relevant members of such class for this task. + * + * @param target CbmTarget ptr to be added + */ + + void AddTarget(CbmTarget* target = nullptr); + + /** + * @brief Add a CbmTarget object to the list of targets with a key as trg_name. + * + * @param trg_name key under which the @param target will be stored. + * If the key is an empty string, added targets will be named as target_<n_of_targets>. + */ + void AddTarget(std::string trg_name = "", CbmTarget* target = nullptr); + + void Exec(Option_t*); + InitStatus Init(); + void FinishTask(); + + private: + /* Beam Spot sampling range + * It is dynamically caculate when multiple targets planes are provided + */ + double fSampleRangeXmin{-10}; // cm + double fSampleRangeXmax{+10}; // cm + double fSampleRangeYmin{-10}; // cm + double fSampleRangeYmax{+10}; // cm + double fSampleRangeZmin{-10}; // cm + double fSampleRangeZmax{+10}; // cm + + /* Bin width for Beam Spot 2D histograms */ + double fSampleBinSizeX{0.01}; + double fSampleBinSizeY{0.01}; + double fSampleBinSizeZ{0.01}; + + std::vector<CbmStsTrack*> fStsTrks; + std::map<int32_t, std::vector<CbmStsHit*>> fStsHits; + + std::map<std::string, CbmTarget*> fTargets; + + + TClonesArray* fCbmEvtArray{nullptr}; + TClonesArray* fStsTrkArray{nullptr}; + TClonesArray* fStsHitArray{nullptr}; + TClonesArray* fStsCluArray{nullptr}; + + /** \brief Reconstruct the beam spot at each target planes */ + void BeamSpotReco(); + + void BookHistograms(); + + /** \brief Extrapolate a track-let to a target plane*/ + TVector3 ExtrapolateTrackTo(CbmPixelHit*, CbmPixelHit*, CbmTarget*); + + /** \brief Process an Cbm events + * It filters event based on the provided CbmCutMap + */ + void ProcessEvent(CbmEvent*); + + /** \brief Process an STS track */ + void ProcessStsTrack(CbmGlobalTrack*); + + /** \brief Process an STS hit + * It filters hits based on the provided CbmCutMap + */ + void ProcessHit(CbmStsHit*); + + ClassDef(CbmStsRecoBeamSpot, 1); +}; +#endif diff --git a/analysis/detectors/sts/CbmStsResolution.cxx b/analysis/detectors/sts/CbmStsResolution.cxx new file mode 100644 index 0000000000000000000000000000000000000000..ba67102876af191051be05f5bf1702e3a4ed0f1c --- /dev/null +++ b/analysis/detectors/sts/CbmStsResolution.cxx @@ -0,0 +1,355 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#include "CbmStsResolution.h" + +#include "CbmPixelHit.h" +#include "CbmStsAddress.h" +#include "CbmStsTrack.h" +#include "TPaveStats.h" +#include "TStyle.h" + +CbmStsResolution::CbmStsResolution(double d_max) : fImpactParMax(d_max) +{ + LOG(debug) << "Creating an instance of CbmStsResolution ..."; +} + +void CbmStsResolution::BookHistograms() +{ + for (auto& [element, geo] : fStsGeoInfo) { + std::string h_name_base = element < 8 ? Form("Sts%d", element) : Form("Sts0x%x", element); + + LOG(debug) << Form("Booking for %s", h_name_base.c_str()); + + double x_min = geo[0]; + double x_max = geo[1]; + double y_min = geo[2]; + double y_max = geo[3]; + + unsigned int nb_bins_x = (x_max - x_min) / cbm_sts_utils::kStsDx; + unsigned int nb_bins_y = (y_max - y_min) / cbm_sts_utils::kStsDy; + + + double dr = 5e-4; + double r_min = -2.7 - 0.5 * dr; + double r_max = +2.7 + 0.5 * dr; + unsigned int nb_bins_r = (r_max - r_min) / dr; + + for (auto mod : {":all", ":trk"}) { + std::string h_name; + h_name = Form("%s_dx_vs_x%s", h_name_base.c_str(), mod); + fH2D[h_name] = std::make_unique<TH2D>(h_name.c_str(), "", nb_bins_r, r_min, r_max, nb_bins_x, x_min, x_max); + fH2D[h_name]->GetXaxis()->SetTitle(Form("X_{trk} - X_{hit} [cm]")); + fH2D[h_name]->GetYaxis()->SetTitle("X_{hit} [cm]"); + fH2D[h_name]->GetXaxis()->CenterTitle(true); + fH2D[h_name]->GetYaxis()->CenterTitle(true); + + h_name = Form("%s_dx_vs_y%s", h_name_base.c_str(), mod); + fH2D[h_name] = std::make_unique<TH2D>(h_name.c_str(), "", nb_bins_r, r_min, r_max, nb_bins_y, y_min, y_max); + fH2D[h_name]->GetXaxis()->SetTitle(Form("X_{trk} - X_{hit} [cm]")); + fH2D[h_name]->GetYaxis()->SetTitle("Y_{hit} [cm]"); + fH2D[h_name]->GetXaxis()->CenterTitle(true); + fH2D[h_name]->GetYaxis()->CenterTitle(true); + + h_name = Form("%s_dy_vs_x%s", h_name_base.c_str(), mod); + fH2D[h_name] = std::make_unique<TH2D>(h_name.c_str(), "", nb_bins_r, r_min, r_max, nb_bins_x, x_min, x_max); + fH2D[h_name]->GetXaxis()->SetTitle(Form("Y_{trk} - Y_{hit} [cm]")); + fH2D[h_name]->GetYaxis()->SetTitle("X_{hit} [cm]"); + fH2D[h_name]->GetXaxis()->CenterTitle(true); + fH2D[h_name]->GetYaxis()->CenterTitle(true); + + h_name = Form("%s_dy_vs_y%s", h_name_base.c_str(), mod); + fH2D[h_name] = std::make_unique<TH2D>(h_name.c_str(), "", nb_bins_r, r_min, r_max, nb_bins_y, y_min, y_max); + fH2D[h_name]->GetXaxis()->SetTitle(Form("Y_{trk} - Y_{hit} [cm]")); + fH2D[h_name]->GetYaxis()->SetTitle("Y_{hit} [cm]"); + fH2D[h_name]->GetXaxis()->CenterTitle(true); + fH2D[h_name]->GetYaxis()->CenterTitle(true); + } + if (element < 8) { + std::string h_name = Form("Sts%d_ref_hits", element); + fH2D[h_name] = std::make_unique<TH2D>(h_name.c_str(), "", nb_bins_x, x_min, x_max, nb_bins_y, y_min, y_max); + fH2D[h_name]->GetXaxis()->SetTitle(Form("X_{TrkHit} [cm]")); + fH2D[h_name]->GetYaxis()->SetTitle("Y_{TrkHit} [cm]"); + fH2D[h_name]->GetXaxis()->CenterTitle(true); + fH2D[h_name]->GetYaxis()->CenterTitle(true); + } + } +} + +void CbmStsResolution::ProcessEvent(CbmEvent* evt) +{ + if (fAnalysisCuts != nullptr && !fAnalysisCuts->CheckEvent(evt)) return; + + int nb_sts_hits = evt->GetNofData(ECbmDataType::kStsHit); + LOG(debug) << Form("Processing event with %d StsHit", nb_sts_hits); + for (int hit_evt_idx = 0; hit_evt_idx < nb_sts_hits; hit_evt_idx++) { + int sts_hit_idx = evt->GetIndex(ECbmDataType::kStsHit, hit_evt_idx); + CbmStsHit* hit = (CbmStsHit*) fStsHitArray->UncheckedAt(sts_hit_idx); + ProcessHit(hit); + } + int nb_of_glob_trk = evt->GetNofData(ECbmDataType::kGlobalTrack); + for (int evt_glob_trk_idx = 0; evt_glob_trk_idx < nb_of_glob_trk; evt_glob_trk_idx++) { + int glob_trk_idx = evt->GetIndex(ECbmDataType::kGlobalTrack, evt_glob_trk_idx); + CbmGlobalTrack* glob_trk = (CbmGlobalTrack*) fGlbTrkArray->UncheckedAt(glob_trk_idx); + ProcessGlobalTrack(glob_trk); + } + BuildResidual(); +} + +void CbmStsResolution::BuildResidual() +{ + int n_of_units = fStsHits.size(); + + for (auto trk : fGlbTrks) { + auto sts_trk_idx = trk->GetStsTrackIndex(); + if (sts_trk_idx == -1) continue; + + CbmStsTrack* sts_track = (CbmStsTrack*) fStsTrkArray->UncheckedAt(sts_trk_idx); + if (((CbmStsTrack*) fStsTrkArray->UncheckedAt(sts_trk_idx))->GetNofStsHits() != 2) continue; + + CbmStsHit* hit_i = (CbmStsHit*) fStsHitArray->UncheckedAt(sts_track->GetStsHitIndex(0)); + CbmStsHit* hit_j = (CbmStsHit*) fStsHitArray->UncheckedAt(sts_track->GetStsHitIndex(1)); + + // Charge cut to Sts track hits + if (fTrkHitQmin > 0 + && (cbm_sts_utils::GetHitCharge(hit_i, fStsCluArray) < fTrkHitQmin + || cbm_sts_utils::GetHitCharge(hit_j, fStsCluArray) < fTrkHitQmin)) + continue; + + int unit_i = CbmStsAddress::GetElementId(hit_i->GetAddress(), kStsUnit); + int unit_j = CbmStsAddress::GetElementId(hit_j->GetAddress(), kStsUnit); + + // Checkk track crossed eneabled sensors + if (fActiveRefSensors.size()) { + if (std::find(fActiveRefSensors.begin(), fActiveRefSensors.end(), hit_i->GetAddress()) == fActiveRefSensors.end()) + continue; + if (std::find(fActiveRefSensors.begin(), fActiveRefSensors.end(), hit_j->GetAddress()) == fActiveRefSensors.end()) + continue; + } + + double x_i = hit_i->GetX(); + double y_i = hit_i->GetY(); + double z_i = hit_i->GetZ(); + + double x_j = hit_j->GetX(); + double y_j = hit_j->GetY(); + double z_j = hit_j->GetZ(); + + fH2D[Form("Sts%d_ref_hits", unit_i)]->Fill(x_i, y_i); + fH2D[Form("Sts%d_ref_hits", unit_j)]->Fill(x_j, y_j); + + double x_0, y_0, t_x, t_y; + if (tracking_use_two_sts_) { + t_x = (x_i - x_j) / (z_i - z_j); + t_y = (y_i - y_j) / (z_i - z_j); + x_0 = x_i - t_x * z_i; + y_0 = y_i - t_y * z_i; + } + else { + t_x = trk->GetParamFirst()->GetTx(); + t_y = trk->GetParamFirst()->GetTy(); + x_0 = x_i - t_x * z_i; + y_0 = y_i - t_y * z_i; + } + + double d_trk_trg = TMath::Sqrt(x_0 * x_0 + y_0 * y_0); + + if (fImpactParMax > 0 && fImpactParMax < d_trk_trg) continue; + + for (int uut = 0; uut < n_of_units; uut++) { + if (uut == unit_i || uut == unit_j) continue; + + CbmStsHit* closest_hit = nullptr; + double min_dist = 1e+6; + for (auto hit_dut : fStsHits[uut]) { + double x_dut = hit_dut->GetX(); + double y_dut = hit_dut->GetY(); + double z_dut = hit_dut->GetZ(); + + double x_trk = x_i + t_x * (z_dut - z_i); + double y_trk = y_i + t_y * (z_dut - z_i); + + double dx = x_trk - x_dut; + double dy = y_trk - y_dut; + + fH2D[Form("Sts0x%x_dx_vs_x:all", hit_dut->GetAddress())]->Fill(dx, x_dut); + fH2D[Form("Sts0x%x_dx_vs_y:all", hit_dut->GetAddress())]->Fill(dx, y_dut); + fH2D[Form("Sts0x%x_dy_vs_x:all", hit_dut->GetAddress())]->Fill(dy, x_dut); + fH2D[Form("Sts0x%x_dy_vs_y:all", hit_dut->GetAddress())]->Fill(dy, y_dut); + + fH2D[Form("Sts%d_dx_vs_x:all", uut)]->Fill(dx, x_dut); + fH2D[Form("Sts%d_dx_vs_y:all", uut)]->Fill(dx, y_dut); + fH2D[Form("Sts%d_dy_vs_x:all", uut)]->Fill(dy, x_dut); + fH2D[Form("Sts%d_dy_vs_y:all", uut)]->Fill(dy, y_dut); + + if (min_dist > dx * dx + dy * dy) { + min_dist = dx * dx + dy * dy; + closest_hit = hit_dut; + } + } // hit_dut loop + + if (closest_hit) { + double x_dut = closest_hit->GetX(); + double y_dut = closest_hit->GetY(); + double z_dut = closest_hit->GetZ(); + + double x_trk = x_i + t_x * (z_dut - z_i); + double y_trk = y_i + t_y * (z_dut - z_i); + + double dx = x_trk - x_dut; + double dy = y_trk - y_dut; + + fH2D[Form("Sts0x%x_dx_vs_x:trk", closest_hit->GetAddress())]->Fill(dx, x_dut); + fH2D[Form("Sts0x%x_dx_vs_y:trk", closest_hit->GetAddress())]->Fill(dx, y_dut); + fH2D[Form("Sts0x%x_dy_vs_x:trk", closest_hit->GetAddress())]->Fill(dy, x_dut); + fH2D[Form("Sts0x%x_dy_vs_y:trk", closest_hit->GetAddress())]->Fill(dy, y_dut); + + fH2D[Form("Sts%d_dx_vs_x:trk", uut)]->Fill(dx, x_dut); + fH2D[Form("Sts%d_dx_vs_y:trk", uut)]->Fill(dx, y_dut); + fH2D[Form("Sts%d_dy_vs_x:trk", uut)]->Fill(dy, x_dut); + fH2D[Form("Sts%d_dy_vs_y:trk", uut)]->Fill(dy, y_dut); + } + } // uut loop + } // track loop + + fStsHits.clear(); + fGlbTrks.clear(); +} + +void CbmStsResolution::ProcessHit(CbmStsHit* hit) +{ + if (hit == nullptr) return; + + if (fAnalysisCuts != nullptr && !fAnalysisCuts->CheckStsHit(hit, fStsCluArray)) return; + + int32_t address = hit->GetAddress(); + int32_t unit = CbmStsAddress::GetElementId(address, kStsUnit); + + fStsHits[unit].push_back(hit); +} + +void CbmStsResolution::EnableRefSensor(int32_t address) { fActiveRefSensors.push_back(address); } + +void CbmStsResolution::ProcessGlobalTrack(CbmGlobalTrack* trk) +{ + if (trk == nullptr) return; + + auto sts_trk_idx = trk->GetStsTrackIndex(); + auto rich_trk_idx = trk->GetRichRingIndex(); + auto much_trk_idx = trk->GetMuchTrackIndex(); + auto trd_trk_idx = trk->GetTrdTrackIndex(); + auto tof_trk_idx = trk->GetTofTrackIndex(); + float trk_p_val = TMath::Prob(trk->GetChi2(), trk->GetNDF()); + + // Apply GlobalTracks cuts + int32_t mvd_trk_size = sts_trk_idx != -1 && fStsTrkArray != nullptr + ? ((CbmStsTrack*) fStsTrkArray->UncheckedAt(sts_trk_idx))->GetNofMvdHits() + : 0; + + int32_t sts_trk_size = sts_trk_idx != -1 && fStsTrkArray != nullptr + ? ((CbmStsTrack*) fStsTrkArray->UncheckedAt(sts_trk_idx))->GetNofStsHits() + : 0; + + int32_t rich_trk_size = rich_trk_idx != -1 && fRchTrkArray != nullptr + ? ((CbmTrack*) fRchTrkArray->UncheckedAt(rich_trk_idx))->GetNofHits() + : 0; + + int32_t much_trk_size = much_trk_idx != -1 && fMchTrkArray != nullptr + ? ((CbmTrack*) fMchTrkArray->UncheckedAt(much_trk_idx))->GetNofHits() + : 0; + + int32_t trd_trk_size = trd_trk_idx != -1 && fTrdTrkArray != nullptr + ? ((CbmTrack*) fTrdTrkArray->UncheckedAt(trd_trk_idx))->GetNofHits() + : 0; + + int32_t tof_trk_size = tof_trk_idx != -1 && fTofTrkArray != nullptr + ? ((CbmTrack*) fTofTrkArray->UncheckedAt(tof_trk_idx))->GetNofHits() + : 0; + + if (fAnalysisCuts != nullptr + && (!fAnalysisCuts->Check(CbmCutId::kGlobalTrackMvdSize, mvd_trk_size) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackStsSize, sts_trk_size) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackRichSize, rich_trk_size) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackMuchSize, much_trk_size) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackTrdSize, trd_trk_size) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackTofSize, tof_trk_size) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackChi2, trk->GetChi2()) + || !fAnalysisCuts->Check(CbmCutId::kGlobalTrackPval, trk_p_val))) { + return; + } + + fGlbTrks.push_back(trk); +} + +void CbmStsResolution::Exec(Option_t*) +{ + switch (fInputType) { + case kEventMode: { + if (fCbmEvtArray == nullptr) { + LOG(error) << "Invalid branch for event-mode: Branch CbmEvent -> nullptr"; + break; + } + + uint32_t nb_events = fCbmEvtArray->GetEntriesFast(); + LOG(info) << "Processing entry " << entry_ << "\tCbmEvent: " << nb_events; + + for (uint32_t evt_idx = 0; evt_idx < nb_events; evt_idx++) { + CbmEvent* event = (CbmEvent*) fCbmEvtArray->UncheckedAt(evt_idx); + ProcessEvent(event); + } + break; + } + case kMCTimeMode: { + break; + } + case kMCEventMode: + case kTimeMode: + default: { + uint32_t n_of_hits = fStsHitArray->GetEntriesFast(); + uint32_t n_of_glb_trk = fGlbTrkArray->GetEntriesFast(); + LOG(info) << "Processing entry " << entry_ << "\tStsHit: " << n_of_hits << "\tGlobalTrack: " << n_of_glb_trk; + + for (uint32_t hit_idx = 0; hit_idx < n_of_hits; hit_idx++) { + ProcessHit((CbmStsHit*) fStsHitArray->UncheckedAt(hit_idx)); + } + + for (uint32_t glb_trk_idx = 0; glb_trk_idx < n_of_glb_trk; glb_trk_idx++) { + ProcessGlobalTrack((CbmGlobalTrack*) fGlbTrkArray->UncheckedAt(glb_trk_idx)); + } + + BuildResidual(); + break; + } + } + entry_++; +} + +InitStatus CbmStsResolution::Init() +{ + LOG(debug) << "Init CbmStsResolution ..."; + + FairRootManager* ioman = FairRootManager::Instance(); + if (ioman != nullptr) { + + fCbmEvtArray = (TClonesArray*) ioman->GetObject("CbmEvent"); + + fGlbTrkArray = (TClonesArray*) ioman->GetObject("GlobalTrack"); + fStsTrkArray = (TClonesArray*) ioman->GetObject("StsTrack"); + fRchTrkArray = (TClonesArray*) ioman->GetObject("RichTrack"); + fMchTrkArray = (TClonesArray*) ioman->GetObject("MuchTrack"); + fTrdTrkArray = (TClonesArray*) ioman->GetObject("TrdTrack"); + fTofTrkArray = (TClonesArray*) ioman->GetObject("TofTrack"); + + fStsHitArray = (TClonesArray*) ioman->GetObject("StsHit"); + fStsCluArray = (TClonesArray*) ioman->GetObject("StsCluster"); + } + + LoadSetup(); + + BookHistograms(); + + return kSUCCESS; +} + +void CbmStsResolution::Finish() { SaveToFile(); } diff --git a/analysis/detectors/sts/CbmStsResolution.h b/analysis/detectors/sts/CbmStsResolution.h new file mode 100644 index 0000000000000000000000000000000000000000..a6938e855cc901c5db71e5617233f4cc7470427d --- /dev/null +++ b/analysis/detectors/sts/CbmStsResolution.h @@ -0,0 +1,133 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#ifndef CBMSTSRESOLUTION_H +#define CBMSTSRESOLUTION_H + +#include "CbmEvent.h" +#include "CbmGlobalTrack.h" +#include "CbmStsAnaBase.h" +#include "CbmStsCluster.h" +#include "CbmStsHit.h" +#include "CbmStsUtils.h" + +#include <FairTask.h> + +#include <TClonesArray.h> + +#include <cstring> +#include <map> +#include <unordered_map> +#include <unordered_set> + + +/** + * \class CbmStsResolution + * \brief Task for estimating the spatial resolution of the STS detector using track-hit residuals. + * + * The `CbmStsResolution` class is responsible for calculating the residuals between the hits + * in the Silicon Tracking System (STS) and the corresponding reconstructed tracks. These + * residuals are used to estimate the spatial resolution of the STS detector. + * + * ## Output: + * The class produces a set of 2D histograms showing the distribution of residuals + * as a function of the reconstructed hit coordinates. These histograms provide insight into + * the detector's resolution performance across different spatial regions. + * + * If functon EnableRefSensor() is called during analysis configuration, the residuals will be only produced + * when the track hit the selected sensors. This is especially useful to restrict bad performing sensors. + * + * ## Track Extrapolation Modes: + * The residuals can be computed using two approaches: + * + * 1. **Local STS-based extrapolation:** Uses only two STS hits to form a straight-line + * extrapolation to calculate the expected hit position. + * + * 2. **Global track-based extrapolation:** Utilizes the full set of global track parameters + * obtained from the reconstruction process to extrapolate the expected position at the hit. + * + * ## The residual are calculated in two modes:alignas + * - `":all"`: All hits are used for the residual calculation. (large combinatoric) + * - `":trk"`: Only the hits clsosest to the extrapolation point is used (selection bias, less combinatoric). + * + * ## Track selection: + * It can be configured using the `CbmCutMap` class. + * Additionally, the user can set a minimum charge threshold for the STS hits attached to the track + * and set a cut to the maximun value for the impact parameter. + * + * ## Future Extension: + * The class is planned to be extended with functionality to support **track refitting using + * only STS hits**, which would allow for improved and more localized resolution studies + * decoupled from the global tracking performance. + * + * Inherits from: + * - `FairTask`: Base class for FairRoot tasks, enabling integration with the FAIR framework. + * - `CbmStsAnaBase`: Provides STS-specific analysis utilities and interfaces. + * + */ +class CbmStsResolution : public FairTask, public CbmStsAnaBase { + public: + CbmStsResolution() = default; + ~CbmStsResolution() = default; + ; + + /** \brief Constructor */ + CbmStsResolution(double); + + InitStatus Init(); + void Exec(Option_t*); + void Finish(); + + void SetInputType(InputType type) { fInputType = type; }; + + void EnableRefSensor(int32_t); + + void SetMinTrkHitQ(double q) { fTrkHitQmin = q; } + + void TrackUseTwoSts() { tracking_use_two_sts_ = true; } + + private: + double fImpactParMax{-1}; + double fTrkHitQmin{-1}; + + std::vector<CbmGlobalTrack*> fGlbTrks; + std::map<int32_t, std::vector<CbmStsHit*>> fStsHits; + + InputType fInputType = InputType::kEventMode; + + bool tracking_use_two_sts_{false}; + + std::vector<int32_t> fActiveRefSensors; + + TClonesArray* fCbmEvtArray{nullptr}; + + TClonesArray* fGlbTrkArray{nullptr}; + TClonesArray* fStsTrkArray{nullptr}; + TClonesArray* fRchTrkArray{nullptr}; + TClonesArray* fMchTrkArray{nullptr}; + TClonesArray* fTrdTrkArray{nullptr}; + TClonesArray* fTofTrkArray{nullptr}; + + TClonesArray* fStsHitArray{nullptr}; + TClonesArray* fStsCluArray{nullptr}; + + void BookHistograms(); + + /** \brief Process an Cbm events + * It filters event based on the provided CbmCutMap + */ + void ProcessEvent(CbmEvent*); + + /** \brief Process an STS hit + * It filters hits based on the provided CbmCutMap + */ + void ProcessHit(CbmStsHit*); + + void ProcessGlobalTrack(CbmGlobalTrack* trk); + + void BuildResidual(); + + ClassDef(CbmStsResolution, 1); +}; +#endif diff --git a/analysis/detectors/sts/CbmStsTimeCal.cxx b/analysis/detectors/sts/CbmStsTimeCal.cxx new file mode 100644 index 0000000000000000000000000000000000000000..2df8ae30532f63219d0b9b976f2e387507bd4bbf --- /dev/null +++ b/analysis/detectors/sts/CbmStsTimeCal.cxx @@ -0,0 +1,606 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#include "CbmStsTimeCal.h" + +#include "CbmBmonDigi.h" +#include "CbmFsdDigi.h" +#include "CbmMuchDigi.h" +#include "CbmMvdDigi.h" +#include "CbmRichDigi.h" +#include "CbmStsDigi.h" +#include "CbmTofDigi.h" +#include "CbmTrdDigi.h" + +#include <TColor.h> +#include <TDirectory.h> +#include <TF1.h> +#include <TFitResult.h> +#include <TLine.h> +#include <TPaveText.h> +#include <TStyle.h> + +#include <cassert> +#include <ctime> +#include <fstream> +#include <iostream> +#include <typeinfo> + +#include <fmt/core.h> +#include <yaml-cpp/emitter.h> +#include <yaml-cpp/emittermanip.h> +#include <yaml-cpp/node/node.h> + +CbmStsTimeCal::CbmStsTimeCal(ECbmModuleId ref_sys, double t_min, double t_max) + : fTimeWindowMin(t_min) + , fTimeWindowMax(t_max) + , fRefSystem(ref_sys) +{ +} + +CbmStsTimeCal::CbmStsTimeCal(int run_id, ECbmModuleId ref_sys, double t_min, double t_max) +{ + fRunId = run_id; + fTimeWindowMin = t_min; + fTimeWindowMax = t_max; + fRefSystem = ref_sys; +} + + +InitStatus CbmStsTimeCal::Init() +{ + FairRootManager* ioman = FairRootManager::Instance(); + if (ioman != nullptr) { + fDigiManager = CbmDigiManager::Instance(); + fDigiManager->Init(); + + fCbmEvtArray = (TClonesArray*) ioman->GetObject("CbmEvent"); + + if (!fDigiManager->IsPresent(ECbmModuleId::kSts)) LOG(fatal) << GetName() << ": No StsDigi branch in input!"; + if (!fDigiManager->IsPresent(fRefSystem)) + LOG(fatal) << GetName() << ": No " << ToString(fRefSystem) << " branch in input!"; + + LoadWalkFromFile(); + + fOutputPath = fRunId > 0 ? Form("%d", fRunId) : "."; + if (gSystem->AccessPathName(fOutputPath.c_str(), kFileExists)) { + if (system(("mkdir -p " + fOutputPath).c_str())) { // Check output folder + LOG(warning) << "Could not create output directory\n Setting output path at current location:\n"; + } + } + fReportOffset = std::make_unique<TFile>(Form("%s/cbm_sts_time_cal_offset.root", fOutputPath.c_str()), "RECREATE"); + fReportFile = std::make_unique<TFile>(Form("%s/cbm_sts_time_cal_report.root", fOutputPath.c_str()), "RECREATE"); + fReportFit = std::make_unique<TFile>(Form("%s/cbm_sts_time_cal_fits.root", fOutputPath.c_str()), "RECREATE"); + + return kSUCCESS; + } + return kERROR; +} + +void CbmStsTimeCal::InitTimeWalkMap(int32_t address) +{ + for (unsigned int asic_idx = 0; asic_idx < 16; asic_idx++) { + fWalkMapRaw[address][asic_idx] = std::vector<double>(32, 0); + auto loaded_par = fWalkMap.Get(address, asic_idx); + if (loaded_par.size() == 31) { + for (int adc = 1; adc <= 31; adc++) { + fWalkMapRaw[address][asic_idx][adc] = loaded_par[adc - 1]; + } + } + } +} + +void CbmStsTimeCal::SetWalkFile(std::string f_name) +{ + LOG(debug) << Form("Setting user define time calibration file: %s", f_name.c_str()); + fParFile = f_name; +} + +void CbmStsTimeCal::LoadWalkFromFile() +{ + if (!fParFile.length()) return; + + if (TString(fParFile.c_str()).EndsWith(".yaml") || TString(fParFile.c_str()).EndsWith(".yml")) { + LOG(info) << Form("Loading time calibration from parameter file: %s", fParFile.c_str()); + fWalkMap = cbm::algo::yaml::ReadFromFile<cbm::algo::sts::WalkMap>(fParFile); + fGlobalTimeOffset = fWalkMap.GetSystemTimeOffset(); + LOG(info) << "Current system offset: " << fGlobalTimeOffset << " ns"; + } +} + +void CbmStsTimeCal::BookHistograms(int32_t address) +{ + LOG(debug) << Form("Booking histograms for module: 0x%x", address); + + std::string h_name, h_title; + int nb_time_bins = (std::abs(fTimeWindowMax - fTimeWindowMin) + kStsClock) / kStsClock; + + int unit = CbmStsAddress::GetElementId(address, kStsUnit); + int ladd = CbmStsAddress::GetElementId(address, kStsLadder); + int modu = CbmStsAddress::GetElementId(address, kStsModule); + std::string smart_name = Form("U%d_L%d_M%d", unit, ladd, modu); + + for (int asic_idx = 0; asic_idx < 16; asic_idx++) { + h_name = Form("0x%x_%02d", address, asic_idx); + h_title = Form("%s_%02d", smart_name.c_str(), asic_idx); + + fH2D[h_name] = + std::make_unique<TH2D>(h_name.c_str(), h_title.c_str(), nb_time_bins, fTimeWindowMin - 0.5 * kStsClock, + fTimeWindowMax + 0.5 * kStsClock, 31, 1, 32); + + fH2D[h_name]->GetXaxis()->SetTitle(Form("t_{%s} - t_{StsDigi} [ns]", ToString(fRefSystem).c_str())); + fH2D[h_name]->GetYaxis()->SetTitle("Charge_{StsDigi} [ADC]"); + } + + h_name = Form("0x%x_dt_vs_channel", address); + h_title = smart_name; + fH2D[h_name] = std::make_unique<TH2D>(h_name.c_str(), h_title.c_str(), 2048, 0, 2048, nb_time_bins, + fTimeWindowMin - 0.5 * kStsClock, fTimeWindowMax + 0.5 * kStsClock); + + fH2D[h_name]->GetYaxis()->SetTitle(Form("t_{%s} - t_{StsDigi} [ns]", ToString(fRefSystem).c_str())); + fH2D[h_name]->GetXaxis()->SetTitle("Channel_{StsDigi}"); + + fAddressBook.insert(address); +} + +void CbmStsTimeCal::Exec(Option_t*) +{ + LOG(info) << "Running CbmStsTimeCal - Entry " << entry_; + + if (fRefSystem == ECbmModuleId::kBmon) CheckTiming<CbmBmonDigi>(); + if (fRefSystem == ECbmModuleId::kTof) CheckTiming<CbmTofDigi>(); + if (fRefSystem == ECbmModuleId::kRich) CheckTiming<CbmRichDigi>(); + + entry_++; +} + +template<class Digi> +void CbmStsTimeCal::CheckTiming() +{ + auto sts_digis_ = fDigiManager->GetArray<CbmStsDigi>(); + auto ref_digis_ = fDigiManager->GetArray<Digi>(); + + size_t nb_sts_digis = fDigiManager->GetNofDigis(ECbmModuleId::kSts); + size_t nb_ref_digis = fDigiManager->GetNofDigis(Digi::GetSystem()); + + LOG(info) << nb_sts_digis << "\t" << nb_ref_digis; + + // Loops over Ref Detector + for (size_t ref_digi_idx = 0; ref_digi_idx < nb_ref_digis - 1; ref_digi_idx++) { + const Digi* ref_digi = &ref_digis_[ref_digi_idx]; + double ref_digi_time = ref_digi->GetTime(); + + if (fAnalysisCuts != nullptr + && !fAnalysisCuts->Check(CbmCutId::kBmonDigiSide, CbmTofAddress::GetChannelSide(ref_digi->GetAddress()))) + continue; + + double first_digi_in_window = ref_digi_time + fTimeWindowMin; + double last_digi_in_window = ref_digi_time + fTimeWindowMax; + // Find first_digi_in_window + int lo = 0, hi = nb_sts_digis - 1; + while (lo < hi) { + int m = (lo + hi) / 2; + if (sts_digis_[m].GetTime() >= first_digi_in_window) { + hi = m; + } + else { + lo = m + 1; + } + } + int lower_hitB = lo; + + // Find last_digi_in_window + lo = 0, hi = nb_sts_digis - 1; + while (lo < hi) { + int m = (lo + hi + 1) / 2; + if (sts_digis_[m].GetTime() <= last_digi_in_window) { + lo = m; + } + else { + hi = m - 1; + } + } + int upper_hitB = lo; + if (lower_hitB > upper_hitB) { + LOG(debug) << "Not time match found for the current hit within the time window"; + continue; + } + + for (int sts_digi_idx = lower_hitB; sts_digi_idx < upper_hitB; sts_digi_idx++) { + const CbmStsDigi* sts_digi = &sts_digis_[sts_digi_idx]; + int32_t sts_digi_addr = sts_digi->GetAddress(); + + if (!fAddressBook.count(sts_digi_addr)) { + BookHistograms(sts_digi_addr); + InitTimeWalkMap(sts_digi_addr); + } + + int32_t sts_digi_chan = sts_digi->GetChannel(); + int32_t sts_digi_char = sts_digi->GetCharge(); + int32_t asic_idx = sts_digi_chan / 128; + double sts_digi_time = sts_digi->GetTime(); + + fH2D[Form("0x%x_%02d", sts_digi_addr, asic_idx)]->Fill(ref_digi_time - sts_digi_time, sts_digi_char); + fH2D[Form("0x%x_dt_vs_channel", sts_digi_addr)]->Fill(sts_digi_chan, ref_digi_time - sts_digi_time); + } // end sts loop + } // end ref loop +} + + +struct FitStsTime { + std::unique_ptr<TH1> h_x; + std::unique_ptr<TF1> f_x; + std::unique_ptr<TF1> f_peak; + std::unique_ptr<TF1> f_bkgd; + TFitResultPtr fit_ptr; + bool use_fit_result{false}; + double time_offset{0}; + + TCanvas* Draw() + { + if (h_x == nullptr) { + return nullptr; + } + int pad_x = 1024; + int pad_y = 1024; + + std::unique_ptr<TCanvas> canvas = std::make_unique<TCanvas>("_c0", "", pad_x, pad_y); + canvas->cd(); + + double line_width = 0.06; + double pave0_top_edge = 0.95; + double pave0_bot_edge = pave0_top_edge - line_width * 3; + double pave1_top_edge = pave0_bot_edge; + double pave1_bot_edge = pave1_top_edge - line_width * 3; + + h_x->SetLineStyle(1); + h_x->SetLineWidth((0.002 * pad_y)); + h_x->SetLineColor(kBlack); + h_x->DrawClone("histo"); + + if (use_fit_result) { + f_x->SetTitle("Fitted function"); + f_x->SetLineStyle(1); + f_x->SetLineWidth((0.002 * pad_y)); + f_x->SetLineColor(kRed); + f_x->DrawClone("same"); + } + + if (use_fit_result) { + f_peak->SetTitle("Peak"); + f_peak->SetLineStyle(kDotted); + f_peak->SetLineWidth((0.002 * pad_y)); + f_peak->SetLineColor(kBlue); + f_peak->DrawClone("same"); + std::unique_ptr<TPaveText> pave = std::make_unique<TPaveText>(0.16, pave0_bot_edge, 0.45, pave0_top_edge, "NDC"); + pave->SetBorderSize(1); + pave->SetFillColor(0); + pave->SetTextAlign(12); + pave->AddText(Form("p0 = %.3f", f_peak->GetParameter(0)))->SetTextColor(kBlue); + pave->AddText(Form("p1 = %.3f", f_peak->GetParameter(1)))->SetTextColor(kBlue); + pave->AddText(Form("p2 = %.3f", f_peak->GetParameter(2)))->SetTextColor(kBlue); + pave->DrawClone(); + } + + if (use_fit_result) { + f_bkgd->SetTitle("Background"); + f_bkgd->SetLineStyle(kDotted); + f_bkgd->SetLineWidth((0.002 * pad_y)); + f_bkgd->SetLineColor(kOrange); + f_bkgd->DrawClone("same"); + std::unique_ptr<TPaveText> pave = std::make_unique<TPaveText>(0.16, pave1_bot_edge, 0.45, pave1_top_edge, "NDC"); + pave->SetBorderSize(1); + pave->SetFillColor(0); + pave->SetTextAlign(12); + pave->AddText(Form("p0 = %.3f", f_bkgd->GetParameter(0)))->SetTextColor(kOrange); + pave->AddText(Form("p1 = %.3f", f_bkgd->GetParameter(1)))->SetTextColor(kOrange); + pave->AddText(Form("p2 = %.3f", f_bkgd->GetParameter(2)))->SetTextColor(kOrange); + pave->DrawClone(); + } + + TLine offset(time_offset, h_x->GetMinimum(), time_offset, h_x->GetMaximum()); + offset.SetLineWidth(0.004 * pad_y); + offset.SetLineColor(kGray); + offset.SetLineStyle(kDotted); + offset.DrawClone(); + + // Fit convergence + std::unique_ptr<TPaveText> fit_pave = std::make_unique<TPaveText>(0.65, 0.75, 0.95, 0.95, "NDC"); + fit_pave->SetBorderSize(1); + fit_pave->SetFillColor(0); + fit_pave->SetTextAlign(12); + fit_pave->AddText(Form("NDF = %d", fit_ptr->Ndf())); + fit_pave->AddText(Form("Chi2 = %.3f", fit_ptr->Chi2())); + fit_pave->AddText(Form("PRob = %E", fit_ptr->Prob())); + fit_pave->DrawClone(); + + return (TCanvas*) canvas->Clone(); + } + + FitStsTime(TH1D* h) + { + if (h == nullptr) { + return; + } + h_x = std::make_unique<TH1D>(*h); + h_x->Scale(1. / h_x->Integral()); + + // Fit configuration + double time_resolution = 4.8; + double h_max = h_x->GetBinCenter(h_x->GetMaximumBin()); + double fit_x_min = h_max - 10. * time_resolution; + double fit_x_max = h_max + 10. * time_resolution; + + f_x = std::make_unique<TF1>("f_x", "[0]*TMath::Gaus(x, [1], [2]) + [3] + [4]*x + [5]*x*x", fit_x_min, fit_x_max); + f_peak = std::make_unique<TF1>("f_peak", "[0]*TMath::Gaus(x, [1], [2])", fit_x_min, fit_x_max); + f_bkgd = std::make_unique<TF1>("f_bkgd", "[0] + [1]*x + [2]*x*x", fit_x_min, fit_x_max); + + // Initial values + f_x->SetParameter(0, 0.5); + f_x->SetParameter(1, h_max); + f_x->SetParameter(2, time_resolution); + f_x->SetParameter(5, -1e-4); + + // Limits + f_x->SetParLimits(0, 0.0, 1.0); + f_x->SetParLimits(1, h_max - 2. * time_resolution, h_max + 2. * time_resolution); + f_x->SetParLimits(2, 3.125, 25); + f_x->SetParLimits(5, -1, 0.0); + + + fit_ptr = h_x->Fit("f_x", "SQN0", "", fit_x_min, fit_x_max); + + f_x->SetParameters(fit_ptr->GetParams()); + f_peak->SetParameter(0, fit_ptr->Parameter(0)); + f_peak->SetParameter(1, fit_ptr->Parameter(1)); + f_peak->SetParameter(2, fit_ptr->Parameter(2)); + f_bkgd->SetParameter(0, fit_ptr->Parameter(3)); + f_bkgd->SetParameter(1, fit_ptr->Parameter(4)); + f_bkgd->SetParameter(2, fit_ptr->Parameter(5)); + + bool par_out_of_limits = + fit_ptr->Parameter(1) < h_max - 2. * time_resolution || h_max + 2. * time_resolution < fit_ptr->Parameter(1); + + use_fit_result = !fit_ptr && fit_ptr->IsValid() && !par_out_of_limits; + + if (use_fit_result) { + time_offset = f_peak->GetParameter(1); + } + else { + time_offset = h_max; + } + } +}; + +double CbmStsTimeCal::FindModuleOffset(int32_t address) +{ + + double module_offset = 0; + TH2D* dt_vs_chn = fH2D[Form("0x%x_dt_vs_channel", address)].get(); + if (dt_vs_chn != nullptr) { + TH1D* p_y = (TH1D*) dt_vs_chn->ProjectionY(); + + double peak = p_y->GetBinCenter(p_y->GetMaximumBin()); + + FitStsTime offset_fit(p_y); + TCanvas* drawing = offset_fit.Draw(); + if (drawing != nullptr) { + fReportOffset->cd(); + drawing->Write(Form("0x%x_offset", address)); + } + + if (offset_fit.use_fit_result) { + module_offset = offset_fit.time_offset; + } + else { + module_offset = peak; + } + } + return module_offset; +} + +void CbmStsTimeCal::CheckTimeWalk() +{ + /* This function can be optimized by parallelizing the loop over modules or ASIC idx, depending on the ratio + * For mCBM case, there are usually less than 16 modules so it makes more sense to parallelize the ASIC idx loop + * but for the full CBM case, the amount of modules greatly exceeds the number of ASIC per modules (16) + */ + for (int32_t module : fAddressBook) { // loop over modules + double module_offset = FindModuleOffset(module); + for (unsigned int asic_idx = 0; asic_idx < 16; asic_idx++) { // loop over ASICs + + double adc_channel[32]; + double adc_channel_err[32]; + double time_offset[32]; + double time_offset_err[32]; + int n_pts = 0; + + std::string h_name = Form("0x%x_%02d", module, asic_idx); + if (!fH2D.count(h_name)) return; + + auto h = fH2D[h_name].get(); + + for (int adc = 1; adc <= 31; adc++) { + TH1D* h_x = h->ProjectionX(Form("%s_dt", h_name.c_str()), adc, adc); + + FitStsTime offset_fit(h_x); + TCanvas* drawing = offset_fit.Draw(); + if (drawing != nullptr) { + std::string fit_folder = Form("0x%x/%02d", module, asic_idx); + TDirectory* dir = (TDirectory*) fReportFit->Get(fit_folder.c_str()); + if (!dir) { + dir = fReportFit->mkdir(fit_folder.c_str()); + } + dir->cd(); + drawing->Write(Form("0x%x_%02d_%02d", module, asic_idx, adc)); + } + + if (offset_fit.use_fit_result) { + time_offset[n_pts] = offset_fit.fit_ptr->Parameter(1); + time_offset_err[n_pts] = offset_fit.fit_ptr->Parameter(2); + } + else { + /* If the fitting for individual ADC of the ASIC was unsuccessful + * the time offset is taken as the one estimated for the whole module + */ + time_offset[n_pts] = module_offset; + time_offset_err[n_pts] = h_x->GetRMS(); + } + adc_channel[n_pts] = adc; + adc_channel_err[n_pts] = 0; + + // Update time walk map + fWalkMapRaw[module][asic_idx][adc] += time_offset[n_pts]; + + n_pts++; + + } // ---- end ADC loop ---- + + fG1D[h_name] = std::make_unique<TGraphErrors>(n_pts, adc_channel, time_offset, adc_channel_err, time_offset_err); + fG1D[h_name]->SetTitle(Form("%s : StsDigi_0x%x time off-set", ToString(fRefSystem).c_str(), module)); + fG1D[h_name]->GetXaxis()->SetTitle("Charge_{StsDigi} [ADC]"); + fG1D[h_name]->GetYaxis()->SetTitle(Form("t_{%s} - t_{StsDigi} [ns]", ToString(fRefSystem).c_str())); + fG1D[h_name]->SetMinimum(fTimeWindowMin); + fG1D[h_name]->SetMaximum(fTimeWindowMax); + + } // ---- end module loop ---- + } // ---- end ASICs loop ---- +} + +double CbmStsTimeCal::FindGlobalOffset() +{ + double setup_offset = 0; + double min_sum_sigma = 999; + for (int32_t module : fAddressBook) { // module loop + double module_avg_sigma = 0; + double module_avg_offset = 0; + int n_of_values = 0; + + for (unsigned int asic_idx = 0; asic_idx < 16; asic_idx++) { // asic loop + std::string h_name = Form("0x%x_%02d", module, asic_idx); + auto g = fG1D[h_name].get(); + + int n_of_pto = g->GetN(); + int lower_bound = n_of_pto > 5 ? n_of_pto - 5 : 0; + + // Calculate the average value of the sigma for the last ADC + for (int pto = n_of_pto; pto > lower_bound - 5; pto--) { + double offset = g->GetPointY(pto); + double sigma = g->GetErrorY(pto); + if (sigma) { + module_avg_sigma += sigma; + module_avg_offset += offset; + n_of_values++; + } + } + } // end asic loop + + module_avg_sigma = n_of_values ? module_avg_sigma / n_of_values : -1; + module_avg_offset = n_of_values ? module_avg_offset / n_of_values : -1; + + if (module_avg_sigma && module_avg_sigma < min_sum_sigma) { + min_sum_sigma = module_avg_sigma; + setup_offset = module_avg_offset; + } + } // end module loop + + return setup_offset; +} + +void CbmStsTimeCal::WriteYAML() +{ + std::string o_yaml_file = fOutputPath + "/StsTimeCalibration.yaml"; + LOG(info) << "Writing parameter file to:" << o_yaml_file; + + YAML::Emitter walk_map; + walk_map << YAML::BeginMap; + walk_map << YAML::Key << "timeOffset"; + walk_map << YAML::Value << (int) fGlobalTimeOffset; + walk_map << YAML::Key << "WalkMap"; + walk_map << YAML::Value << YAML::BeginMap; + for (const auto& [module, asics] : fWalkMapRaw) { + walk_map << YAML::Key << fmt::format("0x{:x}", module); + walk_map << YAML::Value << YAML::BeginSeq; + for (const auto& [asic, pars] : asics) { + // Begin a flow sequence + walk_map << YAML::Flow << YAML::BeginSeq; + for (const auto& value : pars) { + walk_map << fmt::format("{:+.3f}", value); // Add '+' for positive numbers + } + // End the flow sequence + walk_map << YAML::EndSeq; + } + walk_map << YAML::EndSeq; + } + walk_map << YAML::EndMap << YAML::EndMap; + assert(walk_map.good()); + + std::ofstream walk_map_out(o_yaml_file); + assert(walk_map_out.is_open()); + + walk_map_out << walk_map.c_str(); + walk_map_out.close(); +} + +void CbmStsTimeCal::Finish() +{ + CheckTimeWalk(); + + // resize ADC offset vector to 31 channels from 0 [0-30`] + for (auto& [module, asics] : fWalkMapRaw) { // Loop over modules + for (auto& [asic_idx, asic_par] : asics) { // Loop over ASICs + for (int adc = 1; adc <= 31; adc++) { // loop over ADC channels 31 channels [1 - 31] + asic_par[adc - 1] = asic_par[adc]; + } + asic_par.pop_back(); + } + } + + WriteYAML(); + + DrawResults(); + SaveToFile(); + + + fH1D.clear(); + fH2D.clear(); +} + + +void CbmStsTimeCal::DrawResults() +{ + std::unique_ptr<TCanvas> adc_vs_dt_1d = + std::make_unique<TCanvas>("adc_vs_dt_1d", "adc_vs_dt_1d", 10, 10, 4 * 1024, 4 * 1024); + adc_vs_dt_1d->Divide(4, 4); + + std::unique_ptr<TCanvas> adc_vs_dt_2d = + std::make_unique<TCanvas>("adc_vs_dt_2d", "adc_vs_dt_2d", 10, 10, 4 * 1024, 4 * 1024); + adc_vs_dt_2d->Divide(4, 4); + + std::unique_ptr<TCanvas> dt_vs_chn = std::make_unique<TCanvas>("dt_vs_chn", "dt_vs_chn", 10, 10, 1024, 1024); + + fReportFile->cd(); + for (int32_t sensor : fAddressBook) { + for (int asic_idx = 0; asic_idx < 16; asic_idx++) { + adc_vs_dt_1d->cd(asic_idx + 1); + if (fG1D[Form("0x%x_%02d", sensor, asic_idx)] != nullptr) { + fG1D[Form("0x%x_%02d", sensor, asic_idx)]->Draw("APL"); + } + + adc_vs_dt_2d->cd(asic_idx + 1); + if (fH2D[Form("0x%x_%02d", sensor, asic_idx)] != nullptr) { + fH2D[Form("0x%x_%02d", sensor, asic_idx)]->Draw("colz"); + } + } + + dt_vs_chn->cd(); + if (fH2D[Form("0x%x_dt_vs_channel", sensor)] != nullptr) { + fH2D[Form("0x%x_dt_vs_channel", sensor)]->Draw("colz"); + } + + adc_vs_dt_1d->Write(Form("adc_vs_dt_1d_0x%x", sensor)); + adc_vs_dt_2d->Write(Form("adc_vs_dt_2d_0x%x", sensor)); + dt_vs_chn->Write(Form("dt_vs_chn_0x%x", sensor)); + } +} diff --git a/analysis/detectors/sts/CbmStsTimeCal.h b/analysis/detectors/sts/CbmStsTimeCal.h new file mode 100644 index 0000000000000000000000000000000000000000..a308ec8dfa6c3972cfc76b09083b60e98ed9174d --- /dev/null +++ b/analysis/detectors/sts/CbmStsTimeCal.h @@ -0,0 +1,170 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#ifndef CBMSTSTIMECAL_H +#define CBMSTSTIMECAL_H + +#include "CbmDefs.h" +#include "CbmDigiManager.h" +#include "CbmStsAnaBase.h" +#include "CbmStsUtils.h" +#include "sts/WalkMap.h" +#include "yaml/Yaml.h" + +#include <FairTask.h> + +#include <TClonesArray.h> + +#include <cstring> +#include <map> +#include <unordered_set> + +/** \class CbmStsTimeCal + * \brief Task for time calibration of STS hits. + * + * This class inherits from FairTask and CbmStsAnaBase. + * It provides functionality for calibrating the time of STS hits. + */ + +/** + * @class CbmStsTimeCal + * @brief STS Digi-level time calibration task for ASIC and ADC synchronization. + * + * This class derives from FairTask and CbmStsAnaBase, and performs + * time calibration of the STS (Silicon Tracking System) by determining + * precise time offsets for each ASIC and ADC. The calibration removes + * time walk effects to improve timing accuracy. + * + * Key features: + * + * - **Time Offset Calculation**: + * Individual time offset values are found from the time difference distribution t_StsDigi - t_RefDigi (whole combinatoric inside search window). + * 1. Pre-determination of the time offset is obtained as the mean value of the time correlatiion peak for a given module + * 2. Further refining is attempted by fitting the time difference distribution with a N(dt,mu, sigma) + P_2(dt, p0,p1,p2). + * 3. ASIC ADC granularity is attemped. If failed, module offset is used. + * + * - **Reference Detector Selection**: + * A reference detector system can be specified. + * By default, the BMon (Beam Monitor) system is used. + * + * - **Reporting and Diagnostics**: + * Generates a set of reporting histograms that illustrate the timing + * distribution and calibration results. + * + * - **Organized Output by Run ID**: + * When a run ID is provided, all output files (e.g., parameters, + * histograms, reports) are stored in a dedicated folder named after + * the run ID, ensuring organized data handling. + * + * - **Output File Generation**: + * The task generates a YAML parameter file containing the individual time offset + * for each ASIC ADC. + * + * - **Using pre-existing time calibration**: + * Pre-existing YAML parameter files should be provided IF THE INPUT DATA WAS UNPACKED using such file. + * + * - **Time windows**: + * The time window for the calibration can be set using the lower and upper limits. + * - Very large windows can slow down the code due to high combinatorics. + * - To smmal windows can lead to biased result. The search windows should not be smaller than 25 ns. + * + * This task is essential for achieving precise timing synchronization + * across all readout electronics in the STS subsystem. + */ +class CbmStsTimeCal : public FairTask, public CbmStsAnaBase { + public: + CbmStsTimeCal() = default; + ~CbmStsTimeCal() = default; + + /** \brief Parameterized constructor + * @param system reference detector for time + * @param lower_lim lower boundary for time difference between @param system digis and STS digis (default: -60). + * @param upper_lim lower boundary for time difference between @param system digis and STS digis (default: +60). + */ + CbmStsTimeCal(ECbmModuleId system, double lower_lim = -60, double upper_lim = +60); + + /** \brief Parameterized constructor + * @param system reference detector for time + * @param lower_lim lower boundary for time difference between @param system digis and STS digis (default: -60). + * @param upper_lim lower boundary for time difference between @param system digis and STS digis (default: +60). + */ + CbmStsTimeCal(int run_id, ECbmModuleId system, double lower_lim, double upper_lim); + + /** + * @brief Set parameter file used during unpacking + * @param file_name full/relative path to the parameters file + */ + void SetWalkFile(std::string par_file); + + InitStatus Init(); + void Exec(Option_t*); + void Finish(); + + + private: + double fGlobalTimeOffset{0}; + double fTimeWindowMin{-60}; + double fTimeWindowMax{+60}; + const double kStsClock{3.125}; + std::string fParFile{""}; + std::string fOutputPath{""}; + + std::map<int /* Module */, std::map<int /* ASIC */, std::vector<double> /* ADC offset */>> fWalkMapRaw; + cbm::algo::sts::WalkMap fWalkMap; + + std::unique_ptr<TFile> fReportOffset; + std::unique_ptr<TFile> fReportFit; + + ECbmModuleId fRefSystem{ECbmModuleId::kBmon}; + + CbmDigiManager* fDigiManager{nullptr}; + + TClonesArray* fCbmEvtArray{nullptr}; + + /** + * @brief Book histograms. + * @param address The module address for which histograms will be booked. + */ + void BookHistograms(int32_t address); + + /** \brief Extract Time Walk parameters */ + void CheckTimeWalk(); + + /** \brief Extract Time general offset for a given module */ + double FindModuleOffset(int32_t); + + /** \brief Find a common offset for STS setup. + * It is taken as the average of the time offset for ADC > 25 + * for the setup module with the narrower dt distribution + */ + double FindGlobalOffset(); + + /** \brief Check timing for a specific digi type + * It fills StsDigi time difference respect to fRefSystem Digis histograms + */ + template<class Digi> + void CheckTiming(); + + /** + * @brief Initialized the Time Walk map + * @param address The module address for which the initialization is done + */ + void InitTimeWalkMap(int32_t address); + + /** + * @brief Load the time calibration parameters from user define file + */ + void LoadWalkFromFile(); + + /** + * @brief Write parameters file in YAML format + * @param o_path output folder where to save the file + */ + void WriteYAML(); + + void DrawResults(); + + ClassDef(CbmStsTimeCal, 1); +}; +#endif diff --git a/analysis/detectors/sts/CbmStsUtils.cxx b/analysis/detectors/sts/CbmStsUtils.cxx new file mode 100644 index 0000000000000000000000000000000000000000..c3b0c0c1bbca77d3b60f6158a1b123d1a98a40c5 --- /dev/null +++ b/analysis/detectors/sts/CbmStsUtils.cxx @@ -0,0 +1,143 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#include "CbmStsUtils.h" + +#include "CbmStsAddress.h" +#include "CbmStsCluster.h" +#include "CbmStsParSetModule.h" + +#include <cassert> + +int32_t cbm_sts_utils::GetHitCluSizeF(CbmStsHit* hit, TClonesArray* sts_clu_array) +{ + if (hit == nullptr || sts_clu_array == nullptr) { + return -1; + } + return ((CbmStsCluster*) sts_clu_array->UncheckedAt(hit->GetFrontClusterId()))->GetSize(); +} +int32_t cbm_sts_utils::GetHitCluSizeB(CbmStsHit* hit, TClonesArray* sts_clu_array) +{ + if (hit == nullptr || sts_clu_array == nullptr) { + return -1; + } + return ((CbmStsCluster*) sts_clu_array->UncheckedAt(hit->GetBackClusterId()))->GetSize(); +} +/* Get StsHit charge as average of front and back cluster charges */ +double cbm_sts_utils::GetHitChargeF(CbmStsHit* hit, TClonesArray* sts_clu_array) +{ + if (hit == nullptr || sts_clu_array == nullptr) { + return -1; + } + return ((CbmStsCluster*) sts_clu_array->UncheckedAt(hit->GetFrontClusterId()))->GetCharge(); +} + +double cbm_sts_utils::GetHitChargeB(CbmStsHit* hit, TClonesArray* sts_clu_array) +{ + if (hit == nullptr || sts_clu_array == nullptr) { + return -1; + } + return ((CbmStsCluster*) sts_clu_array->UncheckedAt(hit->GetBackClusterId()))->GetCharge(); +} + +/* Get StsHit cluster time */ +double cbm_sts_utils::GetHitTimeF(CbmStsHit* hit, TClonesArray* sts_clu_array) +{ + if (hit == nullptr || sts_clu_array == nullptr) { + return -1; + } + return ((CbmStsCluster*) sts_clu_array->UncheckedAt(hit->GetFrontClusterId()))->GetTime(); +} + +double cbm_sts_utils::GetHitTimeB(CbmStsHit* hit, TClonesArray* sts_clu_array) +{ + if (hit == nullptr || sts_clu_array == nullptr) { + return -1; + } + return ((CbmStsCluster*) sts_clu_array->UncheckedAt(hit->GetBackClusterId()))->GetTime(); +} +/* ---- ------------- ---- */ + +double cbm_sts_utils::GetHitCharge(CbmStsHit* hit, TClonesArray* sts_clu_array) +{ + if (hit == nullptr || sts_clu_array == nullptr) { + return -1; + } + double cluster_charge_f = GetHitChargeF(hit, sts_clu_array); + double cluster_charge_b = GetHitChargeB(hit, sts_clu_array); + return 0.5 * (cluster_charge_f + cluster_charge_b); +} + +/* Get charge asymmetry */ +double cbm_sts_utils::GetHitChargeAsy(CbmStsHit* hit, TClonesArray* sts_clu_array) +{ + if (hit == nullptr || sts_clu_array == nullptr) { + return -1; + } + double cluster_charge_f = GetHitChargeF(hit, sts_clu_array); + double cluster_charge_b = GetHitChargeB(hit, sts_clu_array); + return (cluster_charge_f - cluster_charge_b) / (cluster_charge_f + cluster_charge_b); +} + +/** \brief Get the charge asymmetry of a hit **/ +std::pair<cbm_sts_utils::HBinning, cbm_sts_utils::HBinning> +cbm_sts_utils::ChargeBinning(const CbmStsParModule& par_module, const uint32_t max_clu_size) +{ + assert(("Maximum cluster size must be greater than 0", max_clu_size > 0)); + + auto par_asic = par_module.GetAsicParams(); + + double n_side_q_thr = par_asic[0].GetThreshold(); + double n_side_q_dyn = par_asic[0].GetDynRange(); + double p_side_q_thr = par_asic[8].GetThreshold(); + double p_side_q_dyn = par_asic[8].GetDynRange(); + + for (int asic_idx = 0; asic_idx < 16; asic_idx++) { + auto asic = par_asic[asic_idx]; + if (asic_idx < 7) { // n-side + n_side_q_thr = std::min(n_side_q_thr, asic.GetThreshold()); + n_side_q_dyn = std::max(n_side_q_dyn, asic.GetDynRange()); + } + else { // p-side + p_side_q_thr = std::min(p_side_q_thr, asic.GetThreshold()); + p_side_q_dyn = std::max(p_side_q_dyn, asic.GetDynRange()); + } + } + + double n_side_q_bin = n_side_q_dyn / 31; + double p_side_q_bin = p_side_q_dyn / 31; + + double n_side_q_min = n_side_q_thr; + double p_side_q_min = p_side_q_thr; + + double n_side_q_max = (n_side_q_dyn * (31.5 / 31) + 0.5 * n_side_q_bin) * max_clu_size + n_side_q_min; + double p_side_q_max = (p_side_q_dyn * (31.5 / 31) + 0.5 * p_side_q_bin) * max_clu_size + p_side_q_min; + + uint32_t n_side_n_bin = 32 * max_clu_size; + uint32_t p_side_n_bin = 32 * max_clu_size; + + return std::make_pair(cbm_sts_utils::HBinning{n_side_n_bin, n_side_q_min, n_side_q_max}, + cbm_sts_utils::HBinning{p_side_n_bin, p_side_q_min, p_side_q_max}); +} + + +std::set<int> cbm_sts_utils::GetUnits(const std::vector<int32_t> addresses) +{ + std::set<int> units; + for (int32_t address : addresses) { + units.insert(CbmStsAddress::GetElementId(address, kStsUnit)); + } + return units; +} + +std::vector<int32_t> cbm_sts_utils::GetUnitModules(const std::vector<int32_t> addresses, const uint32_t unit) +{ + std::vector<int32_t> unit_modules; + for (int32_t address : addresses) { + if (CbmStsAddress::GetElementId(address, kStsUnit) == unit) { + unit_modules.push_back(address); + } + } + return unit_modules; +} diff --git a/analysis/detectors/sts/CbmStsUtils.h b/analysis/detectors/sts/CbmStsUtils.h new file mode 100644 index 0000000000000000000000000000000000000000..f6c4c98e16862e9fb460a7b0e411e9652fd0dbd6 --- /dev/null +++ b/analysis/detectors/sts/CbmStsUtils.h @@ -0,0 +1,118 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#ifndef CBMSTSUTILS_H +#define CBMSTSUTILS_H + +#include "CbmStsHit.h" +#include "TClonesArray.h" + +#include <set> + +class CbmStsParModule; + +/** \namespace cbm_sts_utils + * \brief Namespace containing utility functions for STS detector. + */ +namespace cbm_sts_utils +{ + static const double kStsClock = 3.125; + static const double kStsDx = 6. / 1024; + static const double kStsDy = kStsDx / std::tan(7.5 * TMath::Pi() / 180); + static const double kStsErrX = kStsDx / sqrt(12); + static const double kStsErrY = kStsDy / sqrt(12); + + /** \brief Get the cluster size of a hit from the front side + * \param hit Pointer to the STS hit + * \param clusters Pointer to the TClonesArray of clusters + * \return Cluster size on the front side + */ + int32_t GetHitCluSizeF(CbmStsHit* hit = nullptr, TClonesArray* clusters = nullptr); + + /** \brief Get the cluster size of a hit from the back side + * \param hit Pointer to the STS hit + * \param clusters Pointer to the TClonesArray of clusters + * \return Cluster size on the back side + */ + int32_t GetHitCluSizeB(CbmStsHit* hit = nullptr, TClonesArray* clusters = nullptr); + + /** \brief Get the charge of a hit as the average of front and back cluster charges + * \param hit Pointer to the STS hit + * \param clusters Pointer to the TClonesArray of clusters + * \return Average charge of the hit + */ + double GetHitCharge(CbmStsHit* hit = nullptr, TClonesArray* clusters = nullptr); + + /** \brief Get the charge of a hit from the back side + * \param hit Pointer to the STS hit + * \param clusters Pointer to the TClonesArray of clusters + * \return Charge of the hit on the back side + */ + double GetHitChargeB(CbmStsHit* hit = nullptr, TClonesArray* clusters = nullptr); + + /** \brief Get the charge of a hit from the front side + * \param hit Pointer to the STS hit + * \param clusters Pointer to the TClonesArray of clusters + * \return Charge of the hit on the front side + */ + double GetHitChargeF(CbmStsHit* hit = nullptr, TClonesArray* clusters = nullptr); + + /** \brief Get the charge of a hit from the back side + * \param hit Pointer to the STS hit + * \param clusters Pointer to the TClonesArray of clusters + * \return Charge of the hit on the back side + */ + double GetHitTimeB(CbmStsHit* hit = nullptr, TClonesArray* clusters = nullptr); + + /** \brief Get the charge of a hit from the front side + * \param hit Pointer to the STS hit + * \param clusters Pointer to the TClonesArray of clusters + * \return Charge of the hit on the front side + */ + double GetHitTimeF(CbmStsHit* hit = nullptr, TClonesArray* clusters = nullptr); + + /** \brief Get the size asymmetry of a hit + * \param hit Pointer to the STS hit + * \param clusters Pointer to the TClonesArray of clusters + * \return Charge asymmetry of the hit + */ + double GetHitSizeAsy(CbmStsHit* hit = nullptr, TClonesArray* clusters = nullptr); + + /** \brief Get the charge asymmetry of a hit + * \param hit Pointer to the STS hit + * \param clusters Pointer to the TClonesArray of clusters + * \return Charge asymmetry of the hit + */ + double GetHitChargeAsy(CbmStsHit* hit = nullptr, TClonesArray* clusters = nullptr); + + /** + * \brief Structure to hold the binning for 1D histogram + **/ + struct HBinning { + uint32_t n_of_bins{0}; + double x_min{0}; + double x_max{0}; + }; + + /** \brief Generate the charge binning from module config obj + * \param par_module Obj containing the module configuration + * \param max_clu_size maximum cluster size to sample + * \return pair binning structure for n(p)-side + */ + std::pair<HBinning, HBinning> ChargeBinning(const CbmStsParModule& par_module, const uint32_t max_clu_size = 1); + + /** \brief Return the STS units from a list of addresses + * \param addresses Vector containing the addresses of the modules + * \return set of units + */ + std::set<int> GetUnits(const std::vector<int32_t> addresses); + + /** \brief From a list of module address, return those that belong to a selected unit + * \param addresses Vector containing the addresses of the modules + * \param unit Selected unit + * \return subset of modules that belong to the selected unit + */ + std::vector<int32_t> GetUnitModules(const std::vector<int32_t> addresses, const uint32_t unit); +}; // namespace cbm_sts_utils +#endif diff --git a/analysis/detectors/sts/tests/CMakeLists.txt b/analysis/detectors/sts/tests/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..49e69527ae7f3e0a1379adaf22f2cca5a38c7b7e --- /dev/null +++ b/analysis/detectors/sts/tests/CMakeLists.txt @@ -0,0 +1,27 @@ +# CMakeList file for library libStsAna dedicated tests +# Last update: Dario Ramirez, 23.04.2025 + +Function(AddBasicTest name) + set(PVT_DEPS + Gtest + GtestMain + CbmData + CbmStsBase + CbmStsAna + ) + + add_executable(${name} ${name}.cxx) + target_include_directories(${name} PRIVATE ${INCLUDE_DIRECTORIES}) + target_link_libraries(${name} PRIVATE ${PVT_DEPS}) + Add_Test( + NAME ${name} + COMMAND ${name} + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + ) +EndFunction() + +set(INCLUDE_DIRECTORIES + ${CMAKE_SOURCE_DIR}/analysis/detectors/sts + ) + +AddBasicTest(_GTestCbmCut) diff --git a/analysis/detectors/sts/tests/_GTestCbmCut.cxx b/analysis/detectors/sts/tests/_GTestCbmCut.cxx new file mode 100644 index 0000000000000000000000000000000000000000..1db1ccf035e3e875ad8dfba553743dae077a20b3 --- /dev/null +++ b/analysis/detectors/sts/tests/_GTestCbmCut.cxx @@ -0,0 +1,62 @@ +/* Copyright (C) 2016-2020 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +#include "CbmCut.h" +#include "gtest/gtest.h" + +TEST(_GTestCbmCut, DefConstructor) +{ + CbmCut<float> cut; + EXPECT_EQ(false, cut.GetMinState()); + EXPECT_EQ(false, cut.GetMaxState()); +} + +TEST(_GTestCbmCut, ParConstructor) +{ + float min = -100; + float max = +100; + CbmCut<float> cut(min, max); + EXPECT_EQ(true, cut.GetMinState()); + EXPECT_EQ(true, cut.GetMaxState()); + EXPECT_EQ(min, cut.GetMin()); + EXPECT_EQ(max, cut.GetMax()); +} + +TEST(_GTestCbmCut, CheckOnDefConstructor) +{ + CbmCut<float> cut; + for (float i = -1e+6; i < 1e+6; i++) { + EXPECT_EQ(true, cut.Check(i)); + } +} + +TEST(_GTestCbmCut, CheckCase1) +{ + float min = -100; + float max = +100; + CbmCut<float> cut(min, max); + for (float i = -1e+6; i < 1e+6; i++) { + if (i < min || i > max) { + EXPECT_EQ(false, cut.Check(i)); + } + else { + EXPECT_EQ(true, cut.Check(i)); + } + } +} + +TEST(_GTestCbmCut, CheckCase2) +{ + float min = +100; + float max = -100; + CbmCut<float> cut(min, max); + for (float i = -1e+6; i < 1e+6; i++) { + if (i <= max || i >= min) { + EXPECT_EQ(true, cut.Check(i)); + } + else { + EXPECT_EQ(false, cut.Check(i)); + } + } +} diff --git a/core/detectors/sts/CbmStsParSetModule.cxx b/core/detectors/sts/CbmStsParSetModule.cxx index b9439025c79e6912447ecb7383a087edaa0c6cb9..62b888a00d98379c01643e762ec469cff6c92c12 100644 --- a/core/detectors/sts/CbmStsParSetModule.cxx +++ b/core/detectors/sts/CbmStsParSetModule.cxx @@ -13,6 +13,7 @@ #include <Logger.h> // for LOG, Logger #include <cassert> // for assert +#include <fstream> // for reading parameters from ASCII file #include <sstream> // for operator<<, basic_ostream, stringstream #include <string> // for char_traits @@ -64,6 +65,84 @@ Bool_t CbmStsParSetModule::getParams(FairParamList*) } // -------------------------------------------------------------------------- +// ----- Read parameters from ASCII file -------------------------------- +Bool_t CbmStsParSetModule::LoadParASCII(std::string file_name) +{ + std::ifstream input(file_name); + if (!input.is_open()) { + LOG(error) << Form("[STS Charge Calibration] Error opening file: %s", file_name.c_str()); + ; + return kFALSE; + } + + LOG(info) << Form("[STS Charge Calibration] Loading configuration from file: %s", file_name.c_str()); + + int32_t address, side, asic_idx, nb_channels, nb_adc; + double dyn_range, threshold, time_res, dead_time, noise, znr; + + // Default ASIC values + std::unique_ptr<CbmStsParAsic> default_asic = + std::make_unique<CbmStsParAsic>(128, 31, 75000., 3000., 5., 800., 1000., 3.9789e-3); + + // Default module configuration + std::unique_ptr<CbmStsParModule> default_module = std::make_unique<CbmStsParModule>(2048, 128); + default_module->SetAllAsics(*default_asic); + + std::string line; + while (std::getline(input, line)) { + + // Skip commented lines + if (line[0] == '#') { + continue; + } + std::istringstream str_stream(line); + str_stream >> std::hex >> address >> std::dec >> side >> asic_idx >> nb_channels >> nb_adc >> dyn_range >> threshold + >> time_res >> dead_time >> noise >> znr; + + if (address == -1) { + LOG(info) << "[STS Charge Calibration] Using same configuration for all modules"; + SetGlobalPar(*default_module); + break; + } + + // Initialize map entry with default configuration + if (!fParams.count(address)) { + fParams[address] = *default_module; + } + + // Custom ASIC loaded from file + std::unique_ptr<CbmStsParAsic> custom_asic = + std::make_unique<CbmStsParAsic>(nb_channels, nb_adc, dyn_range, threshold, time_res, dead_time, noise, znr); + + if (asic_idx == -1) { // Same configuration for all ASIC in the given side + if (side == -1) { // Same configuration for both sides + LOG(info) << Form("[STS Charge Calibration] Using same configuration for all ASICs in module 0x%x", address); + fParams[address].SetAllAsics(*custom_asic); + } + else { + // Configure ASIC all for given side + LOG(info) << Form("[STS Charge Calibration] Using same configuration for all ASICs in module 0x%x, side %u", + address, side); + for (int idx = 0; idx < 8; idx++) { + fParams[address].SetAsic(idx + 8 * side, *custom_asic); + } + } + } + else { + if (side == -1) { + fParams[address].SetAsic(asic_idx, *custom_asic); + fParams[address].SetAsic(asic_idx + 8, *custom_asic); + } + else { + fParams[address].SetAsic(asic_idx + 8 * side, *custom_asic); + } + } + } + + + return kTRUE; +} +// -------------------------------------------------------------------------- // ----- Get condition parameters of a module --------------------------- const CbmStsParModule& CbmStsParSetModule::GetParModule(UInt_t address) diff --git a/core/detectors/sts/CbmStsParSetModule.h b/core/detectors/sts/CbmStsParSetModule.h index 3f516fc4f4adadb24d882c1d29521141529a3c71..6302ee1d96376e02c17a9b9618e68ba9c10b129b 100644 --- a/core/detectors/sts/CbmStsParSetModule.h +++ b/core/detectors/sts/CbmStsParSetModule.h @@ -64,6 +64,11 @@ public: **/ virtual Bool_t getParams(FairParamList* parList); + /** @brief Reading parameters from ASCII. + * * @param file_name path to the parameters file + **/ + Bool_t LoadParASCII(std::string file_name); + /** @brief Get condition parameters of a sensor ** @param Module address diff --git a/macro/sts/cbm_vertex.C b/macro/sts/cbm_vertex.C new file mode 100755 index 0000000000000000000000000000000000000000..12b6baada433f388f5f3db027d90c5c4825cc315 --- /dev/null +++ b/macro/sts/cbm_vertex.C @@ -0,0 +1,101 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +void cbm_vertex(int n_of_ts = -1, + double dca_cut = 0.5, // cm + std::string geo_setup_tag = "mcbm_beam_2024_05_08_nickel", std::string raw_file = "", + std::string rec_file = "", std::string geo_file = "", std::string out_file = "cbm_vertex_dca.root", + bool bAli = true) +{ + // ----- Timer -------------------------------------------------------- + TStopwatch timer; + timer.Start(); + + // --- Logger settings ---------------------------------------------------- + TString logLevel = "DEBUG"; + TString logVerbosity = "veryhigh"; + logLevel = "INFO"; + logVerbosity = "low"; + + FairLogger::GetLogger()->SetLogScreenLevel(logLevel.Data()); + FairLogger::GetLogger()->SetLogVerbosityLevel(logVerbosity.Data()); + // ------------------------------------------------------------------------ + + + // ----- Environment -------------------------------------------------- + std::string srcDir = gSystem->Getenv("VMCWORKDIR"); // top source directory + // ------------------------------------------------------------------------ + + std::unique_ptr<CbmStsAnalysis> run = std::make_unique<CbmStsAnalysis>(); + + FairFileSource* inputSource = new FairFileSource(rec_file.c_str()); + inputSource->AddFriend(raw_file.c_str()); + + FairRootFileSink* outputSink = new FairRootFileSink(out_file.c_str()); + + + if (bAli) { + TString alignmentMatrixFileName = srcDir + "/parameters/mcbm/AlignmentMatrices_" + geo_setup_tag + ".root"; + if (alignmentMatrixFileName.Length() != 0) { + std::map<std::string, TGeoHMatrix>* matrices{nullptr}; + TFile* misalignmentMatrixRootfile = new TFile(alignmentMatrixFileName, "READ"); + if (misalignmentMatrixRootfile->IsOpen()) { + gDirectory->GetObject("MisalignMatrices", matrices); + misalignmentMatrixRootfile->Close(); + } + else { + LOG(error) << "Could not open file " << alignmentMatrixFileName << "\n Exiting"; + exit(1); + } + + if (matrices) { + run->AddAlignmentMatrices(*matrices); + } + else { + LOG(error) << "Alignment required but no matrices found." + << "\n Exiting"; + exit(1); + } + } + } + + run->SetGeomFile(geo_file.c_str()); + run->SetSource(inputSource); + run->SetSink(outputSink); + + + // ------------------------------------------------------------------------ + // Configure Analysis filters + // ------------------------------------------------------------------------ + CbmCutMap ana_filter; + ana_filter.AddCbmCut(CbmCutId::kGlobalTrackStsSize)->SetMin(2); + ana_filter.AddCbmCut(CbmCutId::kGlobalTrackTrdSize)->SetMin(1); + ana_filter.AddCbmCut(CbmCutId::kGlobalTrackTofSize)->SetMin(1); + // ana_filter.AddCbmCut(CbmCutId::kGlobalTrackPval)->SetMax(0.005); + + // ------------------------------------------------------------------------ + // Configure Vertex Finder + // ------------------------------------------------------------------------ + std::shared_ptr<CbmDcaVertexFinder> vertex_finder = std::make_shared<CbmDcaVertexFinder>(dca_cut); + + // ------------------------------------------------------------------------ + // Configure Task + // ------------------------------------------------------------------------ + CbmEventVertexDca* cbm_vtx_cda = new CbmEventVertexDca(); + cbm_vtx_cda->SetVertexFinder(vertex_finder); + cbm_vtx_cda->SetCutMap(&ana_filter); + run->AddTask(cbm_vtx_cda); + + run->Init(); + run->Run(0, n_of_ts); + + // ------------------------------------------------------------------------ + timer.Stop(); + Double_t rtime = timer.RealTime(); + Double_t ctime = timer.CpuTime(); + cout << endl << endl; + cout << "Macro finished successfully." << endl; + cout << "Real time " << rtime << " s, CPU time " << ctime << " s" << endl; + cout << endl; +} diff --git a/macro/sts/sts_beam_spot.C b/macro/sts/sts_beam_spot.C new file mode 100644 index 0000000000000000000000000000000000000000..c927fcadd7ab7587d021b702037e1e7f9bd8e073 --- /dev/null +++ b/macro/sts/sts_beam_spot.C @@ -0,0 +1,123 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +void sts_beam_spot(int n_of_ts = -1, std::string geo_setup_tag = "mcbm_beam_2024_05_08_nickel", + std::string raw_file = "", std::string rec_file = "", std::string geo_file = "", + std::string out_file = "sts_beam_spot.root", bool bAli = true) +{ + // ----- Timer -------------------------------------------------------- + TStopwatch timer; + timer.Start(); + + // --- Logger settings ---------------------------------------------------- + TString logLevel = "DEBUG"; + TString logVerbosity = "veryhigh"; + logLevel = "INFO"; + logVerbosity = "low"; + + FairLogger::GetLogger()->SetLogScreenLevel(logLevel.Data()); + FairLogger::GetLogger()->SetLogVerbosityLevel(logVerbosity.Data()); + // ------------------------------------------------------------------------ + + + // ----- Environment -------------------------------------------------- + std::string srcDir = gSystem->Getenv("VMCWORKDIR"); // top source directory + // ------------------------------------------------------------------------ + + std::unique_ptr<CbmStsAnalysis> run = std::make_unique<CbmStsAnalysis>(); + + FairFileSource* inputSource = new FairFileSource(rec_file.c_str()); + inputSource->AddFriend(raw_file.c_str()); + + FairRootFileSink* outputSink = new FairRootFileSink(out_file.c_str()); + + + if (bAli) { + TString alignmentMatrixFileName = srcDir + "/parameters/mcbm/AlignmentMatrices_" + geo_setup_tag + ".root"; + if (alignmentMatrixFileName.Length() != 0) { + std::map<std::string, TGeoHMatrix>* matrices{nullptr}; + TFile* misalignmentMatrixRootfile = new TFile(alignmentMatrixFileName, "READ"); + if (misalignmentMatrixRootfile->IsOpen()) { + gDirectory->GetObject("MisalignMatrices", matrices); + misalignmentMatrixRootfile->Close(); + } + else { + LOG(error) << "Could not open file " << alignmentMatrixFileName << "\n Exiting"; + exit(1); + } + + if (matrices) { + run->AddAlignmentMatrices(*matrices); + } + else { + LOG(error) << "Alignment required but no matrices found." + << "\n Exiting"; + exit(1); + } + } + } + + run->SetGeomFile(geo_file.c_str()); + run->SetSource(inputSource); + run->SetSink(outputSink); + + + // ------------------------------------------------------------------------ + // Configure Analysis filters + // ------------------------------------------------------------------------ + CbmCutMap ana_filter; + ana_filter.AddCbmCut(CbmCutId::kStsHitCharge)->SetMin(10000); + ana_filter.AddCbmCut(CbmCutId::kEventNofStsHit)->SetMin(2); + + // ------------------------------------------------------------------------ + // Configure mCBM target planes + // ------------------------------------------------------------------------ + double l_k0 = 38.45; + double l_k1 = l_k0 + 0.86 + 11.8 + 1.3 + 0.7; + double l_b1 = l_k0 + 4.99 + 1.2 + 0.75; + + double beam_rot_y = (25.) * TMath::DegToRad(); + CbmTarget Ni; + Ni.SetPosition(0, 0, 0); + Ni.SetRotation(beam_rot_y); + + CbmTarget T0; + T0.SetPosition(-20 * sin(beam_rot_y), 0, -20 * cos(beam_rot_y)); + T0.SetRotation(beam_rot_y); + + CbmTarget K0; + K0.SetPosition(-l_k0 * sin(beam_rot_y), 0, -l_k0 * cos(beam_rot_y)); + K0.SetRotation(beam_rot_y); + + CbmTarget B1; + B1.SetPosition(-l_b1 * sin(beam_rot_y), 0, -l_b1 * cos(beam_rot_y)); + B1.SetRotation(beam_rot_y); + + CbmTarget K1; + K1.SetPosition(-l_k1 * sin(beam_rot_y), 0, -l_k1 * cos(beam_rot_y)); + K1.SetRotation(beam_rot_y); + // ------------------------------------------------------------------------ + + CbmStsRecoBeamSpot* sts_bs = new CbmStsRecoBeamSpot(); + sts_bs->SetCutMap(&ana_filter); + sts_bs->AddTarget("Ni", &Ni); + sts_bs->AddTarget("T0", &T0); + sts_bs->AddTarget("K0", &K0); + sts_bs->AddTarget("B1", &B1); + sts_bs->AddTarget("K1", &K1); + + run->AddTask(sts_bs); + + run->Init(); + run->Run(0, n_of_ts); + + // ------------------------------------------------------------------------ + timer.Stop(); + Double_t rtime = timer.RealTime(); + Double_t ctime = timer.CpuTime(); + cout << endl << endl; + cout << "Macro finished successfully." << endl; + cout << "Real time " << rtime << " s, CPU time " << ctime << " s" << endl; + cout << endl; +} diff --git a/macro/sts/sts_correlation.C b/macro/sts/sts_correlation.C new file mode 100644 index 0000000000000000000000000000000000000000..ac5b37e11bfc4b91ec6e12d38289b6bc01805b1d --- /dev/null +++ b/macro/sts/sts_correlation.C @@ -0,0 +1,91 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +void sts_correlation(int n_of_ts = -1, std::string geo_setup_tag = "mcbm_beam_2024_05_08_nickel", + std::string raw_file = "", std::string rec_file = "", std::string geo_file = "", + std::string out_file = "cbm_sts_correlation.root", bool bAli = true) +{ + // ----- Timer -------------------------------------------------------- + TStopwatch timer; + timer.Start(); + + // --- Logger settings ---------------------------------------------------- + TString logLevel = "DEBUG"; + TString logVerbosity = "veryhigh"; + logLevel = "INFO"; + logVerbosity = "low"; + + FairLogger::GetLogger()->SetLogScreenLevel(logLevel.Data()); + FairLogger::GetLogger()->SetLogVerbosityLevel(logVerbosity.Data()); + // ------------------------------------------------------------------------ + + + // ----- Environment -------------------------------------------------- + std::string srcDir = gSystem->Getenv("VMCWORKDIR"); // top source directory + // ------------------------------------------------------------------------ + + std::unique_ptr<CbmStsAnalysis> run = std::make_unique<CbmStsAnalysis>(); + + FairFileSource* inputSource = new FairFileSource(rec_file.c_str()); + inputSource->AddFriend(raw_file.c_str()); + + FairRootFileSink* outputSink = new FairRootFileSink(out_file.c_str()); + + + if (bAli) { + TString alignmentMatrixFileName = srcDir + "/parameters/mcbm/AlignmentMatrices_" + geo_setup_tag + ".root"; + if (alignmentMatrixFileName.Length() != 0) { + std::map<std::string, TGeoHMatrix>* matrices{nullptr}; + TFile* misalignmentMatrixRootfile = new TFile(alignmentMatrixFileName, "READ"); + if (misalignmentMatrixRootfile->IsOpen()) { + gDirectory->GetObject("MisalignMatrices", matrices); + misalignmentMatrixRootfile->Close(); + } + else { + LOG(error) << "Could not open file " << alignmentMatrixFileName << "\n Exiting"; + exit(1); + } + + if (matrices) { + run->AddAlignmentMatrices(*matrices); + } + else { + LOG(error) << "Alignment required but no matrices found." + << "\n Exiting"; + exit(1); + } + } + } + + run->SetGeomFile(geo_file.c_str()); + run->SetSource(inputSource); + run->SetSink(outputSink); + + + // ------------------------------------------------------------------------ + // Configure Analysis filters + // ------------------------------------------------------------------------ + CbmCutMap ana_filter; + ana_filter.AddCbmCut(CbmCutId::kStsHitCharge)->SetMin(10000); + ana_filter.AddCbmCut(CbmCutId::kEventNofStsHit)->SetMin(2); + + // ------------------------------------------------------------------------ + // Configure Correlation + // ------------------------------------------------------------------------ + CbmStsCorrelation* sts_corr = new CbmStsCorrelation(); + sts_corr->SetCutMap(&ana_filter); + run->AddTask(sts_corr); + + run->Init(); + run->Run(0, n_of_ts); + + // ------------------------------------------------------------------------ + timer.Stop(); + Double_t rtime = timer.RealTime(); + Double_t ctime = timer.CpuTime(); + cout << endl << endl; + cout << "Macro finished successfully." << endl; + cout << "Real time " << rtime << " s, CPU time " << ctime << " s" << endl; + cout << endl; +} diff --git a/macro/sts/sts_efficiency.C b/macro/sts/sts_efficiency.C new file mode 100755 index 0000000000000000000000000000000000000000..091c43dbc4c9de3d30542c422a4dcb61f49900fd --- /dev/null +++ b/macro/sts/sts_efficiency.C @@ -0,0 +1,107 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +void sts_efficiency(int n_of_ts = -1, std::string geo_setup_tag = "mcbm_beam_2024_05_08_nickel", + std::string raw_file = "", std::string rec_file = "", std::string geo_file = "", + std::string out_file = "cbm_sts_efficiency.root", bool bAli = true) +{ + // ----- Timer -------------------------------------------------------- + TStopwatch timer; + timer.Start(); + + // --- Logger settings ---------------------------------------------------- + TString logLevel = "DEBUG"; + TString logVerbosity = "veryhigh"; + logLevel = "INFO"; + logVerbosity = "low"; + + FairLogger::GetLogger()->SetLogScreenLevel(logLevel.Data()); + FairLogger::GetLogger()->SetLogVerbosityLevel(logVerbosity.Data()); + // ------------------------------------------------------------------------ + + + // ----- Environment -------------------------------------------------- + std::string srcDir = gSystem->Getenv("VMCWORKDIR"); // top source directory + // ------------------------------------------------------------------------ + + std::unique_ptr<CbmStsAnalysis> run = std::make_unique<CbmStsAnalysis>(); + + FairFileSource* inputSource = new FairFileSource(rec_file.c_str()); + inputSource->AddFriend(raw_file.c_str()); + + FairRootFileSink* outputSink = new FairRootFileSink(out_file.c_str()); + + + if (bAli) { + TString alignmentMatrixFileName = srcDir + "/parameters/mcbm/AlignmentMatrices_" + geo_setup_tag + ".root"; + if (alignmentMatrixFileName.Length() != 0) { + std::map<std::string, TGeoHMatrix>* matrices{nullptr}; + TFile* misalignmentMatrixRootfile = new TFile(alignmentMatrixFileName, "READ"); + if (misalignmentMatrixRootfile->IsOpen()) { + gDirectory->GetObject("MisalignMatrices", matrices); + misalignmentMatrixRootfile->Close(); + } + else { + LOG(error) << "Could not open file " << alignmentMatrixFileName << "\n Exiting"; + exit(1); + } + + if (matrices) { + run->AddAlignmentMatrices(*matrices); + } + else { + LOG(error) << "Alignment required but no matrices found." + << "\n Exiting"; + exit(1); + } + } + } + + run->SetGeomFile(geo_file.c_str()); + run->SetSource(inputSource); + run->SetSink(outputSink); + + + // ------------------------------------------------------------------------ + // Configure Analysis filters + // ------------------------------------------------------------------------ + CbmCutMap ana_filter; + ana_filter.AddCbmCut(CbmCutId::kStsHitCharge)->SetMin(10000); + ana_filter.AddCbmCut(CbmCutId::kEventNofStsHit)->SetMin(2); + ana_filter.AddCbmCut(CbmCutId::kGlobalTrackStsSize)->SetMin(2); + ana_filter.AddCbmCut(CbmCutId::kGlobalTrackTrdSize)->SetMin(1); + ana_filter.AddCbmCut(CbmCutId::kGlobalTrackTofSize)->SetMin(1); + // ana_filter.AddCbmCut(CbmCutId::kGlobalTrackPval)->SetMax(0.005); + + // ------------------------------------------------------------------------ + // Configure Vertex Finder + // ------------------------------------------------------------------------ + std::shared_ptr<CbmDcaVertexFinder> vertex_finder = std::make_shared<CbmDcaVertexFinder>(0.2); + + // ------------------------------------------------------------------------ + // Configure Efficiency + // ------------------------------------------------------------------------ + CbmStsEfficiency* sts_eff1 = new CbmStsEfficiency(1, // test_layer_(layer_idx) + 0.25, // max_dis_trg_vtx_(cut_trg_vtx) + 0.25, // max_dca_trk_vtx_(cut_trk_vtx) + 0.005 // default_residual(def_res) + ); + + sts_eff1->SetCutMap(&ana_filter); + sts_eff1->SetVertexFinder(vertex_finder.get()); + // sts_eff1->SetResidual("Ref_Sts10_residuals.info"); + run->AddTask(sts_eff1); + + run->Init(); + run->Run(0, n_of_ts); + + // ------------------------------------------------------------------------ + timer.Stop(); + Double_t rtime = timer.RealTime(); + Double_t ctime = timer.CpuTime(); + cout << endl << endl; + cout << "Macro finished successfully." << endl; + cout << "Real time " << rtime << " s, CPU time " << ctime << " s" << endl; + cout << endl; +} diff --git a/macro/sts/sts_hit_ana.C b/macro/sts/sts_hit_ana.C new file mode 100644 index 0000000000000000000000000000000000000000..327d69b2ba271f58e3f8212a876e9ba1a44c508f --- /dev/null +++ b/macro/sts/sts_hit_ana.C @@ -0,0 +1,97 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +void sts_hit_ana(int n_of_ts = -1, std::string geo_setup_tag = "mcbm_beam_2024_05_08_nickel", std::string raw_file = "", + std::string rec_file = "", std::string geo_file = "", std::string out_file = "cbm_sts_hit_ana.root", + bool bAli = true) +{ + // ----- Timer -------------------------------------------------------- + TStopwatch timer; + timer.Start(); + + // --- Logger settings ---------------------------------------------------- + TString logLevel = "DEBUG"; + TString logVerbosity = "veryhigh"; + logLevel = "INFO"; + logVerbosity = "low"; + + FairLogger::GetLogger()->SetLogScreenLevel(logLevel.Data()); + FairLogger::GetLogger()->SetLogVerbosityLevel(logVerbosity.Data()); + // ------------------------------------------------------------------------ + + + // ----- Environment -------------------------------------------------- + std::string srcDir = gSystem->Getenv("VMCWORKDIR"); // top source directory + // ------------------------------------------------------------------------ + + std::unique_ptr<CbmStsAnalysis> run = std::make_unique<CbmStsAnalysis>(); + + FairFileSource* inputSource = new FairFileSource(rec_file.c_str()); + inputSource->AddFriend(raw_file.c_str()); + + FairRootFileSink* outputSink = new FairRootFileSink(out_file.c_str()); + + + if (bAli) { + TString alignmentMatrixFileName = srcDir + "/parameters/mcbm/AlignmentMatrices_" + geo_setup_tag + ".root"; + if (alignmentMatrixFileName.Length() != 0) { + std::map<std::string, TGeoHMatrix>* matrices{nullptr}; + TFile* misalignmentMatrixRootfile = new TFile(alignmentMatrixFileName, "READ"); + if (misalignmentMatrixRootfile->IsOpen()) { + gDirectory->GetObject("MisalignMatrices", matrices); + misalignmentMatrixRootfile->Close(); + } + else { + LOG(error) << "Could not open file " << alignmentMatrixFileName << "\n Exiting"; + exit(1); + } + + if (matrices) { + run->AddAlignmentMatrices(*matrices); + } + else { + LOG(error) << "Alignment required but no matrices found." + << "\n Exiting"; + exit(1); + } + } + } + + run->SetGeomFile(geo_file.c_str()); + run->SetSource(inputSource); + run->SetSink(outputSink); + + + // ------------------------------------------------------------------------ + // Configure Analysis filters + // ------------------------------------------------------------------------ + CbmCutMap ana_filter; + // Filter for all STS hits + ana_filter.AddCbmCut(CbmCutId::kStsHitQasym)->SetRange(-0.8, +0.8); + ana_filter.AddCbmCut(CbmCutId::kStsHitCharge)->SetMin(10000); + + // Filter for track to extract STS hits tracks + ana_filter.AddCbmCut(CbmCutId::kGlobalTrackStsSize)->SetMin(2); + ana_filter.AddCbmCut(CbmCutId::kGlobalTrackTrdSize)->SetMin(1); + ana_filter.AddCbmCut(CbmCutId::kGlobalTrackTofSize)->SetMin(1); + + // ------------------------------------------------------------------------ + // Configure Charge Analysis + // ------------------------------------------------------------------------ + CbmStsHitAna* sts_hit = new CbmStsHitAna(Form("%s/parameters/sts/sts_charge_cal_v24a.par", srcDir.c_str())); + sts_hit->SetCutMap(&ana_filter); + run->AddTask(sts_hit); + + run->Init(); + run->Run(0, n_of_ts); + + // ------------------------------------------------------------------------ + timer.Stop(); + Double_t rtime = timer.RealTime(); + Double_t ctime = timer.CpuTime(); + cout << endl << endl; + cout << "Macro finished successfully." << endl; + cout << "Real time " << rtime << " s, CPU time " << ctime << " s" << endl; + cout << endl; +} diff --git a/macro/sts/sts_resolution.C b/macro/sts/sts_resolution.C new file mode 100755 index 0000000000000000000000000000000000000000..75404c692627d912190878cc70de113fa94c357d --- /dev/null +++ b/macro/sts/sts_resolution.C @@ -0,0 +1,98 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +void sts_resolution(int n_of_ts = -1, std::string geo_setup_tag = "mcbm_beam_2024_05_08_nickel", + std::string raw_file = "", std::string rec_file = "", std::string geo_file = "", + std::string out_file = "cbm_sts_correlation.root", bool bAli = true) +{ + // ----- Timer -------------------------------------------------------- + TStopwatch timer; + timer.Start(); + + // --- Logger settings ---------------------------------------------------- + TString logLevel = "DEBUG"; + TString logVerbosity = "veryhigh"; + logLevel = "INFO"; + logVerbosity = "low"; + + FairLogger::GetLogger()->SetLogScreenLevel(logLevel.Data()); + FairLogger::GetLogger()->SetLogVerbosityLevel(logVerbosity.Data()); + // ------------------------------------------------------------------------ + + + // ----- Environment -------------------------------------------------- + std::string srcDir = gSystem->Getenv("VMCWORKDIR"); // top source directory + // ------------------------------------------------------------------------ + + std::unique_ptr<CbmStsAnalysis> run = std::make_unique<CbmStsAnalysis>(); + + FairFileSource* inputSource = new FairFileSource(rec_file.c_str()); + inputSource->AddFriend(raw_file.c_str()); + + FairRootFileSink* outputSink = new FairRootFileSink(out_file.c_str()); + + + if (bAli) { + TString alignmentMatrixFileName = srcDir + "/parameters/mcbm/AlignmentMatrices_" + geo_setup_tag + ".root"; + if (alignmentMatrixFileName.Length() != 0) { + std::map<std::string, TGeoHMatrix>* matrices{nullptr}; + TFile* misalignmentMatrixRootfile = new TFile(alignmentMatrixFileName, "READ"); + if (misalignmentMatrixRootfile->IsOpen()) { + gDirectory->GetObject("MisalignMatrices", matrices); + misalignmentMatrixRootfile->Close(); + } + else { + LOG(error) << "Could not open file " << alignmentMatrixFileName << "\n Exiting"; + exit(1); + } + + if (matrices) { + run->AddAlignmentMatrices(*matrices); + } + else { + LOG(error) << "Alignment required but no matrices found." + << "\n Exiting"; + exit(1); + } + } + } + + run->SetGeomFile(geo_file.c_str()); + run->SetSource(inputSource); + run->SetSink(outputSink); + + + // ------------------------------------------------------------------------ + // Configure filters + // ------------------------------------------------------------------------ + CbmCutMap ana_filter; + ana_filter.AddCbmCut(CbmCutId::kEventNofGlobalTrack)->SetMin(1); + ana_filter.AddCbmCut(CbmCutId::kGlobalTrackStsSize)->SetMin(2); + ana_filter.AddCbmCut(CbmCutId::kGlobalTrackTrdSize)->SetMin(1); + ana_filter.AddCbmCut(CbmCutId::kGlobalTrackTofSize)->SetMin(1); + ana_filter.AddCbmCut(CbmCutId::kGlobalTrackPval)->SetMax(0.5); + + // ------------------------------------------------------------------------ + // Configure analysis + // ------------------------------------------------------------------------ + CbmStsResolution* sts_resolution = new CbmStsResolution(0.5); + sts_resolution->SetCutMap(&ana_filter); + sts_resolution->EnableRefSensor(0x10000002); // U0 L0 U0 + sts_resolution->EnableRefSensor(0x10018422); // U1 L1 U1 + sts_resolution->SetMinTrkHitQ(10000); + run->AddTask(sts_resolution); + + run->Init(); + run->Run(0, n_of_ts); + + ana_filter.Print(); + // ------------------------------------------------------------------------ + timer.Stop(); + Double_t rtime = timer.RealTime(); + Double_t ctime = timer.CpuTime(); + cout << endl << endl; + cout << "Macro finished successfully." << endl; + cout << "Real time " << rtime << " s, CPU time " << ctime << " s" << endl; + cout << endl; +} diff --git a/macro/sts/sts_time.C b/macro/sts/sts_time.C new file mode 100644 index 0000000000000000000000000000000000000000..64a4013a319c6d79eb64221caeda09e055b6a6af --- /dev/null +++ b/macro/sts/sts_time.C @@ -0,0 +1,56 @@ +/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Dario Ramirez [committer] */ + +void sts_time(int run_id = 2984, int n_of_ts = 2, std::string raw_file = "", + std::string out_file = "cbm_sts_time_cal.root") +{ + // ----- Timer -------------------------------------------------------- + TStopwatch timer; + timer.Start(); + + // --- Logger settings ---------------------------------------------------- + TString logLevel = "DEBUG"; + TString logVerbosity = "veryhigh"; + logLevel = "INFO"; + logVerbosity = "low"; + + FairLogger::GetLogger()->SetLogScreenLevel(logLevel.Data()); + FairLogger::GetLogger()->SetLogVerbosityLevel(logVerbosity.Data()); + // ------------------------------------------------------------------------ + + + // ----- Environment -------------------------------------------------- + std::string srcDir = gSystem->Getenv("VMCWORKDIR"); // top source directory + // ------------------------------------------------------------------------ + + std::unique_ptr<CbmStsAnalysis> run = std::make_unique<CbmStsAnalysis>(); + FairFileSource* inputSource = new FairFileSource(raw_file.c_str()); + FairRootFileSink* outputSink = new FairRootFileSink(out_file.c_str()); + + run->SetSource(inputSource); + run->SetSink(outputSink); + + // ------------------------------------------------------------------------ + // Configure Analysis filters + // ------------------------------------------------------------------------ + CbmCutMap ana_filter; + ana_filter.AddCbmCut(CbmCutId::kBmonDigiSide)->SetRange(1, 1); + + CbmStsTimeCal* sts_time = new CbmStsTimeCal(run_id, ECbmModuleId::kBmon, -100, 100); + sts_time->SetCutMap(&ana_filter); + sts_time->SetWalkFile(Form("%s/parameters/online/StsWalkMap_mcbm2024.yaml", srcDir.c_str())); + run->AddTask(sts_time); + + run->Init(); + run->Run(0, n_of_ts); + + // ------------------------------------------------------------------------ + timer.Stop(); + Double_t rtime = timer.RealTime(); + Double_t ctime = timer.CpuTime(); + cout << endl << endl; + cout << "Macro finished successfully." << endl; + cout << "Real time " << rtime << " s, CPU time " << ctime << " s" << endl; + cout << endl; +}