From 89de6d6a3699ee5afcbe9d060d92465b977b7e64 Mon Sep 17 00:00:00 2001 From: Alexandru Bercuci <abercuci@niham.nipne.ro> Date: Tue, 27 Feb 2024 09:24:44 +0000 Subject: [PATCH] Create geometry of the TRD2D chamber in compiled code. Move macro code from Create_geometry_* macro to namespace cbm::trd:geo. Implement the latest modifications to the design of the chamber and FEB as they are proposed for day 1. --- core/detectors/trd/CMakeLists.txt | 4 +- core/detectors/trd/CbmTrdBaseLinkDef.h | 21 +- core/detectors/trd/CbmTrdDefs.cxx | 20 + core/detectors/trd/CbmTrdDefs.h | 180 ++++- core/detectors/trd/CbmTrdGeoFactory.cxx | 700 ++++++++++++++++++++ core/detectors/trd/CbmTrdGeoFactory.h | 352 ++++++++++ core/detectors/trd/CbmTrdGeoSetup.cxx | 491 ++++++++++++++ core/detectors/trd/CbmTrdGeoSetup.h | 204 ++++++ core/detectors/trd/CbmTrdModuleAbstract.cxx | 28 +- core/detectors/trd/CbmTrdModuleAbstract.h | 77 +-- core/detectors/trd/CbmTrdParMod.cxx | 6 +- core/detectors/trd/CbmTrdParMod.h | 23 +- core/detectors/trd/CbmTrdParModDigi.cxx | 13 +- core/detectors/trd/CbmTrdParModDigi.h | 15 +- core/detectors/trd/CbmTrdParSpadic.cxx | 25 +- reco/detectors/trd/CbmTrdHitProducer.cxx | 38 +- sim/detectors/trd/CbmTrdDigitizer.cxx | 39 +- 17 files changed, 2084 insertions(+), 152 deletions(-) create mode 100644 core/detectors/trd/CbmTrdDefs.cxx create mode 100644 core/detectors/trd/CbmTrdGeoFactory.cxx create mode 100644 core/detectors/trd/CbmTrdGeoFactory.h create mode 100644 core/detectors/trd/CbmTrdGeoSetup.cxx create mode 100644 core/detectors/trd/CbmTrdGeoSetup.h diff --git a/core/detectors/trd/CMakeLists.txt b/core/detectors/trd/CMakeLists.txt index 5b61e9fcad..3855747d70 100644 --- a/core/detectors/trd/CMakeLists.txt +++ b/core/detectors/trd/CMakeLists.txt @@ -3,10 +3,13 @@ set(INCLUDE_DIRECTORIES ) set(SRCS + CbmTrdDefs.cxx CbmTrdGas.cxx CbmTrdContFact.cxx CbmTrdParManager.cxx CbmTrdModuleAbstract.cxx + CbmTrdGeoFactory.cxx + CbmTrdGeoSetup.cxx CbmMcbm2020TrdTshiftPar.cxx CbmTrdParSet.cxx CbmTrdParSetAsic.cxx @@ -74,6 +77,5 @@ generate_cbm_library() # Install file which has no corresponding source file install(FILES - CbmTrdDefs.h DESTINATION include ) diff --git a/core/detectors/trd/CbmTrdBaseLinkDef.h b/core/detectors/trd/CbmTrdBaseLinkDef.h index 6f08f8e0b7..b191955c75 100644 --- a/core/detectors/trd/CbmTrdBaseLinkDef.h +++ b/core/detectors/trd/CbmTrdBaseLinkDef.h @@ -1,6 +1,6 @@ -/* Copyright (C) 2018-2021 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt +/* Copyright (C) 2018-2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt SPDX-License-Identifier: GPL-3.0-only - Authors: Florian Uhlig [committer] */ + Authors: Florian Uhlig [committer], Alexandru Bercuci */ // $Id: TrdBaseLinkDef.h $ @@ -10,11 +10,28 @@ #pragma link off all classes; #pragma link off all functions; +#pragma link C++ namespace cbm::trd; +#pragma link C++ enum cbm::trd::eAsic; +#pragma link C++ enum cbm::trd::ePadPlane; +#pragma link C++ enum cbm::trd::eModuleConfig; #pragma link C++ class CbmTrdGas + ; #pragma link C++ class CbmTrdContFact + ; #pragma link C++ class CbmMcbm2020TrdTshiftPar + ; #pragma link C++ class CbmTrdModuleAbstract + ; +#pragma link C++ namespace cbm::trd::geo; +#pragma link C++ class cbm::trd::geo::ChamberBuilder + ; +#pragma link C++ class cbm::trd::geo::ChamberBuilder::Component + ; +#pragma link C++ class cbm::trd::geo::ChamberBuilder::Radiator + ; +#pragma link C++ class cbm::trd::geo::ChamberBuilder::Window + ; +#pragma link C++ class cbm::trd::geo::ChamberBuilder::Volume + ; +#pragma link C++ class cbm::trd::geo::ChamberBuilder::BackPanel + ; +#pragma link C++ class cbm::trd::geo::ChamberBuilder::FEB + ; +#pragma link C++ class cbm::trd::geo::SetupManager + ; +#pragma link C++ class cbm::trd::geo::Setup + ; +#pragma link C++ class cbm::trd::geo::Setup::Module + ; +#pragma link C++ class cbm::trd::geo::Setup::Asic + ; + #pragma link C++ class CbmTrdParManager + ; #pragma link C++ class CbmTrdParSet + ; #pragma link C++ class CbmTrdParSetAsic + ; diff --git a/core/detectors/trd/CbmTrdDefs.cxx b/core/detectors/trd/CbmTrdDefs.cxx new file mode 100644 index 0000000000..e1272909d7 --- /dev/null +++ b/core/detectors/trd/CbmTrdDefs.cxx @@ -0,0 +1,20 @@ +/* Copyright (C) 2024 Hulubei National Institute of Physics and Nuclear Engineering - Horia Hulubei, Bucharest + SPDX-License-Identifier: GPL-3.0-only + Authors: Alexandru Bercuci [committer] */ + +#include "CbmTrdDefs.h" + +using namespace cbm::trd; + +bool cbm::trd::HasFaspFEE(uint16_t config) { return TESTBIT(config, eModuleConfig::kFEEtyp); } +bool cbm::trd::HasSpadicFEE(uint16_t config) { return !HasFaspFEE(config); } +bool cbm::trd::HasPadPlane2D(uint16_t config) { return TESTBIT(config, eModuleConfig::kPPtyp); } +bool cbm::trd::HasPadPlane1D(uint16_t config) { return !HasPadPlane2D(config); } +void cbm::trd::SetFEE(uint16_t config, bool fasp) +{ + fasp ? SETBIT(config, eModuleConfig::kFEEtyp) : CLRBIT(config, eModuleConfig::kFEEtyp); +} +void cbm::trd::SetPP(uint16_t config, bool twod) +{ + twod ? SETBIT(config, eModuleConfig::kPPtyp) : CLRBIT(config, eModuleConfig::kPPtyp); +} diff --git a/core/detectors/trd/CbmTrdDefs.h b/core/detectors/trd/CbmTrdDefs.h index 9940d58857..e8576dce03 100644 --- a/core/detectors/trd/CbmTrdDefs.h +++ b/core/detectors/trd/CbmTrdDefs.h @@ -1,6 +1,6 @@ -/* Copyright (C) 2020 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt +/* Copyright (C) 2020-2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt SPDX-License-Identifier: GPL-3.0-only - Authors: Pascal Raisig, Florian Uhlig [committer] */ + Authors: Pascal Raisig, Florian Uhlig [committer], Alexandru Bercuci */ /* * Purpose: Reference class for global definitions used within the CbmTrd project @@ -12,17 +12,173 @@ #include "Rtypes.h" -enum class eCbmTrdModuleTypes : Int_t +namespace cbm::trd { - kHighChDensitySmallR = 1, - kLowChDensitySmallR = 3, - kHighChDensityLargeR = 5, - kLowChDensityLargeR = 7, - kMcbmModule = - 8 // FIXME moduleType 8 has multiple definitions, check if non mCbm definitions are really needed. - PR 03/25/2020 - , - kNmoduleTypes = 5 // REMARK this number has to be updated by hand! -}; ///< Enum for moduleTypes of the rectangular TrdModules as used in geometry files. + enum class eAsic : int + { + kSpadic = 0 ///< SPADIC type definition + , + kFasp = 1 ///< FASP ASIC definition + , + kNotSet ///< ASIC not set / recognized + }; + enum class ePadPlane : int + { + k1d = 0 ///< rectangular 1D case + , + k2d = 1 ///< triangular 2D case + , + kNotSet ///< pad=plane not set / recognized + }; + enum class eWindow : int + { + kThin = 0 ///< 1D case (Al+mylar) + , + kThick ///< 2D case (Kapton + C + HC) + , + kNotSet ///< window not set / recognized + }; + enum class eGas : int + { + kAr = 0 ///< ArCO2 active gas + , + kXe ///< XeCO2 active gas + , + kNotSet ///< active gas not set / recognized + }; + /** 16 bits bit map for module configuration + * [0] - chamber's pad-plane type; 0 rectangular pads, 1 triangular pads, \see SetRO + * [1] - chamber's FEE type; 0 SPADIC, 1 FASP, \see SetFEE + * [2] - + * [3] - + * [4] - + * [5] - + * [6] - + * [7] - + */ + enum eModuleConfig + { + kPPtyp = 0 ///< toggle pad-plane type of the chamber + , + kFEEtyp = 1 ///< toggle FEE type for the module + }; + enum class eModuleTypes1D : int + { + kHighChDensitySmallR = 1, + kLowChDensitySmallR = 3, + kHighChDensityLargeR = 5, + kLowChDensityLargeR = 7, + kMcbmModule = + 8 // FIXME moduleType 8 has multiple definitions, check if non mCbm definitions are really needed. - PR 03/25/2020 + , + kNmoduleTypes = 5 // REMARK this number has to be updated by hand! + }; ///< Enum for moduleTypes of the rectangular TrdModules as used in geometry files. + struct FEB { + int nasic = 0; ///< no of asics / feb + int nchannels = 0; ///< no of channels / asic + int nmax = 0; ///< max no of febs on the module + }; + struct READOUT { + int nsec = 0; ///< no of sectors on the pad-plane + int ncol = 0; ///< no of (rectangular - pads) column + int nrow = 0; ///< no of pad rows + int nasic = 0; ///< no of ASICs to read-out the pad-plane + int ndaq = 0; ///< no of concentrators (e.g. CROB) to read-out the pad-plane + float sizex = 0.; ///< size of active area along wires + float sizey[3] = {0.}; ///< size of active area across wires + }; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-variable" + // array of pad geometries in the TRD1D + static READOUT mod1D[9] = { + // module type 0 dummy + {0, 0, 0, 0, 0, 0., {0., 0., 0.}}, + // module type 1 + // number of pads: 80 x 32 = 2560 + // pad size sector 1: 0.68 cm x 1.75 cm = 1.18 cm2 + // pad size sector 0: 0.68 cm x 1.50 cm = 1.01 cm2 + {3, 80, 32, 80, 5, 54., {6.0, 42.0, 6.0}}, + // module type 2 + // number of pads: 80 x 16 = 1280 + // pad size sector 1: 0.68 cm x 3.50 cm = 2.36 cm2 + // pad size sector 0: 0.68 cm x 3.25 cm = 2.19 cm2 + {3, 80, 16, 80, 5, 54., {13.0, 28.0, 13.0}}, + // module type 3 + // number of pads: 80 x 8 = 640 + // number of asic: 20 + // pad size sector 1: 0.68 cm x 6.75 cm = 4.56 cm2 + // pad size sector 0: 0.68 cm x 6.75 cm = 4.56 cm2 + {1, 80, 8, 20, 1, 54., {54., 0., 0.}}, + // module type 4 + // number of pads: 80 x 8 = 640 + // pad size sector 1: 0.68 cm x 6.75 cm = 4.56 cm2 + // pad size sector 0: 0.68 cm x 6.75 cm = 4.56 cm2 + {1, 80, 8, 20, 1, 54., {54., 0., 0.}}, + // module type 5 + // number of pads: 144 x 24 = 3456 + // number of asic: 36 + // pad size sector 1: 0.67 cm x 4.00 cm = 2.67 cm2 + // pad size sector 0: 0.67 cm x 4.00 cm = 2.67 cm2 + {1, 144, 24, 108, 2, 96., {96.0, 0., 0.}}, + // module type 6 + // number of pads: 144 x 16 = 2304 + // pad size sector 1: 0.67 cm x 6.00 cm = 4.00 cm2 + // pad size sector 0: 0.67 cm x 6.00 cm = 4.00 cm2 + {1, 144, 16, 72, 2, 96., {96.0, 0., 0.}}, + // module type 7 + // number of pads: 144 x 8 = 1152 + // number of asic: 36 + // pad size sector 1: 0.67 cm x 12.00 cm = 8.00 cm2 + // pad size sector 0: 0.67 cm x 12.00 cm = 8.00 cm2 + {1, 144, 8, 36, 2, 96., {96.0, 0., 0.}}, + // module type 8 + // number of pads: 144 x 4 = 576 + // pad size sector 1: 0.67 cm x 24.00 cm = 16.00 cm2 + // pad size sector 0: 0.67 cm x 24.00 cm = 16.00 cm2 + {1, 144, 4, 18, 1, 96., {96.0, 0., 0.}}}; + + // array of pad geometries in the TRD2D + static const READOUT mod2D[3] = { + // module type 1 (TRD2D @ CBM) + // number of pads: 72 x 20 = 1440 + // pad size: 0.75 cm x 2.70 cm = 2.03 cm2 + // number of asic: 180 + // number of crob: 5 + {1, 72, 20, 180, 5, 54.0, {54.0, 0., 0.}}, + // module type 9 (TRD2012 @ mCBM) + // number of pads: 72 x 20 = 1440 + // pad size: 0.75 cm x 2.79 cm = 2.03 cm2 + {1, 72, 20, 180, 5, 54.0, {55.8, 0., 0.}}, + // module type 10 (TRD2010) + // number of pads: 32 x 3 = 96 + // pad size : 0.72 cm x 2.72 cm = 1.96 cm2 + {1, 32, 3, 6, 1, 23.04, {8.16, 0., 0.}}}; + + static constexpr FEB faspFeb[2] = { + {6, 16, 30}, ///< FASPRO v1 + {12, 16, 15} ///< FASPRO v2 + }; +#pragma GCC diagnostic pop + + /** \brief Inquire the FEE read-out type of the module + * \return false for SPADIC and true for FASP + */ + bool HasFaspFEE(uint16_t config); + bool HasSpadicFEE(uint16_t config); + /** \brief Inquire the pad-plane type of the chamber + * \return false for TRD-1D and true for TRD-2D + */ + bool HasPadPlane2D(uint16_t config); + bool HasPadPlane1D(uint16_t config); + /** \brief Define the read-out FEE type of the module + * \param[in] set true for FASP and false [default] for SPADIC + */ + void SetFEE(uint16_t config, bool fasp = true); + /** \brief Define the pad-plane type of the chamber + * \param[in] set true for TRD-2D and false [default] for TRD-1D + */ + void SetPP(uint16_t config, bool twod = true); +} // namespace cbm::trd #endif diff --git a/core/detectors/trd/CbmTrdGeoFactory.cxx b/core/detectors/trd/CbmTrdGeoFactory.cxx new file mode 100644 index 0000000000..1c79aaf660 --- /dev/null +++ b/core/detectors/trd/CbmTrdGeoFactory.cxx @@ -0,0 +1,700 @@ +/* Copyright (C) 2024 National Institute of Physics and Nuclear Engineering - Horia Hulubei, Bucharest + SPDX-License-Identifier: GPL-3.0-only + Authors: Alexandru Bercuci [committer] */ + +/** + * \file CbmTrdGeoFactory.cxx + * \brief TRD chamber manager class. + * \author Alexandru Bercuci <abercuci@niham.nipne.ro> + * \date 01/10/2023 +*/ +#include "CbmTrdGeoFactory.h" + +// #include "CbmTrdConstructionDB.h" +#include "CbmTrdAddress.h" + +#include <Logger.h> // for LOG, Logger + +#include <TGeoBBox.h> +#include <TGeoCompositeShape.h> +#include <TGeoManager.h> +#include <TGeoMatrix.h> +#include <TGeoMedium.h> +#include <TGeoVolume.h> +#include <TROOT.h> + +#include <cassert> +using namespace cbm::trd; +using namespace cbm::trd::geo; + +const char* ChamberBuilder::Component::fgName[(int) ChamberBuilder::eGeoPart::kNparts] = {"Radiator", "Window", + "Volume", "BackPanel", "FEB"}; +//________________________________________________________________________________________ +const TGeoMedium* cbm::trd::geo::GetMaterial(const char* mname) +{ + if (fMaterial.find(mname) == fMaterial.end()) { + LOG(error) << "GetMaterial(" << mname << ") failed."; + return nullptr; + } + return fMaterial.at(mname); +} + +//__________________________________________________________________ +bool cbm::trd::geo::ReadModuleInfo(const char* modTxt, info_t& info) +{ + // parse module info + string modName = modTxt; + auto posstart = modName.find("module") + 6; + uint ndigits = 0; + auto partoftype = modName.at(posstart); + while (std::isdigit(partoftype) && (ndigits + posstart) < modName.size()) { + partoftype = modName.at(posstart + ndigits); + ++ndigits; + } + info.type = stoi(modName.substr(posstart, ndigits)); // 6th element+ module type + + posstart += ndigits; + int modCopyNo = stoi(modName.substr(posstart, string::npos)); + if ((modCopyNo / 100000000) < 0) { + LOG(warning) << modTxt << " Module in ancient (< 2013) format. Ask expert."; + return false; + } + + // >= 2014 format has 9 digits + // In TGeoManager numbering starts with 1, so we have to subtract 1. + // int modCopy = ((modCopyNo / 1000000) % 100); // from module copy number + info.id = (modCopyNo % 1000) - 1; + info.superId = ((modCopyNo / 1000) % 100) - 1; + info.rotation = ((modCopyNo / 100000) % 10); // from module copy number + info.address = CbmTrdAddress::GetAddress(info.superId, info.id, 0, 0, 0); + return true; +} + +//__________________________________________________________________ +bool cbm::trd::geo::ReadFebInfo(const char* febTxt, info_t& info) +{ + // parse FEB info + string febName = febTxt; + auto posstart = febName.find("FEB") + 3; + try { + info.type = stoi(febName.substr(posstart, 2)); + info.superType = info.type / 10; + if (info.superType < 0 || info.superType > 2) throw eException::invalid_type; + info.type = info.type % 10; + + int febNo = stoi(febName.substr(posstart + 3, string::npos)); + info.id = febNo % 100; + if (info.superType == 0 && info.id >= faspFeb[info.type].nmax) throw eException::invalid_id; + info.superId = (febNo / 100) % 1000; + info.rotation = (febNo / 100000) % 10; + if (info.rotation > 1) throw eException::invalid_id; + } + catch (invalid_argument a) { // syntax exceptions + LOG(error) << "FEB name does not follow syntax rules : \"FEBxy_identifier\", \"x\" = superType, \"y\" = Type. " + "Couldn't initialize."; + return false; + } + catch (eException e) { // TRD exceptions + LOG(error) << "FEB name does not follow syntax rules"; + switch (e) { + case eException::invalid_type: LOG(info) << " : Family type not recognized"; break; + case eException::invalid_id: LOG(info) << " : Local id not recognized"; break; + default: LOG(info) << " : Un-identified violation."; break; + } + return false; + } + + return true; +} + +//__________________________________________________________________ +int cbm::trd::geo::WriteModuleInfo(info_t* /*info*/) +{ + /** Encryption of module info into the node number + * format : CCRLMMMM + * info.address : not stored, derived from CbmTrdAddress::GetAddress(ly, mod, 0, 0, 0) + * info.superId : layer_id [L] + * info.id : mod_id / layer [M] + * info.type : type according to family + * info.rotation : rotation in 90deg steps [R] + * copy number : [C] + */ + int modInfo = 0; //info.id + info.superId * 100; + return modInfo; +} + +//__________________________________________________________________ +int cbm::trd::geo::WriteFebInfo(info_t* info) +{ + /** Encryption of FEB info into the node number + * format : RPPPII + * info.address : + * info.superId : identification of FEB in production DB [P] + * info.id : identification of FEB placement on the module [I] + * info.type : Board version [T] + * info.rotation : rotation in 180deg steps [R] + * copy number : not used + */ + if (info == nullptr) { + LOG(error) << "WriteFebInfo : Info for FEB is missing"; + return -1; + } + if (info->id < 0 || info->superId < 0 || info->rotation < 0) { + LOG(warning) << "WriteFebInfo : Info for FEB is incomplete : id=" << info->id << " superId=" << info->superId + << " rotatation=" << info->rotation; + return -1; + } + int febInfo = info->id + 100 * info->superId + 100000 * info->rotation; + return febInfo; +} + +//________________________________________________________________________________________ +ChamberBuilder::ChamberBuilder(int typ) : FairTask(Form("module%d", typ)) +{ + fChmbTyp = typ; + + fMaterial["air"] = nullptr; + fMaterial["TRDpefoam20"] = nullptr; + fMaterial["TRDG10"] = nullptr; + fMaterial["TRDkapton"] = nullptr; + fMaterial["TRDgas"] = nullptr; + fMaterial["TRDcopper"] = nullptr; + fMaterial["TRDaramide"] = nullptr; + fMaterial["TRDcarbon"] = nullptr; + fMaterial["aluminium"] = nullptr; + fMaterial["polypropylene"] = nullptr; + fMaterial["silicon"] = nullptr; + + fComponent[(int) eGeoPart::kRadiator] = nullptr; + fComponent[(int) eGeoPart::kWindow] = new Window; + fComponent[(int) eGeoPart::kVolume] = new Volume; + fComponent[(int) eGeoPart::kBackPanel] = new BackPanel; + fComponent[(int) eGeoPart::kFEB] = nullptr; +} + +//________________________________________________________________________________________ +InitStatus ChamberBuilder::Init() +{ + if (HasRadiator()) fComponent[(int) eGeoPart::kRadiator] = new Radiator; + if (HasFEB()) fComponent[(int) eGeoPart::kFEB] = new FEB; + switch (fChmbTyp) { + case 1: + LOG(info) << "Init for TRD2D."; + SetFEE(fConfig, (bool) eAsic::kFasp); + SetPP(fConfig, (bool) ePadPlane::k2d); + activeAreaX = activeAreaY = 54; + sizeX = sizeY = 57; + break; + case (int) eModuleTypes1D::kLowChDensitySmallR: + case (int) eModuleTypes1D::kHighChDensityLargeR: + case (int) eModuleTypes1D::kLowChDensityLargeR: + SetFEE(fConfig, (bool) eAsic::kSpadic); + SetPP(fConfig, (bool) ePadPlane::k1d); + LOG(info) << "Init for TRD1D type " << fChmbTyp << "."; + break; + default: LOG(fatal) << "Unknown TRD chamber type " << fChmbTyp << ". Abort."; break; + } + + // Activate materials od the TRD geometry + TGeoManager* gGeoMan = (TGeoManager*) gROOT->FindObject("FAIRGeom"); + assert(gGeoMan); + for (auto& imat : fMaterial) { + imat.second = gGeoMan->GetMedium(imat.first.data()); + assert(imat.second); + } + + // Instantiate the construction data base of the TRD system + // if (!gDB) { + // assert((gDB = ConstructionDB::Instantiate())); + // } + + // Init sub-components + for (auto icomp : fComponent) { + if (!icomp) continue; + auto status = icomp->Init(); + if (status != kSUCCESS) return status; + } + return kSUCCESS; +} + +//________________________________________________________________________________________ +void ChamberBuilder::Exec(Option_t*) +{ + // estimate total module height and define center + int idx(0); + double vh[(int) eGeoPart::kNparts]; + bool kAdd(true); + double hOffset(0.), hTot(0.); + for (auto icomp : fComponent) { + if (!icomp) continue; + vh[idx] = icomp->GetHeight(); + hTot += vh[idx]; + if (kAdd) { + if (idx == (int) eGeoPart::kVolume) { + hOffset += vh[idx] / 2; + kAdd = false; + } + else + hOffset += vh[idx]; + } + idx++; + } + // add global z offset + hOffset = hTot / 2; + fVol = new TGeoVolume(Form("module%d", fChmbTyp), new TGeoBBox("", sizeX / 2, sizeY / 2, hTot / 2), + cbm::trd::geo::GetMaterial("air")); + fVol->SetLineColor(kGreen); + fVol->SetTransparency(80); + + // Stack-up all sub-components + idx = 0; + double hh(-hTot / 2); + info_t infoFeb; + for (auto icomp : fComponent) { + if (!icomp) continue; + hh += 0.5 * vh[idx]; + switch (idx) { + case (int) eGeoPart::kFEB: { // special case for feb multiple placement. + // FEB characteristics and identification stored in geometry + auto vFeb = new TGeoVolume(icomp->GetName(), new TGeoBBox("", sizeX / 2, sizeY / 2, vh[idx] / 2)); + for (int ifeb(0), jfeb(0); ifeb < Nfebs; ifeb++) { + infoFeb.id = ifeb; + infoFeb.superId = 0; // gDB.GetFebId(imod, ifeb); + infoFeb.rotation = 0; // gDB.GetFebRot(imod, ifeb); + if ((jfeb = WriteFebInfo(&infoFeb)) < 0) continue; + vFeb->AddNode(icomp->fVol, jfeb, new TGeoTranslation("", feb_pos[ifeb][0], feb_pos[ifeb][1], 0.)); + } + fVol->AddNode(vFeb, 1, new TGeoTranslation("", 0, 0, hh)); + } break; + default: fVol->AddNode(icomp->fVol, 1, new TGeoTranslation("", 0., 0., hh)); break; + } + + hh += 0.5 * vh[idx++]; + } +} +//________________________________________________________________________________________ +double ChamberBuilder::Component::GetCenter() const +{ + if (!fVol) return 0.; + double zlo, zhi; + fVol->GetShape()->GetAxisRange(3., zlo, zhi); + return 0.5 * (zlo + zhi); +} +// double ChamberBuilder::Component::GetHeight() const +// { +// if (!fVol) return 0.; +// double zlo, zhi, hh = fVol->GetShape()->GetAxisRange(2, zlo, zhi); +// return hh; +// } + +//________________________________________________________________________________________ +ChamberBuilder::Window::Window() : Component("Window") { ; } + +InitStatus ChamberBuilder::Window::Init() +{ + const double hc_size_x = activeAreaX; + const double hc_size_y = 2 * 0.3 + activeAreaY; + const double win_size_x = hc_size_x + 2 * WIN_FrameX_thickness; + const double win_size_y = hc_size_y + 2 * WIN_FrameY_thickness; + + // Carbon fiber layers + TGeoBBox* winIn_C = new TGeoBBox("winIn_C", win_size_x / 2., win_size_y / 2., winIn_C_thickness / 2.); + TGeoVolume* vol_winIn_C = + new TGeoVolume("winIn_C", winIn_C, cbm::trd::geo::GetMaterial("TRDcarbon") /*carbonVolMed*/); + vol_winIn_C->SetLineColor(kBlack); //kGray); + fHeight = winIn_C_thickness; + + // Honeycomb layer + TGeoBBox* winIn_HC = new TGeoBBox("winIn_HC", hc_size_x / 2., hc_size_y / 2., winIn_HC_thickness / 2.); + TGeoVolume* vol_winIn_HC = new TGeoVolume("winIn_HC", winIn_HC, cbm::trd::geo::GetMaterial("TRDaramide")); + vol_winIn_HC->SetLineColor(kOrange); + fHeight += winIn_HC_thickness; + + // framex + TGeoBBox* winIn_fx = new TGeoBBox("winIn_fx", (hc_size_x + 2 * WIN_FrameX_thickness) / 2, WIN_FrameY_thickness / 2, + winIn_HC_thickness / 2.); + TGeoVolume* vol_winIn_fx = new TGeoVolume("winIn_fx", winIn_fx, /*frameVolMed*/ cbm::trd::geo::GetMaterial("TRDG10")); + vol_winIn_fx->SetLineColor(kBlue); + TGeoBBox* winIn_xout = new TGeoBBox("winIn_xout", hc_size_x / 2 + 2 * WIN_FrameX_thickness, WIN_OutY_thickness / 2, + winIn_HC_thickness / 2.); + TGeoVolume* vol_winIn_xout = + new TGeoVolume("winIn_xout", winIn_xout, cbm::trd::geo::GetMaterial("TRDG10") /*frameVolMed*/); + vol_winIn_xout->SetLineColor(kBlue + 2); + + // framey + TGeoBBox* winIn_fy = new TGeoBBox("winIn_fy", WIN_FrameX_thickness / 2, hc_size_y / 2, winIn_HC_thickness / 2.); + TGeoVolume* vol_winIn_fy = new TGeoVolume("winIn_fy", winIn_fy, cbm::trd::geo::GetMaterial("TRDG10") /*frameVolMed*/); + vol_winIn_fy->SetLineColor(kCyan); + TGeoBBox* winIn_k = + new TGeoBBox("winIn_k", WIN_FrameX_thickness / 2, hc_size_y / 2 + WIN_FrameY_thickness, winIn_HC_thickness / 2.); + TGeoVolume* vol_winIn_k = new TGeoVolume("winIn_k", winIn_k, cbm::trd::geo::GetMaterial("TRDG10") /*frameVolMed*/); + vol_winIn_k->SetLineColor(kViolet); + TGeoBBox* winIn_yout = + new TGeoBBox("winIn_yout", WIN_OutX_thickness / 2, hc_size_y / 2 + WIN_FrameY_thickness + WIN_OutY_thickness, + winIn_HC_thickness / 2.); + TGeoVolume* vol_winIn_yout = + new TGeoVolume("winIn_yout", winIn_yout, cbm::trd::geo::GetMaterial("TRDG10") /*frameVolMed*/); + vol_winIn_yout->SetLineColor(kViolet + 5); + + // Add up all sub-components + fVol = + new TGeoVolume(GetName(), new TGeoBBox("", sizeX / 2, sizeY / 2, fHeight / 2), cbm::trd::geo::GetMaterial("air")); + fVol->SetLineColor(kOrange); + fVol->SetTransparency(50); + + double x, y; + fHeight = -fHeight / 2 + winIn_HC_thickness / 2; + fVol->AddNode(vol_winIn_HC, 1, new TGeoTranslation("", 0., 0., fHeight)); + y = (hc_size_y + WIN_FrameY_thickness) / 2.; + fVol->AddNode(vol_winIn_fx, 1, new TGeoTranslation("", 0., y, fHeight)); + fVol->AddNode(vol_winIn_fx, 2, new TGeoTranslation("", 0., -y, fHeight)); + y += 0.5 * (WIN_FrameY_thickness + WIN_OutY_thickness); + fVol->AddNode(vol_winIn_xout, 1, new TGeoTranslation("", 0., y, fHeight)); + fVol->AddNode(vol_winIn_xout, 2, new TGeoTranslation("", 0., -y, fHeight)); + x = (hc_size_x + WIN_FrameX_thickness) / 2.; + fVol->AddNode(vol_winIn_fy, 1, new TGeoTranslation("", x, 0., fHeight)); + fVol->AddNode(vol_winIn_fy, 2, new TGeoTranslation("", -x, 0., fHeight)); + x += WIN_FrameX_thickness; + fVol->AddNode(vol_winIn_k, 1, new TGeoTranslation("", x, 0., fHeight)); + fVol->AddNode(vol_winIn_k, 2, new TGeoTranslation("", -x, 0., fHeight)); + x += 0.5 * (WIN_FrameX_thickness + WIN_OutX_thickness); + fVol->AddNode(vol_winIn_yout, 1, new TGeoTranslation("", x, 0., fHeight)); + fVol->AddNode(vol_winIn_yout, 2, new TGeoTranslation("", -x, 0., fHeight)); + + fHeight += 0.5 * (winIn_HC_thickness + winIn_C_thickness); + fVol->AddNode(vol_winIn_C, 1, new TGeoTranslation("", 0., 0., fHeight)); + fHeight += 0.5 * winIn_C_thickness; + fHeight *= 2; + return kSUCCESS; +} + +//________________________________________________________________________________________ +ChamberBuilder::Volume::Volume() : Component("Volume") { ; } + +InitStatus ChamberBuilder::Volume::Init() +{ + // Gas. The volume has to be defined only for pads (read-out) area. Take care in the DigiPara definition + TGeoBBox* gas = new TGeoBBox("trd_gas", 0.5 * activeAreaX, 0.5 * activeAreaY, 0.5 * gas_thickness); + TGeoVolume* vol_gas = new TGeoVolume("gas", gas, cbm::trd::geo::GetMaterial("TRDgas") /*gasVolMed*/); + vol_gas->SetLineColor(kRed); + //vol_gas->SetTransparency(80); + TGeoBBox* gas_ext = new TGeoBBox("trd_gas_dstr", 0.5 * activeAreaX, 0.5 * gas_extra, 0.5 * gas_thickness); + TGeoVolume* vol_gas_ext = new TGeoVolume("gas_ext", gas_ext, cbm::trd::geo::GetMaterial("TRDgas") /*gasVolMed*/); + vol_gas_ext->SetLineColor(kMagenta); + //vol_gas_ext->SetTransparency(80); + fHeight = gas_thickness; + + const double gas_size_x = activeAreaX; + const double gas_size_y = activeAreaY + 2 * gas_extra; + + // framex + auto* gas_xin = new TGeoBBox("gas_xin", gas_size_x / 2 + cathode_width, WIN_OutY_thickness / 2, gas_thickness / 2.); + auto* vol_gas_xin = new TGeoVolume("gas_xin", gas_xin, cbm::trd::geo::GetMaterial("TRDG10") /*frameVolMed*/); + vol_gas_xin->SetLineColor(kViolet + 5); + auto* gas_xout = new TGeoBBox("gas_xout", gas_size_x / 2 + cathode_width, WIN_OutY_thickness / 2, + (gas_thickness /*+ ridge_height*/) / 2.); + TGeoVolume* vol_gas_xout = new TGeoVolume("gas_xout", gas_xout, cbm::trd::geo::GetMaterial("TRDG10") /*frameVolMed*/); + vol_gas_xout->SetLineColor(kViolet + 5); + // framey + TGeoBBox* gas_k = new TGeoBBox("gas_k", cathode_width / 2, gas_size_y / 2, ledge_thickness / 2.); + TGeoVolume* vol_gas_k = new TGeoVolume("gas_k", gas_k, cbm::trd::geo::GetMaterial("TRDG10") /*frameVolMed*/); + vol_gas_k->SetLineColor(kViolet); + TGeoBBox* gas_a = new TGeoBBox("gas_a", anode_width / 2, gas_size_y / 2, ledge_thickness / 2.); + TGeoVolume* vol_gas_a = new TGeoVolume("gas_a", gas_a, cbm::trd::geo::GetMaterial("TRDG10") /*frameVolMed*/); + vol_gas_a->SetLineColor(kViolet + 2); + TGeoBBox* gas_d = new TGeoBBox("gas_d", dist_width / 2, gas_size_y / 2, ledge_thickness / 2.); + TGeoVolume* vol_gas_d = new TGeoVolume("gas_d", gas_d, cbm::trd::geo::GetMaterial("TRDG10") /*frameVolMed*/); + vol_gas_d->SetLineColor(kViolet + 4); + TGeoBBox* gas_yout = new TGeoBBox("gas_yout", WIN_OutX_thickness / 2, 2 * WIN_OutY_thickness + gas_size_y / 2, + (gas_thickness /*+ ridge_height*/) / 2.); + TGeoVolume* vol_gas_yout = new TGeoVolume("gas_yout", gas_yout, cbm::trd::geo::GetMaterial("TRDG10") /*frameVolMed*/); + vol_gas_yout->SetLineColor(kViolet + 5); + + // Add up all sub-components + fVol = + new TGeoVolume(GetName(), new TGeoBBox("", sizeX / 2, sizeY / 2, fHeight / 2), cbm::trd::geo::GetMaterial("air")); + fVol->SetLineColor(kYellow); + fVol->SetTransparency(50); + + double x, y; + fHeight = 0.; + fVol->AddNode(vol_gas, 0, new TGeoTranslation("", 0., 0., fHeight)); + x = 0.5 * (gas_size_x + cathode_width); + fVol->AddNode(vol_gas_k, 1, new TGeoTranslation("", x, 0., fHeight - ledge_thickness)); + fVol->AddNode(vol_gas_k, 2, new TGeoTranslation("", -x, 0., fHeight - ledge_thickness)); + x = 0.5 * (gas_size_x + anode_width); + fVol->AddNode(vol_gas_a, 1, new TGeoTranslation("", x, 0., fHeight)); + fVol->AddNode(vol_gas_a, 2, new TGeoTranslation("", -x, 0., fHeight)); + x = 0.5 * (gas_size_x + dist_width); + fVol->AddNode(vol_gas_d, 1, new TGeoTranslation("", x, 0., fHeight + ledge_thickness)); + fVol->AddNode(vol_gas_d, 2, new TGeoTranslation("", -x, 0., fHeight + ledge_thickness)); + x = 0.5 * gas_size_x + cathode_width + 0.5 * WIN_OutX_thickness; + fVol->AddNode(vol_gas_yout, 1, new TGeoTranslation("", x, 0., fHeight /* + ridge_height / 2*/)); + fVol->AddNode(vol_gas_yout, 2, new TGeoTranslation("", -x, 0., fHeight /* + ridge_height / 2*/)); + y = 0.5 * (activeAreaY + gas_extra); + fVol->AddNode(vol_gas_ext, 0, new TGeoTranslation("", 0., y, fHeight)); + fVol->AddNode(vol_gas_ext, 1, new TGeoTranslation("", 0., -y, fHeight)); + y += 0.5 * (gas_extra + WIN_OutY_thickness); + fVol->AddNode(vol_gas_xin, 1, new TGeoTranslation("", 0, y, fHeight)); + fVol->AddNode(vol_gas_xin, 2, new TGeoTranslation("", 0, -y, fHeight)); + y += WIN_OutY_thickness; + fVol->AddNode(vol_gas_xout, 1, new TGeoTranslation("", 0, y, fHeight /* + ridge_height / 2*/)); + fVol->AddNode(vol_gas_xout, 2, new TGeoTranslation("", 0, -y, fHeight /* + ridge_height / 2*/)); + fHeight += gas_thickness; + return kSUCCESS; +} + +//________________________________________________________________________________________ +ChamberBuilder::BackPanel::BackPanel() : Component("BackPanel") { ; } +InitStatus ChamberBuilder::BackPanel::Init() +{ + const double hc_size_x = activeAreaX; + const double hc_size_y = activeAreaY - 2 * BKP_OutY_correct; + const double bkp_size_x = hc_size_x + 2 * BKP_Frame_width; + const double bkp_size_y = hc_size_y + 2 * BKP_Frame_width; + + // Pad Copper + TGeoBBox* pp = new TGeoBBox("pp_cu", activeAreaX / 2., activeAreaY / 2., pp_pads_thickness / 2.); + TGeoVolume* vol_pp = new TGeoVolume("pp_cu", pp, /*padcopperVolMed*/ cbm::trd::geo::GetMaterial("TRDcopper")); + vol_pp->SetLineColor(kBlue); + fHeight = pp_pads_thickness; + + // Pad Plane + TGeoBBox* pp_PCB = new TGeoBBox("pp_pcb", bkp_size_x / 2., bkp_size_y / 2., pp_pcb_thickness / 2.); + TGeoVolume* vol_pp_PCB = new TGeoVolume("pp_pcb2d", pp_PCB, /*padpcbVolMed*/ cbm::trd::geo::GetMaterial("TRDG10")); + vol_pp_PCB->SetLineColor(kGreen); + fHeight += pp_pcb_thickness; + + // Perforated BackPanel structure + auto vol_bp = new TGeoVolumeAssembly("bkp_int"); + auto vol_bp_ASIC = new TGeoVolumeAssembly(""); + // HC : 3 components (HC, PCB, Cu) with 2 sizes + Color_t bp_col[3] = {kOrange, kGray, kRed + 2}; + const char* matName[3] = {"TRDaramide", "TRDG10", "TRDcopper"}; + // build one BP unit + double dxHC[2] = {hc_unitx / 2., (hc_unitx - hc_holex) / 4.}, dyHC[2] = {(hc_unity - hc_holey) / 4., hc_holey / 2.}, + hHCx((hc_unitx + hc_holex) / 4.), hHCy((hc_unity + hc_holey) / 4.), hHC(0.), + hHCz[] = {hc_thickness, cu_pcb_thickness, cu_thickness}; + for (int ibpz(0); ibpz < 3; ibpz++) { + hHC += 0.5 * hHCz[ibpz]; + for (int ibpy(-1), jbp(0); ibpy < 2; ibpy += 2) { + auto bpShp = new TGeoBBox("", dxHC[0], dyHC[0], hHCz[ibpz] / 2.); + auto vol_cmp = new TGeoVolume("", bpShp, cbm::trd::geo::GetMaterial(matName[ibpz])); + vol_cmp->SetLineColor(bp_col[ibpz]); + vol_bp_ASIC->AddNode(vol_cmp, jbp++, new TGeoTranslation("", 0., ibpy * hHCy, hHC)); + } + for (int ibpx(-1), jbp(0); ibpx < 2; ibpx += 2) { + auto bpShp = new TGeoBBox("", dxHC[1], dyHC[1], hHCz[ibpz] / 2.); + auto vol_cmp = new TGeoVolume("", bpShp, cbm::trd::geo::GetMaterial(matName[ibpz])); + vol_cmp->SetLineColor(bp_col[ibpz]); + vol_bp_ASIC->AddNode(vol_cmp, jbp++, new TGeoTranslation("", ibpx * hHCx, 0., hHC)); + } + hHC += 0.5 * hHCz[ibpz]; + } + // build BP plate + for (Int_t c(0), ifc(0); c < 9; c++) { + for (Int_t r(0); r < 10; r++) { + vol_bp->AddNode(vol_bp_ASIC, ifc++, new TGeoTranslation("", (c - 4) * hc_unitx, hc_unity * (0.5 + r), 0.)); + vol_bp->AddNode(vol_bp_ASIC, ifc++, new TGeoTranslation("", (c - 4) * hc_unitx, -hc_unity * (r + 0.5), 0.)); + } + } + fHeight += hHC; + + // framex + auto xoutBd = + new TGeoBBox("", hc_size_x / 2, (BKP_OutY_thickness - BKP_OutY_correct) / 2, (hHC - BKP_Frame_closure) / 2.); + auto xvolBd = new TGeoVolume("", xoutBd, cbm::trd::geo::GetMaterial("TRDG10")); + auto xoutFc = new TGeoBBox("", hc_size_x / 2, (BKP_Frame_width - BKP_OutY_correct) / 2, BKP_Frame_closure / 2.); + auto xvolFc = new TGeoVolume("", xoutFc, cbm::trd::geo::GetMaterial("TRDG10")); + auto vol_bkp_xout = new TGeoVolumeAssembly("bkp_xout"); + vol_bkp_xout->AddNode( + xvolFc, 1, new TGeoTranslation("", 0, (BKP_Frame_width - BKP_OutY_thickness) / 2, (BKP_Frame_closure - hHC) / 2)); + vol_bkp_xout->AddNode(xvolBd, 1, new TGeoTranslation("", 0., 0., BKP_Frame_closure / 2)); + vol_bkp_xout->SetLineColor(kViolet + 2); + + // framey + auto youtBd = + new TGeoBBox("", BKP_OutX_thickness / 2, hc_size_y / 2 + BKP_OutY_thickness, (hHC - BKP_Frame_closure) / 2.); + auto yvolBd = new TGeoVolume("", youtBd, cbm::trd::geo::GetMaterial("TRDG10")); + auto youtFc = new TGeoBBox("", BKP_Frame_width / 2, hc_size_y / 2 + BKP_Frame_width, BKP_Frame_closure / 2.); + auto yvolFc = new TGeoVolume("", youtFc, cbm::trd::geo::GetMaterial("TRDG10")); + auto vol_bkp_yout = new TGeoVolumeAssembly("bkp_yout"); + vol_bkp_yout->AddNode(yvolFc, 1, + new TGeoTranslation("t_bkp_yout_fc", (BKP_Frame_width - BKP_OutX_thickness) / 2, 0., + (BKP_Frame_closure - hHC) / 2)); + vol_bkp_yout->AddNode(yvolBd, 1, new TGeoTranslation("t_bkp_yout_bd", 0., 0., BKP_Frame_closure / 2)); + vol_bkp_yout->SetLineColor(kViolet + 2); + + // Add up all components + fVol = new TGeoVolume(GetName(), new TGeoBBox("", bkp_size_x / 2, bkp_size_y / 2, fHeight / 2), + cbm::trd::geo::GetMaterial("air")); + fVol->SetLineColor(kOrange); + fVol->SetTransparency(50); + + double x, y; + fHeight = -fHeight / 2 + 0.5 * pp_pads_thickness; + fVol->AddNode(vol_pp, 1, new TGeoTranslation("", 0., 0., fHeight)); + fHeight += 0.5 * (pp_pads_thickness + pp_pcb_thickness); + fVol->AddNode(vol_pp_PCB, 1, new TGeoTranslation("", 0., 0., fHeight)); + fHeight += 0.5 * pp_pcb_thickness; + fVol->AddNode(vol_bp, 1, new TGeoTranslation("", 0., 0., fHeight)); + fHeight += 0.5 * hHC; + + x = 0.5 * (hc_size_x + BKP_OutX_thickness); + auto* fy_tra = new TGeoTranslation("", x, 0., fHeight); + fVol->AddNode(vol_bkp_yout, 1, fy_tra); + auto* fy_rot = new TGeoRotation(); + fy_rot->RotateZ(180.); + auto* fy_mat = new TGeoHMatrix(""); + (*fy_mat) = (*fy_rot) * (*fy_tra); + fVol->AddNode(vol_bkp_yout, 2, fy_mat); + y = 0.5 * (hc_size_y + BKP_OutY_thickness + BKP_OutY_correct); + fy_tra = new TGeoTranslation("", 0., y, fHeight); + fVol->AddNode(vol_bkp_xout, 1, fy_tra); + fy_mat = new TGeoHMatrix(""); + (*fy_mat) = (*fy_rot) * (*fy_tra); + fVol->AddNode(vol_bkp_xout, 2, fy_mat); + fHeight += 0.5 * hHC; + fHeight *= 2; + return kSUCCESS; +} + +//________________________________________________________________________________________ +ChamberBuilder::FEB::FEB() : Component("FEB") { ; } + +InitStatus ChamberBuilder::FEB::Init() +{ + // Create the FASPRO FEBs out of all CU/PCB layers + fHeight = FASPRO_zspace; + + TString scu = "", spcb = ""; + TGeoTranslation* tr(nullptr); + double FASPRO_thickness(0.); + for (int ily(0); ily < FASPRO_Nly; ily++) { + // effective Cu layer thickness = h [um * 10-4] * coverage [% * 10-2] + double lyThickEff = FASPRO_ly_cu[ily][0] * FASPRO_ly_cu[ily][1] * 1.e-6; + new TGeoBBox(Form("faspro_ly%02d", ily), FASPRO_length / 2., FASPRO_width / 2., lyThickEff / 2.); + FASPRO_thickness += lyThickEff / 2; + tr = new TGeoTranslation(Form("t_faspro_ly%02d", ily), 0., 0., FASPRO_thickness); + tr->RegisterYourself(); + scu += Form("%cfaspro_ly%02d:t_faspro_ly%02d", (ily ? '+' : ' '), ily, ily); + FASPRO_thickness += lyThickEff / 2; + if (ily == FASPRO_Nly - 1) break; // skip for FR4 + // the FEB dielectric made of PCB + double lyThickPcb = 1.e-4 * FASPRO_ly_pcb[ily] / 2.; + new TGeoBBox(Form("faspro_ly%02d_pcb", ily), FASPRO_length / 2., FASPRO_width / 2., lyThickPcb); + FASPRO_thickness += lyThickPcb; + tr = new TGeoTranslation(Form("t_faspro_ly%02d_pcb", ily), 0., 0., FASPRO_thickness); + tr->RegisterYourself(); + spcb += Form("%cfaspro_ly%02d_pcb:t_faspro_ly%02d_pcb", (ily ? '+' : ' '), ily, ily); + FASPRO_thickness += lyThickPcb; + } + for (int ihole(0); ihole < FASPRO_Nfasp; ihole++) { + new TGeoBBox(Form("faspro_hole%d", ihole), FASPRO_hole_x / 2., FASPRO_hole_y / 2., 1.e-4 + FASPRO_thickness / 2.); + tr = + new TGeoTranslation(Form("t_faspro_hole%d", ihole), HOLE_pos[ihole][0], HOLE_pos[ihole][1], FASPRO_thickness / 2); + tr->RegisterYourself(); + scu += Form("-faspro_hole%d:t_faspro_hole%d", ihole, ihole); + spcb += Form("-faspro_hole%d:t_faspro_hole%d", ihole, ihole); + } + auto faspro_cu = new TGeoCompositeShape("faspro_cu", scu.Data()); + auto vol_faspro_cu = new TGeoVolume("faspro_cu", faspro_cu, cbm::trd::geo::GetMaterial("TRDcopper")); + vol_faspro_cu->SetLineColor(kRed - 3); //vol_faspro_cu->SetTransparency(50); + auto faspro_pcb = new TGeoCompositeShape("faspro_pcb", spcb.Data()); + auto vol_faspro_pcb = new TGeoVolume("faspro_pcb", faspro_pcb, + cbm::trd::geo::GetMaterial("TRDG10") /*febVolMed*/); // the FEB made of PCB + vol_faspro_pcb->SetLineColor(kGreen + 3); + //vol_faspro_pcb->SetTransparency(50); + fHeight += FASPRO_thickness; + + // create FASP ASIC + auto fasp = new TGeoBBox("fasp", FASP_x / 2., FASP_y / 2., FASP_z / 2.); + auto vol_fasp = new TGeoVolume("fasp", fasp, cbm::trd::geo::GetMaterial("silicon")); + vol_fasp->SetLineColor(kBlack); + // create ADC ASIC + auto adc = new TGeoBBox("adc", ADC_x / 2., ADC_y / 2., ADC_z / 2.); + auto vol_adc = new TGeoVolume("adc", adc, cbm::trd::geo::GetMaterial("silicon")); + vol_adc->SetLineColor(kBlack); + // create FPGA ASIC + auto fpga = new TGeoBBox("fpga", FPGA_x / 2., FPGA_y / 2., FPGA_z / 2.); + auto vol_fpga = new TGeoVolume("fpga", fpga, cbm::trd::geo::GetMaterial("silicon")); + vol_fpga->SetLineColor(kBlack); + // create DCDC ASIC + auto dcdc = new TGeoBBox("dcdc", DCDC_x / 2., DCDC_y / 2., DCDC_z / 2.); + auto vol_dcdc = new TGeoVolume("dcdc", dcdc, cbm::trd::geo::GetMaterial("silicon")); + vol_dcdc->SetLineColor(kBlack); + // create FC Coonector + auto connFc = new TGeoBBox("connFc", ConnFC_x / 2., ConnFC_y / 2., ConnFC_z / 2.); + auto vol_conn_fc = new TGeoVolume("connFc", connFc, cbm::trd::geo::GetMaterial("polypropylene")); + vol_conn_fc->SetLineColor(kYellow); + // create BRIDGE Coonector + auto connBrg = new TGeoBBox("connBrg", ConnBRG_x / 2., ConnBRG_y / 2., ConnBRG_z / 2.); + auto vol_conn_brg = new TGeoVolume("connBrg", connBrg, cbm::trd::geo::GetMaterial("polypropylene")); + vol_conn_brg->SetLineColor(kYellow + 2); + fHeight += ConnBRG_z; + + // Init volume: + // FEB family FASPRO + // FEB type v1 (12 FASPs) + int fType = 1; + fVol = new TGeoVolumeAssembly(Form("%s1%d", GetName(), fType)); + fVol->SetLineColor(kGreen); + fVol->SetTransparency(50); + + // Add up all components + fHeight = -0.5 * fHeight + FASPRO_zspace; + fVol->AddNode(vol_faspro_cu, 1, new TGeoTranslation("", 0., 0., fHeight)); + fVol->AddNode(vol_faspro_pcb, 1, new TGeoTranslation("", 0., 0., fHeight)); + // add FASPs on the back side of the FEB + info_t infoAsic; + for (int ifasp(0), jfasp(0); ifasp < faspFeb[fType].nasic; ifasp++) { + vol_fasp->SetTitle(Form("%x", 0xff /*gDB->GetASICMask*/)); + // if ((jfasp = WriteAsicInfo(&infoAsic)) < 0) continue; + fVol->AddNode(vol_fasp, jfasp, + new TGeoTranslation("", FASP_pos[ifasp][0], FASP_pos[ifasp][1], fHeight - FASP_z / 2)); + } + fHeight += FASPRO_thickness; + // add ADCs, FPGAs and DCDC converters on the tob side of the FEB + for (int iadc(0); iadc < FASPRO_Nadc; iadc++) + fVol->AddNode(vol_adc, iadc + 1, new TGeoTranslation("", ADC_pos[iadc][0], ADC_pos[iadc][1], fHeight + ADC_z / 2)); + for (int ifpga(0); ifpga < FASPRO_Nfpga; ifpga++) + fVol->AddNode(vol_fpga, ifpga + 1, + new TGeoTranslation("", FPGA_pos[ifpga][0], FPGA_pos[ifpga][1], fHeight + FPGA_z / 2)); + for (int idcdc(0); idcdc < FASPRO_Ndcdc; idcdc++) + fVol->AddNode(vol_dcdc, idcdc + 1, + new TGeoTranslation("", DCDC_pos[idcdc][0], DCDC_pos[idcdc][1], fHeight + DCDC_z / 2)); + // add connectors to the FEB + for (int ifasp(0); ifasp < FASPRO_Nfasp; ifasp++) + fVol->AddNode(vol_conn_fc, ifasp + 1, + new TGeoTranslation("", ConnFC_pos[ifasp][0], ConnFC_pos[ifasp][1], fHeight + ConnFC_z / 2)); + for (int iconn(0); iconn < 2; iconn++) + fVol->AddNode(vol_conn_brg, iconn + 1, + new TGeoTranslation("", ConnBRG_pos[iconn][0], ConnBRG_pos[iconn][1], fHeight + ConnBRG_z / 2)); + fHeight += ConnBRG_z; + fHeight *= 2; + return kSUCCESS; +} + +//________________________________________________________________________________________ +ChamberBuilder::Radiator::Radiator() : Component("Radiator") { ; } + +InitStatus ChamberBuilder::Radiator::Init() +{ + TGeoBBox* trd_radiator = new TGeoBBox("trd_radiator", sizeX / 2., sizeY / 2., radiator_thickness / 2.); + fVol = new TGeoVolume("Radiator", trd_radiator, cbm::trd::geo::GetMaterial("TRDpefoam20") /*radVolMed*/); + fVol->SetLineColor(kRed); + //trdmod1_radvol->SetTransparency(50); // set transparency for the TRD radiator + + fHeight = radiator_thickness; + return kSUCCESS; +} +/* clang-format off */ +// NamespaceImp(cbm::trd::geo) +ClassImp(cbm::trd::geo::ChamberBuilder) +ClassImp(cbm::trd::geo::ChamberBuilder::Component) +ClassImp(cbm::trd::geo::ChamberBuilder::Radiator) +ClassImp(cbm::trd::geo::ChamberBuilder::Window) +ClassImp(cbm::trd::geo::ChamberBuilder::Volume) +ClassImp(cbm::trd::geo::ChamberBuilder::BackPanel) +ClassImp(cbm::trd::geo::ChamberBuilder::FEB) + /* clang-format on */ diff --git a/core/detectors/trd/CbmTrdGeoFactory.h b/core/detectors/trd/CbmTrdGeoFactory.h new file mode 100644 index 0000000000..8daa2d0474 --- /dev/null +++ b/core/detectors/trd/CbmTrdGeoFactory.h @@ -0,0 +1,352 @@ +/* Copyright (C) 2024 National Institute of Physics and Nuclear Engineering - Horia Hulubei, Bucharest + SPDX-License-Identifier: GPL-3.0-only + Authors: Alexandru Bercuci [committer] */ + +/** + * \file CbmTrdGeoFactory.h + * \brief Builder class for the TRD chamber geometry. + * \author Alexandru Bercuci <abercuci@niham.nipne.ro> + * \date 01/10/2023 + * + * This class manages the relative spatial positioning of the following components of the TRD chamber : + * - Chamber : initialize and place all components of the TRD chamber + * - Radiator : + * - Window : + * - Volume : + * - BackPanel + * - FEE + */ + +#ifndef CBMTRDGEOFACTORY_H_ +#define CBMTRDGEOFACTORY_H_ + +#include "CbmTrdDefs.h" // for trd namespace + +#include <FairTask.h> + +#include <TString.h> + +#include <array> +#include <string> + +class TGeoMedium; +class TGeoTranslation; +class TGeoVolume; +class TGeoVolumeAssembly; +using namespace std; +using namespace cbm::trd; +namespace cbm::trd::geo +{ + enum class eException + { + invalid_type = 0, //! + invalid_id //! + }; + + /** \struct info_t + * \brief Information to be storred in the geoManager path. + * Based on legacy class CbmTrdGeoHandler. + */ + struct info_t { + int address = -1; //! global addressing of element + int id = -1; //! local (wrt installation) id of element + int superId = -1; //! global (wrt setup) id of element + int type = -1; //! version of element wrt superType family + int superType = -1; //! global type wrt TRD design + int rotation = -1; //! rotation angle 0,1,2,3 + }; + /** \function ReadModuleInfo + * \brief Identify information related to the module encrypted in the geoManager path. + * Based on legacy function "void CbmTrdGeoHandler::NavigateTo(const TString& path)" + * \param[in] modTxt : module name in gGeoManager + * \param[in] info : object to be filled with decrypted info + */ + bool ReadModuleInfo(const char* modTxt, info_t& info); + + /** \function ReadFebInfo + * \brief Identify information related to one FEB encrypted in the geoManager path. + * \param[in] febTxt : FEB name in gGeoManager + * \param[in] info : object to be filled with decrypted info + */ + bool ReadFebInfo(const char* febTxt, info_t& info); + + /** \function WriteModuleInfo + * \brief Put up information related to the module and store it in the geoManager path. + * Based on legacy version "void Create_TRD_Geometry.C macro" + */ + int WriteModuleInfo(info_t* info); + + /** \function WriteFebInfo + * \brief Put up information related to the FEB and store it in the geoManager path. + */ + int WriteFebInfo(info_t* info); + + + // chamber generic geometrical definitions + static double sizeX, sizeY; //! total area + static double activeAreaX, activeAreaY; //! active area + static map<const string, const TGeoMedium*> fMaterial = {}; //! material mapping (name, value) + const TGeoMedium* GetMaterial(const char* mname); + + + /** \brief Generic Chamber builder + **/ + class ChamberBuilder : public FairTask { + public: + enum class eGeoPart : int + { + kRadiator = 0, + kWindow, + kVolume, + kBackPanel, + kFEB, + kNparts + }; + enum eConfig + { + kRadiator = 8 //! include radiator in chamber geometry + , + kFEB //! include FEB in chamber geometry + }; + class Component; + class Radiator; + class Window; + class Volume; + class BackPanel; + class FEB; + /** \brief Constructor for the chamber. Adds all elements according to config. + * \param[in] typ TRD chamber type [1, 3, 5, 7]. + **/ + ChamberBuilder(int typ = 1); + + TGeoVolume* GetModule() const { return fVol; } + bool HasFEB() const { return TESTBIT(fConfig, eConfig::kFEB); } + bool HasRadiator() const { return TESTBIT(fConfig, eConfig::kRadiator); } + /** \brief Init task **/ + virtual InitStatus Init(); + /** \brief Executed task **/ + virtual void Exec(Option_t*); + /** \brief Finish task **/ + virtual void Finish() { ; } + + void SetFEB(bool feb = true) { feb ? SETBIT(fConfig, eConfig::kFEB) : CLRBIT(fConfig, eConfig::kFEB); } + void SetRadiator(bool rad = true) + { + rad ? SETBIT(fConfig, eConfig::kRadiator) : CLRBIT(fConfig, eConfig::kRadiator); + } + + private: + ChamberBuilder(const ChamberBuilder&); + ChamberBuilder operator=(const ChamberBuilder&); + + short fConfig = 0; //! bit map of the setter flags + short fChmbTyp = 1; //! chamber type [1, 3, 5, 7] + array<Component*, (int) eGeoPart::kNparts> fComponent = {}; //! list of chamber component builders + static const int Nfebs = faspFeb[1].nmax; + const double feb_pos[Nfebs][2] = {{-18, -21.6}, {0, -21.6}, {18, -21.6}, {-18, -10.8}, {0, -10.8}, + {18, -10.8}, {-18, 0.0}, {0, 0.0}, {18, 0.0}, {-18, +10.8}, + {0, +10.8}, {18, +10.8}, {-18, +21.6}, {0, +21.6}, {18, +21.6}}; + TGeoVolume* fVol = nullptr; //! the geo volume itself + ClassDef(ChamberBuilder, 1) // Manager of the TRD support structure + }; + + + /** \brief Generic sub-component + **/ + class ChamberBuilder::Component : public FairTask { + public: + friend class ChamberBuilder; + /** \brief Constructor of the TRD chamber component. + * It links the chamber class + **/ + Component(const char* name) : FairTask(name) + { + sizeX = 0; + sizeY = 0; + activeAreaX = 0; + activeAreaY = 0; + } + /** Construct the chamber component volume in the reference of the chamber + * The function has to be implemented for each particular component of the TRD chamber + * \return The dimension of the component on the z axis + */ + /** \brief Init task **/ + virtual InitStatus Init() = 0; + /** \brief Executed task **/ + virtual void Exec(Option_t*) { ; } + /** \brief Finish task **/ + virtual void Finish() { ; } + virtual double GetCenter() const; + virtual double GetHeight() const { return fHeight; } + + static const char* fgName[(int) eGeoPart::kNparts]; + + protected: + TGeoVolume* fVol = nullptr; //! the geo volume itself + + private: + Component(const Component&); + //Component operator=(const Component&); + + double fHeight = 0; //! height of the component (local calculation) + ClassDef(ChamberBuilder::Component, 1) // Model for the TRD generic chamber component builder + }; + + class ChamberBuilder::Radiator : public ChamberBuilder::Component { + public: + Radiator(); + /** \brief Init task **/ + virtual InitStatus Init(); + + private: + Radiator(const Radiator&); + const double radiator_thickness = 30.0; + ClassDef(ChamberBuilder::Radiator, 1) // Model for the TRD radiator + }; + + class ChamberBuilder::Window : public ChamberBuilder::Component { + public: + /** \brief Constructor of entrance window for the TRD chamber + **/ + Window(); + /** \brief Init task **/ + virtual InitStatus Init(); + + private: + Window(const Window&); + + const double winIn_C_thickness = 0.012; //! 100um C foil + 25um KaptonAl + const double winIn_HC_thickness = 0.9; //! 9mm HC structure + const double WIN_FrameX_thickness = 0.5; //! entrance window framing 5x9 mm2 + const double WIN_FrameY_thickness = 0.6; //! entrance window framing 5x9 mm2 + const double WIN_OutX_thickness = 0.35; //! outside framing at win 3.5x9 mm2 + const double WIN_OutY_thickness = 0.30; //! outside framing at win 3.5x9 mm2 + ClassDef(ChamberBuilder::Window, 1) // Model for the TRD2D entrance window + }; + + /** \brief Inner class describing a : + **/ + class ChamberBuilder::Volume : public ChamberBuilder::Component { + public: + /** \brief Constructor. + **/ + Volume(); + /** \brief Init task **/ + virtual InitStatus Init(); + + private: + Volume(const Volume&); + + const double gas_extra = 0.6; //! extra volume of gas parallel to wires + const double gas_thickness = 1.2; //! active volume thickness + const double ridge_height = 0.29; //! closure to pad-plane dimension + const double ledge_thickness = gas_thickness / 3.; //! ledge thickness supporting A/K wires + const double cathode_width = 1.0; //! cathode + const double anode_width = 0.65; //! anode + const double dist_width = 0.40; //! distance from anode to PP + const double WIN_OutX_thickness = 0.35; //! outside framing + const double WIN_OutY_thickness = 0.30; //! half of outside framing + ClassDef(ChamberBuilder::Volume, 1) // Model for the TRD active volume + }; + + /** \brief Inner class describing the back panel of composed of + * - pad plane + * - honey-comb structure for reinforcement + * electric shielding + **/ + class ChamberBuilder::BackPanel : public ChamberBuilder::Component { + public: + /** \brief Constructor. + **/ + BackPanel(); + /** \brief Init task **/ + virtual InitStatus Init(); + + private: + BackPanel(const BackPanel&); + TGeoTranslation* tr = nullptr; + TString sexpr = ""; + + const double pp_pads_thickness = 0.0020; //! cu coverage of PP PCB + const double pp_pcb_thickness = 0.0230; //! PP support PCB thickness + const double hc_thickness = 2.30; //! Honneycomb backpanel support thickness + const double hc_unitx = 6.0; //! area opearted by one FASP 6 x 2.7 cm2 + const double hc_unity = 2.7; //! area opearted by one FASP 6 x 2.7 cm2 + const double hc_holex = 2.4; //! dimension of flat-cable hole x-dimension (along wires) + const double hc_holey = 0.8; //! dimension of flat-cable hole x-dimension (along wires) + const double cu_pcb_thickness = 0.0150; //! Electric shield PCB support thickness + const double cu_thickness = 0.0020; //! Electric shield Cu covarage thickness + const double BKP_Frame_width = 1.; //! Global width of the perimetral frame (including indentation) + const double BKP_Frame_closure = 0.25; //! Perimetral frame indentation + const double BKP_OutX_thickness = 0.50; //! outside framing + const double BKP_OutY_thickness = 0.45; //! outside framing + const double BKP_OutY_correct = 0.15; //! framing overlap between th BP and frame + + ClassDef(ChamberBuilder::BackPanel, 1) // Model for the TRD back panel + }; + /** \brief Inner class describing the geometry of the TRD Front End Electronics (FEE): + **/ + class ChamberBuilder::FEB : public ChamberBuilder::Component { + public: + /** \brief Constructor. + **/ + FEB(); + /** \brief Init task **/ + virtual InitStatus Init(); + + private: + FEB(const FEB&); + //ChamberBuilder::FEB operator=(const FEB&); + const double FASP_x = 1.10; //! + const double FASP_y = 1.10; //! + const double FASP_z = 0.10; //! + const double FPGA_x = 2.20; //! + const double FPGA_y = 2.20; //! + const double FPGA_z = 0.18; //! + const double ADC_x = 0.9; //! + const double ADC_y = 1.5; //! + const double ADC_z = 0.1; //! + const double DCDC_x = 1.5012; //! + const double DCDC_y = 0.8992; //! + const double DCDC_z = 0.4319; //! + const double ConnFC_x = 2.37; //! + const double ConnFC_y = 0.535; //! + const double ConnFC_z = 0.266; //! + const double ConnBRG_x = 0.5; //! + const double ConnBRG_y = 4.58; //! + const double ConnBRG_z = 0.658; //! + + static const int FASPRO_Nly = 18; //! + static const int FASPRO_Nfasp = 12; //! + static const int FASPRO_Nadc = 6; //! + static const int FASPRO_Nfpga = 3; //! + static const int FASPRO_Ndcdc = 3; //! + const double FASPRO_zspace = 1.0; //! gap size between boards + const double FASPRO_length = 17.8; //! length of FASP FEBs in cm + const double FASPRO_width = 10.6; //! width of FASP FEBs in cm + const double FASPRO_hole_x = 2.2; //! + const double FASPRO_hole_y = 0.4; //! + const double FASPRO_ly_cu[FASPRO_Nly][2] = { // FASPRO(Cu) layer thickness [um] and covarage [%] + {54, 95}, {34, 10}, {16, 95}, {16, 10}, {16, 95}, {34, 10}, + {16, 95}, {16, 10}, {16, 95}, {16, 95}, {16, 10}, {16, 95}, + {34, 10}, {16, 95}, {16, 10}, {16, 95}, {34, 10}, {54, 95}}; + const double FASPRO_ly_pcb[FASPRO_Nly - 1] = {// FASPRO(FR4) layer thickness [um] + 100, 133, 100, 127, 100, 133, 100, 127, 100, + 127, 100, 133, 100, 127, 100, 133, 100}; + const double HOLE_pos[FASPRO_Nfasp][2] = {{-6, -3.55}, {-6, -1.55}, {-6, 1.55}, {-6, 3.55}, + {0, -3.55}, {0, -1.55}, {0, 1.55}, {0, 3.55}, + {+6, -3.55}, {+6, -1.55}, {+6, 1.55}, {+6, 3.55}}; + const double FASP_pos[FASPRO_Nfasp][2] = {{-6, -4.5}, {-6, -2.5}, {-6, +2.5}, {-6, +4.5}, {0, -4.5}, {0, -2.5}, + {0, +2.5}, {0, +4.5}, {+6, -4.5}, {+6, -2.5}, {+6, +2.5}, {+6, +4.5}}; + const double ADC_pos[FASPRO_Nadc][2] = {{-4.15, -3.35}, {1.85, -3.35}, {7.85, -3.35}, + {-4.15, +3.35}, {1.85, +3.35}, {7.85, +3.35}}; + const double FPGA_pos[FASPRO_Nfpga][2] = {{-6, 0}, {0, 0}, {+6, 0}}; + const double DCDC_pos[FASPRO_Ndcdc][2] = {{-3, 0.1}, {3, -1.2}, {2.89, 0.1}}; + const double ConnFC_pos[FASPRO_Nfasp][2] = {{-6, -4.9}, {-6, -2.9}, {-6, 2.9}, {-6, 4.9}, {0, -4.9}, {0, -2.9}, + {0, 2.9}, {0, 4.9}, {+6, -4.9}, {+6, -2.9}, {+6, 2.9}, {+6, 4.9}}; + const double ConnBRG_pos[2][2] = {{-8.4, 0}, {+8.4, 0}}; + + ClassDef(ChamberBuilder::FEB, 1) // Model for the TRD FEB geometry + }; +} // namespace cbm::trd::geo +#endif // CBMTRDGEOFACTORY_H_ diff --git a/core/detectors/trd/CbmTrdGeoSetup.cxx b/core/detectors/trd/CbmTrdGeoSetup.cxx new file mode 100644 index 0000000000..ac43cbc771 --- /dev/null +++ b/core/detectors/trd/CbmTrdGeoSetup.cxx @@ -0,0 +1,491 @@ +/* Copyright (C) 2024 National Institute of Physics and Nuclear Engineering - Horia Hulubei, Bucharest + SPDX-License-Identifier: GPL-3.0-only + Authors: Alexandru Bercuci [committer] */ + +#include "CbmTrdGeoSetup.h" + +#include "CbmTrdGeoFactory.h" + +#include <FairParGenericSet.h> // for FairParGenericSet +#include <FairParamList.h> // for FairParamList +#include <FairRunAna.h> // for FairRunAna +#include <FairRuntimeDb.h> // for FairRuntimeDb +#include <Logger.h> // for LOG + +#include <TArrayI.h> // for TArrayI +#include <TDatime.h> // for TDatime::AsString() +#include <TGeoElement.h> // for active volume material interogation +#include <TGeoManager.h> // for TGeoManager, gGeoManager +#include <TGeoNode.h> // for TGeoNode +#include <TObjArray.h> // for TObjArray +#include <TSystem.h> // for TSystem::GetUserInfo() + +using namespace cbm::trd::geo; + + +//__________________________________________________________________ +InitStatus SetupManager::Init() +{ + InitStatus stat; + TGeoNode* topNode = gGeoManager->GetTopNode(); + TObjArray* nodes = topNode->GetNodes(); + for (Int_t iNode = 0; iNode < nodes->GetEntriesFast(); iNode++) { + TGeoNode* trdGeo = static_cast<TGeoNode*>(nodes->At(iNode)); + if (string(trdGeo->GetName()).find("trd") != 0) continue; // trd_vXXy top node, e.g. trd_v13a, trd_v14b + fGeoTag = trdGeo->GetName(); + + // init the setup. Collect meta info + string uname = "John Doe", ucontact = "not-defined", udescr = "not-defined"; + // retrieve user name from the system + auto uinfo = gSystem->GetUserInfo(gSystem->GetEffectiveUid()); + if (!uinfo) + LOG(warning) << "Couldn't read user_name."; + else + uname = uinfo->fRealName; + if (fContact.compare("") == 0) + LOG(warning) << "No contact info were provided for responsible " << uname; + else + ucontact = fContact; + if (fDescription.compare("") == 0) + LOG(error) << "No comments provided for setup. Please add them by SetupManager::SetDescription()"; + else + udescr = fDescription; + + // build setup adding all meta info + uname += ";"; + uname += ucontact; + TDatime d; + uname += ";"; + uname += d.AsString(); + uname += ";"; + uname += udescr; + Setup* setup = new Setup(fGeoTag.data(), uname.data()); + Setup::Module modSetup; + //fHardwareSetup.SelectComponentIdMap(fGeometryTag); + TObjArray* layers = trdGeo->GetNodes(); + for (Int_t iLayer = 0; iLayer < layers->GetEntriesFast(); iLayer++) { + TGeoNode* lyGeo = static_cast<TGeoNode*>(layers->At(iLayer)); + if (string(lyGeo->GetName()).find("layer") != 0) continue; // only layers + + TObjArray* modules = lyGeo->GetNodes(); + for (Int_t iModule = 0; iModule < modules->GetEntriesFast(); iModule++) { + TGeoNode* modGeo = static_cast<TGeoNode*>(modules->At(iModule)); + // skip geo elements which are not modules + if (string(modGeo->GetName()).find("module") != 0) continue; + + LOG(info) << " Reading module " << modGeo->GetName() << " [" << lyGeo->GetName() << "]."; + stat = modSetup.init(modGeo); + switch (stat) { + case kERROR: continue; + case kFATAL: LOG(fatal) << GetName() << " Couldn't process geometry file."; break; + case kSUCCESS: setup->addParam(new Setup::Module(modSetup)); break; + } + } // loop over TRD modules / layers + } // loop over TRD layers + break; // loop over CBM systems + } + return kSUCCESS; +} + +//__________________________________________________________________ +void SetupManager::SetParContainers() +{ + FairRuntimeDb* rtdb = FairRunAna::Instance()->GetRuntimeDb(); + fSetup = (Setup*) (rtdb->getContainer("TrdSetup")); +} + +//__________________________________________________________________ +void SetupManager::Finish() +{ + FairRuntimeDb* rtdb = FairRunAna::Instance()->GetRuntimeDb(); + fSetup = (Setup*) (rtdb->getContainer("TrdSetup")); + //fSetup->Print(); +} + +//__________________________________________________________________ +Setup::Setup(const char* n, const char* t) : FairParGenericSet(n, t, "default") +{ + /** Define complementing info besides the geo description for the TRD system setup. + * n = geo tag; e.g. for "trd_v22d_mcbm.geo.root" name = "v22d_mcbm" + * t = meta info; list of ";" separated information in the following order + * - "name" : responsible person's name + * - "email" : responsible person's email + * - "date" : date of creation + */ + fMetaFields.push_back("name"); + fMetaFields.push_back("email"); + fMetaFields.push_back("date"); + size_t ii(0); + if ((ii = Parse()) != fMetaFields.size()) { + Help(); + LOG(warning) << "Number of info fields " << ii << " less than required (" << fMetaFields.size() + << "). Info might be spoiled. Check input.\n"; + } + else { + for (auto info : fMeta) { + if (strcmp(info.second.data(), "") != 0) continue; + Help(info.first.data()); + } + } +} + +//__________________________________________________________________ +const char* Setup::GetInfo(const char* label) const +{ + if (fMeta.find(label) == fMeta.end()) { + Help(label); + return nullptr; + } + return fMeta.at(label).data(); +} + +//__________________________________________________________________ +void Setup::Help(const char* lab) const +{ + LOG(info) << "* meta info; list of \";\" separated information in the following order:"; + LOG(info) << "* - \"name\" : responsible person's name"; + LOG(info) << "* - \"email\" : responsible person's email"; + LOG(info) << "* - \"date\" : date of creation"; + + if (lab) { + if (fMeta.find(lab) == fMeta.end()) + LOG(error) << "Meta info \"" << lab << "\" not defined.\n"; + else + LOG(warning) << "Meta info for\"" << lab << "\" not registered.\n"; + } +} + +//__________________________________________________________________ +size_t Setup::Parse() +{ + size_t idx(0); + string s(GetTitle()); + char* p = strtok(s.data(), ";"); + while (p != nullptr && idx < fMetaFields.size()) { + fMeta[fMetaFields[idx++]] = p; + p = strtok(nullptr, ";"); + } + return idx; +} + +//_______________________________________________________________________________ +Setup::~Setup() { fModule.clear(); } + +//_______________________________________________________________________________ +int Setup::GetModuleId(int i) const +{ + if (i < 0 || i >= (int) GetNrOfModules()) return -1; + uint16_t id = fModule[i]->GetModuleId(); + if (id == 0xffff) + return -1; + else + return id; +} + +//_______________________________________________________________________________ +const Setup::Module* Setup::GetModulePar(int detId) const +{ + for (auto mod : fModule) + if (mod->GetModuleId() == detId) return mod; + return nullptr; +} + +//_______________________________________________________________________________ +bool Setup::getParams(FairParamList* l) +{ + if (!l) return false; + if (!l->fill("Version", &fVersion)) { + LOG(error) << GetName() << "::getParams : Couldn't find \"Version\""; + return false; + } + int nmods(0); + if (!l->fill("NrOfModules", &nmods)) { + LOG(error) << GetName() << "::getParams : Couldn't find \"NrOfModules\""; + return false; + } + TArrayI modId(nmods), typId(nmods), rot(nmods); + if (!l->fill("Trd.Id", &modId)) { + LOG(error) << GetName() << "::getParams : Couldn't find \"Trd.Id\""; + return false; + } + if (!l->fill("Trd.Type", &typId)) { + LOG(error) << GetName() << "::getParams : Couldn't find \"Trd.Type\""; + return false; + } + if (!l->fill("Trd.Rot", &rot)) { + LOG(error) << GetName() << "::getParams : Couldn't find \"Trd.Rot\""; + return false; + } + Text_t textIn[100]; + if (!l->fill("Trd.Gas", textIn, 100)) { + LOG(error) << GetName() << "::getParams : Couldn't find \"Trd.Gas\""; + return false; + } + if (textIn[0] == 'A' && textIn[1] == 'r') + fGas = eGas::kAr; + else + fGas = eGas::kXe; + + for (int imod(0); imod < nmods; imod++) { + fModule.push_back(new Module(Form("Trd%s.%d", (modId[imod] < 0 ? "2D" : "1D"), abs(modId[imod])), GetTitle())); + auto mod = fModule.back(); + mod->fType = typId[imod]; + mod->fRot = rot[imod]; + mod->getParams(l); + } + return true; +} + + +//_______________________________________________________________________________ +void Setup::putParams(FairParamList* l) +{ + if (!l) return; + l->add("Version", fVersion); + int ii(0), nmods = (int) GetNrOfModules(); + l->add("NrOfModules", nmods); + TArrayI modId(nmods), typId(nmods), rot(nmods); + for (auto mod : fModule) { + modId[ii] = mod->GetModuleId() * (mod->GetFamily() == ePadPlane::k1d ? 1 : -1); + typId[ii] = mod->GetType(); + rot[ii] = mod->GetRotation(); + ii++; + } + l->add("Trd.Id", modId); + l->add("Trd.Type", typId); + l->add("Trd.Rot", rot); + if (fGas == eGas::kAr) + l->add("Trd.Gas", "ArCO2"); + else + l->add("Trd.Gas", "XeCO2"); + for (auto mod : fModule) + mod->putParams(l); +} + +//_______________________________________________________________________________ +void Setup::addParam(Module* mod) { fModule.push_back(mod); } + +//_______________________________________________________________________________ +Setup::Module::Module(const char* n, const char* t) : TNamed(n, t) +{ + // string name = n; + // if (name.size() > 6) { + // if (name.substr(3, 2).compare("1D") == 0) fFamily = ePadPlane::k1d; + // else + // fFamily = ePadPlane::k2d; + // fId = stoi(name.substr(6, 10)); + // } +} + +//_______________________________________________________________________________ +Setup::Module::Module(const Module& m0) : TNamed((const TNamed&) m0) +{ + fId = m0.fId; + fFee = m0.fFee; + fFamily = m0.fFamily; + fWindow = m0.fWindow; + fGas = m0.fGas; + fType = m0.fType; + fFeeType = m0.fFeeType; + fRot = m0.fRot; + fDaq = m0.fDaq; + fFEE = m0.fFEE; +} + +//_______________________________________________________________________________ +InitStatus Setup::Module::init(TGeoNode* n) +{ + bool hasRadiator(false); + info_t info; + if (!ReadModuleInfo(n->GetName(), info)) return kERROR; + + TObjArray* components = n->GetNodes(); + for (Int_t icomp = 0; icomp < components->GetEntriesFast(); icomp++) { + TGeoNode* comp = static_cast<TGeoNode*>(components->At(icomp)); + string cname(comp->GetName()); + + if (cname.find(ChamberBuilder::Component::fgName[(int) ChamberBuilder::eGeoPart::kRadiator]) == 0) { + LOG(debug4) << GetName() << " Initialize " << comp->GetName() << " with " + << ChamberBuilder::Component::fgName[(int) ChamberBuilder::eGeoPart::kRadiator]; + hasRadiator = true; // to be used when defining entrance window params + } + else if (cname.find(ChamberBuilder::Component::fgName[(int) ChamberBuilder::eGeoPart::kWindow]) == 0) { + LOG(debug4) << GetName() << " Initialize " << comp->GetName() << " with " + << ChamberBuilder::Component::fgName[(int) ChamberBuilder::eGeoPart::kWindow]; + if (!hasRadiator) + fWindow = eWindow::kNotSet; + else { // determine if the module have a thin (1D) or thick (2D) entrance window + int selectFeature(0); + TIter ipcs(comp->GetNodes()); + while (auto pcs = (TGeoNode*) ipcs()) { + if (string(pcs->GetName()).find("winIn_C") == 0) + selectFeature++; + else if (string(pcs->GetName()).find("winIn_HC") == 0) + selectFeature++; + } + if (selectFeature == 2) + fWindow = eWindow::kThick; + else + fWindow = eWindow::kThin; + + LOG(info) << GetName() << " Initialize TR absorption in " << (fWindow == eWindow::kThick ? "thick" : "thin") + << " entrance window."; + } + } + else if (cname.find(ChamberBuilder::Component::fgName[(int) ChamberBuilder::eGeoPart::kVolume]) == 0) { + LOG(debug4) << GetName() << " Initialize " << comp->GetName() << " with " + << ChamberBuilder::Component::fgName[(int) ChamberBuilder::eGeoPart::kVolume]; + // determine the active gas + auto gas = (TGeoNode*) comp->GetNodes()->FindObject("gas_0"); + if (!gas) + LOG(error) << GetName() << " Couldn't find \"gas_0\" in " << comp->GetName(); + else { + auto activeGas = gas->GetMedium()->GetMaterial(); + for (int imat(0); imat < activeGas->GetNelements(); imat++) { + auto elem = activeGas->GetElement(imat); + switch (elem->Z()) { + case 6: + case 8: break; // do nothing for C and O + case 18: // found Ar + fGas = eGas::kAr; + break; + case 54: // found Xe + fGas = eGas::kXe; + break; + default: + elem->Print(); + LOG(error) << GetName() << " Couldn't found element in active gas from " << comp->GetName(); + break; + } + } + } + if (fGas == eGas::kNotSet) { + LOG(warning) << GetName() << " Couldn't identify active gas type. Use legacy mode."; + return kERROR; + } + LOG(info) << GetName() << " Initialize active gas to " << (fGas == eGas::kXe ? "XeCO2." : "ArCO2."); + } + else if (cname.find(ChamberBuilder::Component::fgName[(int) ChamberBuilder::eGeoPart::kBackPanel]) == 0) { + LOG(debug4) << GetName() << " Initialize " << comp->GetName() << " with " + << ChamberBuilder::Component::fgName[(int) ChamberBuilder::eGeoPart::kBackPanel]; + // find the type of the pad-plane and consequently the TRD family + TIter ibkp(comp->GetNodes()); + while (auto bkp = (TGeoNode*) ibkp()) { + if (string(bkp->GetName()).find("pp_pcb2d") == 0) { + fFamily = ePadPlane::k2d; // 2d + SetName(Form("Trd2D.%d", info.address)); + break; + } + else if (string(bkp->GetName()).find("pp_pcb1d") == 0) { + fFamily = ePadPlane::k1d; // 1d + SetName(Form("Trd1D.%d", info.address)); + break; + } + } + if (fFamily == ePadPlane::kNotSet) { + LOG(error) << GetName() << " Couldn't identify TRD module family for " << n->GetName() << " " << n->GetTitle(); + return kFATAL; + } + LOG(info) << GetName() << " Initialize TRD family to " << (fFamily == ePadPlane::k2d ? "2D." : "1D;"); + } + else if (cname.find(ChamberBuilder::Component::fgName[(int) ChamberBuilder::eGeoPart::kFEB]) == 0) { + LOG(info) << GetName() << " Initialize " << comp->GetName() << " with " + << ChamberBuilder::Component::fgName[(int) ChamberBuilder::eGeoPart::kFEB]; + // find the FEE family, number of ASICs / module and eventually mapping + if (!ReadFebInfo(comp->GetName(), info)) return kERROR; + } + else { + LOG(info) << GetName() << "Geometry of module " << n->GetName() << " uses legacy setup."; + return kERROR; + } + // if (!TString(part->GetName()).BeginsWith("gas_")) continue; // only active gas volume + // + // // Put together the full path to the interesting volume, which + // // is needed to navigate with the geomanager to this volume. + // // Extract the geometry information (size, global position) + // // from this volume. + // TString path = TString("/") + topNode->GetName() + "/" + station->GetName() + "/" + layer->GetName() + "/" + // + module->GetName() + "/" + part->GetName(); + // + // CreateModuleParameters(path); + } + return kSUCCESS; +} + +//_______________________________________________________________________________ +void Setup::Module::putParams(FairParamList* l) +{ + int ii(0); + TArrayI daqId(fDaq.size()); + for (auto ic : fDaq) + daqId[ii++] = ic; + l->add(Form("%s.DaqInfo", GetName()), daqId); + + ii = 0; + auto pads = fFEE[0]->GetPads(); + TArrayI asicSetup(fFEE.size() * (pads.size() + 2)); + for (auto asic : fFEE) { + asicSetup[ii++] = asic->GetId(); // asic id in the module + asicSetup[ii++] = asic->GetMask(); + pads = asic->GetPads(); + for (auto pad : pads) + asicSetup[ii++] = pad; + } + l->add(Form("%s.%sInfo", GetName(), (fFee == eAsic::kSpadic ? "Spadic" : "Fasp")), asicSetup); +} + +//_______________________________________________________________________________ +bool Setup::Module::getParams(FairParamList* l) +{ + READOUT pMod = (fFamily == ePadPlane::k1d ? mod1D[fType] : mod2D[fType]); + FEB pFeb = faspFeb[fFeeType]; + int ndaq = pMod.ndaq; + int nasic = pMod.nasic; + int nch = pFeb.nchannels; + TArrayI daqId(ndaq); + if (!l->fill(Form("%s.DaqInfo", GetName()), &daqId)) { + LOG(error) << GetName() << "::getParams : Couldn't find \"DaqInfo\""; + return false; + } + fDaq.assign(daqId.GetArray(), daqId.GetArray() + ndaq); + + TArrayI asicInfo(nasic); + if (!l->fill(Form("%s.SpadicInfo", GetName()), &asicInfo)) fFee = eAsic::kSpadic; + if (!l->fill(Form("%s.FaspInfo", GetName()), &asicInfo)) + fFee = eAsic::kFasp; + else { + LOG(error) << GetName() << "::getParams : Couldn't find \"AsicInfo\""; + return false; + } + for (int i(0), k(0); i < nasic; i++) { + fFEE.push_back(new Asic(Form("%s%d", (fFee == eAsic::kSpadic ? "Spadic" : "Fasp"), i), GetName())); + auto asic = fFEE.back(); + asic->fUnique = asicInfo[k++]; + asic->fMask = asicInfo[k++]; + asic->fPad.assign(&asicInfo[k], &asicInfo[k + nch]); + k += nch; + } + + return true; +} + +//_______________________________________________________________________________ +Setup::Asic::Asic(const char* n, const char* t) : TNamed(n, t) +{ + string name = n; + // fFamily = eAsic::kFasp; + int nskip(4); + if (name.find("Spadic") == 0) { + // fFamily = eAsic::kSpadic; + nskip = 6; + } + fId = stoi(name.substr(nskip, 10)); +} + +/* clang-format off */ +// NamespaceImp(cbm::trd::geo) +ClassImp(cbm::trd::geo::SetupManager) +ClassImp(cbm::trd::geo::Setup) +ClassImp(cbm::trd::geo::Setup::Module) +ClassImp(cbm::trd::geo::Setup::Asic) + /* clang-format on */ diff --git a/core/detectors/trd/CbmTrdGeoSetup.h b/core/detectors/trd/CbmTrdGeoSetup.h new file mode 100644 index 0000000000..9d82748718 --- /dev/null +++ b/core/detectors/trd/CbmTrdGeoSetup.h @@ -0,0 +1,204 @@ +/* Copyright (C) 2024 National Institute of Physics and Nuclear Engineering - Horia Hulubei, Bucharest + SPDX-License-Identifier: GPL-3.0-only + Authors: Alexandru Bercuci [committer] */ + +/** + * \file CbmTrdGeoSetup.h + * \brief Describing class of the TRD setup geometry, FEE, gas. + * \author Alexandru Bercuci <abercuci@niham.nipne.ro> + * \date 20/10/2023 + * + * This class describe the setup of the TRD detectors including : + * - version : local versioning of the setup (within the TRD group) + * - main-type of TRD version : 1/2 D version + * - sub-type of TRD version : 1, 3, 5, etc + * - rotation : no. of 90 deg + * - gas : gas type + * + * Detector-wise description + * - DAQ : Data AQuisition for each module (CROB/CRI config) + * - FEE : Front-End Electronics description for each modue (ASIC config) + */ + +#ifndef CBMTRDGEOSETUP_H_ +#define CBMTRDGEOSETUP_H_ + +#include "CbmTrdDefs.h" // for trd namespace + +#include <FairParGenericSet.h> // for FairParGenericSet +#include <FairTask.h> // for FairTask + +#include <TNamed.h> // for the base class + +#include <map> +#include <string> +#include <vector> + +using namespace std; +using namespace cbm::trd; +class FairParamList; +class TGeoNode; +namespace cbm::trd::geo +{ + /** \class Setup + * \brief Setup meta info for the TRD system to supplement the geometry + */ + class Setup : public FairParGenericSet { + public: + friend class SetupManager; + class Asic; + class Module; + + /** \brief Setup descriptor for the TRD setup. The identification should be : + * \param[in] n identification of geometry setup to which it is linked. + * \param[in] t responsible name, email and date of creation. + **/ + Setup(const char* n, const char* t); + virtual ~Setup(); + /** \brief Retrieve meta info for the setup according to label. \sa Setup(). + * \param[in] label description of the meta info to be retrieved. In case of un-registered label return nullptr; + **/ + virtual const char* GetInfo(const char* label) const; + /** \brief Get module par by index + * \param[in] i index of the module in the list */ + virtual int GetModuleId(int i) const; + /** \brief Get module par by detector id + * \param[in] detId detector id in the setup */ + virtual const Module* GetModulePar(int detId) const; + /** \brief Retrieve no of modules in the setup*/ + virtual size_t GetNrOfModules() const { return fModule.size(); } + /** \brief Retrieve full list of modules in the setup*/ + vector<Module*> GetModules() { return fModule; } + virtual void addParam(Module* mod); + /** \brief Write out the setup from object to FairParamList*/ + virtual void putParams(FairParamList*); + /** \brief Read in the setup from FairParamList*/ + virtual bool getParams(FairParamList*); + + protected: + int fVersion = 1; ///< + eGas fGas = eGas::kNotSet; ///< + vector<Module*> fModule = {}; ///< list of modules defined in the setup + vector<string> fMetaFields = {}; ///< ordered list of meta info types + map<string, string> fMeta = {}; ///< meta info attached to this setup + + private: + Setup(const Setup&); + /** \brief Help message for user */ + void Help(const char* lab = nullptr) const; + /** \brief Parse setup description for meta info */ + size_t Parse(); + + ClassDef(cbm::trd::geo::Setup, 1) // Setup description of the TRD system, accompanying the geometry + }; // class cbm::trd::geo::Setup + + + /** + * \class Setup::Module + * \brief Meta info for one TRD module + */ + class Setup::Module : public TNamed { + public: + friend class Setup; + friend class SetupManager; + Module(const char* n = "", const char* t = ""); + + int GetModuleId() const { return (fId == 0xffff ? -1 : 1); } + int GetType() const { return fType; } + int GetRotation() const { return fRot; } + ePadPlane GetFamily() const { return fFamily; } + eAsic GetFeeType() const { return fFee; } + eWindow GetWindowType() const { return fWindow; } + vector<int> GetDaq() const { return fDaq; } + vector<Asic*> GetFEE() const { return fFEE; } + + protected: + /** \brief Read info relevant for the module from the geometry*/ + virtual InitStatus init(TGeoNode*); + /** \brief Write out the setup from object to FairParamList*/ + virtual void putParams(FairParamList*); + /** \brief Read in the setup from FairParamList*/ + virtual bool getParams(FairParamList*); + + uint16_t fId = 0xffff; ///< TRD chamber id in the setup + ePadPlane fFamily = ePadPlane::kNotSet; ///< Chamber family (1D / 2D) + eAsic fFee = eAsic::kNotSet; ///< ASIC family (SPADIC/FASP) + eWindow fWindow = eWindow::kNotSet; ///< Entrance window type + eGas fGas = eGas::kNotSet; ///< gas type + int fType = 1; ///< TRD chamber sub-type (e.g. 1, 3, 5, 7 for TRD1D) + int fFeeType = 1; ///< FEB type for each FEE version + int fRot = 0; ///< rotation of chamber in steps of 90 deg + vector<int> fDaq = {}; ///< + vector<Asic*> fFEE = {}; ///< + private: + Module(const Module&); + + ClassDef(cbm::trd::geo::Setup::Module, 1) // Setup description of a TRD module + }; // class cbm::trd::geo::Setup::Module + + + /** + * \class Setup::Asic + * \brief Meta info for one TRD ASIC + */ + class Setup::Asic : public TNamed { + public: + friend class Setup; + friend class SetupManager; + Asic(const char* n, const char* t); + int GetMask() const { return fMask; } + int GetId() const { return (fId == 0xff ? -1 : fId); } + int GetUniqueId() const { return (fUnique == 0xffff ? -1 : fUnique); } + vector<int> GetPads() const { return fPad; } + + private: + Asic(const Asic&); + + uint8_t fId = 0xff; ///< ASIC id in the chamber + uint16_t fUnique = 0xffff; ///< ASIC id in the production + uint16_t fMask = 0xffff; ///< + vector<int> fPad = {}; ///< + + ClassDef(cbm::trd::geo::Setup::Asic, 1) // Setup description of an ASIC + }; // class cbm::trd::geo::Setup::Asic + + + /** + * \class SetupManager + * \brief Generate setup meta info for the TRD system + */ + class SetupManager : public FairTask { + public: + /** \brief Default constructor.*/ + SetupManager() : FairTask("TrdSetupManager") { ; } + /** \brief Destructor.*/ + virtual ~SetupManager() { ; } + /** \brief Inherited from FairTask.*/ + virtual InitStatus Init(); + /** \brief Inherited from FairTask.*/ + virtual void SetParContainers(); + /** \brief Inherited from FairTask.*/ + virtual void Exec(Option_t*) { ; } + /** \brief Inherited from FairTask.*/ + virtual void Finish(); + + void SetContact(const char* contact) { fContact = contact; } + void SetDescription(const char* text) { fDescription = text; } + + private: + SetupManager(const SetupManager&); + SetupManager& operator=(const SetupManager&); + /** \brief .*/ + void CreateModuleParameters(const TString& path); + /** \brief .*/ + bool CreateParFilesFromGeometry(TString outDir = ""); + + string fGeoTag = ""; ///< the setup name + string fContact = ""; ///< contact info (usual email) of the responsible + string fDescription = ""; ///< further description of the current setup + Setup* fSetup = nullptr; ///< the setup object + + ClassDef(cbm::trd::geo::SetupManager, 1) // Manages the creation of meta info for the TRD system setup + }; // class cbm::trd::geo::SetupManager +} // namespace cbm::trd::geo +#endif // CBMTRDGEOSETUP_H_ diff --git a/core/detectors/trd/CbmTrdModuleAbstract.cxx b/core/detectors/trd/CbmTrdModuleAbstract.cxx index 9ba7513d73..7fcfca29e9 100644 --- a/core/detectors/trd/CbmTrdModuleAbstract.cxx +++ b/core/detectors/trd/CbmTrdModuleAbstract.cxx @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2020 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt +/* Copyright (C) 2018-2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt SPDX-License-Identifier: GPL-3.0-only Authors: Florian Uhlig [committer], Alexandru Bercuci */ @@ -9,33 +9,15 @@ #include <Logger.h> //_______________________________________________________________________________ -CbmTrdModuleAbstract::CbmTrdModuleAbstract() - : TNamed() - , fModConfig(0) - , fModAddress(0) - , fLayerId(-1) - , fRotation(0) - , fDigiPar(nullptr) - , fChmbPar(nullptr) - , fAsicPar(nullptr) - , fGainPar(nullptr) - , fGeoPar(nullptr) -{ -} +CbmTrdModuleAbstract::CbmTrdModuleAbstract() : TNamed() {} //_______________________________________________________________________________ CbmTrdModuleAbstract::CbmTrdModuleAbstract(Int_t mod, Int_t ly, Int_t rot) : TNamed("CbmTrdModule", "Abstract TRD module implementation") - , fModConfig(0) - , fModAddress(mod) - , fLayerId(ly) - , fRotation(rot) - , fDigiPar(nullptr) - , fChmbPar(nullptr) - , fAsicPar(nullptr) - , fGainPar(nullptr) - , fGeoPar(nullptr) { + fModAddress = mod; + fLayerId = ly; + fRotation = rot; } //_______________________________________________________________________________ diff --git a/core/detectors/trd/CbmTrdModuleAbstract.h b/core/detectors/trd/CbmTrdModuleAbstract.h index 75bc0ad3bf..388f439e35 100644 --- a/core/detectors/trd/CbmTrdModuleAbstract.h +++ b/core/detectors/trd/CbmTrdModuleAbstract.h @@ -1,10 +1,11 @@ -/* Copyright (C) 2018-2020 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt +/* Copyright (C) 2018-2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt SPDX-License-Identifier: GPL-3.0-only Authors: Florian Uhlig [committer], Alexandru Bercuci */ #ifndef CBMTRDMODULEABSTRACT_H #define CBMTRDMODULEABSTRACT_H +#include "CbmTrdDefs.h" // for cbm::trd namespace #include "CbmTrdParModAsic.h" // for CbmTrdParSetAsic #include "CbmTrdParModDigi.h" // for CbmTrdParModDigi #include "CbmTrdParModGeo.h" // for CbmTrdParModGeo @@ -16,17 +17,22 @@ class CbmTrdParModGain; class CbmTrdParModGas; +using namespace cbm::trd; /** - * \brief Abstract class for TRD module + * \brief Abstract class for the TRD module + * It provides access to the basic properties of the chamber as follows : + * - The chamber geometry (CbmTrdParModGeo) + * - The pad-plane geometry (CbmTrdParModDigi) + * - The ASICs allocation and properties (CbmTrdParModAsic) + * - The gas properties (CbmTrdParModGas) + * + * It define basic configuration properties + * - type in the TRD wall + * - type of pad-plane (1D or 2D) + * - type of FEE ASIC (SPADIC or FASP) **/ class CbmTrdModuleAbstract : public TNamed { public: - enum eCbmTrdModuleDef - { - kTrd2d = 0 ///< toggle pad-plane type of the chamber - , - kFasp = 1 ///< toggle FEE type for the module - }; /** \brief Default constructor.*/ CbmTrdModuleAbstract(); /** \brief Constructor with placement */ @@ -64,14 +70,6 @@ public: */ virtual inline Int_t GetPadRowCol(Int_t address, Int_t& c) const; virtual const Char_t* GetPath() const { return fGeoPar ? fGeoPar->GetTitle() : ""; } - /** \brief Inquire the FEE read-out type of the module - * \return false for SPADIC and true for FASP - */ - bool HasFaspFEE() const { return TESTBIT(fModConfig, eCbmTrdModuleDef::kFasp); } - /** \brief Inquire the pad-plane type of the chamber - * \return false for TRD-1D and true for TRD-2D - */ - bool Has2dPadPlane() const { return TESTBIT(fModConfig, eCbmTrdModuleDef::kTrd2d); } /** \brief Inquire the ASIC par set * \return true for actively masked channel @@ -85,45 +83,18 @@ public: virtual void SetDigiPar(const CbmTrdParModDigi* p) { fDigiPar = p; } virtual void SetGainPar(const CbmTrdParModGain* p) { fGainPar = p; } virtual void SetGeoPar(const CbmTrdParModGeo* p) { fGeoPar = p; } - - /** \brief Define the read-out FEE type of the module - * \param[in] set true for FASP and false [default] for SPADIC - */ - void SetFEE(bool set = true) - { - set ? SETBIT(fModConfig, eCbmTrdModuleDef::kFasp) : CLRBIT(fModConfig, eCbmTrdModuleDef::kFasp); - } - /** \brief Define the pad-plane type of the chamber - * \param[in] set true for TRD-2D and false [default] for TRD-1D - */ - void SetPadPlane(bool set = true) - { - set ? SETBIT(fModConfig, eCbmTrdModuleDef::kTrd2d) : CLRBIT(fModConfig, eCbmTrdModuleDef::kTrd2d); - } - protected: - /** 8 bits bit map for module configuration - * [0] - chamber's pad-plane type; 0 rectangular pads, 1 triangular pads, \see SetRO - * [1] - chamber's FEE type; 0 SPADIC, 1 FASP, \see SetFEE - * [2] - - * [3] - - * [4] - - * [5] - - * [6] - - * [7] - - */ - uint8_t fModConfig; // geometrical definitions imported from CbmTrdGeoHandler - UShort_t fModAddress; ///< unique identifier for current module - Char_t fLayerId; ///< layer identifier - UChar_t fRotation; ///< rotation angle for current module - - // calibration objects - const CbmTrdParModDigi* fDigiPar; ///< read-out description of module - const CbmTrdParModGas* fChmbPar; ///< detection description (HV, drift) of module - CbmTrdParModAsic* fAsicPar; ///< the set of ASIC operating on the module (owned) - const CbmTrdParModGain* fGainPar; ///< Analog to digital conversion for module - const CbmTrdParModGeo* fGeoPar; ///< link to gGeometry for module + UShort_t fModAddress = 0; ///< unique identifier for current module + Char_t fLayerId = -1; ///< layer identifier + UChar_t fRotation = 0; ///< rotation angle for current module + + // calibration objects + const CbmTrdParModDigi* fDigiPar = nullptr; ///< read-out description of module + const CbmTrdParModGas* fChmbPar = nullptr; ///< detection description (HV, drift) of module + CbmTrdParModAsic* fAsicPar = nullptr; ///< the set of ASIC operating on the module (owned) + const CbmTrdParModGain* fGainPar = nullptr; ///< Analog to digital conversion for module + const CbmTrdParModGeo* fGeoPar = nullptr; ///< link to gGeometry for module private: CbmTrdModuleAbstract(const CbmTrdModuleAbstract& ref); diff --git a/core/detectors/trd/CbmTrdParMod.cxx b/core/detectors/trd/CbmTrdParMod.cxx index 8e7d3074e0..847af27fc0 100644 --- a/core/detectors/trd/CbmTrdParMod.cxx +++ b/core/detectors/trd/CbmTrdParMod.cxx @@ -1,13 +1,13 @@ -/* Copyright (C) 2018-2020 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt +/* Copyright (C) 2018-2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt SPDX-License-Identifier: GPL-3.0-only - Authors: Florian Uhlig [committer] */ + Authors: Florian Uhlig [committer], Alexandru Bercuci*/ #include "CbmTrdParMod.h" #include <Logger.h> // for LOG //___________________________________________________________________ -CbmTrdParMod::CbmTrdParMod(const char* name, const char* title) : TNamed(name, title), fModuleId(0) {} +CbmTrdParMod::CbmTrdParMod(const char* name, const char* title) : TNamed(name, title) {} //___________________________________________________________________ CbmTrdParMod::~CbmTrdParMod() { LOG(debug) << GetName() << "::delete[" << GetTitle() << "]"; } diff --git a/core/detectors/trd/CbmTrdParMod.h b/core/detectors/trd/CbmTrdParMod.h index d8e7050e86..cfe9d96188 100644 --- a/core/detectors/trd/CbmTrdParMod.h +++ b/core/detectors/trd/CbmTrdParMod.h @@ -1,12 +1,11 @@ -/* Copyright (C) 2018-2020 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt +/* Copyright (C) 2018-2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt SPDX-License-Identifier: GPL-3.0-only - Authors: Florian Uhlig [committer] */ + Authors: Florian Uhlig [committer], Alexandru Bercuci */ #ifndef CBMTRDPARMOD_H #define CBMTRDPARMOD_H #include <Rtypes.h> // for THashConsistencyHolder, ClassDef -#include <RtypesCore.h> // for Int_t #include <TNamed.h> // for TNamed /** \brief Definition of generic parameters for one TRD module **/ @@ -15,15 +14,21 @@ public: CbmTrdParMod(const char* name = "CbmTrdParMod", const char* title = "TRD generic module definition"); virtual ~CbmTrdParMod(); - virtual Int_t GetModuleId() const { return fModuleId; } + virtual uint16_t GetConfig() const { return fConfig; } + virtual int GetModuleId() const { return fModuleId; } + virtual uint8_t GetVersion() const { return fVersion; } - virtual void SetModuleId(Int_t m) { fModuleId = m; } + virtual void SetConfigId(uint16_t c) { fConfig = c; } + virtual void SetModuleId(int m) { fModuleId = m; } + virtual void SetVersion(uint8_t v) { fVersion = v; } -protected: - Int_t fModuleId; ///< module id -private: + protected: + uint8_t fVersion = 0; ///< version of the parameter + uint16_t fConfig = 0; ///< configuration setup of the module + int fModuleId = 0; ///< module id + private: ClassDef(CbmTrdParMod, - 1) // Definition of generic parameters for one TRD module + 2) // Definition of generic parameters for one TRD module }; #endif diff --git a/core/detectors/trd/CbmTrdParModDigi.cxx b/core/detectors/trd/CbmTrdParModDigi.cxx index 9795e2b1ca..2ff82816de 100644 --- a/core/detectors/trd/CbmTrdParModDigi.cxx +++ b/core/detectors/trd/CbmTrdParModDigi.cxx @@ -1,6 +1,6 @@ -/* Copyright (C) 2018-2020 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt +/* Copyright (C) 2018-2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt SPDX-License-Identifier: GPL-3.0-only - Authors: Florian Uhlig [committer] */ + Authors: Florian Uhlig [committer], Alexandru Bercuci */ #include "CbmTrdParModDigi.h" @@ -19,6 +19,8 @@ #include <stdio.h> // for printf #include <string.h> // for strcmp +using namespace cbm::trd; + //___________________________________________________________________ CbmTrdParModDigi::CbmTrdParModDigi() : CbmTrdParMod("CbmTrdParModDigi", "TRD read-out definition") @@ -219,6 +221,13 @@ void CbmTrdParModDigi::ProjectPositionToNextAnodeWire(Double_t* local_point) con } } +//___________________________________________________________________________ +int CbmTrdParModDigi::GetPadPlaneType() const +{ + if (fVersion == 0) return -1; // legacy parameters + return int(HasPadPlane2D(fConfig)); +} + //___________________________________________________________________________ Int_t CbmTrdParModDigi::GetSector(const Double_t* local_point) const { diff --git a/core/detectors/trd/CbmTrdParModDigi.h b/core/detectors/trd/CbmTrdParModDigi.h index fdc8930515..b06329681b 100644 --- a/core/detectors/trd/CbmTrdParModDigi.h +++ b/core/detectors/trd/CbmTrdParModDigi.h @@ -1,10 +1,11 @@ -/* Copyright (C) 2018-2020 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt +/* Copyright (C) 2018-2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt SPDX-License-Identifier: GPL-3.0-only - Authors: Florian Uhlig [committer], Pascal Raisig */ + Authors: Florian Uhlig [committer], Pascal Raisig, Alexandru Bercuci*/ #ifndef CBMTRDPARMODDIGI_H #define CBMTRDPARMODDIGI_H +#include "CbmTrdDefs.h" // for cbm::trd namespace #include "CbmTrdParMod.h" // for CbmTrdParMod #include <Rtypes.h> // for THashConsistencyHolder, ClassDef @@ -85,6 +86,16 @@ public: Double_t GetX() const { return fX; } Double_t GetY() const { return fY; } Double_t GetZ() const { return fZ; } + + /** \brief Access the basic type of pad plane topology. For convenience also specific accessors are added for each specific pad-plane type. + * \return -1 : legacy version. No info on the pad-plane + * 0 : 1D type + * 1 : 2D type + */ + int GetPadPlaneType() const; + bool IsPadPlane1D() const { return (GetPadPlaneType() >= 0) && !bool(GetPadPlaneType()); } + bool IsPadPlane2D() const { return (GetPadPlaneType() >= 0) && bool(GetPadPlaneType()); } + void Print(Option_t* opt = "") const; void ProjectPositionToNextAnodeWire(Double_t* local_point) const; diff --git a/core/detectors/trd/CbmTrdParSpadic.cxx b/core/detectors/trd/CbmTrdParSpadic.cxx index c59e6e44c3..451179690e 100644 --- a/core/detectors/trd/CbmTrdParSpadic.cxx +++ b/core/detectors/trd/CbmTrdParSpadic.cxx @@ -1,10 +1,10 @@ -/* Copyright (C) 2018-2020 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt +/* Copyright (C) 2018-2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt SPDX-License-Identifier: GPL-3.0-only Authors: Florian Uhlig [committer], Pascal Raisig */ #include "CbmTrdParSpadic.h" -#include "CbmTrdDefs.h" // for eCbmTrdModuleTypes, eCbmTrdModuleType... +#include "CbmTrdDefs.h" // for eModuleTypes1D, eCbmTrdModuleType... #include <FairParamList.h> // for FairParamList #include <Logger.h> // for Logger, LOG @@ -133,14 +133,15 @@ std::uint8_t CbmTrdParSpadic::GetElinkId(size_t componentId, Int_t channelId) } // ---- GetNasicsOnModule ---------------------------------------------------- +using namespace cbm::trd; Int_t CbmTrdParSpadic::GetNasicsOnModule(Int_t moduleType) { switch (moduleType) { - case (Int_t) eCbmTrdModuleTypes::kHighChDensitySmallR: return 80; break; - case (Int_t) eCbmTrdModuleTypes::kLowChDensitySmallR: return 20; break; - case (Int_t) eCbmTrdModuleTypes::kHighChDensityLargeR: return 108; break; - case (Int_t) eCbmTrdModuleTypes::kLowChDensityLargeR: return 36; break; - case (Int_t) eCbmTrdModuleTypes::kMcbmModule: + case (Int_t) eModuleTypes1D::kHighChDensitySmallR: return 80; break; + case (Int_t) eModuleTypes1D::kLowChDensitySmallR: return 20; break; + case (Int_t) eModuleTypes1D::kHighChDensityLargeR: return 108; break; + case (Int_t) eModuleTypes1D::kLowChDensityLargeR: return 36; break; + case (Int_t) eModuleTypes1D::kMcbmModule: return 24; // 24 is the maximum on a kMcbmModule it can also be less break; default: return 1; break; @@ -152,11 +153,11 @@ Int_t CbmTrdParSpadic::GetNasicsPerCrob(Int_t moduleType) { Int_t nAsicsPerCrob = GetNasicsOnModule(moduleType); switch (moduleType) { - case (Int_t) eCbmTrdModuleTypes::kHighChDensitySmallR: nAsicsPerCrob /= 4; break; - case (Int_t) eCbmTrdModuleTypes::kLowChDensitySmallR: nAsicsPerCrob /= 1; break; - case (Int_t) eCbmTrdModuleTypes::kHighChDensityLargeR: nAsicsPerCrob /= 6; break; - case (Int_t) eCbmTrdModuleTypes::kLowChDensityLargeR: nAsicsPerCrob /= 2; break; - case (Int_t) eCbmTrdModuleTypes::kMcbmModule: + case (Int_t) eModuleTypes1D::kHighChDensitySmallR: nAsicsPerCrob /= 4; break; + case (Int_t) eModuleTypes1D::kLowChDensitySmallR: nAsicsPerCrob /= 1; break; + case (Int_t) eModuleTypes1D::kHighChDensityLargeR: nAsicsPerCrob /= 6; break; + case (Int_t) eModuleTypes1D::kLowChDensityLargeR: nAsicsPerCrob /= 2; break; + case (Int_t) eModuleTypes1D::kMcbmModule: nAsicsPerCrob /= 1; // 24 is the maximum on a kMcbmModule it can also be less break; default: nAsicsPerCrob /= -1; break; diff --git a/reco/detectors/trd/CbmTrdHitProducer.cxx b/reco/detectors/trd/CbmTrdHitProducer.cxx index 16e55d6e30..c900451432 100644 --- a/reco/detectors/trd/CbmTrdHitProducer.cxx +++ b/reco/detectors/trd/CbmTrdHitProducer.cxx @@ -1,6 +1,6 @@ -/* Copyright (C) 2018-2021 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt +/* Copyright (C) 2018-2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt SPDX-License-Identifier: GPL-3.0-only - Authors: Pascal Raisig, Florian Uhlig [committer] */ + Authors: Pascal Raisig, Florian Uhlig [committer], Alexandru Bercuci */ #include "CbmTrdHitProducer.h" @@ -93,13 +93,27 @@ CbmTrdModuleRec* CbmTrdHitProducer::AddModule(Int_t address, const CbmTrdParModG if (moduleAddress != address) { LOG(error) << "CbmTrdHitProducer::AddModule: Module ID " << address << " does not match geometry definition " << moduleAddress << ". Module init failed!"; - return NULL; + return nullptr; + } + // try to load read-out parameters for module + const CbmTrdParModDigi* pDigi(NULL); + if (!fDigiPar || !(pDigi = (const CbmTrdParModDigi*) fDigiPar->GetModulePar(address))) { + LOG(error) << GetName() << "::AddModule : No Read-Out params for module " << address << " @ " << pg->GetTitle() + << ". Module init failed!"; + return nullptr; } - LOG(info) << GetName() << "::AddModule(" << pg->GetName() << " " << (moduleType < 9 ? '1' : '2') << "D] mod[" - << moduleAddress << "] ly[" << lyId << "] det[" << moduleAddress << "]"; + + // find the type of TRD detector was hit. Temporary until a new scheme of setup parameters will be but in place. TODO + bool trd2d(false); + if (pDigi->GetPadPlaneType() >= 0) + trd2d = pDigi->IsPadPlane2D(); + else + trd2d = (moduleType >= 9); // legacy support for old pad-plane addresing + LOG(info) << GetName() << "::AddModule(" << pg->GetName() << " " << (trd2d ? '2' : '1') << "D] mod[" << moduleAddress + << "] ly[" << lyId << "] det[" << moduleAddress << "]"; CbmTrdModuleRec* module(nullptr); - if (moduleType >= 9) { + if (trd2d) { module = fModules[address] = new CbmTrdModuleRec2D(address); ((CbmTrdModuleRec2D*) module)->SetUseHelpers(CbmTrdClusterFinder::UseRecoHelpers()); ((CbmTrdModuleRec2D*) module)->SetHitTimeOffset(fHitTimeOffset); @@ -110,15 +124,7 @@ CbmTrdModuleRec* CbmTrdHitProducer::AddModule(Int_t address, const CbmTrdParModG // Load geometry parameters for the module module->SetGeoPar(pg); - - - // try to load read-out parameters for module - const CbmTrdParModDigi* pDigi(NULL); - if (!fDigiPar || !(pDigi = (const CbmTrdParModDigi*) fDigiPar->GetModulePar(address))) { - LOG(warn) << GetName() << "::AddModule : No Read-Out params for modAddress " << address << ". Using default."; - } - else - module->SetDigiPar(pDigi); + module->SetDigiPar(pDigi); // try to load ASIC parameters for module CbmTrdParModAsic* pAsic(NULL); @@ -138,7 +144,7 @@ CbmTrdModuleRec* CbmTrdHitProducer::AddModule(Int_t address, const CbmTrdParModG module->SetChmbPar(pChmb); // try to load Gain parameters for module - if (moduleType >= 9) { + if (trd2d) { const CbmTrdParModGain* pGain(NULL); if (!fGainPar || !(pGain = (const CbmTrdParModGain*) fGainPar->GetModulePar(address))) { //LOG(warn) << GetName() << "::AddModule : No Gain params for modAddress "<< address <<". Using default."; diff --git a/sim/detectors/trd/CbmTrdDigitizer.cxx b/sim/detectors/trd/CbmTrdDigitizer.cxx index e3469521d8..15f0a8e0f3 100644 --- a/sim/detectors/trd/CbmTrdDigitizer.cxx +++ b/sim/detectors/trd/CbmTrdDigitizer.cxx @@ -1,4 +1,4 @@ -/* Copyright (C) 2009-2021 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt +/* Copyright (C) 2009-2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt SPDX-License-Identifier: GPL-3.0-only Authors: Florian Uhlig [committer], Alexandru Bercuci, Etienne Bechtel */ @@ -312,13 +312,26 @@ CbmTrdModuleSim* CbmTrdDigitizer::AddModule(Int_t detId) if (moduleAddress != detId) { LOG(error) << "CbmTrdDigitizer::AddModule: MC module ID " << detId << " does not match geometry definition " << moduleAddress << ". Module init failed!"; - return NULL; + return nullptr; } - LOG(debug) << GetName() << "::AddModule[" << (moduleType < 9 ? '1' : '2') << "D] address[" << moduleAddress << "] ly[" - << lyId << "]"; + // try to load read-out parameters for module + const CbmTrdParModDigi* pDigi(NULL); + if (!fDigiPar || !(pDigi = (const CbmTrdParModDigi*) fDigiPar->GetModulePar(detId))) { + LOG(error) << GetName() << "::AddModule : No Read-Out params for module " << detId << " @ " << path + << ". Module init failed!"; + return nullptr; + } + + // find the type of TRD detector was hit. Temporary until a new scheme of setup parameters will be but in place. TODO + bool trd2d(false); + if (pDigi->GetPadPlaneType() >= 0) + trd2d = pDigi->IsPadPlane2D(); + else + trd2d = (moduleType >= 9); // legacy support for old pad-plane addresing + LOG(debug) << GetName() << "::AddModule(" << path << " " << (trd2d ? '2' : '1') << "D] mod[" << moduleAddress; CbmTrdModuleSim* module(NULL); - if (moduleType >= 9) { - // temporary fix for TRD-2Dh @ mCBM 2021 + if (trd2d) { + // temporary fix for TRD-2Dh @ mCBM 2021 (legacy) if (moduleType == 10) SetUseFASP(kFALSE); else @@ -361,6 +374,7 @@ CbmTrdModuleSim* CbmTrdDigitizer::AddModule(Int_t detId) module->SetMessageConverter(fConverter); module->SetQA(fQA); } + module->SetDigiPar(pDigi); // try to load Geometry parameters for module const CbmTrdParModGeo* pGeo(NULL); @@ -371,17 +385,8 @@ CbmTrdModuleSim* CbmTrdDigitizer::AddModule(Int_t detId) else module->SetGeoPar(pGeo); - // try to load read-out parameters for module - const CbmTrdParModDigi* pDigi(NULL); - if (!fDigiPar || !(pDigi = (const CbmTrdParModDigi*) fDigiPar->GetModulePar(detId))) { - LOG(info) << GetName() << "::AddModule : No Read-Out params for module " << detId << " @ " << path - << ". Using default."; - } - else - module->SetDigiPar(pDigi); - - // TODO check if this works also for ModuleR (moduleType < 9) modules - if (moduleType >= 9) { + // TODO check if this works also for TRD1D modules + if (trd2d) { // try to load ASIC parameters for module CbmTrdParModAsic* pAsic(NULL); if (!fAsicPar || !(pAsic = (CbmTrdParModAsic*) fAsicPar->GetModulePar(detId))) { -- GitLab