diff --git a/CODEOWNERS b/CODEOWNERS index 26021d458ad76..369a7cf8a8463 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -43,7 +43,7 @@ /DataFormats/Detectors/TRD @f3sch @bazinski @wille10 /DataFormats/Detectors/Upgrades @mconcas /DataFormats/Detectors/Upgrades/ITS3 @fgrosa @arossi81 -/DataFormats/Detectors/ZDC @coppedis +/DataFormats/Detectors/ZDC @coppedis @cortesep #/DataFormats/Headers #/DataFormats/Legacy @@ -75,7 +75,7 @@ /Detectors/Upgrades @mconcas /Detectors/Upgrades/ALICE3 @mconcas @njacazio /Detectors/Upgrades/ITS3 @fgrosa @arossi81 @mconcas @f3sch -/Detectors/ZDC @coppedis +/Detectors/ZDC @coppedis @cortesep /Detectors/CTF @shahor02 /Detectors/Raw @shahor02 /Detectors/StrangenessTracking @mconcas @mpuccio @fmazzasc diff --git a/Common/Utils/src/StringUtils.cxx b/Common/Utils/src/StringUtils.cxx index 29c43ec18375b..89f834d9a8f2e 100644 --- a/Common/Utils/src/StringUtils.cxx +++ b/Common/Utils/src/StringUtils.cxx @@ -17,6 +17,7 @@ #include #endif #include +#include using namespace o2::utils; diff --git a/DataFormats/Detectors/TOF/include/DataFormatsTOF/Diagnostic.h b/DataFormats/Detectors/TOF/include/DataFormatsTOF/Diagnostic.h index 8adcdb63e9d21..028da04b3ef70 100644 --- a/DataFormats/Detectors/TOF/include/DataFormatsTOF/Diagnostic.h +++ b/DataFormats/Detectors/TOF/include/DataFormatsTOF/Diagnostic.h @@ -44,6 +44,13 @@ class Diagnostic uint32_t fillEmptyTOF(uint32_t frequency = 1) { return fill(1, frequency); } static ULong64_t getEmptyCrateKey(int crate); static ULong64_t getNoisyChannelKey(int channel); + static ULong64_t getDRMKey(int crate) { return 1000000 + crate * 1000; } + static ULong64_t getDRMerrorKey(int crate, int error) { return getDRMKey(crate) + error; } + uint32_t getFrequencyDRM(int crate) const { return getFrequency(getDRMKey(crate)); } + uint32_t getFrequencyDRMerror(int crate, int error) const { return getFrequency(getDRMerrorKey(crate, error)); } + uint32_t fillDRM(int crate, uint32_t frequency) { return fill(getDRMKey(crate), frequency); } + uint32_t fillDRMerror(int crate, int error, uint32_t frequency) { return fill(getDRMerrorKey(crate, error), frequency); } + static ULong64_t getTRMKey(int crate, int trm); void print(bool longFormat = false) const; void clear() { mVector.clear(); } diff --git a/DataFormats/Detectors/Upgrades/ALICE3/CMakeLists.txt b/DataFormats/Detectors/Upgrades/ALICE3/CMakeLists.txt index b3944c2e502d8..360b50d442d7d 100644 --- a/DataFormats/Detectors/Upgrades/ALICE3/CMakeLists.txt +++ b/DataFormats/Detectors/Upgrades/ALICE3/CMakeLists.txt @@ -10,3 +10,4 @@ # or submit itself to any jurisdiction. add_subdirectory(FD3) +add_subdirectory(TRK) diff --git a/DataFormats/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt b/DataFormats/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt new file mode 100644 index 0000000000000..c239a2a36845d --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright 2019-2026 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_library(DataFormatsTRK + SOURCES src/Cluster.cxx + src/ROFRecord.cxx + PUBLIC_LINK_LIBRARIES O2::CommonDataFormat + O2::DataFormatsITSMFT + O2::SimulationDataFormat +) + +o2_target_root_dictionary(DataFormatsTRK + HEADERS include/DataFormatsTRK/Cluster.h + include/DataFormatsTRK/ROFRecord.h + LINKDEF src/DataFormatsTRKLinkDef.h +) diff --git a/DataFormats/Detectors/Upgrades/ALICE3/TRK/include/DataFormatsTRK/Cluster.h b/DataFormats/Detectors/Upgrades/ALICE3/TRK/include/DataFormatsTRK/Cluster.h new file mode 100644 index 0000000000000..ec68191b3c43f --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/TRK/include/DataFormatsTRK/Cluster.h @@ -0,0 +1,38 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_DATAFORMATSTRK_CLUSTER_H +#define ALICEO2_DATAFORMATSTRK_CLUSTER_H + +#include +#include +#include + +namespace o2::trk +{ + +struct Cluster { + uint16_t chipID = 0; + uint16_t row = 0; + uint16_t col = 0; + uint16_t size = 1; + int16_t subDetID = -1; + int16_t layer = -1; + int16_t disk = -1; + + std::string asString() const; + + ClassDefNV(Cluster, 1); +}; + +} // namespace o2::trk + +#endif diff --git a/DataFormats/Detectors/Upgrades/ALICE3/TRK/include/DataFormatsTRK/ROFRecord.h b/DataFormats/Detectors/Upgrades/ALICE3/TRK/include/DataFormatsTRK/ROFRecord.h new file mode 100644 index 0000000000000..86ee31389fd5f --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/TRK/include/DataFormatsTRK/ROFRecord.h @@ -0,0 +1,75 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_DATAFORMATSTRK_ROFRECORD_H +#define ALICEO2_DATAFORMATSTRK_ROFRECORD_H + +#include "CommonDataFormat/InteractionRecord.h" +#include "CommonDataFormat/RangeReference.h" +#include +#include +#include + +namespace o2::trk +{ + +class ROFRecord +{ + public: + using EvIdx = o2::dataformats::RangeReference; + using BCData = o2::InteractionRecord; + using ROFtype = unsigned int; + + ROFRecord() = default; + ROFRecord(const BCData& bc, ROFtype rof, int idx, int n) + : mBCData(bc), mROFEntry(idx, n), mROFrame(rof) {} + + void setBCData(const BCData& bc) { mBCData = bc; } + void setROFrame(ROFtype rof) { mROFrame = rof; } + void setEntry(EvIdx entry) { mROFEntry = entry; } + void setFirstEntry(int idx) { mROFEntry.setFirstEntry(idx); } + void setNEntries(int n) { mROFEntry.setEntries(n); } + + const BCData& getBCData() const { return mBCData; } + BCData& getBCData() { return mBCData; } + EvIdx getEntry() const { return mROFEntry; } + EvIdx& getEntry() { return mROFEntry; } + int getNEntries() const { return mROFEntry.getEntries(); } + int getFirstEntry() const { return mROFEntry.getFirstEntry(); } + ROFtype getROFrame() const { return mROFrame; } + + std::string asString() const; + + private: + o2::InteractionRecord mBCData; + EvIdx mROFEntry; + ROFtype mROFrame = 0; + + ClassDefNV(ROFRecord, 1); +}; + +struct MC2ROFRecord { + using ROFtype = unsigned int; + + int eventRecordID = -1; + int rofRecordID = 0; + ROFtype minROF = 0; + ROFtype maxROF = 0; + + MC2ROFRecord() = default; + MC2ROFRecord(int evID, int rofRecID, ROFtype mnrof, ROFtype mxrof) : eventRecordID(evID), rofRecordID(rofRecID), minROF(mnrof), maxROF(mxrof) {} + + ClassDefNV(MC2ROFRecord, 1); +}; + +} // namespace o2::trk + +#endif diff --git a/DataFormats/Detectors/Upgrades/ALICE3/TRK/src/Cluster.cxx b/DataFormats/Detectors/Upgrades/ALICE3/TRK/src/Cluster.cxx new file mode 100644 index 0000000000000..6c96692ea5a9e --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/TRK/src/Cluster.cxx @@ -0,0 +1,28 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsTRK/Cluster.h" +#include + +ClassImp(o2::trk::Cluster); + +namespace o2::trk +{ + +std::string Cluster::asString() const +{ + std::ostringstream stream; + stream << "chip=" << chipID << " row=" << row << " col=" << col << " size=" << size + << " subDet=" << subDetID << " layer=" << layer << " disk=" << disk; + return stream.str(); +} + +} // namespace o2::trk diff --git a/DataFormats/Detectors/Upgrades/ALICE3/TRK/src/DataFormatsTRKLinkDef.h b/DataFormats/Detectors/Upgrades/ALICE3/TRK/src/DataFormatsTRKLinkDef.h new file mode 100644 index 0000000000000..36528d9dd2c46 --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/TRK/src/DataFormatsTRKLinkDef.h @@ -0,0 +1,25 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::trk::Cluster + ; +#pragma link C++ class std::vector < o2::trk::Cluster> + ; +#pragma link C++ class o2::trk::ROFRecord + ; +#pragma link C++ class std::vector < o2::trk::ROFRecord> + ; +#pragma link C++ class o2::trk::MC2ROFRecord + ; +#pragma link C++ class std::vector < o2::trk::MC2ROFRecord> + ; + +#endif diff --git a/DataFormats/Detectors/Upgrades/ALICE3/TRK/src/ROFRecord.cxx b/DataFormats/Detectors/Upgrades/ALICE3/TRK/src/ROFRecord.cxx new file mode 100644 index 0000000000000..79745f9854eb7 --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/TRK/src/ROFRecord.cxx @@ -0,0 +1,29 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsTRK/ROFRecord.h" +#include + +ClassImp(o2::trk::ROFRecord); +ClassImp(o2::trk::MC2ROFRecord); + +namespace o2::trk +{ + +std::string ROFRecord::asString() const +{ + std::ostringstream stream; + stream << "IR=" << mBCData.asString() << " ROFrame=" << mROFrame + << " first=" << mROFEntry.getFirstEntry() << " n=" << mROFEntry.getEntries(); + return stream.str(); +} + +} // namespace o2::trk diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h index 1d6c4d9f0e4ea..6389b037c3625 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h @@ -165,6 +165,8 @@ class TrackParametrization GPUd() value_t getTgl() const; GPUhd() value_t getQ2Pt() const; GPUd() value_t getCharge2Pt() const; + GPUd() value_t getR2() const; + GPUd() value_t getR() const; GPUd() int getAbsCharge() const; GPUd() PID getPID() const; GPUd() void setPID(const PID pid, bool passCharge = false); @@ -378,6 +380,20 @@ GPUdi() auto TrackParametrization::getCharge2Pt() const -> value_t return mAbsCharge ? mP[kQ2Pt] : 0.f; } +//____________________________________________________________ +template +GPUdi() auto TrackParametrization::getR2() const -> value_t +{ + return mX * mX + mP[kY] * mP[kY]; +} + +//____________________________________________________________ +template +GPUdi() auto TrackParametrization::getR() const -> value_t +{ + return gpu::CAMath::Sqrt(getR2()); +} + //____________________________________________________________ template GPUdi() int TrackParametrization::getAbsCharge() const diff --git a/Detectors/Align/src/AlignableDetectorTRD.cxx b/Detectors/Align/src/AlignableDetectorTRD.cxx index 080d0f72b2516..6fe8a60ef90f6 100644 --- a/Detectors/Align/src/AlignableDetectorTRD.cxx +++ b/Detectors/Align/src/AlignableDetectorTRD.cxx @@ -178,9 +178,7 @@ int AlignableDetectorTRD::processPoints(GIndex gid, int npntCut, bool inv) auto propagator = o2::base::Propagator::Instance(); // float version! static bool firstCall = true; if (firstCall) { - o2::gpu::GPUO2InterfaceConfiguration config; - config.ReadConfigurableParam(config); - mRecoParam.init(propagator->getNominalBz(), &config.configReconstruction); + mRecoParam.init(propagator->getNominalBz()); firstCall = false; } const auto* transformer = mController->getTRDTransformer(); diff --git a/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx index 5cef707da2cca..38d634c20c828 100644 --- a/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx +++ b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx @@ -35,6 +35,7 @@ o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext co outputs, AlgorithmSpec(adaptFromTask(ccdbRequest)), Options{ + {"save-to-file", VariantType::Bool, false, {"Save calibration object to local file"}}, {"slot-len-sec", VariantType::UInt32, 3600u, {"Duration of each slot in seconds"}}, {"one-object-per-run", VariantType::Bool, false, {"If set, workflow creates only one calibration object per run"}}, {"min-entries-number", VariantType::UInt32, 5000u, {"Minimum number of entries required for a slot to be valid"}}, @@ -45,4 +46,4 @@ o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext co WorkflowSpec workflow; workflow.emplace_back(dataProcessorSpec); return workflow; -} \ No newline at end of file +} diff --git a/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h index d493e2a606613..1d4d4a75842e8 100644 --- a/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h +++ b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h @@ -35,6 +35,8 @@ class FT0EventsPerBcProcessor final : public o2::framework::Task void init(o2::framework::InitContext& ic) final { o2::base::GRPGeomHelper::instance().setRequest(mCCDBRequest); + mSaveToFile = ic.options().get("save-to-file"); + if (ic.options().hasOption("slot-len-sec")) { mSlotLenSec = ic.options().get("slot-len-sec"); } @@ -73,6 +75,10 @@ class FT0EventsPerBcProcessor final : public o2::framework::Task void run(o2::framework::ProcessingContext& pc) final { + const auto& tinfo = pc.services().get(); + if (tinfo.globalRunNumberChanged || mRunNoFromDH < 1) { // new run is starting + mRunNoFromDH = tinfo.runNumber; + } o2::base::GRPGeomHelper::instance().checkUpdates(pc); auto digits = pc.inputs().get>("digits"); o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mCalibrator->getCurrentTFInfo()); @@ -107,6 +113,18 @@ class FT0EventsPerBcProcessor final : public o2::framework::Task << " bytes, valid for " << info->getStartValidityTimestamp() << " : " << info->getEndValidityTimestamp(); output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBPayload, "EventsPerBc", idx}, *image.get()); output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBWrapper, "EventsPerBc", idx}, *info.get()); + if (mSaveToFile) { + std::string fnout = fmt::format("ft0eventsPerBC_run_{}_{}_{}.root", mRunNoFromDH, info->getStartValidityTimestamp(), info->getEndValidityTimestamp()); + try { + TFile flout(fnout.c_str(), "recreate"); + flout.WriteObjectAny(&payload, "o2::ft0::EventsPerBc", o2::ccdb::CcdbApi::CCDBOBJECT_ENTRY); + LOGP(info, R"(Saved to file, can upload as: o2-ccdb-upload -f {} --starttimestamp {} --endtimestamp {} -k "ccdb_object" --path {} -m "runNumber={};AdjustableEOV=true;")", + fnout, info->getStartValidityTimestamp(), info->getEndValidityTimestamp(), info->getPath(), mRunNoFromDH); + flout.Close(); + } catch (const std::exception& ex) { + LOGP(error, "failed to store object to file {}, error: {}", fnout, ex.what()); + } + } } if (tvxHists.size()) { @@ -118,6 +136,8 @@ class FT0EventsPerBcProcessor final : public o2::framework::Task std::shared_ptr mCCDBRequest; std::unique_ptr mCalibrator; bool mOneObjectPerRun; + bool mSaveToFile = false; + int mRunNoFromDH = 0; uint32_t mSlotLenSec; uint32_t mMinNumberOfEntries; int32_t mMinAmplitudeSideA; @@ -125,4 +145,4 @@ class FT0EventsPerBcProcessor final : public o2::framework::Task int32_t mMinSumOfAmplitude; }; } // namespace o2::calibration -#endif \ No newline at end of file +#endif diff --git a/Detectors/GlobalTracking/src/MatchCosmics.cxx b/Detectors/GlobalTracking/src/MatchCosmics.cxx index 90964fb1c05fa..3c20ecad2f36c 100644 --- a/Detectors/GlobalTracking/src/MatchCosmics.cxx +++ b/Detectors/GlobalTracking/src/MatchCosmics.cxx @@ -96,7 +96,6 @@ void MatchCosmics::refitWinners(const o2::globaltracking::RecoContainer& data) mTPCCorrMapsHelper, mBz, tpcClusRefs.data(), 0, tpcClusShMap.data(), tpcClusOccMap.data(), tpcClusOccMap.size(), nullptr, o2::base::Propagator::Instance()); - tpcRefitter->setTrackReferenceX(900); // disable propagation after refit by setting reference to value > 500 } const auto& itsClusters = prepareITSClusters(data); diff --git a/Detectors/GlobalTracking/src/MatchTPCITS.cxx b/Detectors/GlobalTracking/src/MatchTPCITS.cxx index 5f99ad2202073..73216c8ce1eac 100644 --- a/Detectors/GlobalTracking/src/MatchTPCITS.cxx +++ b/Detectors/GlobalTracking/src/MatchTPCITS.cxx @@ -505,7 +505,6 @@ bool MatchTPCITS::prepareTPCData() } mTPCRefitter = std::make_unique(mTPCClusterIdxStruct, mTPCCorrMapsHelper, mBz, mTPCTrackClusIdx.data(), 0, mTPCRefitterShMap.data(), mTPCRefitterOccMap.data(), mTPCRefitterOccMap.size(), nullptr, o2::base::Propagator::Instance()); - mTPCRefitter->setTrackReferenceX(900); // disable propagation after refit by setting reference to value > 500 mNTPCOccBinLength = mTPCRefitter->getParam()->rec.tpc.occupancyMapTimeBins; mTBinClOcc.clear(); if (mNTPCOccBinLength > 1 && mTPCRefitterOccMap.size()) { diff --git a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx index 1b55f9c763e7f..6dfd1cb770d7f 100644 --- a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx @@ -60,9 +60,11 @@ class SecondaryVertexingSpec : public Task public: SecondaryVertexingSpec(std::shared_ptr dr, std::shared_ptr gr, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, GTrackID::mask_t src, bool enabCasc, bool enable3body, bool enableStrangenessTracking, bool enableCCDBParams, bool useMC) : mDataRequest(dr), mGGCCDBRequest(gr), mSrc(src), mEnableCascades(enabCasc), mEnable3BodyVertices(enable3body), mEnableStrangenessTracking(enableStrangenessTracking), mEnableCCDBParams(enableCCDBParams), mUseMC(useMC) { - mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); - mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); - mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); + if (mSrc[GTrackID::TPC]) { + mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); + mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); + } } ~SecondaryVertexingSpec() override = default; void init(InitContext& ic) final; diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx index 05e6a122adec9..ee475acbbcf70 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx @@ -188,7 +188,6 @@ void TPCTrackStudySpec::process(o2::globaltracking::RecoContainer& recoData) if (mTPCTracksArray.size()) { LOGP(info, "Found {} TPC tracks", mTPCTracksArray.size()); mTPCRefitter = std::make_unique(mTPCClusterIdxStruct, &mTPCCorrMapsLoader, prop->getNominalBz(), mTPCTrackClusIdx.data(), 0, mTPCRefitterShMap.data(), mTPCRefitterOccMap.data(), mTPCRefitterOccMap.size(), nullptr, o2::base::Propagator::Instance()); - mTPCRefitter->setTrackReferenceX(900); // disable propagation after refit by setting reference to value > 500 } float vdriftTB = mTPCVDriftHelper.getVDriftObject().getVDrift() * o2::tpc::ParameterElectronics::Instance().ZbinWidth; // VDrift expressed in cm/TimeBin float tpcTBBias = mTPCVDriftHelper.getVDriftObject().getTimeOffset() / (8 * o2::constants::lhc::LHCBunchSpacingMUS); diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx index b8a8f97737b4d..c68e60059cd3f 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx @@ -163,7 +163,6 @@ void TrackingStudySpec::run(ProcessingContext& pc) mTPCRefitter = std::make_unique(&recoData.inputsTPCclusters->clusterIndex, &mTPCCorrMapsLoader, o2::base::Propagator::Instance()->getNominalBz(), recoData.getTPCTracksClusterRefs().data(), 0, recoData.clusterShMapTPC.data(), recoData.occupancyMapTPC.data(), recoData.occupancyMapTPC.size(), nullptr, o2::base::Propagator::Instance()); - mTPCRefitter->setTrackReferenceX(900); // disable propagation after refit by setting reference to value > 500 mNTPCOccBinLength = mTPCRefitter->getParam()->rec.tpc.occupancyMapTimeBins; mTBinClOccBef.clear(); mTBinClOccAft.clear(); diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index eacf514c7a91d..353464e10712d 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -143,8 +143,9 @@ GPUdii() o2::track::TrackParCov buildTrackSeed(const Cluster& cluster1, float snp, q2pt, q2pt2; if (o2::gpu::CAMath::Abs(bz) < 0.01f) { - const float tgp = o2::gpu::CAMath::ATan2(y3 - y1, x3 - x1); - snp = sign * tgp / o2::gpu::CAMath::Sqrt(1.f + tgp * tgp); + const float dx = x3 - x1; + const float dy = y3 - y1; + snp = sign * dy / o2::gpu::CAMath::Hypot(dx, dy); q2pt = sign / track::kMostProbablePt; q2pt2 = 1.f; } else { diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx index b4ac847863d51..d46db96339495 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx @@ -1278,8 +1278,9 @@ track::TrackParCov TrackerTraits::buildTrackSeed(const Cluster& cluster float snp, q2pt, q2pt2; if (mIsZeroField) { - const float tgp = o2::gpu::CAMath::ATan2(y3 - y1, x3 - x1); - snp = sign * tgp / o2::gpu::CAMath::Sqrt(1.f + tgp * tgp); + const float dx = x3 - x1; + const float dy = y3 - y1; + snp = sign * dy / o2::gpu::CAMath::Hypot(dx, dy); q2pt = sign / track::kMostProbablePt; q2pt2 = 1.f; } else { diff --git a/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx b/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx index 8de29c62335b6..fc0dd5dbae7da 100644 --- a/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx @@ -165,12 +165,12 @@ void ClustererDPL::run(ProcessingContext& pc) } } } - int prevFirst{0}; + int prevLast{0}; for (auto& rof : expClusRofVec) { if (rof.getFirstEntry() < 0) { - rof.setFirstEntry(prevFirst); + rof.setFirstEntry(prevLast); } - prevFirst = rof.getFirstEntry(); + prevLast = rof.getFirstEntry() + rof.getNEntries(); } nROFs = expClusRofVec.size(); pc.outputs().snapshot(Output{Origin, "CLUSTERSROF", iLayer}, expClusRofVec); diff --git a/Detectors/TOF/base/include/TOFBase/CalibTOFapi.h b/Detectors/TOF/base/include/TOFBase/CalibTOFapi.h index c3d39d3e978e1..6fb87e72abf62 100644 --- a/Detectors/TOF/base/include/TOFBase/CalibTOFapi.h +++ b/Detectors/TOF/base/include/TOFBase/CalibTOFapi.h @@ -23,6 +23,9 @@ #include "TOFBase/Geo.h" #include "DataFormatsTOF/Diagnostic.h" #include "DataFormatsTOF/TOFFEElightInfo.h" +#include "DataFormatsTOF/CompressedDataFormat.h" + +class TH2F; namespace o2 { @@ -38,10 +41,12 @@ class CalibTOFapi using CcdbApi = o2::ccdb::CcdbApi; public: + static o2::tof::Diagnostic doDRMerrCalibFromQCHisto(const TH2F* histo, const char* file_output_name); + void resetDia(); CalibTOFapi() = default; CalibTOFapi(const std::string url); - CalibTOFapi(long timestamp, o2::dataformats::CalibLHCphaseTOF* phase, o2::dataformats::CalibTimeSlewingParamTOF* slew, Diagnostic* dia = nullptr) : mTimeStamp(timestamp), mLHCphase(phase), mSlewParam(slew), mDiaFreq(dia) {} + CalibTOFapi(long timestamp, o2::dataformats::CalibLHCphaseTOF* phase, o2::dataformats::CalibTimeSlewingParamTOF* slew, Diagnostic* dia = nullptr, Diagnostic* diaDRM = nullptr) : mTimeStamp(timestamp), mLHCphase(phase), mSlewParam(slew), mDiaFreq(dia), mDiaDRMFreq(diaDRM) {} ~CalibTOFapi() { if (mLHCphase) { @@ -53,6 +58,9 @@ class CalibTOFapi if (mDiaFreq) { // delete mDiaFreq; } + if (mDiaDRMFreq) { + // delete mDiaDRMFreq; + } } void setTimeStamp(long t) @@ -69,6 +77,8 @@ class CalibTOFapi void readTimeSlewingParamFromFile(const char* filename); void readDiagnosticFrequencies(); void loadDiagnosticFrequencies(); + void readDiagnosticDRMFrequencies(); + void loadDiagnosticDRMFrequencies(); void readActiveMap(); void loadActiveMap(TOFFEElightInfo* fee); void writeLHCphase(LhcPhase* phase, std::map metadataLHCphase, uint64_t minTimeSTamp, uint64_t maxTimeStamp); @@ -89,6 +99,8 @@ class CalibTOFapi void setLhcPhase(LhcPhase* obj) { mLHCphase = obj; } Diagnostic* getDiagnostic() { return mDiaFreq; } void setDiagnostic(Diagnostic* obj) { mDiaFreq = obj; } + Diagnostic* getDiagnosticDRM() { return mDiaDRMFreq; } + void setDiagnosticDRM(Diagnostic* obj) { mDiaDRMFreq = obj; } int getNoisyThreshold() const { return mNoisyThreshold; } void setNoisyThreshold(int val) { mNoisyThreshold = val; } @@ -102,12 +114,39 @@ class CalibTOFapi void processError(int crate, int trm, int mask); bool isChannelError(int channel) const; bool checkTRMPolicy(int mask) const; + void resetDRMErrors(); + void processErrorDRM(int crate, int codeErr); + bool isChannelDRMError(int channel) const; + bool checkDRMPolicy(int mask) const; + + void setDRMCriticalErrorMask(uint32_t val) { mDRMCriticalErrorMask = val; } + uint32_t getDRMCriticalErrorMask() const { return mDRMCriticalErrorMask; } + float getDRMprobError(int crate, int type) const { return mErrorInDRM[crate][type]; } + + // DRM error codes inherited by EDRMDiagnostic_t in CompressedDataFormat.h (shifted by 4 bits) + static const int DRM_ERRINDEX_SHIFT = 4; + enum DRMcodes { + DRM_HEADER_MISSING = o2::tof::diagnostic::DRM_HEADER_MISSING >> DRM_ERRINDEX_SHIFT, + DRM_TRAILER_MISSING = o2::tof::diagnostic::DRM_TRAILER_MISSING >> DRM_ERRINDEX_SHIFT, + DRM_FEEID_MISMATCH = o2::tof::diagnostic::DRM_FEEID_MISMATCH >> DRM_ERRINDEX_SHIFT, + DRM_ORBIT_MISMATCH = o2::tof::diagnostic::DRM_ORBIT_MISMATCH >> DRM_ERRINDEX_SHIFT, + DRM_CRC_MISMATCH = o2::tof::diagnostic::DRM_CRC_MISMATCH >> DRM_ERRINDEX_SHIFT, + DRM_ENAPARTMASK_DIFFER = o2::tof::diagnostic::DRM_ENAPARTMASK_DIFFER >> DRM_ERRINDEX_SHIFT, + DRM_CLOCKSTATUS_WRONG = o2::tof::diagnostic::DRM_CLOCKSTATUS_WRONG >> DRM_ERRINDEX_SHIFT, + DRM_FAULTSLOTMASK_NOTZERO = o2::tof::diagnostic::DRM_FAULTSLOTMASK_NOTZERO >> DRM_ERRINDEX_SHIFT, + DRM_READOUTTIMEOUT_NOTZERO = o2::tof::diagnostic::DRM_READOUTTIMEOUT_NOTZERO >> DRM_ERRINDEX_SHIFT, + DRM_EVENTWORDS_MISMATCH = o2::tof::diagnostic::DRM_EVENTWORDS_MISMATCH >> DRM_ERRINDEX_SHIFT, + DRM_DIAGNOSTIC_SPARE1 = o2::tof::diagnostic::DRM_DIAGNOSTIC_SPARE1 >> DRM_ERRINDEX_SHIFT, + DRM_DECODE_ERROR = o2::tof::diagnostic::DRM_DECODE_ERROR >> DRM_ERRINDEX_SHIFT, + N_DRM_ERRORS = 12 + }; private: - long mTimeStamp; ///< timeStamp for queries - LhcPhase* mLHCphase = nullptr; ///< object for LHC phase - SlewParam* mSlewParam = nullptr; ///< object for timeslewing (containing info also for offset and problematic) - Diagnostic* mDiaFreq = nullptr; ///< object for Diagnostic Frequency + long mTimeStamp; ///< timeStamp for queries + LhcPhase* mLHCphase = nullptr; ///< object for LHC phase + SlewParam* mSlewParam = nullptr; ///< object for timeslewing (containing info also for offset and problematic) + Diagnostic* mDiaFreq = nullptr; ///< object for Diagnostic Frequency + Diagnostic* mDiaDRMFreq = nullptr; ///< object for Diagnostic Frequency // info from diagnostic int mNoisyThreshold = 1; ///< threshold to be noisy @@ -116,13 +155,17 @@ class CalibTOFapi std::vector> mNoisy; ///< probTRMerror std::vector> mTRMerrorProb; ///< probTRMerror std::vector mTRMmask; ///< mask error for TRM + float mErrorInDRM[Geo::kNCrate][N_DRM_ERRORS] = {}; ///< probability of DRM error + uint32_t mDRMCriticalErrorMask = 0; ///< bit mask for critical DRM errors (e.g. Orbit Mismatch -> 1 << 7, see DataFormats/Detectors/TOF/include/DataFormatsTOF/CompressedDataFormat.h) - bool mIsErrorCh[Geo::NCHANNELS] = {}; ///< channels in error (TRM) - std::vector mFillErrChannel; ///< last error channels filled - bool mIsOffCh[Geo::NCHANNELS] = {}; ///< channels in error (TRM) - bool mIsNoisy[Geo::NCHANNELS] = {}; ///< noisy channels + bool mIsErrorCh[Geo::NCHANNELS] = {}; ///< channels in error (TRM) + std::vector mFillErrChannel; ///< last error channels filled + bool mIsOffCh[Geo::NCHANNELS] = {}; ///< channels in error (TRM) + bool mIsNoisy[Geo::NCHANNELS] = {}; ///< noisy channels + bool mIsErrorDRMCh[Geo::NCHANNELS] = {}; ///< channels in error (DRM) + std::vector mFillErrDRMChannel; ///< last error channels filled - ClassDefNV(CalibTOFapi, 1); + ClassDefNV(CalibTOFapi, 2); }; } // namespace tof } // namespace o2 diff --git a/Detectors/TOF/base/include/TOFBase/Digit.h b/Detectors/TOF/base/include/TOFBase/Digit.h index eef03ef84b97c..afa5662044a3e 100644 --- a/Detectors/TOF/base/include/TOFBase/Digit.h +++ b/Detectors/TOF/base/include/TOFBase/Digit.h @@ -101,13 +101,13 @@ class Digit private: friend class boost::serialization::access; - Int_t mChannel; ///< TOF channel index - uint16_t mTDC; ///< TDC bin number - uint16_t mTOT; ///< TOT bin number - InteractionRecord mIR{0, 0}; ///< InteractionRecord (orbit and bc) when digit occurs - Int_t mLabel; ///< Index of the corresponding entry in the MC label array - Double_t mCalibratedTime; //!< time of the digits after calibration (not persistent; it will be filled during clusterization) - Int_t mElectronIndex; //!/< index in electronic format + Int_t mChannel; ///< TOF channel index + uint16_t mTDC; ///< TDC bin number + uint16_t mTOT; ///< TOT bin number + InteractionRecord mIR{0, 0}; ///< InteractionRecord (orbit and bc) when digit occurs + Int_t mLabel; ///< Index of the corresponding entry in the MC label array + Double_t mCalibratedTime; //!< time of the digits after calibration (not persistent; it will be filled during clusterization) + Int_t mElectronIndex; //!/< index in electronic format uint32_t mTriggerOrbit = 0; //!< orbit id of trigger event // RS: orbit must be 32bits long uint16_t mTriggerBunch = 0; //!< bunch id of trigger event Bool_t mIsUsedInCluster; //!/< flag to declare that the digit was used to build a cluster diff --git a/Detectors/TOF/base/src/CalibTOFapi.cxx b/Detectors/TOF/base/src/CalibTOFapi.cxx index 281498990a9dd..fdc028bde536c 100644 --- a/Detectors/TOF/base/src/CalibTOFapi.cxx +++ b/Detectors/TOF/base/src/CalibTOFapi.cxx @@ -11,11 +11,40 @@ #include "TOFBase/CalibTOFapi.h" #include // for LOG +#include using namespace o2::tof; ClassImp(o2::tof::CalibTOFapi); +o2::tof::Diagnostic CalibTOFapi::doDRMerrCalibFromQCHisto(const TH2F* histo, const char* file_output_name) +{ + // this is a method which translate the QC output in qc/TOF/MO/TaskRaw/DRMCounter (TH2F) into a Diagnotic object for DRM (patter(crate, error), frequency) + // note that, differently from TRM errors, DRM ones are not stored in CTF by design (since very rare, as expected). Such an info is available only at the level of raw sync QC + o2::tof::Diagnostic drmDia; + + for (int j = 1; j <= Geo::kNCrate; j++) { + drmDia.fillDRM(j - 1, histo->GetBinContent(1, j)); + for (int i = 2; i <= histo->GetXaxis()->GetNbins(); i++) { + if (histo->GetBinContent(1, j)) { + if (histo->GetBinContent(i, j) > 0) { + drmDia.fillDRMerror(j - 1, i - 1, histo->GetBinContent(i, j)); + } + } + } + } + + TFile* fo = new TFile(file_output_name, "RECREATE"); + fo->WriteObjectAny(&drmDia, drmDia.Class_Name(), "ccdb_object"); + fo->Close(); + LOG(debug) << "DRM error ccdb object created in " << file_output_name << " with this content"; + drmDia.print(true); + + return drmDia; +} + +//______________________________________________________________________ + void CalibTOFapi::resetDia() { memset(mEmptyCrateProb, 0., Geo::kNCrate * 4); @@ -38,7 +67,7 @@ void CalibTOFapi::readActiveMap() { auto& mgr = CcdbManager::instance(); long timems = long(mTimeStamp) * 1000; - LOG(info) << "TOF get active map with timestamp (ms) = " << timems; + LOG(debug) << "TOF get active map with timestamp (ms) = " << timems; auto fee = mgr.getForTimeStamp("TOF/Calib/FEELIGHT", timems); loadActiveMap(fee); } @@ -116,11 +145,23 @@ void CalibTOFapi::readDiagnosticFrequencies() { auto& mgr = CcdbManager::instance(); long timems = long(mTimeStamp) * 1000; - LOG(info) << "TOF get Diagnostics with timestamp (ms) = " << timems; + LOG(info) << "TOF get TRM Diagnostics with timestamp (ms) = " << timems; mDiaFreq = mgr.getForTimeStamp("TOF/Calib/Diagnostic", timems); loadDiagnosticFrequencies(); } + +//______________________________________________________________________ + +void CalibTOFapi::readDiagnosticDRMFrequencies() +{ + auto& mgr = CcdbManager::instance(); + long timems = long(mTimeStamp) * 1000; + LOG(info) << "TOF get DRM Diagnostics with timestamp (ms) = " << timems; + mDiaFreq = mgr.getForTimeStamp("TOF/Calib/TRMerrors", timems); + + loadDiagnosticDRMFrequencies(); +} //______________________________________________________________________ void CalibTOFapi::loadDiagnosticFrequencies() @@ -210,6 +251,37 @@ void CalibTOFapi::loadDiagnosticFrequencies() //______________________________________________________________________ +void CalibTOFapi::loadDiagnosticDRMFrequencies() +{ + mDiaDRMFreq->print(); + + for (int ic = 0; ic < Geo::kNCrate; ic++) { // loop over crates + float DRMcounters = mDiaDRMFreq->getFrequencyDRM(ic); + + if (DRMcounters < 1) { + for (int ie = 0; ie < N_DRM_ERRORS; ie++) { + mErrorInDRM[ic][ie] = 0.; + } + continue; + } + + for (int ie = 0; ie < N_DRM_ERRORS; ie++) { // loop over error types + int ie_shifted = ie + DRM_ERRINDEX_SHIFT; + + float frequency = mDiaDRMFreq->getFrequencyDRMerror(ic, ie_shifted) * 1. / DRMcounters; // error frequency + if (frequency > 1) { + frequency = 1.; + } + if (frequency > 1E-6) { + LOG(debug) << "DRMmap: Crate = " << ic << " - error = " << ie << " - frequency = " << frequency; + } + mErrorInDRM[ic][ie] = frequency; + } + } +} + +//______________________________________________________________________ + void CalibTOFapi::writeLHCphase(LhcPhase* phase, std::map metadataLHCphase, uint64_t minTimeStamp, uint64_t maxTimeStamp) { @@ -330,6 +402,17 @@ void CalibTOFapi::resetTRMErrors() //______________________________________________________________________ +void CalibTOFapi::resetDRMErrors() +{ + for (auto index : mFillErrDRMChannel) { + mIsErrorDRMCh[index] = false; + } + + mFillErrDRMChannel.clear(); +} + +//______________________________________________________________________ + void CalibTOFapi::processError(int crate, int trm, int mask) { if (checkTRMPolicy(mask)) { // check the policy of TRM -> true=good TRM @@ -348,6 +431,32 @@ void CalibTOFapi::processError(int crate, int trm, int mask) //______________________________________________________________________ +void CalibTOFapi::processErrorDRM(int crate, int codeErr) +{ + int mask = 1 << codeErr; + + if (checkDRMPolicy(mask)) { + return; + } + + LOG(debug) << "DRMmask: crate = " << crate << " - mask = " << mask << " - critical mask = " << mDRMCriticalErrorMask; + + for (int trm = 3; trm < 13; trm++) { + int ech = (crate << 12) + ((trm - 3) << 8); + for (int i = ech; i < ech + 256; i++) { + int channel = Geo::getCHFromECH(i); + if (channel == -1 || mIsErrorDRMCh[channel] == true) { + continue; + } + + mIsErrorDRMCh[channel] = true; + mFillErrDRMChannel.push_back(channel); + } + } +} + +//______________________________________________________________________ + bool CalibTOFapi::checkTRMPolicy(int mask) const { return false; @@ -355,7 +464,25 @@ bool CalibTOFapi::checkTRMPolicy(int mask) const //______________________________________________________________________ +bool CalibTOFapi::checkDRMPolicy(int mask) const +{ + return !(mDRMCriticalErrorMask & mask); +} + +//______________________________________________________________________ + bool CalibTOFapi::isChannelError(int channel) const { return mIsErrorCh[channel]; } + +//______________________________________________________________________ + +bool CalibTOFapi::isChannelDRMError(int channel) const +{ + if (mIsErrorDRMCh[channel]) { + int detId[5]; + o2::tof::Geo::getVolumeIndices(channel, detId); + } + return mIsErrorDRMCh[channel]; +} diff --git a/Detectors/TOF/prototyping/CMakeLists.txt b/Detectors/TOF/prototyping/CMakeLists.txt index 1ce2268f1358a..7dfc9f8cb7361 100644 --- a/Detectors/TOF/prototyping/CMakeLists.txt +++ b/Detectors/TOF/prototyping/CMakeLists.txt @@ -32,6 +32,16 @@ o2_add_test_root_macro(findLabels.C O2::TOFBase LABELS tof) +o2_add_test_root_macro(makeDRMobj_tof.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsTOF + O2::TOFBase + LABELS tof) + +o2_add_test_root_macro(checkDRMobj_tof.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsTOF + O2::TOFBase + LABELS tof) + o2_add_test_root_macro(findTOFclusterFromLabel.C PUBLIC_LINK_LIBRARIES O2::DataFormatsTOF O2::SimulationDataFormat @@ -59,3 +69,8 @@ o2_add_test_root_macro(macroEvTime.C PUBLIC_LINK_LIBRARIES O2::TOFBase O2::TOFReconstruction LABELS tof) + +install( + FILES makeDRMobj_tof.C + checkDRMobj_tof.C + DESTINATION share/macro/) diff --git a/Detectors/TOF/prototyping/checkDRMobj_tof.C b/Detectors/TOF/prototyping/checkDRMobj_tof.C new file mode 100644 index 0000000000000..81381852b15df --- /dev/null +++ b/Detectors/TOF/prototyping/checkDRMobj_tof.C @@ -0,0 +1,42 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "TFile.h" +#include "TH2F.h" +#include "TCanvas.h" +#include "TOFBase/CalibTOFapi.h" +#endif + +void checkDRMobj_tof(const char* fname = "ccdb.root") +{ + TFile* f = new TFile(fname); + + TH2F* hErrors = new TH2F("hDRMerrors", ";error code; frequency", 30, 0, 30, 72, 0, 72); + + o2::tof::Diagnostic* drmDia = (o2::tof::Diagnostic*)f->Get("ccdb_object"); + + for (int j = 1; j <= 72; j++) { + uint32_t patternRDH = o2::tof::Diagnostic::getDRMKey(j - 1); + for (int i = 1; i <= hErrors->GetXaxis()->GetNbins(); i++) { + uint32_t pattern = o2::tof::Diagnostic::getDRMerrorKey(j - 1, i - 1); + if (drmDia->getFrequency(patternRDH)) { + hErrors->SetBinContent(i, j, drmDia->getFrequency(pattern) * 1. / drmDia->getFrequency(patternRDH)); + } + } + } + + TCanvas* c = new TCanvas(); + c->cd(1); + hErrors->Draw("colz"); + + drmDia->print(true); +} diff --git a/Detectors/TOF/prototyping/makeDRMobj_tof.C b/Detectors/TOF/prototyping/makeDRMobj_tof.C new file mode 100644 index 0000000000000..2ae79d501b369 --- /dev/null +++ b/Detectors/TOF/prototyping/makeDRMobj_tof.C @@ -0,0 +1,45 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "TFile.h" +#include "TH2F.h" +#include "TOFBase/CalibTOFapi.h" +#endif + +void makeDRMobj_tof(const char* inputfile = "TObject_1764607157510.root", bool dummy = false) +{ + if (dummy) { + o2::tof::Diagnostic drmDia; + for (int j = 1; j <= 72; j++) { + drmDia.fill(o2::tof::Diagnostic::getDRMKey(j - 1)); + } + + TFile* fo = new TFile("ccdb.root", "RECREATE"); + fo->WriteObjectAny(&drmDia, drmDia.Class_Name(), "ccdb_object"); + fo->Close(); + + return; + } + + TFile* f = new TFile(inputfile); + TH2F* h = (TH2F*)f->Get("ccdb_object"); + + float maxVal = h->GetBinContent(h->GetMaximumBin()); + + if (maxVal > 1E6) { // to avoid to pass value causing overflow + h->Scale(1E6 / maxVal); + } + + o2::tof::Diagnostic drmDia; + + drmDia = o2::tof::CalibTOFapi::doDRMerrCalibFromQCHisto(h, "ccdb.root"); +} diff --git a/Detectors/TOF/reconstruction/src/Clusterer.cxx b/Detectors/TOF/reconstruction/src/Clusterer.cxx index 0b393bfd45e78..dbfa472ce4112 100644 --- a/Detectors/TOF/reconstruction/src/Clusterer.cxx +++ b/Detectors/TOF/reconstruction/src/Clusterer.cxx @@ -54,11 +54,9 @@ void Clusterer::calibrateStrip() // LOG(info) << "channel = " << dig->getChannel(); dig->setBC(dig->getBC() - mBCOffset); // RS Don't use raw BC, always start from the beginning of the TF double calib = mCalibApi->getTimeCalibration(dig->getChannel(), dig->getTOT() * Geo::TOTBIN_NS); - //printf("channel %d) isProblematic = %d, fractionUnderPeak = %f\n",dig->getChannel(),mCalibApi->isProblematic(dig->getChannel()),mCalibApi->getFractionUnderPeak(dig->getChannel())); // toberem bool isProbOrError = mAreCalibStored ? mCalibApi->isChannelError(dig->getChannel()) || mCalibApi->isNoisy(dig->getChannel()) : mCalibApi->isChannelError(dig->getChannel()) || mCalibApi->isNoisy(dig->getChannel()) || mCalibApi->isProblematic(dig->getChannel()); dig->setIsProblematic(isProbOrError); - dig->setCalibratedTime(dig->getTDC() * Geo::TDCBIN + dig->getBC() * o2::constants::lhc::LHCBunchSpacingNS * 1E3 - Geo::LATENCYWINDOW * 1E3 - calib); //TODO: to be checked that "-" is correct, and we did not need "+" instead :-) - //printf("calibration correction = %f\n",calib); // toberem + dig->setCalibratedTime(dig->getTDC() * Geo::TDCBIN + dig->getBC() * o2::constants::lhc::LHCBunchSpacingNS * 1E3 - Geo::LATENCYWINDOW * 1E3 - calib); // TODO: to be checked that "-" is correct, and we did not need "+" instead :-) } } diff --git a/Detectors/TOF/simulation/include/TOFSimulation/Digitizer.h b/Detectors/TOF/simulation/include/TOFSimulation/Digitizer.h index 5153f168f176f..4e369cecf6e26 100644 --- a/Detectors/TOF/simulation/include/TOFSimulation/Digitizer.h +++ b/Detectors/TOF/simulation/include/TOFSimulation/Digitizer.h @@ -148,7 +148,10 @@ class Digitizer : public WindowFiller float mTotLastHit[10]; Int_t mXLastShift[10]; Int_t mZLastShift[10]; - ClassDefNV(Digitizer, 1); + + float mIsCrateRDHerr[Geo::kNCrate]; + + ClassDefNV(Digitizer, 2); }; } // namespace tof } // namespace o2 diff --git a/Detectors/TOF/simulation/src/Detector.cxx b/Detectors/TOF/simulation/src/Detector.cxx index 2bfb76613dae5..97d5e03851291 100644 --- a/Detectors/TOF/simulation/src/Detector.cxx +++ b/Detectors/TOF/simulation/src/Detector.cxx @@ -637,7 +637,6 @@ void Detector::makeStripsInModules(Float_t ytof, Float_t zlenA) const // Define MRPC strip volume, called FSTR // Insert FSTR volume in FLTA/B/C volumes // - // ciao Float_t yFLT = ytof * 0.5 - Geo::MODULEWALLTHICKNESS; ///////////////// Detector itself ////////////////////// diff --git a/Detectors/TOF/simulation/src/Digitizer.cxx b/Detectors/TOF/simulation/src/Digitizer.cxx index ec899bd35fbff..e2c4fdcca9abe 100644 --- a/Detectors/TOF/simulation/src/Digitizer.cxx +++ b/Detectors/TOF/simulation/src/Digitizer.cxx @@ -95,7 +95,7 @@ int Digitizer::process(const std::vector* hits, std::vector* dig const double max_hit_time = TOFSimParams::Instance().max_hit_time; // hits array of TOF hits for a given simulated event - // digits passed from external to be filled, in continuous readout mode we will push it on mDigitsPerTimeFrame vector of vectors of digits + // digits passed from external to be filled, in continuous readout mode we will push it on mDigitsPerTimeFrame, final vector of digits // printf("process event time = %f with %ld hits\n",mEventTime.getTimeNS(),hits->size()); @@ -891,6 +891,7 @@ void Digitizer::fillOutputContainer(std::vector& digits) // fill diagnostics mCalibApi->resetTRMErrors(); + mCalibApi->resetDRMErrors(); float p = gRandom->Rndm(); if (mCalibApi->getEmptyTOFProb() > p) { // check empty TOF for (int i = 0; i < Geo::kNCrate; i++) { @@ -906,6 +907,14 @@ void Digitizer::fillOutputContainer(std::vector& digits) info.setEmptyCrate(i); isEmptyCrate[i] = true; } else { // check if filling diagnostic (noisy will be masked in clusterization, then skip here) + // Fill DRM RDH errors + for (int ie = 0; ie < mCalibApi->N_DRM_ERRORS; ie++) { + p = gRandom->Rndm(); + if (mCalibApi->getDRMprobError(i, ie) > p) { + mCalibApi->processErrorDRM(i, ie); + } + } + isEmptyCrate[i] = false; int slotreached = -1; const std::vector>& trmProg = mCalibApi->getTRMerrorProb(); @@ -955,7 +964,7 @@ void Digitizer::fillOutputContainer(std::vector& digits) for (auto [key, dig] : dmap) { int crate = Geo::getCrateFromECH(Geo::getECHFromCH(dig.getChannel())); - if (isEmptyCrate[crate] || mCalibApi->isChannelError(dig.getChannel())) { + if (isEmptyCrate[crate] || mCalibApi->isChannelError(dig.getChannel()) || mCalibApi->isChannelDRMError(dig.getChannel())) { // flag digits to be removed keyToBeRemoved.push_back(key); } diff --git a/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx index cd5e3960160a6..539ae25862865 100644 --- a/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx +++ b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx @@ -126,9 +126,7 @@ void TrackInterpolation::init(o2::dataformats::GlobalTrackID::mask_t src, o2::da mFastTransform = std::move(TPCFastTransformHelperO2::instance()->create(0)); mBz = o2::base::Propagator::Instance()->getNominalBz(); - o2::gpu::GPUO2InterfaceConfiguration config; - config.ReadConfigurableParam(config); - mRecoParam.init(mBz, &config.configReconstruction); + mRecoParam.init(mBz); mGeoTRD = o2::trd::Geometry::instance(); mParams = &SpacePointsCalibConfParam::Instance(); diff --git a/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx b/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx index 5c66e4635987f..0debfa72dd7fa 100644 --- a/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx +++ b/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx @@ -51,7 +51,7 @@ BOOST_AUTO_TEST_CASE(CATracking_test1) GPUO2Interface tracker; float solenoidBz = -5.00668; // B-field - float refX = 1000.; // transport tracks to this x after tracking, >500 for disabling + float refX = 83.; // transport tracks to this x after tracking, >500 for disabling bool continuous = false; // time frame data v.s. triggered events GPUO2InterfaceConfiguration config; diff --git a/Detectors/TPC/workflow/src/CalibdEdxSpec.cxx b/Detectors/TPC/workflow/src/CalibdEdxSpec.cxx index 7c2e2db8188e8..15ea241a7b350 100644 --- a/Detectors/TPC/workflow/src/CalibdEdxSpec.cxx +++ b/Detectors/TPC/workflow/src/CalibdEdxSpec.cxx @@ -13,6 +13,7 @@ /// \brief Workflow for time based dE/dx calibration. /// \author Thiago Badaró +#include "GPUO2InterfaceConfiguration.inc" #include "TPCWorkflow/CalibdEdxSpec.h" // o2 includes @@ -26,7 +27,6 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" -#include "GPUO2ConfigurableParam.h" #include "TPCCalibration/CalibdEdx.h" #include "TPCWorkflow/ProcessingHelpers.h" #include "TPCBaseRecSim/CDBTypes.h" @@ -70,8 +70,9 @@ class CalibdEdxDevice : public Task mCalib->setElectronCut(fitThreshold, fitPasses, fitThresholdLowFactor); mCalib->setMaterialType(mMatType); - mCustomdEdxFileName = o2::gpu::GPUConfigurableParamGPUSettingsO2::Instance().dEdxCorrFile; - mDisableTimeGain = o2::gpu::GPUConfigurableParamGPUSettingsO2::Instance().dEdxDisableResidualGain; + const auto& gpuConfig = GPU_GET_CONFIG(GPUSettingsO2); + mCustomdEdxFileName = gpuConfig.dEdxCorrFile; + mDisableTimeGain = gpuConfig.dEdxDisableResidualGain; if (mDisableTimeGain) { LOGP(info, "TimeGain correction was disabled via GPU_global.dEdxDisableResidualGain=1"); diff --git a/Detectors/TPC/workflow/src/CalibratordEdxSpec.cxx b/Detectors/TPC/workflow/src/CalibratordEdxSpec.cxx index 87e339f0643f4..dea1d85899675 100644 --- a/Detectors/TPC/workflow/src/CalibratordEdxSpec.cxx +++ b/Detectors/TPC/workflow/src/CalibratordEdxSpec.cxx @@ -13,6 +13,7 @@ /// \brief Workflow for time based dE/dx calibration. /// \author Thiago Badaró +#include "GPUO2InterfaceConfiguration.inc" #include "TPCWorkflow/CalibratordEdxSpec.h" #include @@ -29,7 +30,6 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" -#include "GPUO2ConfigurableParam.h" #include "TPCCalibration/CalibratordEdx.h" #include "TPCWorkflow/ProcessingHelpers.h" #include "DetectorsBase/GRPGeomHelper.h" @@ -86,8 +86,9 @@ class CalibratordEdxDevice : public Task mCalibrator->setTrackDebug(trackDebug); mCalibrator->setMakeGaussianFits(makeGaussianFits); - mCustomdEdxFileName = o2::gpu::GPUConfigurableParamGPUSettingsO2::Instance().dEdxCorrFile; - mDisableTimeGain = o2::gpu::GPUConfigurableParamGPUSettingsO2::Instance().dEdxDisableResidualGain; + const auto& gpuConfig = GPU_GET_CONFIG(GPUSettingsO2); + mCustomdEdxFileName = gpuConfig.dEdxCorrFile; + mDisableTimeGain = gpuConfig.dEdxDisableResidualGain; if (mDisableTimeGain) { LOGP(info, "TimeGain correction was disabled via GPU_global.dEdxDisableResidualGain=1"); diff --git a/Detectors/TPC/workflow/src/TPCRefitter.cxx b/Detectors/TPC/workflow/src/TPCRefitter.cxx index 51ff2516524a9..43a55526246fe 100644 --- a/Detectors/TPC/workflow/src/TPCRefitter.cxx +++ b/Detectors/TPC/workflow/src/TPCRefitter.cxx @@ -336,7 +336,6 @@ void TPCRefitterSpec::process(o2::globaltracking::RecoContainer& recoData) } mTPCRefitter = std::make_unique(mTPCClusterIdxStruct, &mTPCCorrMapsLoader, prop->getNominalBz(), mTPCTrackClusIdx.data(), 0, mTPCRefitterShMap.data(), mTPCRefitterOccMap.data(), mTPCRefitterOccMap.size(), nullptr, prop); - mTPCRefitter->setTrackReferenceX(900); // disable propagation after refit by setting reference to value > 500 mVdriftTB = mTPCVDriftHelper.getVDriftObject().getVDrift() * o2::tpc::ParameterElectronics::Instance().ZbinWidth; // VDrift expressed in cm/TimeBin mTPCTBBias = mTPCVDriftHelper.getVDriftObject().getTimeOffset() / (8 * o2::constants::lhc::LHCBunchSpacingMUS); diff --git a/Detectors/TRD/calibration/src/TrackBasedCalib.cxx b/Detectors/TRD/calibration/src/TrackBasedCalib.cxx index ae1f7b33c6bba..0d551e7b5f33d 100644 --- a/Detectors/TRD/calibration/src/TrackBasedCalib.cxx +++ b/Detectors/TRD/calibration/src/TrackBasedCalib.cxx @@ -36,9 +36,7 @@ void TrackBasedCalib::reset() void TrackBasedCalib::init() { bz = o2::base::Propagator::Instance()->getNominalBz(); - o2::gpu::GPUO2InterfaceConfiguration config; - config.ReadConfigurableParam(config); - mRecoParam.init(bz, &config.configReconstruction); + mRecoParam.init(bz); } void TrackBasedCalib::setInput(const o2::globaltracking::RecoContainer& input) diff --git a/Detectors/TRD/qc/src/Tracking.cxx b/Detectors/TRD/qc/src/Tracking.cxx index 9a0df7efa323b..da2d05794e2d8 100644 --- a/Detectors/TRD/qc/src/Tracking.cxx +++ b/Detectors/TRD/qc/src/Tracking.cxx @@ -26,9 +26,7 @@ using namespace o2::trd::constants; void Tracking::init() { - o2::gpu::GPUO2InterfaceConfiguration config; - config.ReadConfigurableParam(config); - mRecoParam.init(o2::base::Propagator::Instance()->getNominalBz(), &config.configReconstruction); + mRecoParam.init(o2::base::Propagator::Instance()->getNominalBz()); } void Tracking::setInput(const o2::globaltracking::RecoContainer& input) diff --git a/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx index 9e7ef089faeef..0f578efd3aa5b 100644 --- a/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx +++ b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx @@ -294,7 +294,6 @@ void TRDGlobalTracking::run(ProcessingContext& pc) mTPCClusterIdxStruct = &inputTracks.inputsTPCclusters->clusterIndex; mTPCRefitter = std::make_unique(mTPCClusterIdxStruct, &mTPCCorrMapsLoader, o2::base::Propagator::Instance()->getNominalBz(), inputTracks.getTPCTracksClusterRefs().data(), 0, inputTracks.clusterShMapTPC.data(), inputTracks.occupancyMapTPC.data(), inputTracks.occupancyMapTPC.size(), nullptr, o2::base::Propagator::Instance()); - mTPCRefitter->setTrackReferenceX(900); // disable propagation after refit by setting reference to value > 500 auto tmpInputContainer = getRecoInputContainer(pc, &mChainTracking->mIOPtrs, &inputTracks, mUseMC); auto tmpContainer = GPUWorkflowHelper::fillIOPtr(mChainTracking->mIOPtrs, inputTracks, mUseMC, nullptr, GTrackID::getSourcesMask("TRD"), mTrkMask, GTrackID::mask_t{GTrackID::MASK_NONE}); mTrackletsRaw = inputTracks.getTRDTracklets(); diff --git a/Detectors/Upgrades/ALICE3/FT3/base/include/FT3Base/FT3BaseParam.h b/Detectors/Upgrades/ALICE3/FT3/base/include/FT3Base/FT3BaseParam.h index b286aa068611c..7160067f075f7 100644 --- a/Detectors/Upgrades/ALICE3/FT3/base/include/FT3Base/FT3BaseParam.h +++ b/Detectors/Upgrades/ALICE3/FT3/base/include/FT3Base/FT3BaseParam.h @@ -42,9 +42,6 @@ struct FT3BaseParam : public o2::conf::ConfigurableParamHelper { Float_t etaOut = 1.5; Float_t Layerx2X0 = 0.01; - // FT3Geometry::External file - std::string configFile = ""; // Overrides geoModel parameter when provided - O2ParamDef(FT3BaseParam, "FT3Base"); }; diff --git a/Detectors/Upgrades/ALICE3/FT3/base/include/FT3Base/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/FT3/base/include/FT3Base/GeometryTGeo.h index bb22af7ad7f9d..3c78850dffb55 100644 --- a/Detectors/Upgrades/ALICE3/FT3/base/include/FT3Base/GeometryTGeo.h +++ b/Detectors/Upgrades/ALICE3/FT3/base/include/FT3Base/GeometryTGeo.h @@ -101,9 +101,6 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo static const char* composeSymNameSensor(Int_t d, Int_t lr); protected: - static constexpr int MAXLAYERS = 15; ///< max number of active layers - - Int_t mNumberOfLayers; ///< number of layers static std::string sInnerVolumeName; ///< Mother inner volume name static std::string sVolumeName; ///< Mother volume name static std::string sLayerName; ///< Layer name diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/Detector.h b/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/Detector.h index a68f8cf7788b6..8bc4b7f634d7c 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/Detector.h +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/Detector.h @@ -25,7 +25,6 @@ #include "TGeoManager.h" // for gGeoManager, TGeoManager (ptr only) #include "TLorentzVector.h" // for TLorentzVector #include "TVector3.h" // for TVector3 -#include "FT3Base/FT3BaseParam.h" class FairVolume; class TGeoVolume; @@ -34,25 +33,10 @@ class TParticle; class TString; -namespace o2 -{ -namespace ft3 +namespace o2::ft3 { class GeometryTGeo; -} -} // namespace o2 -namespace o2 -{ -namespace ft3 -{ -class FT3Layer; -} -} // namespace o2 - -namespace o2 -{ -namespace ft3 -{ +class FT3BaseParam; class FT3Layer; class Detector : public o2::base::DetImpl @@ -108,8 +92,16 @@ class Detector : public o2::base::DetImpl void PostTrack() override { ; } void PreTrack() override { ; } + static constexpr int IdxForwardDisks = 0; + static constexpr int IdxBackwardDisks = 1; /// Returns the number of layers - Int_t getNumberOfLayers() const { return mNumberOfLayers; } + size_t getNumberOfLayers() const + { + if (mLayerName[IdxBackwardDisks].size() != mLayerName[IdxForwardDisks].size()) { + LOG(fatal) << "Number of layers in the two directions are different! Returning 0."; + } + return mLayerName[IdxBackwardDisks].size(); + } void buildBasicFT3(const FT3BaseParam& param); void buildFT3V1(); @@ -117,16 +109,10 @@ class Detector : public o2::base::DetImpl void buildFT3Scoping(); void buildFT3NewVacuumVessel(); void buildFT3ScopingV3(); - void buildFT3FromFile(std::string); - - GeometryTGeo* mGeometryTGeo; //! access to geometry details - - void exportLayout(); protected: std::vector mLayerID; - std::vector> mLayerName; - Int_t mNumberOfLayers; + std::array, 2> mLayerName; // Two sets of layer names, one per direction (forward/backward) private: /// this is transient data about track passing the sensor @@ -154,16 +140,15 @@ class Detector : public o2::base::DetImpl Detector& operator=(const Detector&); - std::vector> mLayers; - bool mIsPipeActivated = true; //! If Alice 3 pipe is present append inner disks to vacuum volume to avoid overlaps + std::array, 2> mLayers; // Two sets of layers, one per direction (forward/backward) + bool mIsPipeActivated = true; //! If Alice 3 pipe is present append inner disks to vacuum volume to avoid overlaps template friend class o2::base::DetImpl; ClassDefOverride(Detector, 1); }; -} // namespace ft3 -} // namespace o2 +} // namespace o2::ft3 #ifdef USESHM namespace o2 diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/FT3Layer.h b/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/FT3Layer.h index 44a0ef0f7d8bc..44fd8eb08e444 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/FT3Layer.h +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/FT3Layer.h @@ -36,7 +36,7 @@ class FT3Layer : public TObject FT3Layer() = default; // Sample layer constructor - FT3Layer(Int_t layerDirection, Int_t layerNumber, std::string layerName, Float_t z, Float_t rIn, Float_t rOut, Float_t Layerx2X0); + FT3Layer(Int_t layerDirection, Int_t layerNumber, std::string layerName, Float_t z, Float_t rIn, Float_t rOut, Float_t Layerx2X0, bool partOfMiddleLayers); /// Copy constructor FT3Layer(const FT3Layer&) = default; @@ -51,6 +51,7 @@ class FT3Layer : public TObject auto getInnerRadius() const { return mInnerRadius; } auto getOuterRadius() const { return mOuterRadius; } auto getDirection() const { return mDirection; } + bool getIsInMiddleLayer() const { return mIsMiddleLayer; } auto getZ() const { return mZ; } auto getx2X0() const { return mx2X0; } @@ -77,14 +78,15 @@ class FT3Layer : public TObject static TGeoMedium* medFoam; private: - Int_t mLayerNumber = -1; ///< Current layer number - Int_t mDirection; ///< Layer direction 0=Forward 1 = Backward - std::string mLayerName; ///< Current layer name - Double_t mInnerRadius; ///< Inner radius of this layer - Double_t mOuterRadius; ///< Outer radius of this layer - Double_t mZ; ///< Z position of the layer - Double_t mChipThickness; ///< Chip thickness - Double_t mx2X0; ///< Layer material budget x/X0 + Int_t mLayerNumber = -1; ///< Current layer number + Int_t mDirection; ///< Layer direction 0=Forward 1 = Backward + bool mIsMiddleLayer = true; ///< Wether this layer is part of the middle layers + std::string mLayerName; ///< Current layer name + Double_t mInnerRadius; ///< Inner radius of this layer + Double_t mOuterRadius; ///< Outer radius of this layer + Double_t mZ; ///< Z position of the layer + Double_t mChipThickness; ///< Chip thickness + Double_t mx2X0; ///< Layer material budget x/X0 ClassDefOverride(FT3Layer, 0); // ALICE 3 EndCaps geometry }; diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx index 4b139272834f1..0a93a4061ae44 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx @@ -59,97 +59,6 @@ Detector::Detector() { } -//_________________________________________________________________________________________________ -void Detector::buildFT3FromFile(std::string configFileName) -{ - // Geometry description from file. One line per disk - // z_layer r_in r_out Layerx2X0 - // This simple file reader is not failproof. Do not add empty lines! - - /* - # Sample FT3 configuration - # z_layer r_in r_out Layerx2X0 - -45.3 2.5 9.26 0.0042 - -46.7 2.5 9.26 0.0042 - -48.6 2.5 9.8 0.0042 - -50.0 2.5 9.8 0.0042 - -52.4 2.5 10.43 0.0042 - -53.8 2.5 10.43 0.0042 - -67.7 3.82 13.01 0.0042 - -69.1 3.82 13.01 0.0042 - -76.1 3.92 14.35 0.0042 - -77.5 3.92 14.35 0.0042 - */ - - mLayerName.clear(); - mLayers.clear(); - mLayerID.clear(); - mLayerName.resize(1); - mLayers.resize(1); - - LOG(info) << "Building FT3 Detector: From file"; - LOG(info) << " FT3 detector configuration: " << configFileName; - std::ifstream ifs(configFileName.c_str()); - if (!ifs.good()) { - LOG(fatal) << " Invalid FT3Base.configFile!"; - } - std::string tempstr; - float z_layer, r_in, r_out, Layerx2X0; - char delimiter; - int layerNumber = 0; - while (std::getline(ifs, tempstr)) { - if (tempstr[0] == '#') { - LOG(info) << " Comment: " << tempstr; - continue; - } - std::istringstream iss(tempstr); - iss >> z_layer; - iss >> r_in; - iss >> r_out; - iss >> Layerx2X0; - - int direction = 1; // Forwards - if (z_layer < 0) { - // Backwards - direction = 0; - } - - std::string directionName = std::to_string(direction); - std::string layerName = GeometryTGeo::getFT3LayerPattern() + directionName + std::string("_") + std::to_string(layerNumber); - mLayerName[0].push_back(layerName); - LOG(info) << "Adding Layer " << layerName << " at z = " << z_layer << " ; direction = " << direction << " ; r_in = " << r_in << " ; r_out = " << r_out << " x/X0 = " << Layerx2X0; - auto& thisLayer = mLayers[0].emplace_back(direction, layerNumber, layerName, z_layer, r_in, r_out, Layerx2X0); - layerNumber++; - } - - mNumberOfLayers = layerNumber; - LOG(info) << " Loaded FT3 Detector with " << mNumberOfLayers << " layers"; -} - -//_________________________________________________________________________________________________ -void Detector::exportLayout() -{ - // Export FT3 Layout description to file. - // One line per disk: - // z_layer r_in r_out Layerx2X0 - - std::string configFileName = "FT3_layout.cfg"; - - LOG(info) << "Exporting FT3 Detector layout to " << configFileName; - - std::ofstream fOut(configFileName.c_str(), std::ios::out); - if (!fOut) { - printf("Cannot open file\n"); - return; - } - fOut << "# z_layer r_in r_out Layerx2X0" << std::endl; - for (auto layers_dir : mLayers) { - for (auto layer : layers_dir) { - fOut << layer.getZ() << " " << layer.getInnerRadius() << " " << layer.getOuterRadius() << " " << layer.getx2X0() << std::endl; - } - } -} - //_________________________________________________________________________________________________ void Detector::buildBasicFT3(const FT3BaseParam& param) { @@ -158,28 +67,27 @@ void Detector::buildBasicFT3(const FT3BaseParam& param) LOG(info) << "Building FT3 Detector: Conical Telescope"; - auto z_first = param.z0; - auto z_length = param.zLength; - auto etaIn = param.etaIn; - auto etaOut = param.etaOut; - auto Layerx2X0 = param.Layerx2X0; - mNumberOfLayers = param.nLayers; - mLayerName.resize(2); - mLayerName[0].resize(mNumberOfLayers); - mLayerName[1].resize(mNumberOfLayers); + const int numberOfLayers = param.nLayers; + const auto z_first = param.z0; + const auto z_length = param.zLength; + const auto etaIn = param.etaIn; + const auto etaOut = param.etaOut; + const auto Layerx2X0 = param.Layerx2X0; + mLayerName[IdxBackwardDisks].resize(numberOfLayers); + mLayerName[IdxForwardDisks].resize(numberOfLayers); mLayerID.clear(); - mLayers.resize(2); - for (int direction : {0, 1}) { - for (int layerNumber = 0; layerNumber < mNumberOfLayers; layerNumber++) { - std::string layerName = GeometryTGeo::getFT3LayerPattern() + std::to_string(layerNumber + mNumberOfLayers * direction); + for (int direction : {IdxBackwardDisks, IdxForwardDisks}) { + for (int layerNumber = 0; layerNumber < numberOfLayers; layerNumber++) { + std::string layerName = GeometryTGeo::getFT3LayerPattern() + std::to_string(layerNumber + numberOfLayers * direction); mLayerName[direction][layerNumber] = layerName; // Adds evenly spaced layers - float layerZ = z_first + (layerNumber * z_length / (mNumberOfLayers - 1)) * std::copysign(1, z_first); - float rIn = std::abs(layerZ * std::tan(2.f * std::atan(std::exp(-etaIn)))); - float rOut = std::abs(layerZ * std::tan(2.f * std::atan(std::exp(-etaOut)))); - auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, layerZ, rIn, rOut, Layerx2X0); + const float layerZ = z_first + (layerNumber * z_length / numberOfLayers) * std::copysign(1, z_first); + const float rIn = std::abs(layerZ * std::tan(2.f * std::atan(std::exp(-etaIn)))); + const float rOut = std::abs(layerZ * std::tan(2.f * std::atan(std::exp(-etaOut)))); + const bool isMiddleLayer = layerNumber < 3; + auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, layerZ, rIn, rOut, Layerx2X0, isMiddleLayer); } } } @@ -192,10 +100,10 @@ void Detector::buildFT3V1() LOG(info) << "Building FT3 Detector: V1"; - mNumberOfLayers = 10; - float sensorThickness = 30.e-4; - float layersx2X0 = 1.e-2; - std::vector> layersConfig{ + const int numberOfLayers = 10; + const float sensorThickness = 30.e-4; + const float layersx2X0 = 1.e-2; + const std::vector> layersConfig{ {26., .5, 3., 0.1f * layersx2X0}, // {z_layer, r_in, r_out, Layerx2X0} {30., .5, 3., 0.1f * layersx2X0}, {34., .5, 3., 0.1f * layersx2X0}, @@ -207,14 +115,12 @@ void Detector::buildFT3V1() {220., 3.5, 80.f, layersx2X0}, {279., 3.5, 80.f, layersx2X0}}; - mLayerName.resize(2); - mLayerName[0].resize(mNumberOfLayers); - mLayerName[1].resize(mNumberOfLayers); + mLayerName[IdxBackwardDisks].resize(numberOfLayers); + mLayerName[IdxForwardDisks].resize(numberOfLayers); mLayerID.clear(); - mLayers.resize(2); - for (auto direction : {0, 1}) { - for (int layerNumber = 0; layerNumber < mNumberOfLayers; layerNumber++) { + for (auto direction : {IdxBackwardDisks, IdxForwardDisks}) { + for (int layerNumber = 0; layerNumber < numberOfLayers; layerNumber++) { std::string directionName = std::to_string(direction); std::string layerName = GeometryTGeo::getFT3LayerPattern() + directionName + std::string("_") + std::to_string(layerNumber); mLayerName[direction][layerNumber] = layerName; @@ -226,7 +132,8 @@ void Detector::buildFT3V1() LOG(info) << "Adding Layer " << layerName << " at z = " << z; // Add layers - auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, z, rIn, rOut, x0); + const bool isMiddleLayer = layerNumber < 3; + auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, z, rIn, rOut, x0, isMiddleLayer); } } } @@ -239,10 +146,10 @@ void Detector::buildFT3V3b() LOG(info) << "Building FT3 Detector: V3b"; - mNumberOfLayers = 12; + const int numberOfLayers = 12; float sensorThickness = 30.e-4; float layersx2X0 = 1.e-2; - std::vector> layersConfig{ + std::vector> layersConfig{ {26., .5, 3., 0.1f * layersx2X0}, // {z_layer, r_in, r_out, Layerx2X0} {30., .5, 3., 0.1f * layersx2X0}, {34., .5, 3., 0.1f * layersx2X0}, @@ -256,14 +163,12 @@ void Detector::buildFT3V3b() {340., 12.5, 80.f, layersx2X0}, {400., 14.7, 80.f, layersx2X0}}; - mLayerName.resize(2); - mLayerName[0].resize(mNumberOfLayers); - mLayerName[1].resize(mNumberOfLayers); + mLayerName[IdxBackwardDisks].resize(numberOfLayers); + mLayerName[IdxForwardDisks].resize(numberOfLayers); mLayerID.clear(); - mLayers.resize(2); - for (auto direction : {0, 1}) { - for (int layerNumber = 0; layerNumber < mNumberOfLayers; layerNumber++) { + for (auto direction : {IdxBackwardDisks, IdxForwardDisks}) { + for (int layerNumber = 0; layerNumber < numberOfLayers; layerNumber++) { std::string directionName = std::to_string(direction); std::string layerName = GeometryTGeo::getFT3LayerPattern() + directionName + std::string("_") + std::to_string(layerNumber); mLayerName[direction][layerNumber] = layerName; @@ -275,7 +180,8 @@ void Detector::buildFT3V3b() LOG(info) << "Adding Layer " << layerName << " at z = " << z; // Add layers - auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, z, rIn, rOut, x0); + const bool isMiddleLayer = layerNumber < 3; + auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, z, rIn, rOut, x0, isMiddleLayer); } } } @@ -291,10 +197,10 @@ void Detector::buildFT3NewVacuumVessel() LOG(info) << "Building FT3 Detector: After Upgrade Days March 2024 version"; - mNumberOfLayers = 9; - float sensorThickness = 30.e-4; - float layersx2X0 = 1.e-2; - std::vector> layersConfigCSide{ + const int numberOfLayers = 9; + const float sensorThickness = 30.e-4; + const float layersx2X0 = 1.e-2; + const std::vector> layersConfigCSide{ {77., 7.0, 35., layersx2X0}, // {z_layer, r_in, r_out, Layerx2X0} {100., 7.0, 35., layersx2X0}, {122., 7.0, 35., layersx2X0}, @@ -305,7 +211,7 @@ void Detector::buildFT3NewVacuumVessel() {300., 7.0, 68.f, layersx2X0}, {350., 7.0, 68.f, layersx2X0}}; - std::vector> layersConfigASide{ + const std::vector> layersConfigASide{ {77., 5.0, 35., layersx2X0}, // {z_layer, r_in, r_out, Layerx2X0} {100., 5.0, 35., layersx2X0}, {122., 5.0, 35., layersx2X0}, @@ -316,14 +222,12 @@ void Detector::buildFT3NewVacuumVessel() {300., 5.0, 68.f, layersx2X0}, {350., 5.0, 68.f, layersx2X0}}; - mLayerName.resize(2); - mLayerName[0].resize(mNumberOfLayers); - mLayerName[1].resize(mNumberOfLayers); + mLayerName[IdxBackwardDisks].resize(numberOfLayers); + mLayerName[IdxForwardDisks].resize(numberOfLayers); mLayerID.clear(); - mLayers.resize(2); - for (auto direction : {0, 1}) { - for (int layerNumber = 0; layerNumber < mNumberOfLayers; layerNumber++) { + for (auto direction : {IdxBackwardDisks, IdxForwardDisks}) { + for (int layerNumber = 0; layerNumber < numberOfLayers; layerNumber++) { std::string directionName = std::to_string(direction); std::string layerName = GeometryTGeo::getFT3LayerPattern() + directionName + std::string("_") + std::to_string(layerNumber); mLayerName[direction][layerNumber] = layerName; @@ -342,7 +246,8 @@ void Detector::buildFT3NewVacuumVessel() LOG(info) << "Adding Layer " << layerName << " at z = " << z; // Add layers - auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, z, rIn, rOut, x0); + const bool isMiddleLayer = layerNumber < 3; + auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, z, rIn, rOut, x0, isMiddleLayer); } } } @@ -356,52 +261,45 @@ void Detector::buildFT3ScopingV3() LOG(info) << "Building FT3 Detector: v3 scoping version"; - mNumberOfLayers = 6; - float sensorThickness = 30.e-4; - float layersx2X0 = 1.e-2; - std::vector> layersConfigCSide{ - {77., 10.0, 35., layersx2X0}, // {z_layer, r_in, r_out, Layerx2X0} - {100., 10.0, 35., layersx2X0}, - {122., 10.0, 35., layersx2X0}, - {150., 20.0, 68.f, layersx2X0}, - {180., 20.0, 68.f, layersx2X0}, - {220., 20.0, 68.f, layersx2X0}}; - - std::vector> layersConfigASide{ - {77., 10.0, 35., layersx2X0}, // {z_layer, r_in, r_out, Layerx2X0} - {100., 10.0, 35., layersx2X0}, - {122., 10.0, 35., layersx2X0}, - {150., 20.0, 68.f, layersx2X0}, - {180., 20.0, 68.f, layersx2X0}, - {220., 20.0, 68.f, layersx2X0}}; - - mLayerName.resize(2); - mLayerName[0].resize(mNumberOfLayers); - mLayerName[1].resize(mNumberOfLayers); + const int numberOfLayers = 6; + const float sensorThickness = 30.e-4; + const float layersx2X0 = 1.e-2; + using LayerConfig = std::array; // {z_layer, r_in, r_out, Layerx2X0} + const std::array layersConfigCSide{LayerConfig{77., 10.0, 35., layersx2X0}, + LayerConfig{100., 10.0, 35., layersx2X0}, + LayerConfig{122., 10.0, 35., layersx2X0}, + LayerConfig{150., 20.0, 68.f, layersx2X0}, + LayerConfig{180., 20.0, 68.f, layersx2X0}, + LayerConfig{220., 20.0, 68.f, layersx2X0}}; + + const std::array layersConfigASide{LayerConfig{77., 10.0, 35., layersx2X0}, + LayerConfig{100., 10.0, 35., layersx2X0}, + LayerConfig{122., 10.0, 35., layersx2X0}, + LayerConfig{150., 20.0, 68.f, layersx2X0}, + LayerConfig{180., 20.0, 68.f, layersx2X0}, + LayerConfig{220., 20.0, 68.f, layersx2X0}}; + const std::array enabled{true, true, true, true, true, true}; // To enable or disable layers for debug purpose + mLayerID.clear(); - mLayers.resize(2); - for (auto direction : {0, 1}) { - for (int layerNumber = 0; layerNumber < mNumberOfLayers; layerNumber++) { - std::string directionName = std::to_string(direction); - std::string layerName = GeometryTGeo::getFT3LayerPattern() + directionName + std::string("_") + std::to_string(layerNumber); - mLayerName[direction][layerNumber] = layerName; - float z, rIn, rOut, x0; - if (direction == 0) { // C-Side - z = layersConfigCSide[layerNumber][0]; - rIn = layersConfigCSide[layerNumber][1]; - rOut = layersConfigCSide[layerNumber][2]; - x0 = layersConfigCSide[layerNumber][3]; - } else if (direction == 1) { // A-Side - z = layersConfigASide[layerNumber][0]; - rIn = layersConfigASide[layerNumber][1]; - rOut = layersConfigASide[layerNumber][2]; - x0 = layersConfigASide[layerNumber][3]; + for (int direction : {IdxBackwardDisks, IdxForwardDisks}) { + mLayerName[direction].clear(); + const std::array& layerConfig = (direction == IdxBackwardDisks) ? layersConfigCSide : layersConfigASide; + for (int layerNumber = 0; layerNumber < numberOfLayers; layerNumber++) { + if (!enabled[layerNumber]) { + continue; } - - LOG(info) << "Adding Layer " << layerName << " at z = " << z; + const std::string directionName = std::to_string(direction); + const std::string layerName = GeometryTGeo::getFT3LayerPattern() + directionName + std::string("_") + std::to_string(layerNumber); + mLayerName[direction].push_back(layerName.c_str()); + const float z = layerConfig[layerNumber][0]; + const float rIn = layerConfig[layerNumber][1]; + const float rOut = layerConfig[layerNumber][2]; + const float x0 = layerConfig[layerNumber][3]; + LOG(info) << "buildFT3ScopingV3 -> Adding Layer " << layerNumber << "/" << numberOfLayers << " " << layerName << " at z = " << z; // Add layers - auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, z, rIn, rOut, x0); + const bool isMiddleLayer = layerNumber < 3; + auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, z, rIn, rOut, x0, isMiddleLayer); } } } @@ -413,10 +311,10 @@ void Detector::buildFT3Scoping() LOG(info) << "Building FT3 Detector: Scoping document version"; - mNumberOfLayers = 12; - float sensorThickness = 30.e-4; - float layersx2X0 = 1.e-2; - std::vector> layersConfig{ + const int numberOfLayers = 12; + const float sensorThickness = 30.e-4; + const float layersx2X0 = 1.e-2; + const std::vector> layersConfig{ {26., .5, 2.5, 0.1f * layersx2X0}, // {z_layer, r_in, r_out, Layerx2X0} {30., .5, 2.5, 0.1f * layersx2X0}, {34., .5, 2.5, 0.1f * layersx2X0}, @@ -430,26 +328,24 @@ void Detector::buildFT3Scoping() {300., 5.0, 68.f, layersx2X0}, {350., 5.0, 68.f, layersx2X0}}; - mLayerName.resize(2); - mLayerName[0].resize(mNumberOfLayers); - mLayerName[1].resize(mNumberOfLayers); + mLayerName[IdxBackwardDisks].resize(numberOfLayers); + mLayerName[IdxForwardDisks].resize(numberOfLayers); mLayerID.clear(); - mLayers.resize(2); - for (auto direction : {0, 1}) { - for (int layerNumber = 0; layerNumber < mNumberOfLayers; layerNumber++) { + for (auto direction : {IdxBackwardDisks, IdxForwardDisks}) { + for (int layerNumber = 0; layerNumber < numberOfLayers; layerNumber++) { std::string directionName = std::to_string(direction); std::string layerName = GeometryTGeo::getFT3LayerPattern() + directionName + std::string("_") + std::to_string(layerNumber); mLayerName[direction][layerNumber] = layerName; auto& z = layersConfig[layerNumber][0]; - auto& rIn = layersConfig[layerNumber][1]; auto& rOut = layersConfig[layerNumber][2]; auto& x0 = layersConfig[layerNumber][3]; LOG(info) << "Adding Layer " << layerName << " at z = " << z; // Add layers - auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, z, rIn, rOut, x0); + const bool isMiddleLayer = layerNumber < 3; + auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, z, rIn, rOut, x0, isMiddleLayer); } } } @@ -464,24 +360,17 @@ Detector::Detector(bool active) // FT3 Base configuration parameters auto& ft3BaseParam = FT3BaseParam::Instance(); - if (ft3BaseParam.configFile != "") { - LOG(info) << "FT3 Geometry configuration file provided. Overriding FT3Base.geoModel configuration."; - buildFT3FromFile(ft3BaseParam.configFile); - - } else { - switch (ft3BaseParam.geoModel) { - case Default: - buildFT3ScopingV3(); // v3 Dec 25 - break; - case Telescope: - buildBasicFT3(ft3BaseParam); // BasicFT3 = Parametrized telescopic detector (equidistant layers) - break; - default: - LOG(fatal) << "Invalid Geometry.\n"; - break; - } + switch (ft3BaseParam.geoModel) { + case Default: + buildFT3ScopingV3(); // v3 Dec 25 + break; + case Telescope: + buildBasicFT3(ft3BaseParam); // BasicFT3 = Parametrized telescopic detector (equidistant layers) + break; + default: + LOG(fatal) << "Invalid Geometry.\n"; + break; } - exportLayout(); } //_________________________________________________________________________________________________ @@ -494,7 +383,6 @@ Detector::Detector(const Detector& rhs) { mLayerID = rhs.mLayerID; mLayerName = rhs.mLayerName; - mNumberOfLayers = rhs.mNumberOfLayers; } //_________________________________________________________________________________________________ @@ -527,7 +415,6 @@ Detector& Detector::operator=(const Detector& rhs) mLayerID = rhs.mLayerID; mLayerName = rhs.mLayerName; - mNumberOfLayers = rhs.mNumberOfLayers; mLayers = rhs.mLayers; mTrackData = rhs.mTrackData; @@ -543,8 +430,6 @@ void Detector::InitializeO2Detector() // Define the list of sensitive volumes LOG(info) << "Initialize FT3 O2Detector"; - mGeometryTGeo = GeometryTGeo::Instance(); - defineSensitiveVolumes(); } @@ -693,12 +578,10 @@ void Detector::ConstructGeometry() void Detector::createGeometry() { - mGeometryTGeo = GeometryTGeo::Instance(); - TGeoVolume* volFT3 = new TGeoVolumeAssembly(GeometryTGeo::getFT3VolPattern()); TGeoVolume* volIFT3 = new TGeoVolumeAssembly(GeometryTGeo::getFT3InnerVolPattern()); - LOG(info) << "GeometryBuilder::buildGeometry volume name = " << GeometryTGeo::getFT3VolPattern(); + LOG(info) << "FT3: createGeometry volume name = " << GeometryTGeo::getFT3VolPattern(); TGeoVolume* vALIC = gGeoManager->GetVolume("barrel"); if (!vALIC) { @@ -710,69 +593,40 @@ void Detector::createGeometry() LOG(info) << "Running simulation with no beam pipe."; } - LOG(debug) << "FT3 createGeometry: " - << Form("gGeoManager name is %s title is %s", gGeoManager->GetName(), gGeoManager->GetTitle()); - - if (mLayers.size() == 2) { // V1 and telescope - if (!A3IPvac) { - for (int direction : {0, 1}) { // Backward layers at mLayers[0]; Forward layers at mLayers[1] - std::string directionString = direction ? "Forward" : "Backward"; - LOG(info) << "Creating FT3 " << directionString << " layers:"; - for (int iLayer = 0; iLayer < mLayers[direction].size(); iLayer++) { - mLayers[direction][iLayer].createLayer(volFT3); - } - } - vALIC->AddNode(volFT3, 2, new TGeoTranslation(0., 30., 0.)); - } else { // If beampipe is enabled append inner disks to beampipe filling volume, this should be temporary. - for (int direction : {0, 1}) { - std::string directionString = direction ? "Forward" : "Backward"; - LOG(info) << "Creating FT3 " << directionString << " layers:"; - for (int iLayer = 0; iLayer < mLayers[direction].size(); iLayer++) { - if (iLayer < 3) { - mLayers[direction][iLayer].createLayer(volIFT3); - } else { - mLayers[direction][iLayer].createLayer(volFT3); - } - } - } - A3IPvac->AddNode(volIFT3, 2, new TGeoTranslation(0., 0., 0.)); - vALIC->AddNode(volFT3, 2, new TGeoTranslation(0., 30., 0.)); - } - - for (auto direction : {0, 1}) { - std::string directionString = direction ? "Forward" : "Backward"; - LOG(info) << "Registering FT3 " << directionString << " LayerIDs:"; + // This will need to adapt to the new scheme + if (!A3IPvac) { + for (int direction : {IdxBackwardDisks, IdxForwardDisks}) { // Backward layers at mLayers[0]; Forward layers at mLayers[1] + const std::string directionString = direction ? "Forward" : "Backward"; + LOG(info) << " Creating FT3 without beampipe " << directionString << " layers:"; for (int iLayer = 0; iLayer < mLayers[direction].size(); iLayer++) { - auto layerID = gMC ? TVirtualMC::GetMC()->VolId(Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), direction, iLayer)) : 0; - mLayerID.push_back(layerID); - LOG(info) << " " << directionString << " layer " << iLayer << " LayerID " << layerID; + mLayers[direction][iLayer].createLayer(volFT3); } } - } - - if (mLayers.size() == 1) { // All layers registered at mLayers[0], used when building from file - LOG(info) << "Creating FT3 layers:"; - if (A3IPvac) { - for (int iLayer = 0; iLayer < mLayers[0].size(); iLayer++) { - if (std::abs(mLayers[0][iLayer].getZ()) < 25) { - mLayers[0][iLayer].createLayer(volIFT3); + vALIC->AddNode(volFT3, 2, new TGeoTranslation(0., 30., 0.)); + } else { // If beampipe is enabled append inner disks to beampipe filling volume, this should be temporary. + for (int direction : {IdxBackwardDisks, IdxForwardDisks}) { + const std::string directionString = direction ? "Forward" : "Backward"; + LOG(info) << " Creating FT3 " << directionString << " layers:"; + for (int iLayer = 0; iLayer < mLayers[direction].size(); iLayer++) { + LOG(info) << " Creating " << directionString << " layer " << iLayer; + if (mLayers[direction][iLayer].getIsInMiddleLayer()) { // ML disks + mLayers[direction][iLayer].createLayer(volIFT3); } else { - mLayers[0][iLayer].createLayer(volFT3); + mLayers[direction][iLayer].createLayer(volFT3); } } - A3IPvac->AddNode(volIFT3, 2, new TGeoTranslation(0., 0., 0.)); - vALIC->AddNode(volFT3, 2, new TGeoTranslation(0., 30., 0.)); - } else { - for (int iLayer = 0; iLayer < mLayers[0].size(); iLayer++) { - mLayers[0][iLayer].createLayer(volFT3); - } - vALIC->AddNode(volFT3, 2, new TGeoTranslation(0., 30., 0.)); } - LOG(info) << "Registering FT3 LayerIDs:"; - for (int iLayer = 0; iLayer < mLayers[0].size(); iLayer++) { - auto layerID = gMC ? TVirtualMC::GetMC()->VolId(Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), 0, iLayer)) : 0; + A3IPvac->AddNode(volIFT3, 2, new TGeoTranslation(0., 0., 0.)); + vALIC->AddNode(volFT3, 2, new TGeoTranslation(0., 30., 0.)); + } + + for (auto direction : {IdxBackwardDisks, IdxForwardDisks}) { + std::string directionString = direction ? "Forward" : "Backward"; + LOG(info) << " Registering FT3 " << directionString << " LayerIDs for " << mLayers[direction].size() << " layers:"; + for (int iLayer = 0; iLayer < mLayers[direction].size(); iLayer++) { + auto layerID = gMC ? TVirtualMC::GetMC()->VolId(Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), direction, iLayer)) : 0; mLayerID.push_back(layerID); - LOG(info) << " mLayerID[" << iLayer << "] = " << layerID; + LOG(info) << " " << directionString << " layer " << iLayer << " LayerID " << layerID; } } } @@ -786,40 +640,34 @@ void Detector::defineSensitiveVolumes() TString volumeName; LOG(info) << "Adding FT3 Sensitive Volumes"; - // The names of the FT3 sensitive volumes have the format: FT3Sensor_(0,1)_(0...sNumberLayers-1) - if (mLayers.size() == 2) { - for (int direction : {0, 1}) { - for (int iLayer = 0; iLayer < mNumberOfLayers; iLayer++) { - volumeName = o2::ft3::GeometryTGeo::getFT3SensorPattern() + std::to_string(iLayer); - if (iLayer < 3) { // ML disks - v = geoManager->GetVolume(Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), direction, iLayer)); - AddSensitiveVolume(v); - } else { // OT disks - for (int sensor_count = 0; sensor_count < MAX_SENSORS; ++sensor_count) { - std::string sensor_name_front = "FT3Sensor_front_" + std::to_string(iLayer) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); - std::string sensor_name_back = "FT3Sensor_back_" + std::to_string(iLayer) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); - v = geoManager->GetVolume(sensor_name_front.c_str()); - if (v) { - AddSensitiveVolume(v); - } - v = geoManager->GetVolume(sensor_name_back.c_str()); - if (v) { - AddSensitiveVolume(v); - } + for (int direction : {IdxBackwardDisks, IdxForwardDisks}) { + for (int iLayer = 0; iLayer < getNumberOfLayers(); iLayer++) { + LOG(info) << "Adding FT3 Sensitive Volume for direction " << direction << " layer " << iLayer << "/" << getNumberOfLayers(); + volumeName = o2::ft3::GeometryTGeo::getFT3SensorPattern() + std::to_string(iLayer); + if (mLayers[direction][iLayer].getIsInMiddleLayer()) { // ML disks + const std::string sensorName = Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), direction, iLayer); + v = geoManager->GetVolume(sensorName.c_str()); + if (!v) { + geoManager->GetListOfVolumes()->ls(); + LOG(fatal) << "Could not find volume " << sensorName << " for direction " << direction << " layer " << iLayer; + } + AddSensitiveVolume(v); + } else { // OT disks + for (int sensor_count = 0; sensor_count < MAX_SENSORS; ++sensor_count) { + std::string sensor_name_front = "FT3sensor_front_" + std::to_string(iLayer) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + std::string sensor_name_back = "FT3sensor_back_" + std::to_string(iLayer) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + v = geoManager->GetVolume(sensor_name_front.c_str()); + if (v) { + AddSensitiveVolume(v); + } + v = geoManager->GetVolume(sensor_name_back.c_str()); + if (v) { + AddSensitiveVolume(v); } } } } } - - if (mLayers.size() == 1) { - for (int iLayer = 0; iLayer < mLayers[0].size(); iLayer++) { - volumeName = o2::ft3::GeometryTGeo::getFT3SensorPattern() + std::to_string(iLayer); - v = geoManager->GetVolume(Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), mLayers[0][iLayer].getDirection(), iLayer)); - LOG(info) << "Adding FT3 Sensitive Volume => " << v->GetName(); - AddSensitiveVolume(v); - } - } } //_________________________________________________________________________________________________ diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx index 97f42eca6143f..1ad4d1aad1eeb 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx @@ -16,7 +16,6 @@ #include "FT3Simulation/FT3Layer.h" #include "FT3Base/GeometryTGeo.h" -#include "FT3Simulation/Detector.h" #include // for LOG @@ -55,20 +54,29 @@ TGeoMedium* FT3Layer::waterMed = nullptr; TGeoMaterial* FT3Layer::foamMat = nullptr; TGeoMedium* FT3Layer::medFoam = nullptr; -FT3Layer::FT3Layer(Int_t layerDirection, Int_t layerNumber, std::string layerName, Float_t z, Float_t rIn, Float_t rOut, Float_t Layerx2X0) +FT3Layer::FT3Layer(Int_t layerDirection, Int_t layerNumber, std::string layerName, Float_t z, Float_t rIn, Float_t rOut, Float_t Layerx2X0, bool partOfMiddleLayers) { // Creates a simple parametrized EndCap layer covering the given // pseudorapidity range at the z layer position mDirection = layerDirection; mLayerNumber = layerNumber; + mIsMiddleLayer = partOfMiddleLayers; mLayerName = layerName; mZ = layerDirection ? std::abs(z) : -std::abs(z); mx2X0 = Layerx2X0; mInnerRadius = rIn; mOuterRadius = rOut; - auto Si_X0 = 9.5; + const double Si_X0 = 9.5; mChipThickness = Layerx2X0 * Si_X0; + // Sanity checks + if (std::isnan(mZ)) { + LOG(fatal) << "FT3 Layer " << mLayerNumber << " has z = NaN, which is not a valid number."; + } + if (mZ < 0.001 && mZ > -0.001) { + LOG(fatal) << "FT3 Layer " << mLayerNumber << " has z = " << mZ << " cm, which is very close to 0."; + } + LOG(info) << "Creating FT3 Layer " << mLayerNumber << " ; direction " << mDirection; LOG(info) << " Using silicon X0 = " << Si_X0 << " to emulate layer radiation length."; LOG(info) << " Layer z = " << mZ << " ; R_in = " << mInnerRadius << " ; R_out = " << mOuterRadius << " ; x2X0 = " << mx2X0 << " ; ChipThickness = " << mChipThickness; @@ -110,8 +118,8 @@ void FT3Layer::createSeparationLayer_waterCooling(TGeoVolume* motherVolume, cons FT3Layer::initialize_mat(); - double carbonFiberThickness = 0.01; - double foamSpacingThickness = 0.5; + const double carbonFiberThickness = 0.01; // cm + const double foamSpacingThickness = 0.5; // cm TGeoTube* carbonFiberLayer = new TGeoTube(mInnerRadius, mOuterRadius, carbonFiberThickness / 2); @@ -122,15 +130,15 @@ void FT3Layer::createSeparationLayer_waterCooling(TGeoVolume* motherVolume, cons carbonFiberLayerVol1->SetLineColor(kGray + 2); carbonFiberLayerVol2->SetLineColor(kGray + 2); - double zSeparation = foamSpacingThickness / 2.0 + carbonFiberThickness / 2.0; + const double zSeparation = foamSpacingThickness / 2.0 + carbonFiberThickness / 2.0; motherVolume->AddNode(carbonFiberLayerVol1, 1, new TGeoTranslation(0, 0, mZ - zSeparation)); motherVolume->AddNode(carbonFiberLayerVol2, 1, new TGeoTranslation(0, 0, mZ + zSeparation)); - double pipeOuterRadius = 0.20; - double kaptonThickness = 0.0025; - double pipeInnerRadius = pipeOuterRadius - kaptonThickness; - double pipeMaxLength = mOuterRadius * 2.0; + const double pipeOuterRadius = 0.20; + const double kaptonThickness = 0.0025; + const double pipeInnerRadius = pipeOuterRadius - kaptonThickness; + const double pipeMaxLength = mOuterRadius * 2.0; int name_it = 0; @@ -199,8 +207,8 @@ void FT3Layer::createSeparationLayer(TGeoVolume* motherVolume, const std::string FT3Layer::initialize_mat(); - double carbonFiberThickness = 0.01; - double foamSpacingThickness = 1.0; + constexpr double carbonFiberThickness = 0.01; // cm + constexpr double foamSpacingThickness = 1.0; // cm TGeoTube* carbonFiberLayer = new TGeoTube(mInnerRadius, mOuterRadius, carbonFiberThickness / 2); TGeoTube* foamLayer = new TGeoTube(mInnerRadius, mOuterRadius, foamSpacingThickness / 2); @@ -215,16 +223,19 @@ void FT3Layer::createSeparationLayer(TGeoVolume* motherVolume, const std::string foamLayerVol->SetFillColorAlpha(kBlack, 1.0); carbonFiberLayerVol2->SetLineColor(kGray + 2); - double zSeparation = foamSpacingThickness / 2.0 + carbonFiberThickness / 2.0; + const double zSeparation = foamSpacingThickness / 2.0 + carbonFiberThickness / 2.0; - motherVolume->AddNode(carbonFiberLayerVol1, 1, new TGeoTranslation(0, 0, mZ - zSeparation)); - motherVolume->AddNode(foamLayerVol, 1, new TGeoTranslation(0, 0, mZ)); - motherVolume->AddNode(carbonFiberLayerVol2, 1, new TGeoTranslation(0, 0, mZ + zSeparation)); + motherVolume->AddNode(carbonFiberLayerVol1, 1, new TGeoTranslation(0, 0, 0 - zSeparation)); + motherVolume->AddNode(foamLayerVol, 1, new TGeoTranslation(0, 0, 0)); + motherVolume->AddNode(carbonFiberLayerVol2, 1, new TGeoTranslation(0, 0, 0 + zSeparation)); } void FT3Layer::createLayer(TGeoVolume* motherVolume) { - if (mLayerNumber >= 0 && mLayerNumber < 3) { + if (mLayerNumber < 0) { + LOG(fatal) << "Invalid layer number " << mLayerNumber << " for FT3 layer."; + } + if (mIsMiddleLayer) { // ML disks std::string chipName = o2::ft3::GeometryTGeo::getFT3ChipPattern() + std::to_string(mLayerNumber), sensName = Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), mDirection, mLayerNumber); @@ -255,7 +266,7 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) LOG(info) << "Inserting " << layerVol->GetName() << " inside " << motherVolume->GetName(); motherVolume->AddNode(layerVol, 1, FwdDiskCombiTrans); - } else if (mLayerNumber >= 3) { + } else { // OT disks FT3Module module; @@ -264,11 +275,23 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) std::string backLayerName = o2::ft3::GeometryTGeo::getFT3LayerPattern() + std::to_string(mDirection) + std::to_string(mLayerNumber) + "_Back"; std::string separationLayerName = "FT3SeparationLayer" + std::to_string(mDirection) + std::to_string(mLayerNumber); + TGeoMedium* medAir = gGeoManager->GetMedium("FT3_AIR$"); + TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, 10 * mChipThickness / 2); + TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); + layerVol->SetLineColor(kYellow + 2); + // createSeparationLayer_waterCooling(motherVolume, separationLayerName); - createSeparationLayer(motherVolume, separationLayerName); + createSeparationLayer(layerVol, separationLayerName); // create disk faces - module.createModule(mZ, mLayerNumber, mDirection, mInnerRadius, mOuterRadius, 0., "front", "rectangular", motherVolume); - module.createModule(mZ, mLayerNumber, mDirection, mInnerRadius, mOuterRadius, 0., "back", "rectangular", motherVolume); + module.createModule(0, mLayerNumber, mDirection, mInnerRadius, mOuterRadius, 0., "front", "rectangular", layerVol); + module.createModule(0, mLayerNumber, mDirection, mInnerRadius, mOuterRadius, 0., "back", "rectangular", layerVol); + + // Finally put everything in the mother volume + auto* FwdDiskRotation = new TGeoRotation("FwdDiskRotation", 0, 0, 180); + auto* FwdDiskCombiTrans = new TGeoCombiTrans(0, 0, mZ, FwdDiskRotation); + + LOG(info) << "Inserting " << layerVol->GetName() << " inside " << motherVolume->GetName(); + motherVolume->AddNode(layerVol, 1, FwdDiskCombiTrans); } } diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx index 9318554837706..20a481cb36046 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx @@ -199,7 +199,7 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double bottom_y_pos_value = 3.5; bottom_y_neg_value = -3.5; } else { - std::cout << "Different config - to determine offsets needed." << std::endl; + LOG(warning) << "Different config - to determine offsets needed for " << "Rin = " << Rin << " ; sensor_height = " << sensor_height << " ; sensor_width = " << sensor_width << " layer " << layerNumber; x_condition_min = -Rin; x_condition_max = Rin; adjust_bottom_y_pos = false; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/README.md b/Detectors/Upgrades/ALICE3/IOTOF/README.md index 044798076b485..fba4d12252af6 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/README.md +++ b/Detectors/Upgrades/ALICE3/IOTOF/README.md @@ -14,16 +14,16 @@ Configurables for various sub-detectors are presented in the following Table: [link to definitions](./base/include/IOTOFBase/IOTOFBaseParam.h) -| Options | Choices | Comments | -| ----------------------------- | ---------------------------------------------------------------- | ------------------------------------------- | -| `IOTOFBase.enableInnerTOF` | `true` (default), `false` | Enable inner TOF barrel layer | -| `IOTOFBase.enableOuterTOF` | `true` (default), `false` | Enable outer TOF barrel layer | -| `IOTOFBase.enableForwardTOF` | `true` (default), `false` | Enable forward TOF endcap | -| `IOTOFBase.enableBackwardTOF` | `true` (default), `false` | Enable backward TOF endcap | -| `IOTOFBase.segmentedInnerTOF` | `false` (default), `true` | Use segmented geometry for inner TOF | -| `IOTOFBase.segmentedOuterTOF` | `false` (default), `true` | Use segmented geometry for outer TOF | -| `IOTOFBase.detectorPattern` | ` ` (default), `v3b`, `v3b1a`, `v3b1b`, `v3b2a`, `v3b2b`, `v3b3` | Optional layout pattern | -| ----------------------------- | ------------------------- | ------------------------------------------- | +| Options | Choices | Comments | +| ----------------------------- | ---------------------------------------------------------------- | ---------------------------------------------- | +| `IOTOFBase.enableInnerTOF` | `true` (default), `false` | Enable inner TOF barrel layer | +| `IOTOFBase.enableOuterTOF` | `true` (default), `false` | Enable outer TOF barrel layer | +| `IOTOFBase.enableForwardTOF` | `true` (default), `false` | Enable forward TOF endcap | +| `IOTOFBase.enableBackwardTOF` | `true` (default), `false` | Enable backward TOF endcap | +| `IOTOFBase.segmentedInnerTOF` | `false` (default), `true` | Use segmented geometry for inner TOF | +| `IOTOFBase.segmentedOuterTOF` | `false` (default), `true` | Use segmented geometry for outer TOF | +| `IOTOFBase.detectorPattern` | ` ` (default), `v3b`, `v3b1a`, `v3b1b`, `v3b2a`, `v3b2b`, `v3b3` | Optional layout pattern | +| `IOTOFBase.x2x0` | `0.02` (default) | Chip thickness in fractions of the rad. lenght | For example, a geometry with fully cylindrical tracker barrel (for all layers in VD, ML and OT) can be obtained by diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h index b74fc6d6869dd..91d005415891d 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h @@ -21,13 +21,14 @@ namespace iotof { struct IOTOFBaseParam : public o2::conf::ConfigurableParamHelper { - bool enableInnerTOF = true; - bool enableOuterTOF = true; - bool enableForwardTOF = true; - bool enableBackwardTOF = true; - std::string detectorPattern = ""; - bool segmentedInnerTOF = false; // If the inner TOF layer is segmented - bool segmentedOuterTOF = false; // If the outer TOF layer is segmented + bool enableInnerTOF = true; // Enable Inner TOF layer + bool enableOuterTOF = true; // Enable Outer TOF layer + bool enableForwardTOF = true; // Enable Forward TOF layer + bool enableBackwardTOF = true; // Enable Backward TOF layer + std::string detectorPattern = ""; // Layouts of the detector + bool segmentedInnerTOF = false; // If the inner TOF layer is segmented + bool segmentedOuterTOF = false; // If the outer TOF layer is segmented + float x2x0 = 0.02f; // thickness expressed in radiation length, for all layers for the moment O2ParamDef(IOTOFBaseParam, "IOTOFBase"); }; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Detector.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Detector.h index f3c4e3ddd6276..acf754e1b1fa8 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Detector.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Detector.h @@ -60,7 +60,7 @@ class Detector : public o2::base::DetImpl return nullptr; } - void configLayers(bool itof = true, bool otof = true, bool ftof = true, bool btof = true, std::string pattern = "", bool itofSegmented = false, bool otofSegmented = false); + void configLayers(bool itof = true, bool otof = true, bool ftof = true, bool btof = true, std::string pattern = "", bool itofSegmented = false, bool otofSegmented = false, const float x2x0 = 0.02f); void configServices(); void createMaterials(); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx index c056df5fd34ca..d4e34c582bbed 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx @@ -41,7 +41,7 @@ Detector::Detector(bool active) configLayers(iotofPars.enableInnerTOF, iotofPars.enableOuterTOF, iotofPars.enableForwardTOF, iotofPars.enableBackwardTOF, iotofPars.detectorPattern, - iotofPars.segmentedInnerTOF, iotofPars.segmentedOuterTOF); + iotofPars.segmentedInnerTOF, iotofPars.segmentedOuterTOF, iotofPars.x2x0); } Detector::~Detector() @@ -57,12 +57,13 @@ void Detector::ConstructGeometry() createGeometry(); } -void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::string pattern, bool itofSegmented, bool otofSegmented) +void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::string pattern, bool itofSegmented, bool otofSegmented, + const float x2x0) { - float radiusInnerTof = 19.f; - float radiusOuterTof = 85.f; - float lengthInnerTof = 124.f; + const float radiusInnerTof = 19.f; + const float radiusOuterTof = 85.f; + const float lengthInnerTof = 124.f; float lengthOuterTof = 680.f; std::pair radiusRangeDiskTof = {15.f, 100.f}; float zForwardTof = 370.f; @@ -97,23 +98,25 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str } if (itof) { // iTOF mITOFLayer = itofSegmented ? ITOFLayer(std::string{GeometryTGeo::getITOFLayerPattern()}, - radiusInnerTof, 0.f, lengthInnerTof, 0.f, 0.02f, ITOFLayer::kBarrelSegmented, + radiusInnerTof, 0.f, lengthInnerTof, 0.f, x2x0, ITOFLayer::kBarrelSegmented, 24, 5.42, 10.0, 10) : ITOFLayer(std::string{GeometryTGeo::getITOFLayerPattern()}, - radiusInnerTof, 0.f, lengthInnerTof, 0.f, 0.02f, ITOFLayer::kBarrel); + radiusInnerTof, 0.f, lengthInnerTof, 0.f, x2x0, ITOFLayer::kBarrel); } if (otof) { // oTOF mOTOFLayer = otofSegmented ? OTOFLayer(std::string{GeometryTGeo::getOTOFLayerPattern()}, - radiusOuterTof, 0.f, lengthOuterTof, 0.f, 0.02f, OTOFLayer::kBarrelSegmented, + radiusOuterTof, 0.f, lengthOuterTof, 0.f, x2x0, OTOFLayer::kBarrelSegmented, 62, 9.74, 5.0, 54) : OTOFLayer(std::string{GeometryTGeo::getOTOFLayerPattern()}, - radiusOuterTof, 0.f, lengthOuterTof, 0.f, 0.02f, OTOFLayer::kBarrel); + radiusOuterTof, 0.f, lengthOuterTof, 0.f, x2x0, OTOFLayer::kBarrel); } if (ftof) { - mFTOFLayer = FTOFLayer(std::string{GeometryTGeo::getFTOFLayerPattern()}, radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, zForwardTof, 0.02f, FTOFLayer::kDisk); // fTOF + mFTOFLayer = FTOFLayer(std::string{GeometryTGeo::getFTOFLayerPattern()}, + radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, zForwardTof, x2x0, FTOFLayer::kDisk); // fTOF } if (btof) { - mBTOFLayer = BTOFLayer(std::string{GeometryTGeo::getBTOFLayerPattern()}, radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, -zForwardTof, 0.02f, BTOFLayer::kDisk); // bTOF + mBTOFLayer = BTOFLayer(std::string{GeometryTGeo::getBTOFLayerPattern()}, + radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, -zForwardTof, x2x0, BTOFLayer::kDisk); // bTOF } } diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx index 32a24fc46f94c..1744e4c4510bb 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx @@ -40,7 +40,7 @@ Layer::Layer(std::string layerName, float rInn, float rOut, float zLength, float mModulesPerStave(modulesPerStave), mTiltAngle(staveTiltAngle) { - float Si_X0 = 9.5f; + const float Si_X0 = 9.5f; mChipThickness = mX2X0 * Si_X0; std::string name = ""; switch (layout) { diff --git a/Detectors/Upgrades/ALICE3/TRK/README.md b/Detectors/Upgrades/ALICE3/TRK/README.md index 8b3a7984bb233..9730c6f6efff7 100644 --- a/Detectors/Upgrades/ALICE3/TRK/README.md +++ b/Detectors/Upgrades/ALICE3/TRK/README.md @@ -17,6 +17,7 @@ Configurables for various sub-detectors are presented in the following Table: | `TRKBase.layoutVD` | `kIRIS4` (default), `kIRISFullCyl`, `kIRIS5`, `kIRIS4a` | [link to definitions](./base/include/TRKBase/TRKBaseParam.h) | | `TRKBase.layoutML` | `kCylinder`, `kTurboStaves` (default), `kStaggered` | | | `TRKBase.layoutOT` | `kCylinder`, `kTurboStaves`, `kStaggered` (default) | | +| `TRKBase.layoutSRV` | `kPeacockv1` (default), `kLOISymm` | `kLOISymm` produces radially symmetric service volumes, as used in the LoI | For example, a geometry with fully cylindrical tracker barrel (for all layers in VD, ML and OT) can be obtained by ```bash diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h index bb1597f2967e4..21d86378f59ec 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h @@ -104,8 +104,21 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache bool isTrackingFrameCachedMLOT() const { return !mCacheRefXMLOT.empty(); } void fillTrackingFramesCacheMLOT(); - float getSensorRefAlphaMLOT(int index) const { return mCacheRefAlphaMLOT[index]; } - float getSensorXMLOT(int index) const { return mCacheRefXMLOT[index]; } + float getSensorRefAlphaMLOT(int chipId) const + { + assert(getSubDetID(chipId) != 0 && "Called MLOT getter with VD chipId"); + const int local = chipId - getNumberOfActivePartsVD(); + assert(local >= 0 && local < (int)mCacheRefAlphaMLOT.size()); + return mCacheRefAlphaMLOT[local]; + } + + float getSensorXMLOT(int chipId) const + { + assert(getSubDetID(chipId) != 0 && "Called MLOT getter with VD chipId"); + const int local = chipId - getNumberOfActivePartsVD(); + assert(local >= 0 && local < (int)mCacheRefXMLOT.size()); + return mCacheRefXMLOT[local]; + } // create matrix for tracking to local frame for MLOT TGeoHMatrix& createT2LMatrixMLOT(int); @@ -222,8 +235,7 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache std::vector mCacheRefXMLOT; /// cache for X of ML and OT std::vector mCacheRefAlphaMLOT; /// cache for sensor ref alpha ML and OT - eLayout mLayoutML; // Type of segmentation for the middle layers - eLayout mLayoutOT; // Type of segmentation for the outer layers + eMLOTLayout mLayoutMLOT; // ML and OT detector layout design private: static std::unique_ptr sInstance; diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/SegmentationChip.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/SegmentationChip.h index 8110191931e44..7ee569c9bd8e8 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/SegmentationChip.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/SegmentationChip.h @@ -198,7 +198,7 @@ class SegmentationChip zCol = col * PitchColVD + 0.5 * (PitchColVD - constants::VD::petal::layer::length); } else if (subDetID == 1) { // ML/OT xRow = 0.5 * (constants::moduleMLOT::chip::width - constants::moduleMLOT::chip::passiveEdgeReadOut - PitchRowMLOT) - (row * PitchRowMLOT); - zCol = col * PitchRowMLOT + 0.5 * (PitchRowMLOT - constants::moduleMLOT::chip::length); + zCol = col * PitchColMLOT + 0.5 * (PitchColMLOT - constants::moduleMLOT::chip::length); } } diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h index 232e7e04b09cd..63e961db44505 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h @@ -19,18 +19,6 @@ namespace o2 { namespace trk { - -enum eOverallGeom { - kDefaultRadii = 0, // After Upgrade Days March 2024 - kModRadii, -}; - -enum eLayout { - kCylinder = 0, - kTurboStaves, - kStaggered, -}; - enum eVDLayout { kIRIS4 = 0, kIRISFullCyl, @@ -39,20 +27,28 @@ enum eVDLayout { kIRIS4a, }; +enum eMLOTLayout { + kCylindrical = 0, + kSegmented, +}; + +enum eSrvLayout { + kPeacockv1 = 0, + kLOISymm, +}; + struct TRKBaseParam : public o2::conf::ConfigurableParamHelper { std::string configFile = ""; float serviceTubeX0 = 0.02f; // X0 Al2O3 Bool_t irisOpen = false; - eOverallGeom overallGeom = kDefaultRadii; // Overall geometry option, to be used in Detector::buildTRKMiddleOuterLayers - - eLayout layoutML = kTurboStaves; // Type of segmentation for the middle layers - eLayout layoutOT = kStaggered; // Type of segmentation for the outer layers - eVDLayout layoutVD = kIRIS4; // VD detector layout design + eVDLayout layoutVD = kIRIS4; // VD detector layout design + eMLOTLayout layoutMLOT = kSegmented; // ML and OT detector layout design + eSrvLayout layoutSRV = kPeacockv1; // Layout of services - eLayout getLayoutML() const { return layoutML; } - eLayout getLayoutOT() const { return layoutOT; } eVDLayout getLayoutVD() const { return layoutVD; } + eMLOTLayout getLayoutMLOT() const { return layoutMLOT; } + eSrvLayout getLayoutSRV() const { return layoutSRV; } O2ParamDef(TRKBaseParam, "TRKBase"); }; diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx index b5535af781910..1a81723a18f63 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx @@ -76,10 +76,9 @@ void GeometryTGeo::Build(int loadTrans) LOGP(fatal, "Geometry is not loaded"); } - mLayoutML = o2::trk::TRKBaseParam::Instance().getLayoutML(); - mLayoutOT = o2::trk::TRKBaseParam::Instance().getLayoutOT(); + mLayoutMLOT = o2::trk::TRKBaseParam::Instance().getLayoutMLOT(); - LOG(debug) << "Layout ML: " << mLayoutML << ", Layout OL: " << mLayoutOT; + LOG(debug) << "Overall layout ML and OT: " << mLayoutMLOT; mNumberOfLayersMLOT = extractNumberOfLayersMLOT(); mNumberOfPetalsVD = extractNumberOfPetalsVD(); @@ -127,9 +126,9 @@ void GeometryTGeo::Build(int loadTrans) } setSize(numberOfChipsTotal); - fillMatrixCache(loadTrans); defineMLOTSensors(); fillTrackingFramesCacheMLOT(); + fillMatrixCache(loadTrans); } //__________________________________________________________________________ @@ -403,9 +402,9 @@ TString GeometryTGeo::getMatrixPath(int index) const TString path = Form("/cave_1/barrel_1/%s_2/", GeometryTGeo::getTRKVolPattern()); // handling cylindrical configuration for ML and/or OT - // needed bercause of the different numbering scheme in the geometry for the cylindrical case wrt the staggered and turbo ones + // needed because of the different numbering scheme in the geometry for the cylindrical case wrt the staggered and turbo ones if (subDetID == 1) { - if ((layer < 4 && mLayoutML == eLayout::kCylinder) || (layer > 3 && mLayoutOT == eLayout::kCylinder)) { + if ((layer < 4 && mLayoutMLOT == eMLOTLayout::kCylindrical) || (layer > 3 && mLayoutMLOT == eMLOTLayout::kCylindrical)) { stave = 1; mod = 1; chip = 1; diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt index d9908bbfeb1e5..edd9c785d89ce 100644 --- a/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt @@ -28,4 +28,12 @@ o2_add_test_root_macro(CheckTracksCA.C O2::TRKBase O2::TRKSimulation O2::Steer - LABELS trk COMPILE_ONLY) \ No newline at end of file + LABELS trk COMPILE_ONLY) + +o2_add_test_root_macro(CheckClusters.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsTRK + O2::SimulationDataFormat + O2::Framework + O2::TRKBase + O2::TRKSimulation + LABELS trk COMPILE_ONLY) diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckClusters.C b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckClusters.C new file mode 100644 index 0000000000000..327577102d86e --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckClusters.C @@ -0,0 +1,417 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CheckClusters.C +/// \brief Macro to check TRK clusters and compare cluster positions to MC hit positions + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DataFormatsTRK/Cluster.h" +#include "DataFormatsTRK/ROFRecord.h" +#include "TRKBase/GeometryTGeo.h" +#include "TRKBase/SegmentationChip.h" +#include "TRKSimulation/Hit.h" +#include "ITSMFTSimulation/AlpideSimResponse.h" +#include "CCDB/BasicCCDBManager.h" +#include "MathUtils/Cartesian.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "DetectorsBase/GeometryManager.h" +#include "Framework/Logger.h" +#endif + +void CheckClusters(const std::string& clusfile = "o2clus_trk.root", + const std::string& hitfile = "o2sim_HitsTRK.root", + const std::string& inputGeom = "o2sim_geometry.root", + const std::string& ccdbUrl = "http://alice-ccdb.cern.ch", + long ccdbTimestamp = -1, + bool batch = false) +{ + gROOT->SetBatch(batch); + + using o2::MCCompLabel; + using ROFRec = o2::trk::ROFRecord; + using MC2ROF = o2::trk::MC2ROFRecord; + using HitVec = std::vector; + using MC2HITS_map = std::unordered_map; // maps (trackID << 32) + chipID -> hit index + + // ── Chip response (for hit-segment propagation to charge-collection plane) ── + // Fetches the same AlpideSimResponse from CCDB as the digitizer (IT3/Calib/APTSResponse) + // and computes Y-intersection planes with the same formulas from Digitizer::init() + auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance(); + ccdbMgr.setURL(ccdbUrl); + if (ccdbTimestamp > 0) { + ccdbMgr.setTimestamp(ccdbTimestamp); + } + auto* alpResp = ccdbMgr.get("IT3/Calib/APTSResponse"); + if (!alpResp) { + LOGP(fatal, "Cannot retrieve AlpideSimResponse from CCDB at {}", ccdbUrl); + return; + } + const float depthMax = alpResp->getDepthMax(); + + // ── Y-plane shifts: why VD and ML/OT need different values ──────────────── + // + // The APTS pixel response (AlpideSimResponse) uses an internal Y axis where: + // + // y = depthMax ── beam-entry (top) surface + // y = 0 ── charge-collection plane ← where clusters form + // y < 0 ── substrate (no response) + // + // The digitizer (Digitizer::init()) brings hit Y coordinates into this frame + // by adding a per-sub-detector shift before querying the response: + // + // y_APTS = y_local + shift [Digitizer.cxx ::processHit] + // + // The collection plane (y_APTS = 0) is therefore at y_local = −shift + // in the detector local frame. That is the Y value used here when + // propagating the MC hit segment to a single representative point. + // + // ── VD (vertex detector – curved sensors) ───────────────────────────────── + // After SegmentationChip::curvedToFlat() (convention: yFlat = dist − R): + // outer face (beam-entry): yFlat = +halfThickVD = +10 µm + // inner face (exit): yFlat = −halfThickVD = −10 µm + // The digitizer uses: + // + // mSimRespVDShift = depthMax − halfThickVD + // + // so the collection plane (y_APTS = 0) corresponds to: + // + // yPlaneVD = alice3resp::responseYShift = +5 µm + // + // i.e. 5 µm inside from the outer (entry) face. ✓ + // + // ── ML/OT (middle/outer tracker – flat sensors) ──────────────────────────── + // The local Y origin is at the GEOMETRIC CENTRE of the sensor volume. + // The outer (entry) surface is at y_local = +SiliconThicknessMLOT/2. + // The digitizer uses: + // + // mSimRespMLOTShift = depthMax − SiliconThicknessMLOT / 2 + // + // so the collection plane (y_APTS = 0) is at: + // + // yPlaneMLOT = SiliconThicknessMLOT/2 − depthMax + // + // ────────────────────────────────────────────────────────────────────────── + const float halfThicknessMLOT = o2::trk::SegmentationChip::SiliconThicknessMLOT / 2.f; + const float yPlaneVD = (float)o2::trk::constants::alice3resp::responseYShift; // VD: collection plane 5 µm inside outer (entry) face in flat local frame + const float yPlaneMLOT = halfThicknessMLOT - depthMax; // MLOT: entry @ +halfThick, collection depthMax below entry + LOGP(info, "Response depthMax = {:.4f} cm | VD Y-plane = {:.4f} cm | ML/OT Y-plane = {:.4f} cm", + depthMax, yPlaneVD, yPlaneMLOT); + + // ── Geometry ─────────────────────────────────────────────────────────────── + o2::base::GeometryManager::loadGeometry(inputGeom); + auto gman = o2::trk::GeometryTGeo::Instance(); + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); + + // ── Hits ─────────────────────────────────────────────────────────────────── + TFile fileH(hitfile.data()); + auto* hitTree = dynamic_cast(fileH.Get("o2sim")); + if (!hitTree) { + LOGP(error, "Cannot find o2sim tree in {}", hitfile); + return; + } + std::vector mc2hitVec; + std::vector hitVecPool; + mc2hitVec.resize(hitTree->GetEntries()); + hitVecPool.resize(hitTree->GetEntries(), nullptr); + + // ── Clusters ─────────────────────────────────────────────────────────────── + TFile fileC(clusfile.data()); + auto* clusTree = dynamic_cast(fileC.Get("o2sim")); + if (!clusTree) { + LOGP(error, "Cannot find o2sim tree in {}", clusfile); + return; + } + + std::vector* clusArr = nullptr; + std::vector* rofRecVecP = nullptr; + std::vector* patternsPtr = nullptr; + clusTree->SetBranchAddress("TRKClusterComp", &clusArr); + clusTree->SetBranchAddress("TRKClustersROF", &rofRecVecP); + if (clusTree->GetBranch("TRKClusterPatt") != nullptr) { + clusTree->SetBranchAddress("TRKClusterPatt", &patternsPtr); + } + + o2::dataformats::MCTruthContainer* clusLabArr = nullptr; + std::vector mc2rofVec, *mc2rofVecP = &mc2rofVec; + bool hasMC = (clusTree->GetBranch("TRKClusterMCTruth") != nullptr); + if (hasMC) { + clusTree->SetBranchAddress("TRKClusterMCTruth", &clusLabArr); + clusTree->SetBranchAddress("TRKClustersMC2ROF", &mc2rofVecP); + } + + clusTree->GetEntry(0); + const unsigned int nROFRec = rofRecVecP ? (unsigned int)rofRecVecP->size() : 0u; + LOGP(info, "Number of ROF records: {}", nROFRec); + auto pattIt = patternsPtr ? patternsPtr->cbegin() : std::vector::const_iterator{}; + + // ── Build per-ROF MC event range ─────────────────────────────────────────── + std::vector mcEvMin(nROFRec, (int)hitTree->GetEntries()); + std::vector mcEvMax(nROFRec, -1); + if (hasMC) { + for (int imc = (int)mc2rofVec.size(); imc--;) { + const auto& mc2rof = mc2rofVec[imc]; + if (mc2rof.rofRecordID < 0) { + continue; + } + for (unsigned int irfd = mc2rof.maxROF - mc2rof.minROF + 1; irfd--;) { + unsigned int irof = mc2rof.rofRecordID + irfd; + if (irof >= nROFRec) { + continue; + } + if (mcEvMin[irof] > imc) { + mcEvMin[irof] = imc; + } + if (mcEvMax[irof] < imc) { + mcEvMax[irof] = imc; + } + } + } + } + + // ── Output ───────────────────────────────────────────────────────────────── + TFile fout("CheckClusters.root", "recreate"); + // columns: event, MC track label, + // local hit x/z (flat frame), global hit x/y/z (midpoint), + // global cluster x/y/z, local cluster x/z, + // residuals dx/dz (local, cluster - hit), + // ROF frame, cluster size, chipID, layer, disk, subDetID, row, col, pt + TNtuple nt("ntc", "TRK cluster ntuple", + "event:mcTrackID:hitLocX:hitLocZ:hitGlobX:hitGlobY:hitGlobZ:clusGlobX:clusGlobY:clusGlobZ:clusLocX:clusLocZ:rofFrame:clusSize:chipID:layer:disk:subdet:row:col:pt"); + + // ── Counters ─────────────────────────────────────────────────────────────── + long nTot{0}, nInvalidLabel{0}, nNoMCHit{0}, nValid{0}; + + // ── Main loop ────────────────────────────────────────────────────────────── + for (unsigned int irof = 0; irof < nROFRec; irof++) { + const auto& rofRec = (*rofRecVecP)[irof]; + + // Cache MC hit events for this ROF + if (hasMC) { + for (int im = mcEvMin[irof]; im <= mcEvMax[irof]; im++) { + if (hitVecPool[im] == nullptr) { + hitTree->SetBranchAddress("TRKHit", &hitVecPool[im]); + hitTree->GetEntry(im); + auto& mc2hit = mc2hitVec[im]; + const auto* hv = hitVecPool[im]; + for (int ih = (int)hv->size(); ih--;) { + const auto& hit = (*hv)[ih]; + uint64_t key = (uint64_t(hit.GetTrackID()) << 32) + hit.GetDetectorID(); + mc2hit.emplace(key, ih); + } + } + } + } + + for (int icl = 0; icl < rofRec.getNEntries(); icl++) { + const int clEntry = rofRec.getFirstEntry() + icl; + const auto& cluster = (*clusArr)[clEntry]; + nTot++; + + // ── Parse pattern → center-of-gravity within bounding box ────────── + // The cluster stores the bounding-box top-left pixel (row, col). + // The pattern stream encodes [rowSpan, colSpan, bitmap...] for each cluster. + // We accumulate pixel row/col offsets to obtain a sub-pixel CoG correction. + float cogDr{0.f}, cogDc{0.f}; // mean offsets from bbox origin (pixels) + if (patternsPtr) { + const uint8_t rowSpan = *pattIt++; + const uint8_t colSpan = *pattIt++; + const int nBytes = (rowSpan * colSpan + 7) / 8; + int nPix{0}, pixIdx{0}; + for (int ib = 0; ib < nBytes; ib++) { + const uint8_t byte = *pattIt++; + for (int bit = 7; bit >= 0 && pixIdx < rowSpan * colSpan; bit--, pixIdx++) { + if (byte & (1 << bit)) { + cogDr += pixIdx / colSpan; + cogDc += pixIdx % colSpan; + nPix++; + } + } + } + if (nPix > 1) { + cogDr /= nPix; + cogDc /= nPix; + } + } + + // ── Cluster local → global (CoG position) ───────────────────────────── + // Get local coords of the bounding-box corner pixel, then apply the + // fractional CoG displacement using the pixel pitch. + // Formula from detectorToLocalUnchecked: + // VD : xRow = 0.5*(width[lay]-pitchRow) - row*pitchRow → row↑ xRow↓ + // zCol = col*pitchCol + 0.5*(pitchCol-length) → col↑ zCol↑ + // MLOT: same structure with MLOT pitches + float clLocX{0.f}, clLocZ{0.f}; + o2::trk::SegmentationChip::detectorToLocalUnchecked( + cluster.row, cluster.col, clLocX, clLocZ, + cluster.subDetID, cluster.layer, cluster.disk); + const float pitchRow = (cluster.subDetID == 0) + ? o2::trk::SegmentationChip::PitchRowVD + : o2::trk::SegmentationChip::PitchRowMLOT; + const float pitchCol = (cluster.subDetID == 0) + ? o2::trk::SegmentationChip::PitchColVD + : o2::trk::SegmentationChip::PitchColMLOT; + clLocX -= cogDr * pitchRow; // increasing row → decreasing xRow + clLocZ += cogDc * pitchCol; // increasing col → increasing zCol + const float yResponse = (cluster.subDetID == 0) ? yPlaneVD : yPlaneMLOT; + // For VD the L2G matrix is built in the *curved* local frame (quasi-Cartesian, + // origin at the beam axis). Convert flat (clLocX, 0) → curved (xC, yC) first. + // For MLOT (flat sensors) the local frame is already Cartesian: pass directly. + // clLocX is already in the flat frame from detectorToLocalUnchecked + CoG and + // does NOT need any further transformation for the residual comparison. + o2::math_utils::Point3D locC; + if (cluster.subDetID == 0) { + auto cv = o2::trk::SegmentationChip::flatToCurved(cluster.layer, clLocX, 0.f); + locC = {cv.X(), cv.Y(), clLocZ}; + } else { + locC = {clLocX, yResponse, clLocZ}; + } + auto gloC = gman->getMatrixL2G(cluster.chipID)(locC); + + if (!hasMC || clusLabArr == nullptr) { + // No MC info: just fill geometry columns, leave residuals as 0 + std::array data = { + -1.f, -1.f, + 0.f, 0.f, 0.f, 0.f, 0.f, + (float)gloC.X(), (float)gloC.Y(), (float)gloC.Z(), + clLocX, clLocZ, + (float)rofRec.getROFrame(), (float)cluster.size, (float)cluster.chipID, + (float)cluster.layer, (float)cluster.disk, (float)cluster.subDetID, + (float)cluster.row, (float)cluster.col, -1.f}; + nt.Fill(data.data()); + continue; + } + + // ── MC label ─────────────────────────────────────────────────────── + const auto& labels = clusLabArr->getLabels(clEntry); + if (labels.empty() || !labels[0].isValid()) { + nInvalidLabel++; + continue; + } + const auto& lab = labels[0]; + const int trID = lab.getTrackID(); + const int evID = lab.getEventID(); + + // ── Find matching MC hit ──────────────────────────────────────────── + const auto& mc2hit = mc2hitVec[evID]; + uint64_t key = (uint64_t(trID) << 32) + cluster.chipID; + auto hitEntry = mc2hit.find(key); + if (hitEntry == mc2hit.end()) { + nNoMCHit++; + continue; + } + const auto& hit = (*hitVecPool[evID])[hitEntry->second]; + const float pt = TMath::Hypot(hit.GetPx(), hit.GetPy()); + + // ── Hit global midpoint ──────────────────────────────────────────── + const auto& gloHend = hit.GetPos(); + const auto& gloHsta = hit.GetPosStart(); + o2::math_utils::Point3D gloHmid( + 0.5f * (gloHend.X() + gloHsta.X()), + 0.5f * (gloHend.Y() + gloHsta.Y()), + 0.5f * (gloHend.Z() + gloHsta.Z())); + + // ── Hit global → local ───────────────────────────── + o2::math_utils::Point3D locHsta = gman->getMatrixL2G(cluster.chipID) ^ (gloHsta); // inverse L2G + o2::math_utils::Point3D locHend = gman->getMatrixL2G(cluster.chipID) ^ (gloHend); // inverse L2G + + // ── Propagate hit segment to the sensor response surface ─────────────── + // Rather than the geometric midpoint, find where the track segment crosses + // the response plane (y = responseYShift in the flat local frame). + // For VD (curved): convert both endpoints to flat frame first. + // For ML/OT (flat): use local coordinates directly. + float hitLocX{0.f}, hitLocZ{0.f}; + if (cluster.subDetID == 0) { // VD – curved sensor + auto flatSta = o2::trk::SegmentationChip::curvedToFlat(cluster.layer, locHsta.X(), locHsta.Y()); + auto flatEnd = o2::trk::SegmentationChip::curvedToFlat(cluster.layer, locHend.X(), locHend.Y()); + float x0 = flatSta.X(), y0 = flatSta.Y(), z0 = locHsta.Z(); + float dltx = flatEnd.X() - x0, dlty = flatEnd.Y() - y0, dltz = locHend.Z() - z0; + float r = (std::abs(dlty) > 1e-9f) ? (yPlaneVD - y0) / dlty : 0.5f; + hitLocX = x0 + r * dltx; + hitLocZ = z0 + r * dltz; + } else { // ML/OT – flat sensor + float x0 = locHsta.X(), y0 = locHsta.Y(), z0 = locHsta.Z(); + float dltx = locHend.X() - x0, dlty = locHend.Y() - y0, dltz = locHend.Z() - z0; + float r = (std::abs(dlty) > 1e-9f) ? (yPlaneMLOT - y0) / dlty : 0.5f; + hitLocX = x0 + r * dltx; + hitLocZ = z0 + r * dltz; + } + + nValid++; + std::array data = { + (float)evID, (float)trID, + hitLocX, hitLocZ, + (float)gloHmid.X(), (float)gloHmid.Y(), (float)gloHmid.Z(), + (float)gloC.X(), (float)gloC.Y(), (float)gloC.Z(), + clLocX, clLocZ, + (float)rofRec.getROFrame(), (float)cluster.size, (float)cluster.chipID, + (float)cluster.layer, (float)cluster.disk, (float)cluster.subDetID, + (float)cluster.row, (float)cluster.col, pt}; + nt.Fill(data.data()); + } + } + + // ── Summary ──────────────────────────────────────────────────────────────── + LOGP(info, "=== TRK Cluster vs Hit Summary ==="); + LOGP(info, "Total clusters: {}", nTot); + LOGP(info, "Valid (hit matched): {}", nValid); + LOGP(info, "Invalid/noise MC labels: {}", nInvalidLabel); + LOGP(info, "MC hit not found: {}", nNoMCHit); + // ── Visualisation ────────────────────────────────────────────────────────── + auto canvGlobal = new TCanvas("canvGlobal", "Cluster global positions", 1600, 800); + canvGlobal->Divide(2, 1); + canvGlobal->cd(1); + nt.Draw("clusGlobY:clusGlobX>>h_yx(500,-50,50,500,-50,50)", "", "colz"); + canvGlobal->cd(2); + nt.Draw("clusGlobY:clusGlobZ>>h_yz(500,-100,100,500,-50,50)", "", "colz"); + canvGlobal->SaveAs("trk_clusters_global.png"); + + auto canvRes = new TCanvas("canvRes", "Residuals (cluster - hit) [cm]", 1600, 1200); + canvRes->Divide(2, 3); + canvRes->cd(1)->SetLogy(); + nt.Draw("hitLocX-clusLocX>>h_dx_VD(200,-0.02,0.02)", "subdet==0&&event>=0"); + canvRes->cd(2)->SetLogy(); + nt.Draw("hitLocZ-clusLocZ>>h_dz_VD(200,-0.02,0.02)", "subdet==0&&event>=0"); + canvRes->cd(3)->SetLogy(); + nt.Draw("hitLocX-clusLocX>>h_dx_MLOT(200,-0.02,0.02)", "subdet==1&&event>=0"); + canvRes->cd(4)->SetLogy(); + nt.Draw("hitLocZ-clusLocZ>>h_dz_MLOT(200,-0.02,0.02)", "subdet==1&&event>=0"); + canvRes->cd(5)->SetLogz(); + nt.Draw("hitLocX-clusLocX:hitLocZ-clusLocZ>>h_dxdz_VD(200,-0.02,0.02,200,-0.02,0.02)", "subdet==0&&event>=0", "colz"); + canvRes->cd(6); + nt.Draw("hitLocX-clusLocX:hitLocZ-clusLocZ>>h_dxdz_MLOT(200,-0.02,0.02,200,-0.02,0.02)", "subdet==1&&event>=0", "colz"); + canvRes->SaveAs("trk_residuals.png"); + + auto canvResVsLayer = new TCanvas("canvResVsLayer", "Residuals vs layer", 1600, 600); + canvResVsLayer->Divide(2, 1); + canvResVsLayer->cd(1); + nt.Draw("hitLocX-clusLocX:layer>>h_dx_vs_lay(20,0,20,200,-0.02,0.02)", "event>=0", "prof"); + canvResVsLayer->cd(2); + nt.Draw("hitLocZ-clusLocZ:layer>>h_dz_vs_lay(20,0,20,200,-0.02,0.02)", "event>=0", "prof"); + canvResVsLayer->SaveAs("trk_residuals_vs_layer.png"); + + fout.cd(); + nt.Write(); + fout.Close(); + + LOGP(info, "Output saved to CheckClusters.root and PNG files"); +} diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckDigits.C b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckDigits.C index 5d60592a96f41..618dbe929a943 100644 --- a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckDigits.C +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckDigits.C @@ -19,11 +19,13 @@ #include #include #include +#include +#include #include "TRKBase/SegmentationChip.h" #include "TRKBase/GeometryTGeo.h" #include "DataFormatsITSMFT/Digit.h" -#include "ITSMFTSimulation/Hit.h" +#include "TRKSimulation/Hit.h" #include "MathUtils/Utils.h" #include "SimulationDataFormat/ConstMCTruthContainer.h" #include "SimulationDataFormat/IOMCTruthContainerView.h" @@ -36,14 +38,50 @@ #define ENABLE_UPGRADES -void CheckDigits(std::string digifile = "trkdigits.root", std::string hitfile = "o2sim_HitsTRK.root", std::string inputGeom = "", std::string paramfile = "o2sim_par.root") +void addTLines(float pitch) { + // Add grid lines at multiples of pitch on the current pad + if (!gPad) + return; + + gPad->Update(); + + Double_t xmin = gPad->GetUxmin(); + Double_t xmax = gPad->GetUxmax(); + Double_t ymin = gPad->GetUymin(); + Double_t ymax = gPad->GetUymax(); + + // Calculate the first vertical line position (multiple of pitch) + int nLinesX = 0; + for (float x = xmin; x <= xmax && nLinesX < 1000; x += pitch, nLinesX++) { + TLine* line = new TLine(x, ymin, x, ymax); + line->SetLineStyle(2); + line->SetLineColor(kGray); + line->Draw("same"); + } + + // Calculate the first horizontal line position (multiple of pitch) + int nLinesY = 0; + for (float y = ymin; y <= ymax && nLinesY < 1000; y += pitch, nLinesY++) { + TLine* line = new TLine(xmin, y, xmax, y); + line->SetLineStyle(2); + line->SetLineColor(kGray); + line->Draw("same"); + } + + gPad->Modified(); + gPad->Update(); +} + +void CheckDigits(std::string digifile = "trkdigits.root", std::string hitfile = "o2sim_HitsTRK.root", std::string inputGeom = "o2sim_geometry.root", std::string paramfile = "o2sim_par.root") +{ + gStyle->SetPalette(55); using namespace o2::base; using namespace o2::trk; using o2::itsmft::Digit; - using o2::itsmft::Hit; + using o2::trk::Hit; using o2::trk::SegmentationChip; @@ -64,7 +102,7 @@ void CheckDigits(std::string digifile = "trkdigits.root", std::string hitfile = TFile* hitFile = TFile::Open(hitfile.data()); TTree* hitTree = (TTree*)hitFile->Get("o2sim"); int nevH = hitTree->GetEntries(); // hits are stored as one event per entry - std::vector*> hitArray(nevH, nullptr); + std::vector*> hitArray(nevH, nullptr); std::vector> mc2hitVec(nevH); @@ -273,110 +311,124 @@ void CheckDigits(std::string digifile = "trkdigits.root", std::string hitfile = auto canvXY = new TCanvas("canvXY", "", 1600, 2400); canvXY->Divide(2, 3); canvXY->cd(1); - nt->Draw("y:x >>h_y_vs_x_VD(1000, -3, 3, 1000, -3, 3)", "id < 36 ", "colz"); + nt->Draw("y:x >>h_y_vs_x_VD(1000, -3, 3, 1000, -3, 3)", "id < 12 ", "colz"); canvXY->cd(2); - nt->Draw("y:z>>h_y_vs_z_VD(1000, -26, 26, 1000, -3, 3)", "id < 36 ", "colz"); + nt->Draw("y:z>>h_y_vs_z_VD(1000, -26, 26, 1000, -3, 3)", "id < 12 ", "colz"); canvXY->cd(3); - nt->Draw("y:x>>h_y_vs_x_ML(1000, -25, 25, 1000, -25, 25)", "id >= 36 && id < 106 ", "colz"); + nt->Draw("y:x>>h_y_vs_x_ML(1000, -25, 25, 1000, -25, 25)", "id >= 12 && id < 5132 ", "colz"); canvXY->cd(4); - nt->Draw("y:z>>h_y_vs_z_ML(1000, -70, 70, 1000, -25, 25)", "id >= 36 && id < 106 ", "colz"); + nt->Draw("y:z>>h_y_vs_z_ML(1000, -70, 70, 1000, -25, 25)", "id >= 12 && id < 5132 ", "colz"); canvXY->cd(5); - nt->Draw("y:x>>h_y_vs_x_OT(1000, -85, 85, 1000, -85, 85)", "id >= 106 ", "colz"); + nt->Draw("y:x>>h_y_vs_x_OT(1000, -85, 85, 1000, -85, 85)", "id >= 5132 ", "colz"); canvXY->cd(6); - nt->Draw("y:z>>h_y_vs_z_OT(1000, -85, 85, 1000, -130, 130)", "id >= 106 ", "colz"); + nt->Draw("y:z>>h_y_vs_z_OT(1000, -85, 85, 1000, -130, 130)", "id >= 5132 ", "colz"); canvXY->SaveAs("trkdigits_y_vs_x_vs_z.pdf"); // z distributions auto canvZ = new TCanvas("canvZ", "", 800, 2400); canvZ->Divide(1, 3); canvZ->cd(1); - nt->Draw("z>>h_z_VD(500, -26, 26)", "id < 36 "); + nt->Draw("z>>h_z_VD(500, -26, 26)", "id < 12 "); canvZ->cd(2); - nt->Draw("z>>h_z_ML(500, -70, 70)", "id >= 36 && id < 106 "); + nt->Draw("z>>h_z_ML(500, -70, 70)", "id >= 12 && id < 5132 "); canvZ->cd(3); - nt->Draw("z>>h_z_OT(500, -85, 85)", "id >= 106 "); + nt->Draw("z>>h_z_OT(500, -85, 85)", "id >= 5132 "); canvZ->SaveAs("trkdigits_z.pdf"); // dz distributions (difference between local position of digits and hits in x and z) auto canvdZ = new TCanvas("canvdZ", "", 800, 2400); canvdZ->Divide(1, 3); canvdZ->cd(1); - nt->Draw("dz>>h_dz_VD(500, -0.05, 0.05)", "id < 36 "); + nt->Draw("dz>>h_dz_VD(500, -0.05, 0.05)", "id < 12 "); canvdZ->cd(2); - nt->Draw("dz>>h_dz_ML(500, -0.05, 0.05)", "id >= 36 && id < 106 "); + nt->Draw("dz>>h_dz_ML(500, -0.05, 0.05)", "id >= 12 && id < 5132 "); canvdZ->cd(3); - nt->Draw("dz>>h_dz_OT(500, -0.05, 0.05)", "id >= 106 "); + nt->Draw("dz>>h_dz_OT(500, -0.05, 0.05)", "id >= 5132 "); canvdZ->SaveAs("trkdigits_dz.pdf"); + canvdZ->SaveAs("trkdigits_dz.root"); // distributions of differences between local positions of digits and hits in x and z auto canvdXdZ = new TCanvas("canvdXdZ", "", 1600, 2400); canvdXdZ->Divide(2, 3); canvdXdZ->cd(1); - nt->Draw("dx:dz>>h_dx_vs_dz_VD(300, -0.03, 0.03, 300, -0.03, 0.03)", "id < 36", "colz"); + nt->Draw("dx:dz>>h_dx_vs_dz_VD(500, -0.005, 0.005, 500, -0.005, 0.005)", "id < 12", "colz"); + addTLines(o2::trk::SegmentationChip::PitchRowVD); auto h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_VD"); LOG(info) << "dx, dz"; Info("VD", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); Info("VD", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); canvdXdZ->cd(2); - nt->Draw("dx:dz>>h_dx_vs_dz_VD_z(300, -0.03, 0.03, 300, -0.03, 0.03)", "id < 36 && abs(z)<2", "colz"); + nt->Draw("dx:dz>>h_dx_vs_dz_VD_z(500, -0.005, 0.005, 500, -0.005, 0.005)", "id < 12 && abs(z)<0.5", "colz"); + addTLines(o2::trk::SegmentationChip::PitchRowVD); h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_VD_z"); - Info("VD |z|<2", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); - Info("VD |z|<2", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); + Info("VD |z|<1", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); + Info("VD |z|<1", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); canvdXdZ->cd(3); - nt->Draw("dx:dz>>h_dx_vs_dz_ML(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 36 && id < 106", "colz"); + nt->Draw("dx:dz>>h_dx_vs_dz_ML(600, -0.03, 0.03, 600, -0.03, 0.03)", "id >= 12 && id < 5132", "colz"); + addTLines(o2::trk::SegmentationChip::PitchRowMLOT); h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_ML"); Info("ML", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); Info("ML", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); canvdXdZ->cd(4); - nt->Draw("dx:dz>>h_dx_vs_dz_ML_z(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 36 && id < 106 && abs(z)<2", "colz"); + nt->Draw("dx:dz>>h_dx_vs_dz_ML_z(600, -0.03, 0.03, 600, -0.03, 0.03)", "id >= 12 && id < 5132 && abs(z)<2", "colz"); + addTLines(o2::trk::SegmentationChip::PitchRowMLOT); h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_ML_z"); Info("ML |z|<2", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); Info("ML |z|<2", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); canvdXdZ->SaveAs("trkdigits_dx_vs_dz.pdf"); canvdXdZ->cd(5); - nt->Draw("dx:dz>>h_dx_vs_dz_OT(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 106", "colz"); + nt->Draw("dx:dz>>h_dx_vs_dz_OT(600, -0.03, 0.03, 600, -0.03, 0.03)", "id >= 5132", "colz"); + addTLines(o2::trk::SegmentationChip::PitchRowMLOT); h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_OT"); Info("OT", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); Info("OT", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); canvdXdZ->cd(6); - nt->Draw("dx:dz>>h_dx_vs_dz_OT_z(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 106 && abs(z)<2", "colz"); + nt->Draw("dx:dz>>h_dx_vs_dz_OT_z(600, -0.03, 0.03, 600, -0.03, 0.03)", "id >= 5132 && abs(z)<2", "colz"); h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_OT_z"); + addTLines(o2::trk::SegmentationChip::PitchRowMLOT); Info("OT |z|<2", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); Info("OT |z|<2", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); canvdXdZ->SaveAs("trkdigits_dx_vs_dz.pdf"); + canvdXdZ->SaveAs("trkdigits_dx_vs_dz.root"); // distribution of differences between hit start and hit end in local coordinates auto canvdXdZHit = new TCanvas("canvdXdZHit", "", 1600, 2400); canvdXdZHit->Divide(2, 3); canvdXdZHit->cd(1); - nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_VD(300, -0.03, 0.03, 300, -0.03, 0.03)", "id < 36", "colz"); + nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_VD(300, -0.03, 0.03, 300, -0.03, 0.03)", "id < 12", "colz"); + addTLines(o2::trk::SegmentationChip::PitchRowVD); LOG(info) << "dxH, dzH"; h = (TH2F*)gPad->GetPrimitive("h_dxH_vs_dzH_VD"); Info("VD", "RMS(dxH)=%.1f mu", h->GetRMS(2) * 1e4); Info("VD", "RMS(dzH)=%.1f mu", h->GetRMS(1) * 1e4); canvdXdZHit->cd(2); - nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_VD_z(300, -0.03, 0.03, 300, -0.03, 0.03)", "id < 36 && abs(z)<2", "colz"); + nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_VD_z(300, -0.03, 0.03, 300, -0.03, 0.03)", "id < 12 && abs(z)<2", "colz"); + addTLines(o2::trk::SegmentationChip::PitchRowVD); h = (TH2F*)gPad->GetPrimitive("h_dxH_vs_dzH_VD_z"); Info("VD |z|<2", "RMS(dxH)=%.1f mu", h->GetRMS(2) * 1e4); Info("VD |z|<2", "RMS(dzH)=%.1f mu", h->GetRMS(1) * 1e4); canvdXdZHit->cd(3); - nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_ML(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 36 && id < 106", "colz"); + nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_ML(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 12 && id < 5132", "colz"); + addTLines(o2::trk::SegmentationChip::PitchRowMLOT); h = (TH2F*)gPad->GetPrimitive("h_dxH_vs_dzH_ML"); Info("ML", "RMS(dxH)=%.1f mu", h->GetRMS(2) * 1e4); Info("ML", "RMS(dzH)=%.1f mu", h->GetRMS(1) * 1e4); canvdXdZHit->cd(4); - nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_ML_z(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 36 && id < 106 && abs(z)<2", "colz"); + nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_ML_z(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 12 && id < 5132 && abs(z)<2", "colz"); + addTLines(o2::trk::SegmentationChip::PitchRowMLOT); h = (TH2F*)gPad->GetPrimitive("h_dxH_vs_dzH_ML_z"); Info("ML |z|<2", "RMS(dxH)=%.1f mu", h->GetRMS(2) * 1e4); Info("ML |z|<2", "RMS(dzH)=%.1f mu", h->GetRMS(1) * 1e4); canvdXdZHit->SaveAs("trkdigits_dxH_vs_dzH.pdf"); canvdXdZHit->cd(5); - nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_OT(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 106", "colz"); + nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_OT(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 5132", "colz"); + addTLines(o2::trk::SegmentationChip::PitchRowMLOT); h = (TH2F*)gPad->GetPrimitive("h_dxH_vs_dzH_OT"); Info("OT", "RMS(dxH)=%.1f mu", h->GetRMS(2) * 1e4); Info("OT", "RMS(dzH)=%.1f mu", h->GetRMS(1) * 1e4); canvdXdZHit->cd(6); - nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_OT_z(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 106 && abs(z)<2", "colz"); + nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_OT_z(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 5132 && abs(z)<2", "colz"); + addTLines(o2::trk::SegmentationChip::PitchRowMLOT); h = (TH2F*)gPad->GetPrimitive("h_dxH_vs_dzH_OT_z"); Info("OT |z|<2", "RMS(dxH)=%.1f mu", h->GetRMS(2) * 1e4); Info("OT |z|<2", "RMS(dzH)=%.1f mu", h->GetRMS(1) * 1e4); diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt index 01ddc783d192b..b9866c7d6aa4d 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt @@ -12,12 +12,14 @@ o2_add_library(TRKReconstruction TARGETVARNAME targetName SOURCES src/TimeFrame.cxx + src/Clusterer.cxx PUBLIC_LINK_LIBRARIES O2::ITStracking O2::GPUCommon Microsoft.GSL::GSL O2::CommonConstants O2::DataFormatsITSMFT + O2::DataFormatsTRK O2::SimulationDataFormat O2::ITSBase O2::ITSReconstruction @@ -31,4 +33,5 @@ o2_add_library(TRKReconstruction o2_target_root_dictionary(TRKReconstruction HEADERS include/TRKReconstruction/TimeFrame.h + include/TRKReconstruction/Clusterer.h LINKDEF src/TRKReconstructionLinkDef.h) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/Clusterer.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/Clusterer.h new file mode 100644 index 0000000000000..abddafa312fb9 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/Clusterer.h @@ -0,0 +1,182 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Clusterer.h +/// \brief Definition of the TRK cluster finder + +#ifndef ALICEO2_TRK_CLUSTERER_H +#define ALICEO2_TRK_CLUSTERER_H + +// uncomment to allow diagonal clusters, e.g. |* | +// | *| +#define _ALLOW_DIAGONAL_TRK_CLUSTERS_ + +#include "DataFormatsITSMFT/Digit.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsITSMFT/ClusterPattern.h" +#include "DataFormatsTRK/Cluster.h" +#include "DataFormatsTRK/ROFRecord.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "TRKBase/Specs.h" +#include +#include +#include +#include +#include +#include + +namespace o2::trk +{ + +class GeometryTGeo; + +class Clusterer +{ + public: + static constexpr int MaxLabels = 10; + static constexpr int MaxHugeClusWarn = 5; + + using Digit = o2::itsmft::Digit; + using DigROFRecord = o2::itsmft::ROFRecord; + using DigMC2ROFRecord = o2::itsmft::MC2ROFRecord; + using ClusterTruth = o2::dataformats::MCTruthContainer; + using ConstDigitTruth = o2::dataformats::ConstMCTruthContainerView; + using Label = o2::MCCompLabel; + + //---------------------------------------------- + struct BBox { + uint16_t chipID = 0xffff; + uint16_t rowMin = 0xffff, colMin = 0xffff; + uint16_t rowMax = 0, colMax = 0; + explicit BBox(uint16_t c) : chipID(c) {} + bool isInside(uint16_t r, uint16_t c) const { return r >= rowMin && r <= rowMax && c >= colMin && c <= colMax; } + uint16_t rowSpan() const { return rowMax - rowMin + 1; } + uint16_t colSpan() const { return colMax - colMin + 1; } + bool isAcceptableSize() const + { + return rowSpan() <= o2::itsmft::ClusterPattern::MaxRowSpan && + colSpan() <= o2::itsmft::ClusterPattern::MaxColSpan; + } + void adjust(uint16_t r, uint16_t c) + { + if (r < rowMin) { + rowMin = r; + } + if (r > rowMax) { + rowMax = r; + } + if (c < colMin) { + colMin = c; + } + if (c > colMax) { + colMax = c; + } + } + }; + + //---------------------------------------------- + struct ClustererThread { + Clusterer* parent = nullptr; + // column buffers (pre-cluster state); extra sentinel entries at [0] and [size-1] + int* column1 = nullptr; + int* column2 = nullptr; + int* curr = nullptr; ///< current column pre-cluster indices + int* prev = nullptr; ///< previous column pre-cluster indices + int size = constants::moduleMLOT::chip::nRows + 2; ///< reallocated per chip in initChip + + // pixels[i] = {next_in_chain, global_digit_index} + std::vector> pixels; + std::vector preClusterHeads; + std::vector preClusterIndices; + uint16_t currCol = 0xffff; + bool noLeftCol = true; + + std::array labelsBuff; ///< MC label buffer for one cluster + std::vector> pixArrBuff; ///< (row,col) pixel buffer for pattern + + // per-thread output (accumulated, then merged back by caller) + std::vector clusters; + std::vector patterns; + ClusterTruth labels; + + ///< reset column buffer + void resetColumn(int* buff) const { std::memset(buff, -1, sizeof(int) * (size - 2)); } + ///< swap current and previous column buffers + void swapColumnBuffers() { std::swap(prev, curr); } + + ///< append pixel ip to the pre-cluster headed at preClusterIndex + void expandPreCluster(uint32_t ip, uint16_t row, int preClusterIndex) + { + auto& firstIndex = preClusterHeads[preClusterIndices[preClusterIndex]]; + pixels.emplace_back(firstIndex, ip); + firstIndex = pixels.size() - 1; + curr[row] = preClusterIndex; + } + + ///< start a new pre-cluster with pixel ip at given row + void addNewPreCluster(uint32_t ip, uint16_t row) + { + preClusterHeads.push_back(pixels.size()); + pixels.emplace_back(-1, ip); + int lastIndex = preClusterIndices.size(); + preClusterIndices.push_back(lastIndex); + curr[row] = lastIndex; + } + + void fetchMCLabels(uint32_t digID, const ConstDigitTruth* labelsDig, int& nfilled); + void initChip(gsl::span digits, uint32_t first, GeometryTGeo* geom); + void updateChip(gsl::span digits, uint32_t ip); + void finishChip(gsl::span digits, + const ConstDigitTruth* labelsDigPtr, ClusterTruth* labelsClusPtr, + GeometryTGeo* geom); + void finishChipSingleHitFast(gsl::span digits, uint32_t hit, + const ConstDigitTruth* labelsDigPtr, ClusterTruth* labelsClusPtr, + GeometryTGeo* geom); + void processChip(gsl::span digits, int chipFirst, int chipN, + std::vector* clustersOut, std::vector* patternsOut, + const ConstDigitTruth* labelsDigPtr, ClusterTruth* labelsClusPtr, + GeometryTGeo* geom); + void streamCluster(const BBox& bbox, const std::vector>& pixbuf, + uint32_t totalCharge, bool doLabels, int nlab, + uint16_t chipID, int subDetID, int layer, int disk); + + ~ClustererThread() + { + delete[] column1; + delete[] column2; + } + explicit ClustererThread(Clusterer* par = nullptr) : parent(par) {} + ClustererThread(const ClustererThread&) = delete; + ClustererThread& operator=(const ClustererThread&) = delete; + }; + //---------------------------------------------- + + void process(gsl::span digits, + gsl::span digitROFs, + std::vector& clusters, + std::vector& patterns, + std::vector& clusterROFs, + const ConstDigitTruth* digitLabels = nullptr, + ClusterTruth* clusterLabels = nullptr, + gsl::span digMC2ROFs = {}, + std::vector* clusterMC2ROFs = nullptr); + + private: + int mNHugeClus = 0; + std::unique_ptr mThread; + std::vector mSortIdx; ///< reusable per-ROF sort buffer +}; + +} // namespace o2::trk + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h index f42a1c897efb6..98e9658d1c2fe 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h @@ -38,8 +38,8 @@ class GeometryTGeo; /// TRK TimeFrame class that extends ITS TimeFrame functionality /// This allows for customization of tracking algorithms specific to the TRK detector -template -class TimeFrame : public o2::its::TimeFrame +template +class TimeFrame : public o2::its::TimeFrame { public: TimeFrame() = default; @@ -50,8 +50,6 @@ class TimeFrame : public o2::its::TimeFrame /// Process hits from TTree to initialize ROFs /// \param hitsTree Tree containing TRK hits - /// \param mcHeaderTree Tree containing MC event headers - /// \param nEvents Number of events to process /// \param gman TRK geometry manager instance /// \param config Configuration parameters for hit reconstruction int loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, const nlohmann::json& config); @@ -61,7 +59,8 @@ class TimeFrame : public o2::its::TimeFrame /// \param nRofs Number of ROFs (Read-Out Frames) /// \param nEvents Number of events to process /// \param inROFpileup Number of events per ROF - void getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup); + /// \param rofLength ROF length in BCs (must match what was used in loadROFsFromHitTree) + void getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup, uint32_t rofLength = 198); }; } // namespace trk diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/Clusterer.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/Clusterer.cxx new file mode 100644 index 0000000000000..bdaa76319c1f2 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/Clusterer.cxx @@ -0,0 +1,419 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Clusterer.cxx +/// \brief Implementation of the TRK cluster finder + +#include "TRKReconstruction/Clusterer.h" +#include "TRKBase/GeometryTGeo.h" + +#include +#include + +namespace o2::trk +{ + +//__________________________________________________ +void Clusterer::process(gsl::span digits, + gsl::span digitROFs, + std::vector& clusters, + std::vector& patterns, + std::vector& clusterROFs, + const ConstDigitTruth* digitLabels, + ClusterTruth* clusterLabels, + gsl::span digMC2ROFs, + std::vector* clusterMC2ROFs) +{ + if (!mThread) { + mThread = std::make_unique(this); + } + + auto* geom = o2::trk::GeometryTGeo::Instance(); + + for (size_t iROF = 0; iROF < digitROFs.size(); ++iROF) { + const auto& inROF = digitROFs[iROF]; + const auto outFirst = static_cast(clusters.size()); + const int first = inROF.getFirstEntry(); + const int nEntries = inROF.getNEntries(); + + if (nEntries == 0) { + clusterROFs.emplace_back(inROF.getBCData(), inROF.getROFrame(), outFirst, 0); + continue; + } + + // Sort digit indices within this ROF by (chipID, col, row) so we can process + // chip by chip, column by column -- the same ordering the ALPIDE scanner expects. + mSortIdx.resize(nEntries); + std::iota(mSortIdx.begin(), mSortIdx.end(), first); + std::sort(mSortIdx.begin(), mSortIdx.end(), [&digits](int a, int b) { + const auto& da = digits[a]; + const auto& db = digits[b]; + if (da.getChipIndex() != db.getChipIndex()) { + return da.getChipIndex() < db.getChipIndex(); + } + if (da.getColumn() != db.getColumn()) { + return da.getColumn() < db.getColumn(); + } + return da.getRow() < db.getRow(); + }); + + // Process one chip at a time + int sliceStart = 0; + while (sliceStart < nEntries) { + const int chipFirst = sliceStart; + const uint16_t chipID = digits[mSortIdx[sliceStart]].getChipIndex(); + while (sliceStart < nEntries && digits[mSortIdx[sliceStart]].getChipIndex() == chipID) { + ++sliceStart; + } + const int chipN = sliceStart - chipFirst; + + mThread->processChip(digits, chipFirst, chipN, &clusters, &patterns, digitLabels, clusterLabels, geom); + } + + clusterROFs.emplace_back(inROF.getBCData(), inROF.getROFrame(), + outFirst, static_cast(clusters.size()) - outFirst); + } + + if (clusterMC2ROFs && !digMC2ROFs.empty()) { + clusterMC2ROFs->reserve(clusterMC2ROFs->size() + digMC2ROFs.size()); + for (const auto& in : digMC2ROFs) { + clusterMC2ROFs->emplace_back(in.eventRecordID, in.rofRecordID, in.minROF, in.maxROF); + } + } +} + +//__________________________________________________ +void Clusterer::ClustererThread::processChip(gsl::span digits, + int chipFirst, int chipN, + std::vector* clustersOut, + std::vector* patternsOut, + const ConstDigitTruth* labelsDigPtr, + ClusterTruth* labelsClusPtr, + GeometryTGeo* geom) +{ + // chipFirst and chipN are relative to mSortIdx (i.e. mSortIdx[chipFirst..chipFirst+chipN-1] + // are the global digit indices for this chip, already sorted by col then row). + // We use parent->mSortIdx to resolve the global index of each pixel. + const auto& sortIdx = parent->mSortIdx; + + if (chipN == 1) { + finishChipSingleHitFast(digits, sortIdx[chipFirst], labelsDigPtr, labelsClusPtr, geom); + } else { + initChip(digits, sortIdx[chipFirst], geom); + for (int i = chipFirst + 1; i < chipFirst + chipN; ++i) { + updateChip(digits, sortIdx[i]); + } + finishChip(digits, labelsDigPtr, labelsClusPtr, geom); + } + + // Flush per-thread output into the caller's containers + if (!clusters.empty()) { + clustersOut->insert(clustersOut->end(), clusters.begin(), clusters.end()); + clusters.clear(); + } + if (!patterns.empty()) { + patternsOut->insert(patternsOut->end(), patterns.begin(), patterns.end()); + patterns.clear(); + } + if (labelsClusPtr && labels.getNElements()) { + labelsClusPtr->mergeAtBack(labels); + labels.clear(); + } +} + +//__________________________________________________ +void Clusterer::ClustererThread::initChip(gsl::span digits, uint32_t first, GeometryTGeo* geom) +{ + const uint16_t chipID = digits[first].getChipIndex(); + + // Determine the number of rows for this chip's sensor type + size = constants::moduleMLOT::chip::nRows + 2; // default for ML/OT + if (geom) { + if (geom->getSubDetID(chipID) == 0) { // VD + const int layer = geom->getLayer(chipID); + size = constants::VD::petal::layer::nRows[layer] + 2; + } + } + + delete[] column1; + delete[] column2; + column1 = new int[size]; + column2 = new int[size]; + column1[0] = column1[size - 1] = -1; + column2[0] = column2[size - 1] = -1; + prev = column1 + 1; + curr = column2 + 1; + resetColumn(curr); + + pixels.clear(); + preClusterHeads.clear(); + preClusterIndices.clear(); + + const auto& pix = digits[first]; + currCol = pix.getColumn(); + curr[pix.getRow()] = 0; + preClusterHeads.push_back(0); + preClusterIndices.push_back(0); + pixels.emplace_back(-1, first); + noLeftCol = true; +} + +//__________________________________________________ +void Clusterer::ClustererThread::updateChip(gsl::span digits, uint32_t ip) +{ + const auto& pix = digits[ip]; + uint16_t row = pix.getRow(); + + if (currCol != pix.getColumn()) { + swapColumnBuffers(); + resetColumn(curr); + noLeftCol = false; + if (pix.getColumn() > currCol + 1) { + // gap: no connection with previous column + currCol = pix.getColumn(); + addNewPreCluster(ip, row); + noLeftCol = true; + return; + } + currCol = pix.getColumn(); + } + + bool orphan = true; + + if (noLeftCol) { + if (curr[row - 1] >= 0) { + expandPreCluster(ip, row, curr[row - 1]); + return; + } + } else { +#ifdef _ALLOW_DIAGONAL_TRK_CLUSTERS_ + int neighbours[]{curr[row - 1], prev[row], prev[row + 1], prev[row - 1]}; +#else + int neighbours[]{curr[row - 1], prev[row]}; +#endif + for (auto pci : neighbours) { + if (pci < 0) { + continue; + } + if (orphan) { + expandPreCluster(ip, row, pci); + orphan = false; + continue; + } + // merge two pre-clusters: assign the smaller index to both + if (preClusterIndices[pci] < preClusterIndices[curr[row]]) { + preClusterIndices[curr[row]] = preClusterIndices[pci]; + } else { + preClusterIndices[pci] = preClusterIndices[curr[row]]; + } + } + } + if (orphan) { + addNewPreCluster(ip, row); + } +} + +//__________________________________________________ +void Clusterer::ClustererThread::finishChip(gsl::span digits, + const ConstDigitTruth* labelsDigPtr, + ClusterTruth* labelsClusPtr, + GeometryTGeo* geom) +{ + const uint16_t chipID = digits[pixels[0].second].getChipIndex(); + + for (size_t i1 = 0; i1 < preClusterHeads.size(); ++i1) { + auto ci = preClusterIndices[i1]; + if (ci < 0) { + continue; + } + BBox bbox(chipID); + int nlab = 0; + uint32_t totalCharge = 0; + pixArrBuff.clear(); + + // Walk the linked list for this pre-cluster head + auto collectPixels = [&](int head) { + int next = head; + while (next >= 0) { + const auto& pixEntry = pixels[next]; + const auto& d = digits[pixEntry.second]; + uint16_t r = d.getRow(), c = d.getColumn(); + pixArrBuff.emplace_back(r, c); + bbox.adjust(r, c); + totalCharge += d.getCharge(); + if (labelsClusPtr) { + fetchMCLabels(pixEntry.second, labelsDigPtr, nlab); + } + next = pixEntry.first; + } + }; + + collectPixels(preClusterHeads[i1]); + preClusterIndices[i1] = -1; + + for (size_t i2 = i1 + 1; i2 < preClusterHeads.size(); ++i2) { + if (preClusterIndices[i2] != ci) { + continue; + } + collectPixels(preClusterHeads[i2]); + preClusterIndices[i2] = -1; + } + + // Determine geometry info + int subDetID = -1, layer = -1, disk = -1; + if (geom) { + subDetID = geom->getSubDetID(chipID); + layer = geom->getLayer(chipID); + disk = geom->getDisk(chipID); + } + + const bool doLabels = (labelsClusPtr != nullptr); + if (bbox.isAcceptableSize()) { + streamCluster(bbox, pixArrBuff, totalCharge, doLabels, nlab, chipID, subDetID, layer, disk); + } else { + // Huge cluster: split into MaxRowSpan x MaxColSpan tiles (same as ITS3) + auto warnLeft = MaxHugeClusWarn - parent->mNHugeClus; + if (warnLeft > 0) { + LOGP(warn, "Splitting huge TRK cluster: chipID {}, rows {}:{} cols {}:{}{}", + chipID, bbox.rowMin, bbox.rowMax, bbox.colMin, bbox.colMax, + warnLeft == 1 ? " (further warnings muted)" : ""); + parent->mNHugeClus++; + } + BBox bboxT(chipID); + bboxT.colMin = bbox.colMin; + do { + bboxT.rowMin = bbox.rowMin; + bboxT.colMax = std::min(bbox.colMax, uint16_t(bboxT.colMin + o2::itsmft::ClusterPattern::MaxColSpan - 1)); + do { + bboxT.rowMax = std::min(bbox.rowMax, uint16_t(bboxT.rowMin + o2::itsmft::ClusterPattern::MaxRowSpan - 1)); + std::vector> subPix; + uint32_t subCharge = 0; + for (const auto& [r, c] : pixArrBuff) { + if (bboxT.isInside(r, c)) { + subPix.emplace_back(r, c); + subCharge += 1; + } + } + if (!subPix.empty()) { + streamCluster(bboxT, subPix, subCharge, doLabels, nlab, chipID, subDetID, layer, disk); + } + bboxT.rowMin = bboxT.rowMax + 1; + } while (bboxT.rowMin <= bbox.rowMax); + bboxT.colMin = bboxT.colMax + 1; + } while (bboxT.colMin <= bbox.colMax); + } + } + // flush per-thread output to the caller via processChip +} + +//__________________________________________________ +void Clusterer::ClustererThread::finishChipSingleHitFast(gsl::span digits, uint32_t hit, + const ConstDigitTruth* labelsDigPtr, + ClusterTruth* labelsClusPtr, + GeometryTGeo* geom) +{ + const auto& d = digits[hit]; + const uint16_t chipID = d.getChipIndex(); + const uint16_t row = d.getRow(); + const uint16_t col = d.getColumn(); + + if (labelsClusPtr) { + int nlab = 0; + fetchMCLabels(hit, labelsDigPtr, nlab); + const auto cnt = static_cast(clusters.size()); + for (int i = nlab; i--;) { + labels.addElement(cnt, labelsBuff[i]); + } + } + + // 1×1 pattern: rowSpan=1, colSpan=1, one byte = 0x80 + patterns.emplace_back(1); + patterns.emplace_back(1); + patterns.emplace_back(0x80); + + Cluster cluster; + cluster.chipID = chipID; + cluster.row = row; + cluster.col = col; + cluster.size = 1; + if (geom) { + cluster.subDetID = geom->getSubDetID(chipID); + cluster.layer = geom->getLayer(chipID); + cluster.disk = geom->getDisk(chipID); + } + clusters.emplace_back(cluster); +} + +//__________________________________________________ +void Clusterer::ClustererThread::streamCluster(const BBox& bbox, + const std::vector>& pixbuf, + uint32_t totalCharge, + bool doLabels, int nlab, + uint16_t chipID, int subDetID, int layer, int disk) +{ + if (doLabels) { + const auto cnt = static_cast(clusters.size()); + for (int i = nlab; i--;) { + labels.addElement(cnt, labelsBuff[i]); // accumulate in thread-local buffer + } + } + + const uint16_t rowSpanW = bbox.rowSpan(); + const uint16_t colSpanW = bbox.colSpan(); + + // Encode the pixel pattern bitmap (rowSpan, colSpan, bytes...) + std::array patt{}; + for (const auto& [r, c] : pixbuf) { + uint32_t ir = r - bbox.rowMin, ic = c - bbox.colMin; + int nbit = ir * colSpanW + ic; + patt[nbit >> 3] |= (0x1 << (7 - (nbit % 8))); + } + patterns.emplace_back(static_cast(rowSpanW)); + patterns.emplace_back(static_cast(colSpanW)); + int nBytes = (rowSpanW * colSpanW + 7) / 8; + patterns.insert(patterns.end(), patt.begin(), patt.begin() + nBytes); + + Cluster cluster; + cluster.chipID = chipID; + cluster.row = bbox.rowMin; + cluster.col = bbox.colMin; + cluster.size = static_cast(pixbuf.size()); + cluster.subDetID = static_cast(subDetID); + cluster.layer = static_cast(layer); + cluster.disk = static_cast(disk); + clusters.emplace_back(cluster); +} + +//__________________________________________________ +void Clusterer::ClustererThread::fetchMCLabels(uint32_t digID, const ConstDigitTruth* labelsDig, int& nfilled) +{ + if (nfilled >= MaxLabels) { + return; + } + if (!labelsDig || digID >= labelsDig->getIndexedSize()) { + return; + } + const auto& lbls = labelsDig->getLabels(digID); + for (int i = lbls.size(); i--;) { + int ic = nfilled; + for (; ic--;) { + if (labelsBuff[ic] == lbls[i]) { + return; // already present + } + } + labelsBuff[nfilled++] = lbls[i]; + if (nfilled >= MaxLabels) { + break; + } + } +} + +} // namespace o2::trk diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h index 09ab598ec626c..4eda22e350852 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h @@ -16,5 +16,6 @@ #pragma link off all functions; #pragma link C++ class o2::trk::TimeFrame < 11> + ; +#pragma link C++ class o2::trk::Clusterer + ; #endif diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx index 610a08450d5ee..6e8876f609b39 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx @@ -23,11 +23,13 @@ #include #include +using o2::its::clearResizeBoundedVector; + namespace o2::trk { -template -int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, const nlohmann::json& config) +template +int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, const nlohmann::json& config) { constexpr std::array startLayer{0, 3}; const Long64_t nEvents = hitsTree->GetEntries(); @@ -39,23 +41,39 @@ int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, const int inROFpileup{config.contains("inROFpileup") ? config["inROFpileup"].get() : 1}; - // Calculate number of ROFs and initialize data structures - this->mNrof = (nEvents + inROFpileup - 1) / inROFpileup; + // Calculate number of ROFs + const int nRofs = (nEvents + inROFpileup - 1) / inROFpileup; + + // Set up ROF timing for all layers (no staggering in TRK simulation, all layers read out together) + constexpr uint32_t rofLength = 198; // ROF length in BC + o2::its::ROFOverlapTable overlapTable; + for (int iLayer = 0; iLayer < NLayers; ++iLayer) { + overlapTable.defineLayer(iLayer, nRofs, rofLength, 0, 0, 0); + } + overlapTable.init(); + this->setROFOverlapTable(overlapTable); + + // Set up the vertex lookup table timing (pre-allocate, vertices will be filled later) + o2::its::ROFVertexLookupTable vtxLookupTable; + for (int iLayer = 0; iLayer < NLayers; ++iLayer) { + vtxLookupTable.defineLayer(iLayer, nRofs, rofLength, 0, 0, 0); + } + vtxLookupTable.init(); // pre-allocate without vertices + this->setROFVertexLookupTable(vtxLookupTable); // Reset and prepare ROF data structures - for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + for (int iLayer{0}; iLayer < NLayers; ++iLayer) { this->mMinR[iLayer] = std::numeric_limits::max(); this->mMaxR[iLayer] = std::numeric_limits::lowest(); this->mROFramesClusters[iLayer].clear(); - this->mROFramesClusters[iLayer].resize(this->mNrof + 1, 0); + this->mROFramesClusters[iLayer].resize(nRofs + 1, 0); this->mUnsortedClusters[iLayer].clear(); this->mTrackingFrameInfo[iLayer].clear(); this->mClusterExternalIndices[iLayer].clear(); } // Pre-count hits to reserve memory efficiently - int totalNHits{0}; - std::array clusterCountPerLayer{}; + std::array clusterCountPerLayer{}; for (Long64_t iEvent = 0; iEvent < nEvents; ++iEvent) { hitsTree->GetEntry(iEvent); for (const auto& hit : *trkHit) { @@ -64,25 +82,24 @@ int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, } int subDetID = gman->getSubDetID(hit.GetDetectorID()); const int layer = startLayer[subDetID] + gman->getLayer(hit.GetDetectorID()); - if (layer >= nLayers) { + if (layer >= NLayers) { continue; } ++clusterCountPerLayer[layer]; - totalNHits++; } } - // Reserve memory for all layers - for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + // Reserve memory for all layers (mClusterSize is now per-layer) + for (int iLayer{0}; iLayer < NLayers; ++iLayer) { this->mUnsortedClusters[iLayer].reserve(clusterCountPerLayer[iLayer]); this->mTrackingFrameInfo[iLayer].reserve(clusterCountPerLayer[iLayer]); this->mClusterExternalIndices[iLayer].reserve(clusterCountPerLayer[iLayer]); + clearResizeBoundedVector(this->mClusterSize[iLayer], clusterCountPerLayer[iLayer], this->mMemoryPool.get()); } - clearResizeBoundedVector(this->mClusterSize, totalNHits, this->mMemoryPool.get()); std::array resolution{0.001, 0.001, 0.001, 0.001, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004}; - if (config["geometry"]["pitch"].size() == nLayers) { - for (int iLayer{0}; iLayer < config["geometry"]["pitch"].size(); ++iLayer) { + if (config["geometry"]["pitch"].size() == static_cast(NLayers)) { + for (size_t iLayer{0}; iLayer < config["geometry"]["pitch"].size(); ++iLayer) { LOGP(info, "Setting resolution for layer {} from config", iLayer); LOGP(info, "Layer {} pitch {} cm", iLayer, config["geometry"]["pitch"][iLayer].get()); resolution[iLayer] = config["geometry"]["pitch"][iLayer].get() / std::sqrt(12.f); @@ -90,9 +107,10 @@ int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, } LOGP(info, "Number of active parts in VD: {}", gman->getNumberOfActivePartsVD()); - int hitCounter{0}; - auto labels = new dataformats::MCTruthContainer(); + // One shared MC label container for all layers + auto* labels = new dataformats::MCTruthContainer(); + int hitCounter{0}; int iRof{0}; // Current ROF index for (Long64_t iEvent = 0; iEvent < nEvents; ++iEvent) { hitsTree->GetEntry(iEvent); @@ -108,7 +126,7 @@ int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, o2::math_utils::Point3D gloXYZ; o2::math_utils::Point3D trkXYZ; float r{0.f}; - if (layer >= nLayers) { + if (layer >= NLayers) { continue; } if (layer >= 3) { @@ -139,11 +157,12 @@ int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, std::array{trkXYZ.y(), trkXYZ.z()}, std::array{resolution[layer] * resolution[layer], 0., resolution[layer] * resolution[layer]}); /// Rotate to the global frame - this->addClusterToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), this->mUnsortedClusters[layer].size()); + const int clusterIdxInLayer = this->mUnsortedClusters[layer].size(); + this->addClusterToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), clusterIdxInLayer); this->addClusterExternalIndexToLayer(layer, hitCounter); MCCompLabel label{hit.GetTrackID(), static_cast(iEvent), 0}; labels->addElement(hitCounter, label); - this->mClusterSize[hitCounter] = 1; // For compatibility with cluster-based tracking, set cluster size to 1 for hits + this->mClusterSize[layer][clusterIdxInLayer] = 1; hitCounter++; } trkHit->clear(); @@ -154,21 +173,23 @@ int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, for (unsigned int iLayer{0}; iLayer < this->mUnsortedClusters.size(); ++iLayer) { this->mROFramesClusters[iLayer][iRof] = this->mUnsortedClusters[iLayer].size(); // effectively calculating an exclusive sum } - // Update primary vertices ROF structure } - this->mClusterLabels = labels; } - return this->mNrof; + + // Set the shared labels container for all layers + for (int iLayer = 0; iLayer < NLayers; ++iLayer) { + this->mClusterLabels[iLayer] = labels; + } + + return nRofs; } -template -void TimeFrame::getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup) +template +void TimeFrame::getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup, uint32_t rofLength) { auto mcheader = new o2::dataformats::MCEventHeader; mcHeaderTree->SetBranchAddress("MCEventHeader.", &mcheader); - this->mROFramesPV.clear(); - this->mROFramesPV.resize(nRofs + 1, 0); this->mPrimaryVertices.clear(); int iRof{0}; @@ -178,14 +199,24 @@ void TimeFrame::getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs vertex.setXYZ(mcheader->GetX(), mcheader->GetY(), mcheader->GetZ()); vertex.setNContributors(30); vertex.setChi2(0.f); - LOGP(debug, "ROF {}: Added primary vertex at ({}, {}, {})", iRof, mcheader->GetX(), mcheader->GetY(), mcheader->GetZ()); - this->mPrimaryVertices.push_back(vertex); + + // Set proper BC timestamp for vertex-ROF compatibility + // The vertex timestamp is set to the center of its ROF with half-ROF as error + const uint32_t rofCenter = static_cast(rofLength * iRof + rofLength / 2); + const uint16_t rofHalf = static_cast(rofLength / 2); + vertex.setTimeStamp({rofCenter, rofHalf}); + + LOGP(debug, "ROF {}: Added primary vertex at ({}, {}, {}) with BC timestamp [{}, +/-{}]", + iRof, mcheader->GetX(), mcheader->GetY(), mcheader->GetZ(), rofCenter, rofHalf); + this->addPrimaryVertex(vertex); if ((iEvent + 1) % inROFpileup == 0 || iEvent == nEvents - 1) { iRof++; - this->mROFramesPV[iRof] = this->mPrimaryVertices.size(); // effectively calculating an exclusive sum } } this->mMultiplicityCutMask.resize(nRofs, true); /// all ROFs are valid with MC primary vertices. + + // Update the vertex lookup table with the newly added vertices + this->updateROFVertexLookupTable(); } // Explicit template instantiation for TRK with 11 layers diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h index bbafcf3f8f979..15ed63e46e21f 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h @@ -37,8 +37,8 @@ struct DPLDigitizerParam : public o2::conf::ConfigurableParamHelper public: Detector(bool active); Detector(); + Detector(const Detector& other); ~Detector(); // Factory method @@ -66,8 +67,7 @@ class Detector : public o2::base::DetImpl return nullptr; } - void configDefault(); - void buildTRKMiddleOuterLayers(); + void configMLOT(); void configFromFile(std::string fileName = "alice3_TRK_layout.txt"); void configToFile(std::string fileName = "alice3_TRK_layout.txt"); @@ -88,8 +88,8 @@ class Detector : public o2::base::DetImpl double mEnergyLoss; // energy loss } mTrackData; //! transient data GeometryTGeo* mGeometryTGeo; //! - std::vector* mHits; // ITSMFT ones for the moment - std::vector mLayers; + std::vector* mHits; // Derived from ITSMFT + std::vector> mLayers; TRKServices mServices; // Houses the services of the TRK, but not the Iris tracker std::vector mFirstOrLastLayers; // Names of the first or last layers diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DigiParams.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DigiParams.h index 0463a68a77c3e..23824eaefa13d 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DigiParams.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DigiParams.h @@ -110,8 +110,8 @@ class DigiParams float mStrobeLength = 0; ///< length of the strobe in ns (sig. over threshold checked in this window only) double mTimeOffset = -2 * infTime; ///< time offset (in seconds!) to calculate ROFrame from hit time int mROFrameBiasInBC = 0; ///< misalignment of the ROF start in BC - int mChargeThreshold = 1; ///< charge threshold in Nelectrons - int mMinChargeToAccount = 1; ///< minimum charge contribution to account + int mChargeThreshold = 75; ///< charge threshold in Nelectrons + int mMinChargeToAccount = 7; ///< minimum charge contribution to account int mNSimSteps = 475; ///< number of steps in response simulation float mNSimStepsInv = 1. / mNSimSteps; ///< its inverse diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h index 39dd7752cc010..6077d9e5f9839 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h @@ -22,57 +22,113 @@ namespace o2 { namespace trk { -class TRKLayer +enum class MatBudgetParamMode { + Thickness, + X2X0 +}; + +class TRKCylindricalLayer { public: - TRKLayer() = default; - TRKLayer(int layerNumber, std::string layerName, float rInn, float rOut, int numberOfModules, float layerX2X0); - TRKLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thick); - ~TRKLayer() = default; - - void setLayout(eLayout layout) { mLayout = layout; }; + TRKCylindricalLayer() = default; + TRKCylindricalLayer(int layerNumber, std::string layerName, float rInn, float length, float thickOrX2X0, MatBudgetParamMode mode); + virtual ~TRKCylindricalLayer() = default; auto getInnerRadius() const { return mInnerRadius; } auto getOuterRadius() const { return mOuterRadius; } - auto getZ() const { return constants::moduleMLOT::length * mNumberOfModules; } + auto getZ() const { return mLength; } auto getx2X0() const { return mX2X0; } auto getChipThickness() const { return mChipThickness; } auto getNumber() const { return mLayerNumber; } auto getName() const { return mLayerName; } - TGeoVolume* createSensor(std::string type); - TGeoVolume* createDeadzone(std::string type); - TGeoVolume* createMetalStack(std::string type); - TGeoVolume* createChip(std::string type); - TGeoVolume* createModule(std::string type); - TGeoVolume* createStave(std::string type); - TGeoVolume* createHalfStave(std::string type); - void createLayer(TGeoVolume* motherVolume); - - private: - // TGeo objects outside logical volumes can cause errors. Only used in case of kStaggered and kTurboStaves layouts - static constexpr float mLogicalVolumeThickness = 1.3; + virtual TGeoVolume* createSensor(); + virtual TGeoVolume* createMetalStack(); + virtual void createLayer(TGeoVolume* motherVolume); + protected: // User defined parameters for the layer, to be set in the constructor int mLayerNumber; std::string mLayerName; float mInnerRadius; float mOuterRadius; - int mNumberOfModules; + float mLength; float mX2X0; float mChipThickness; // Fixed parameters for the layer, to be set based on the specifications of the chip and module - eLayout mLayout = kCylinder; - float mChipWidth = constants::moduleMLOT::chip::width; - float mChipLength = constants::moduleMLOT::chip::length; - float mDeadzoneWidth = constants::moduleMLOT::chip::passiveEdgeReadOut; - float mSensorThickness = constants::moduleMLOT::silicon::thickness; - int mHalfNumberOfChips = 4; + static constexpr double sSensorThickness = constants::moduleMLOT::silicon::thickness; static constexpr float Si_X0 = 9.5f; - ClassDef(TRKLayer, 2); + ClassDef(TRKCylindricalLayer, 0); +}; + +class TRKSegmentedLayer : public TRKCylindricalLayer +{ + public: + TRKSegmentedLayer() = default; + TRKSegmentedLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode); + ~TRKSegmentedLayer() override = default; + + TGeoVolume* createSensor() override; + TGeoVolume* createDeadzone(); + TGeoVolume* createMetalStack() override; + TGeoVolume* createChip(); + TGeoVolume* createModule(); + virtual TGeoVolume* createStave() = 0; + void createLayer(TGeoVolume* motherVolume) override = 0; + + protected: + int mNumberOfModules; + + // Fixed parameters for the layer, to be set based on the specifications of the chip and module + static constexpr double sChipWidth = constants::moduleMLOT::chip::width; + static constexpr double sChipLength = constants::moduleMLOT::chip::length; + static constexpr double sDeadzoneWidth = constants::moduleMLOT::chip::passiveEdgeReadOut; + static constexpr double sModuleLength = constants::moduleMLOT::length; + static constexpr double sModuleWidth = constants::moduleMLOT::width; + static constexpr int sHalfNumberOfChips = 4; + + // TGeo objects outside logical volumes can cause errors + static constexpr float sLogicalVolumeThickness = 1.3; + + ClassDefOverride(TRKSegmentedLayer, 0); +}; + +class TRKMLLayer : public TRKSegmentedLayer +{ + public: + TRKMLLayer() = default; + TRKMLLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode); + ~TRKMLLayer() override = default; + + TGeoVolume* createStave() override; + void createLayer(TGeoVolume* motherVolume) override; + + private: + static constexpr double sStaveWidth = constants::ML::width; + + ClassDefOverride(TRKMLLayer, 0); +}; + +class TRKOTLayer : public TRKSegmentedLayer +{ + public: + TRKOTLayer() = default; + TRKOTLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode); + ~TRKOTLayer() override = default; + + TGeoVolume* createStave() override; + TGeoVolume* createHalfStave(); + void createLayer(TGeoVolume* motherVolume) override; + + private: + static constexpr double sHalfStaveWidth = constants::OT::halfstave::width; + static constexpr double sInStaveOverlap = constants::moduleMLOT::gaps::outerEdgeLongSide + constants::moduleMLOT::chip::passiveEdgeReadOut + 0.1; // 1.5mm outer-edge + 1mm deadzone + 1mm (true) overlap + static constexpr double sStaveWidth = constants::OT::width - sInStaveOverlap; + + ClassDefOverride(TRKOTLayer, 0) }; } // namespace trk diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKServices.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKServices.h index 8dd3968743024..79033f48cb0b9 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKServices.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKServices.h @@ -51,6 +51,8 @@ class TRKServices : public FairModule void createMiddleServices(TGeoVolume* motherVolume); void createOuterDisksServices(TGeoVolume* motherVolume); void createOuterBarrelServices(TGeoVolume* motherVolume); + void createMLServicesPeacock(TGeoVolume* motherVolume); + void createOTServicesPeacock(TGeoVolume* motherVolume); void createVacuumCompositeShape(); void excavateFromVacuum(TString shapeToExcavate); void registerVacuum(TGeoVolume* motherVolume); @@ -65,12 +67,14 @@ class TRKServices : public FairModule float mColdPlateX0; // Services + float mFiberArea = 7.1e-2; // cm^2 + float mPowerBundleArea = 1.13; // cm^2 float mFiberComposition[2] = {0.5, 0.5}; // SiO2, PE - float mPowerBundleComposition[2] = {0.09, 0.91}; // Cu, PE + float mPowerBundleComposition[2] = {0.08, 0.92}; // Cu, PE (with jacket) float mPowerBundleJacketComposition[2] = {0.06, 0.94}; // Cu, PE float mWaterBundleComposition[2] = {0.56, 0.44}; // PU, H2O float mWaterBundleDiskComposition[2] = {0.44, 0.56}; // PU, H2O - float mMiddleDiskThickness = 1.0; // cm + // float mMiddleDiskThickness = 1.0; // cm std::vector mCableFanWeights = {0.5, 0.3, 0.2}; // relative weights of the fan layers ClassDefOverride(TRKServices, 1); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index 2ad1d52ba73c4..8e13d31e7915c 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -54,17 +54,24 @@ Detector::Detector(bool active) if (trkPars.configFile != "") { configFromFile(trkPars.configFile); } else { - buildTRKMiddleOuterLayers(); + configMLOT(); configToFile(); configServices(); } LOGP(info, "Summary of TRK configuration:"); for (auto& layer : mLayers) { - LOGP(info, "Layer: {} name: {} r: {} cm | z: {} cm | thickness: {} cm", layer.getNumber(), layer.getName(), layer.getInnerRadius(), layer.getZ(), layer.getChipThickness()); + LOGP(info, "Layer: {} name: {} r: {} cm | z: {} cm | thickness: {} cm", layer->getNumber(), layer->getName(), layer->getInnerRadius(), layer->getZ(), layer->getChipThickness()); } } +Detector::Detector(const Detector& other) + : o2::base::DetImpl(other), + mTrackData(), + mHits(o2::utils::createSimVector()) +{ +} + Detector::~Detector() { if (mHits) { @@ -78,78 +85,42 @@ void Detector::ConstructGeometry() createGeometry(); } -void Detector::configDefault() -{ - - // Build TRK detector according to the scoping document - - mLayers.clear(); - - LOGP(warning, "Loading Scoping Document configuration for ALICE3 TRK"); - mLayers.emplace_back(0, GeometryTGeo::getTRKLayerPattern() + std::to_string(0), 3.78f, 10, 100.e-3); - mLayers.emplace_back(1, GeometryTGeo::getTRKLayerPattern() + std::to_string(1), 7.f, 10, 100.e-3); - mLayers.emplace_back(2, GeometryTGeo::getTRKLayerPattern() + std::to_string(2), 12.f, 10, 100.e-3); - mLayers.emplace_back(3, GeometryTGeo::getTRKLayerPattern() + std::to_string(3), 20.f, 10, 100.e-3); - mLayers.emplace_back(4, GeometryTGeo::getTRKLayerPattern() + std::to_string(4), 30.f, 10, 100.e-3); - mLayers.emplace_back(5, GeometryTGeo::getTRKLayerPattern() + std::to_string(5), 45.f, 20, 100.e-3); - mLayers.emplace_back(6, GeometryTGeo::getTRKLayerPattern() + std::to_string(6), 60.f, 20, 100.e-3); - mLayers.emplace_back(7, GeometryTGeo::getTRKLayerPattern() + std::to_string(7), 80.f, 20, 100.e-3); -} - -void Detector::buildTRKMiddleOuterLayers() +void Detector::configMLOT() { auto& trkPars = TRKBaseParam::Instance(); mLayers.clear(); - switch (trkPars.overallGeom) { - case kDefaultRadii: - // Build the TRK detector according to changes proposed during - // https://indico.cern.ch/event/1407704/ - // to adhere to the changes that were presented at the ALICE 3 Upgrade days in March 2024 - // L3 -> 7 cm, L4 -> 9 cm, L5 -> 12 cm, L6 -> 20 cm - - LOGP(warning, "Loading \"After Upgrade Days March 2024\" configuration for ALICE3 TRK"); - LOGP(warning, "Building TRK with new vacuum vessel and L3 at 7 cm, L4 at 9 cm, L5 at 12 cm, L6 at 20 cm"); - mLayers.emplace_back(0, GeometryTGeo::getTRKLayerPattern() + std::to_string(0), 7.f, 10, 100.e-3); - LOGP(info, "TRKLayer created. Name: {}", GeometryTGeo::getTRKLayerPattern() + std::to_string(0)); - mLayers.emplace_back(1, GeometryTGeo::getTRKLayerPattern() + std::to_string(1), 9.f, 10, 100.e-3); - mLayers.emplace_back(2, GeometryTGeo::getTRKLayerPattern() + std::to_string(2), 12.f, 10, 100.e-3); - mLayers.emplace_back(3, GeometryTGeo::getTRKLayerPattern() + std::to_string(3), 20.f, 10, 100.e-3); - mLayers.emplace_back(4, GeometryTGeo::getTRKLayerPattern() + std::to_string(4), 30.f, 10, 100.e-3); - mLayers.emplace_back(5, GeometryTGeo::getTRKLayerPattern() + std::to_string(5), 45.f, 20, 100.e-3); - mLayers.emplace_back(6, GeometryTGeo::getTRKLayerPattern() + std::to_string(6), 60.f, 20, 100.e-3); - mLayers.emplace_back(7, GeometryTGeo::getTRKLayerPattern() + std::to_string(7), 80.f, 20, 100.e-3); + const std::vector rInn{7.f, 9.f, 12.f, 20.f, 30.f, 45.f, 60.f, 80.f}; + const float thick = 100.e-3; + + switch (trkPars.layoutMLOT) { + case kCylindrical: { + const std::vector length{128.35f, 128.35f, 128.35f, 128.35f, 128.35f, 256.7f, 256.7f, 256.7f}; + LOGP(warning, "Loading cylindrical configuration for ALICE3 TRK"); + for (int i{0}; i < 8; ++i) { + std::string name = GeometryTGeo::getTRKLayerPattern() + std::to_string(i); + mLayers.push_back(std::make_unique(i, name, rInn[i], length[i], thick, MatBudgetParamMode::Thickness)); + } break; - case kModRadii: - LOGP(warning, "Loading \"Alternative\" configuration for ALICE3 TRK"); - LOGP(warning, "Building TRK with new vacuum vessel and L3 at 7 cm, L4 at 11 cm, L5 at 15 cm, L6 at 19 cm"); - mLayers.emplace_back(0, GeometryTGeo::getTRKLayerPattern() + std::to_string(0), 7.f, 10, 100.e-3); - LOGP(info, "TRKLayer created. Name: {}", GeometryTGeo::getTRKLayerPattern() + std::to_string(0)); - mLayers.emplace_back(1, GeometryTGeo::getTRKLayerPattern() + std::to_string(1), 11.f, 10, 100.e-3); - mLayers.emplace_back(2, GeometryTGeo::getTRKLayerPattern() + std::to_string(2), 15.f, 10, 100.e-3); - mLayers.emplace_back(3, GeometryTGeo::getTRKLayerPattern() + std::to_string(3), 20.f, 10, 100.e-3); - mLayers.emplace_back(4, GeometryTGeo::getTRKLayerPattern() + std::to_string(4), 30.f, 10, 100.e-3); - mLayers.emplace_back(5, GeometryTGeo::getTRKLayerPattern() + std::to_string(5), 45.f, 20, 100.e-3); - mLayers.emplace_back(6, GeometryTGeo::getTRKLayerPattern() + std::to_string(6), 60.f, 20, 100.e-3); - mLayers.emplace_back(7, GeometryTGeo::getTRKLayerPattern() + std::to_string(7), 80.f, 20, 100.e-3); + } + case kSegmented: { + const std::vector nMods{10, 10, 10, 10, 10, 20, 20, 20}; + LOGP(warning, "Loading segmented configuration for ALICE3 TRK"); + for (int i{0}; i < 8; ++i) { + std::string name = GeometryTGeo::getTRKLayerPattern() + std::to_string(i); + if (i < 4) { + mLayers.push_back(std::make_unique(i, name, rInn[i], nMods[i], thick, MatBudgetParamMode::Thickness)); + } else { + mLayers.push_back(std::make_unique(i, name, rInn[i], nMods[i], thick, MatBudgetParamMode::Thickness)); + } + } break; + } default: - LOGP(fatal, "Unknown option {} for buildTRKMiddleOuterLayers", static_cast(trkPars.overallGeom)); + LOGP(fatal, "Unknown option {} for configMLOT", static_cast(trkPars.layoutMLOT)); break; } - - // Middle layers - mLayers[0].setLayout(trkPars.layoutML); - mLayers[1].setLayout(trkPars.layoutML); - mLayers[2].setLayout(trkPars.layoutML); - mLayers[3].setLayout(trkPars.layoutML); - - // Outer tracker - mLayers[4].setLayout(trkPars.layoutOT); - mLayers[5].setLayout(trkPars.layoutOT); - mLayers[6].setLayout(trkPars.layoutOT); - mLayers[7].setLayout(trkPars.layoutOT); } void Detector::configFromFile(std::string fileName) @@ -160,6 +131,8 @@ void Detector::configFromFile(std::string fileName) LOGP(fatal, "File {} not found, aborting.", fileName); } + auto& trkPars = TRKBaseParam::Instance(); + mLayers.clear(); LOGP(info, "Overriding geometry of ALICE3 TRK using {} file.", fileName); @@ -178,7 +151,26 @@ void Detector::configFromFile(std::string fileName) while (getline(ss, substr, '\t')) { tmpBuff.push_back(std::stof(substr)); } - mLayers.emplace_back(layerCount, GeometryTGeo::getTRKLayerPattern() + std::to_string(layerCount), tmpBuff[0], tmpBuff[1], tmpBuff[2]); + + std::string name = GeometryTGeo::getTRKLayerPattern() + std::to_string(layerCount); + switch (trkPars.layoutMLOT) { + case kCylindrical: + mLayers.push_back(std::make_unique(layerCount, name, tmpBuff[0], tmpBuff[1], tmpBuff[2], MatBudgetParamMode::Thickness)); + break; + case kSegmented: { + int nMods = static_cast(tmpBuff[1]); + if (layerCount < 4) { + mLayers.push_back(std::make_unique(layerCount, name, tmpBuff[0], nMods, tmpBuff[2], MatBudgetParamMode::Thickness)); + } else { + mLayers.push_back(std::make_unique(layerCount, name, tmpBuff[0], nMods, tmpBuff[2], MatBudgetParamMode::Thickness)); + } + break; + } + default: + LOGP(fatal, "Unknown option {} for configMLOT", static_cast(trkPars.layoutMLOT)); + break; + } + ++layerCount; } } @@ -188,8 +180,8 @@ void Detector::configToFile(std::string fileName) LOGP(info, "Exporting TRK Detector layout to {}", fileName); std::ofstream conFile(fileName.c_str(), std::ios::out); conFile << "/// TRK configuration file: inn_radius z_length lay_thickness" << std::endl; - for (auto layer : mLayers) { - conFile << layer.getInnerRadius() << "\t" << layer.getZ() << "\t" << layer.getChipThickness() << std::endl; + for (const auto& layer : mLayers) { + conFile << layer->getInnerRadius() << "\t" << layer->getZ() << "\t" << layer->getChipThickness() << std::endl; } } @@ -254,7 +246,7 @@ void Detector::createGeometry() vTRK->SetTitle(vstrng); for (auto& layer : mLayers) { - layer.createLayer(vTRK); + layer->createLayer(vTRK); } // Add service for inner tracker diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx index 0fd8c7820ce28..52eaccfe045f7 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx @@ -66,6 +66,7 @@ void Digitizer::init() float thicknessMLOT = o2::trk::SegmentationChip::SiliconThicknessMLOT; // 0.01 cm = 100 um --- based on geometry currently present LOG(info) << "Using response name: " << mRespName; + mSimRespOrientation = false; if (mRespName == "APTS") { // default mSimRespVDScaleX = o2::trk::constants::apts::pitchX / o2::trk::SegmentationChip::PitchRowVD; @@ -73,6 +74,7 @@ void Digitizer::init() mSimRespVDShift = mChipSimRespVD->getDepthMax(); // the curved, rescaled, sensors have a width from 0 to -45. Must add ~10 um (= max depth) to match the APTS response. mSimRespMLOTScaleX = o2::trk::constants::apts::pitchX / o2::trk::SegmentationChip::PitchRowMLOT; mSimRespMLOTScaleZ = o2::trk::constants::apts::pitchZ / o2::trk::SegmentationChip::PitchColMLOT; + mSimRespOrientation = true; /// APTS response function is flipped along x wrt the ones of ALPIDE and ALICE3 } else if (mRespName == "ALICE3") { mSimRespVDScaleX = o2::trk::constants::alice3resp::pitchX / o2::trk::SegmentationChip::PitchRowVD; mSimRespVDScaleZ = o2::trk::constants::alice3resp::pitchZ / o2::trk::SegmentationChip::PitchColVD; @@ -84,7 +86,6 @@ void Digitizer::init() } mSimRespMLOTShift = mChipSimRespMLOT->getDepthMax() - thicknessMLOT / 2.f; // the shift should be done considering the rescaling done to adapt to the wrong silicon thickness. TODO: remove the scaling factor for the depth when the silicon thickness match the simulated response - mSimRespOrientation = false; // importing the parameters from DPLDigitizerParam.h auto& dOptTRK = DPLDigitizerParam::Instance(); @@ -116,11 +117,11 @@ void Digitizer::process(const std::vector* hits, int evID, int srcID) { // digitize single event, the time must have been set beforehand - LOG(debug) << " Digitizing " << mGeometry->getName() << " (ID: " << mGeometry->getDetID() - << ") hits of entry " << evID << " from source " << srcID - << " at time " << mEventTime << " ROFrame= " << mNewROFrame << ")" - << " cont.mode: " << isContinuous() - << " Min/Max ROFrames " << mROFrameMin << "/" << mROFrameMax; + LOG(info) << " Digitizing " << mGeometry->getName() << " (ID: " << mGeometry->getDetID() + << ") hits of event " << evID << " from source " << srcID + << " at time " << mEventTime.getTimeNS() << " ROFrame = " << mNewROFrame + << " cont.mode: " << isContinuous() + << " Min/Max ROFrames " << mROFrameMin << "/" << mROFrameMax; std::cout << "Printing segmentation info: " << std::endl; SegmentationChip::Print(); @@ -154,7 +155,7 @@ void Digitizer::process(const std::vector* hits, int evID, int srcID) //_______________________________________________________________________ void Digitizer::setEventTime(const o2::InteractionTimeRecord& irt) { - LOG(info) << "Setting event time "; + LOG(info) << "Setting event time to " << irt.getTimeNS() << " ns after orbit 0 bc 0"; // assign event time in ns mEventTime = irt; if (!mParams.isContinuous()) { @@ -279,7 +280,7 @@ void Digitizer::processHit(const o2::trk::Hit& hit, uint32_t& maxFr, int evID, i return; } float timeInROF = hit.GetTime() * sec2ns; - LOG(debug) << "timeInROF: " << timeInROF; + LOG(debug) << "Hit time: " << timeInROF << " ns"; if (timeInROF > 20e3) { const int maxWarn = 10; static int warnNo = 0; @@ -292,7 +293,7 @@ void Digitizer::processHit(const o2::trk::Hit& hit, uint32_t& maxFr, int evID, i if (isContinuous()) { timeInROF += mCollisionTimeWrtROF; } - if (timeInROF < 0) { + if (mIsBeforeFirstRO && timeInROF < 0) { // disregard this hit because it comes from an event byefore readout starts and it does not effect this RO LOG(debug) << "Ignoring hit with timeInROF = " << timeInROF; return; @@ -468,7 +469,7 @@ void Digitizer::processHit(const o2::trk::Hit& hit, uint32_t& maxFr, int evID, i if (colDest < 0 || colDest >= colSpan) { continue; } - respMatrix[rowDest][colDest] += rspmat->getValue(irow, icol, mSimRespOrientation ? !flipRow : flipRow, !flipCol); + respMatrix[rowDest][colDest] += rspmat->getValue(irow, icol, mSimRespOrientation ? !flipRow : flipRow, flipCol); } } } @@ -501,7 +502,6 @@ void Digitizer::processHit(const o2::trk::Hit& hit, uint32_t& maxFr, int evID, i if (mDeadChanMap && mDeadChanMap->isNoisy(chipID, rowIS, colIS)) { continue; } - registerDigits(chip, roFrameAbs, timeInROF, nFrames, rowIS, colIS, nEle, lbl); } } @@ -517,7 +517,7 @@ void Digitizer::registerDigits(o2::trk::ChipDigitsContainer& chip, uint32_t roFr LOG(debug) << "Registering digits for chip " << chip.getChipIndex() << " at ROFrame " << roFrame << " row " << row << " col " << col << " nEle " << nEle << " label " << lbl; float tStrobe = mParams.getStrobeDelay() - tInROF; // strobe start wrt signal start - for (int i = 0; i < nROF; i++) { + for (int i = 0; i < nROF; i++) { // loop on all the ROFs occupied by the same signal to calculate the charge accumulated in that ROF uint32_t roFr = roFrame + i; int nEleROF = mParams.getSignalShape().getCollectedCharge(nEle, tStrobe, tStrobe + mParams.getStrobeLength()); tStrobe += mParams.getROFrameLength(); // for the next ROF @@ -536,8 +536,9 @@ void Digitizer::registerDigits(o2::trk::ChipDigitsContainer& chip, uint32_t roFr o2::itsmft::PreDigit* pd = chip.findDigit(key); if (!pd) { chip.addDigit(key, roFr, row, col, nEleROF, lbl); - LOG(debug) << "Added digit " << key << " " << roFr << " " << row << " " << col << " " << nEleROF; + LOG(debug) << "Added digit with key: " << key << " ROF: " << roFr << " row: " << row << " col: " << col << " charge: " << nEleROF; } else { // there is already a digit at this slot, account as PreDigitExtra contribution + LOG(debug) << "Added to pre-digit with key: " << key << " ROF: " << roFr << " row: " << row << " col: " << col << " charge: " << nEleROF; pd->charge += nEleROF; if (pd->labelRef.label == lbl) { // don't store the same label twice continue; diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx index 53cc6ab11850d..39c7b3598d19b 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx @@ -25,400 +25,308 @@ namespace o2 { namespace trk { -TRKLayer::TRKLayer(int layerNumber, std::string layerName, float rInn, float rOut, int numberOfModules, float layerX2X0) - : mLayerNumber(layerNumber), mLayerName(layerName), mInnerRadius(rInn), mOuterRadius(rOut), mNumberOfModules(numberOfModules), mX2X0(layerX2X0) +TRKCylindricalLayer::TRKCylindricalLayer(int layerNumber, std::string layerName, float rInn, float length, float thickOrX2X0, MatBudgetParamMode mode) + : mLayerNumber(layerNumber), mLayerName(layerName), mInnerRadius(rInn), mLength(length) { - mChipThickness = mX2X0 * Si_X0; - LOGP(info, "Creating layer: id: {} rInner: {} rOuter: {} zLength: {} x2X0: {}", mLayerNumber, mInnerRadius, mOuterRadius, getZ(), mX2X0); -} + if (mode == MatBudgetParamMode::Thickness) { + mChipThickness = thickOrX2X0; + mX2X0 = thickOrX2X0 / Si_X0; + mOuterRadius = rInn + thickOrX2X0; + } else if (mode == MatBudgetParamMode::X2X0) { + mX2X0 = thickOrX2X0; + mChipThickness = thickOrX2X0 * Si_X0; + mOuterRadius = rInn + thickOrX2X0 * Si_X0; + } -TRKLayer::TRKLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thick) - : mLayerNumber(layerNumber), mLayerName(layerName), mInnerRadius(rInn), mNumberOfModules(numberOfModules), mChipThickness(thick) -{ - mOuterRadius = rInn + thick; - mX2X0 = mChipThickness / Si_X0; - LOGP(info, "Creating layer: id: {} rInner: {} rOuter: {} zLength: {} x2X0: {}", mLayerNumber, mInnerRadius, mOuterRadius, getZ(), mX2X0); + LOGP(info, "Creating layer: id: {} rInner: {} rOuter: {} zLength: {} x2X0: {}", mLayerNumber, mInnerRadius, mOuterRadius, mLength, mX2X0); } -TGeoVolume* TRKLayer::createSensor(std::string type) +TGeoVolume* TRKCylindricalLayer::createSensor() { TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); std::string sensName = GeometryTGeo::getTRKSensorPattern() + std::to_string(mLayerNumber); - - TGeoShape* sensor; - - if (type == "cylinder") { - sensor = new TGeoTube(mInnerRadius, mInnerRadius + mSensorThickness, (constants::moduleMLOT::length * mNumberOfModules) / 2); // TO BE CHECKED !!! - } else if (type == "flat") { - sensor = new TGeoBBox((mChipWidth - mDeadzoneWidth) / 2, mSensorThickness / 2, mChipLength / 2); // TO BE CHECKED !!! - } else { - LOGP(fatal, "Sensor of type '{}' is not implemented", type); - } - + TGeoShape* sensor = new TGeoTube(mInnerRadius, mInnerRadius + sSensorThickness, mLength / 2); TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi); sensVol->SetLineColor(kYellow); return sensVol; }; -TGeoVolume* TRKLayer::createDeadzone(std::string type) +TGeoVolume* TRKCylindricalLayer::createMetalStack() { TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); - std::string deadName = GeometryTGeo::getTRKDeadzonePattern() + std::to_string(mLayerNumber); + std::string metalName = GeometryTGeo::getTRKMetalStackPattern() + std::to_string(mLayerNumber); + TGeoShape* metalStack = new TGeoTube(mInnerRadius + sSensorThickness, mInnerRadius + mChipThickness, mLength / 2); + TGeoVolume* metalVol = new TGeoVolume(metalName.c_str(), metalStack, medSi); + metalVol->SetLineColor(kGray); - TGeoShape* deadzone; + return metalVol; +}; - if (type == "cylinder") { - deadzone = new TGeoTube(mInnerRadius, mInnerRadius + mSensorThickness, 0); // TO BE CHECKED !!! - } else if (type == "flat") { - deadzone = new TGeoBBox(mDeadzoneWidth / 2, mSensorThickness / 2, mChipLength / 2); // TO BE CHECKED !!! - } else { - LOGP(fatal, "Deadzone of type '{}' is not implemented", type); - } +void TRKCylindricalLayer::createLayer(TGeoVolume* motherVolume) +{ + TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); + TGeoTube* layer = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, mLength / 2); + TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); + layerVol->SetLineColor(kYellow); + + TGeoVolume* sensVol = createSensor(); + LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), layerVol->GetName()); + layerVol->AddNode(sensVol, 1, nullptr); + + TGeoVolume* metalVol = createMetalStack(); + LOGP(debug, "Inserting {} in {} ", metalVol->GetName(), layerVol->GetName()); + layerVol->AddNode(metalVol, 1, nullptr); + + LOGP(debug, "Inserting {} in {} ", layerVol->GetName(), motherVolume->GetName()); + motherVolume->AddNode(layerVol, 1, nullptr); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +TRKSegmentedLayer::TRKSegmentedLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode) + : TRKCylindricalLayer(layerNumber, layerName, rInn, numberOfModules * sModuleLength, thickOrX2X0, mode), mNumberOfModules(numberOfModules) +{ +} + +TGeoVolume* TRKSegmentedLayer::createSensor() +{ + TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + std::string sensName = GeometryTGeo::getTRKSensorPattern() + std::to_string(mLayerNumber); + TGeoShape* sensor = new TGeoBBox((sChipWidth - sDeadzoneWidth) / 2, sSensorThickness / 2, sChipLength / 2); + TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi); + sensVol->SetLineColor(kYellow); + + return sensVol; +} +TGeoVolume* TRKSegmentedLayer::createDeadzone() +{ + TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + std::string deadName = GeometryTGeo::getTRKDeadzonePattern() + std::to_string(mLayerNumber); + TGeoShape* deadzone = new TGeoBBox(sDeadzoneWidth / 2, sSensorThickness / 2, sChipLength / 2); TGeoVolume* deadVol = new TGeoVolume(deadName.c_str(), deadzone, medSi); deadVol->SetLineColor(kGray); return deadVol; -}; +} -TGeoVolume* TRKLayer::createMetalStack(std::string type) +TGeoVolume* TRKSegmentedLayer::createMetalStack() { TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); std::string metalName = GeometryTGeo::getTRKMetalStackPattern() + std::to_string(mLayerNumber); - - TGeoShape* metalStack; - - if (type == "cylinder") { - metalStack = new TGeoTube(mInnerRadius + mSensorThickness, mInnerRadius + mChipThickness, (constants::moduleMLOT::length * mNumberOfModules) / 2); // TO BE CHECKED !!! - } else if (type == "flat") { - metalStack = new TGeoBBox(mChipWidth / 2, (mChipThickness - mSensorThickness) / 2, mChipLength / 2); // TO BE CHECKED !!! - } else { - LOGP(fatal, "Metal stack of type '{}' is not implemented", type); - } - + TGeoShape* metalStack = new TGeoBBox(sChipWidth / 2, (mChipThickness - sSensorThickness) / 2, sChipLength / 2); TGeoVolume* metalVol = new TGeoVolume(metalName.c_str(), metalStack, medSi); metalVol->SetLineColor(kGray); return metalVol; -}; +} -TGeoVolume* TRKLayer::createChip(std::string type) +TGeoVolume* TRKSegmentedLayer::createChip() { TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); std::string chipName = GeometryTGeo::getTRKChipPattern() + std::to_string(mLayerNumber); - - TGeoShape* chip; - TGeoVolume* chipVol; - - TGeoVolume* sensVol; - TGeoVolume* deadVol; - TGeoVolume* metalVol; - - if (type == "cylinder") { - chip = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, (constants::moduleMLOT::length * mNumberOfModules) / 2); - chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); - - sensVol = createSensor("cylinder"); - LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); - chipVol->AddNode(sensVol, 1, nullptr); - - metalVol = createMetalStack("cylinder"); - LOGP(debug, "Inserting {} in {} ", metalVol->GetName(), chipVol->GetName()); - chipVol->AddNode(metalVol, 1, nullptr); - - // deadVol = createDeadzone("cylinder"); - } else if (type == "flat") { - chip = new TGeoBBox(mChipWidth / 2, mChipThickness / 2, mChipLength / 2); // TO BE CHECKED !!! - chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); - - sensVol = createSensor("flat"); - deadVol = createDeadzone("flat"); - metalVol = createMetalStack("flat"); - - TGeoCombiTrans* transSens = new TGeoCombiTrans(); - transSens->SetTranslation(-mDeadzoneWidth / 2, -(mChipThickness - mSensorThickness) / 2, 0); // TO BE CHECKED !!! - LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); - chipVol->AddNode(sensVol, 1, transSens); - - TGeoCombiTrans* transDead = new TGeoCombiTrans(); - transDead->SetTranslation((mChipWidth - mDeadzoneWidth) / 2, -(mChipThickness - mSensorThickness) / 2, 0); // TO BE CHECKED !!! - LOGP(debug, "Inserting {} in {} ", deadVol->GetName(), chipVol->GetName()); - chipVol->AddNode(deadVol, 1, transDead); - - TGeoCombiTrans* transMetal = new TGeoCombiTrans(); - transMetal->SetTranslation(0, mSensorThickness / 2, 0); // TO BE CHECKED !!! - LOGP(debug, "Inserting {} in {} ", metalVol->GetName(), chipVol->GetName()); - chipVol->AddNode(metalVol, 1, transMetal); - } else { - LOGP(fatal, "Sensor of type '{}' is not implemented", type); - } - + TGeoShape* chip = new TGeoBBox(sChipWidth / 2, mChipThickness / 2, sChipLength / 2); + TGeoVolume* chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); chipVol->SetLineColor(kYellow); + TGeoVolume* sensVol = createSensor(); + TGeoCombiTrans* transSens = new TGeoCombiTrans(); + // transSens->SetTranslation(-sDeadzoneWidth / 2, -(mChipThickness - sSensorThickness) / 2, 0); + transSens->SetTranslation(-sDeadzoneWidth / 2, (mChipThickness - sSensorThickness) / 2, 0); + LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); + chipVol->AddNode(sensVol, 1, transSens); + + TGeoVolume* deadVol = createDeadzone(); + TGeoCombiTrans* transDead = new TGeoCombiTrans(); + // transDead->SetTranslation((sChipWidth - sDeadzoneWidth) / 2, -(mChipThickness - sSensorThickness) / 2, 0); + transDead->SetTranslation((sChipWidth - sDeadzoneWidth) / 2, (mChipThickness - sSensorThickness) / 2, 0); + LOGP(debug, "Inserting {} in {} ", deadVol->GetName(), chipVol->GetName()); + chipVol->AddNode(deadVol, 1, transDead); + + TGeoVolume* metalVol = createMetalStack(); + TGeoCombiTrans* transMetal = new TGeoCombiTrans(); + // transMetal->SetTranslation(0, sSensorThickness / 2, 0); + transMetal->SetTranslation(0, -sSensorThickness / 2, 0); + LOGP(debug, "Inserting {} in {} ", metalVol->GetName(), chipVol->GetName()); + chipVol->AddNode(metalVol, 1, transMetal); + return chipVol; } -TGeoVolume* TRKLayer::createModule(std::string type) +TGeoVolume* TRKSegmentedLayer::createModule() { TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); std::string moduleName = GeometryTGeo::getTRKModulePattern() + std::to_string(mLayerNumber); + TGeoShape* module = new TGeoBBox(sModuleWidth / 2, mChipThickness / 2, sModuleLength / 2); + TGeoVolume* moduleVol = new TGeoVolume(moduleName.c_str(), module, medSi); + moduleVol->SetLineColor(kYellow); - TGeoShape* module; - TGeoVolume* moduleVol; - - if (type == "cylinder") { - double moduleLength = constants::moduleMLOT::length * mNumberOfModules; - - module = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, moduleLength / 2); - moduleVol = new TGeoVolume(moduleName.c_str(), module, medSi); - - TGeoVolume* chipVol = createChip("cylinder"); - LOGP(debug, "Inserting {} in {} ", chipVol->GetName(), moduleVol->GetName()); - moduleVol->AddNode(chipVol, 1, nullptr); - } else if (type == "flat") { - double moduleWidth = constants::moduleMLOT::width; - double moduleLength = constants::moduleMLOT::length; - - module = new TGeoBBox(moduleWidth / 2, mChipThickness / 2, moduleLength / 2); // TO BE CHECKED !!! - moduleVol = new TGeoVolume(moduleName.c_str(), module, medSi); - - for (int iChip = 0; iChip < mHalfNumberOfChips; iChip++) { - TGeoVolume* chipVolLeft = createChip("flat"); - TGeoVolume* chipVolRight = createChip("flat"); - - // Put the chips in the correct position - double xLeft = -moduleWidth / 2 + constants::moduleMLOT::gaps::outerEdgeLongSide + constants::moduleMLOT::chip::width / 2; - double zLeft = -moduleLength / 2 + constants::moduleMLOT::gaps::outerEdgeShortSide + iChip * (constants::moduleMLOT::chip::length + constants::moduleMLOT::gaps::interChips) + constants::moduleMLOT::chip::length / 2; - - TGeoCombiTrans* transLeft = new TGeoCombiTrans(); - transLeft->SetTranslation(xLeft, 0, zLeft); // TO BE CHECKED !!! - TGeoRotation* rot = new TGeoRotation(); - rot->RotateY(180); - transLeft->SetRotation(rot); - LOGP(debug, "Inserting {} in {} ", chipVolLeft->GetName(), moduleVol->GetName()); - moduleVol->AddNode(chipVolLeft, iChip * 2, transLeft); - - double xRight = +moduleWidth / 2 - constants::moduleMLOT::gaps::outerEdgeLongSide - constants::moduleMLOT::chip::width / 2; - double zRight = -moduleLength / 2 + constants::moduleMLOT::gaps::outerEdgeShortSide + iChip * (constants::moduleMLOT::chip::length + constants::moduleMLOT::gaps::interChips) + constants::moduleMLOT::chip::length / 2; - - TGeoCombiTrans* transRight = new TGeoCombiTrans(); - transRight->SetTranslation(xRight, 0, zRight); // TO BE CHECKED !!! - LOGP(debug, "Inserting {} in {} ", chipVolRight->GetName(), moduleVol->GetName()); - moduleVol->AddNode(chipVolRight, iChip * 2 + 1, transRight); - } - } else { - LOGP(fatal, "Chip of type '{}' is not implemented", type); + for (int iChip = 0; iChip < sHalfNumberOfChips; iChip++) { + TGeoVolume* chipVolLeft = createChip(); + double xLeft = -sModuleWidth / 2 + constants::moduleMLOT::gaps::outerEdgeLongSide + constants::moduleMLOT::chip::width / 2; + double zLeft = -sModuleLength / 2 + constants::moduleMLOT::gaps::outerEdgeShortSide + iChip * (constants::moduleMLOT::chip::length + constants::moduleMLOT::gaps::interChips) + constants::moduleMLOT::chip::length / 2; + TGeoCombiTrans* transLeft = new TGeoCombiTrans(); + transLeft->SetTranslation(xLeft, 0, zLeft); + TGeoRotation* rot = new TGeoRotation(); + rot->RotateY(180); + transLeft->SetRotation(rot); + LOGP(debug, "Inserting {} in {} ", chipVolLeft->GetName(), moduleVol->GetName()); + moduleVol->AddNode(chipVolLeft, iChip * 2, transLeft); + + TGeoVolume* chipVolRight = createChip(); + double xRight = +sModuleWidth / 2 - constants::moduleMLOT::gaps::outerEdgeLongSide - constants::moduleMLOT::chip::width / 2; + double zRight = -sModuleLength / 2 + constants::moduleMLOT::gaps::outerEdgeShortSide + iChip * (constants::moduleMLOT::chip::length + constants::moduleMLOT::gaps::interChips) + constants::moduleMLOT::chip::length / 2; + TGeoCombiTrans* transRight = new TGeoCombiTrans(); + transRight->SetTranslation(xRight, 0, zRight); + LOGP(debug, "Inserting {} in {} ", chipVolRight->GetName(), moduleVol->GetName()); + moduleVol->AddNode(chipVolRight, iChip * 2 + 1, transRight); } - moduleVol->SetLineColor(kYellow); - return moduleVol; } -TGeoVolume* TRKLayer::createHalfStave(std::string type) -{ - TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); - std::string halfStaveName = GeometryTGeo::getTRKHalfStavePattern() + std::to_string(mLayerNumber); - - TGeoShape* halfStave; - TGeoVolume* halfStaveVol; - - double halfStaveLength = constants::moduleMLOT::length * mNumberOfModules; - - if (type == "cylinder") { - halfStave = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, halfStaveLength / 2); - halfStaveVol = new TGeoVolume(halfStaveName.c_str(), halfStave, medSi); - - TGeoVolume* moduleVol = createModule("cylinder"); - LOGP(debug, "Inserting {} in {} ", moduleVol->GetName(), halfStaveVol->GetName()); - halfStaveVol->AddNode(moduleVol, 1, nullptr); - } else if (type == "flat") { - double moduleLength = constants::moduleMLOT::length; - double halfStaveWidth = constants::OT::halfstave::width; - - halfStave = new TGeoBBox(halfStaveWidth / 2, mChipThickness / 2, halfStaveLength / 2); - halfStaveVol = new TGeoVolume(halfStaveName.c_str(), halfStave, medSi); - - for (int iModule = 0; iModule < mNumberOfModules; iModule++) { - TGeoVolume* moduleVol = createModule("flat"); - - // Put the modules in the correct position - double zPos = -0.5 * mNumberOfModules * moduleLength + (iModule + 0.5) * moduleLength; - - TGeoCombiTrans* trans = new TGeoCombiTrans(); - trans->SetTranslation(0, 0, zPos); // TO BE CHECKED !!! - - LOGP(debug, "Inserting {} in {} ", moduleVol->GetName(), halfStaveVol->GetName()); - halfStaveVol->AddNode(moduleVol, iModule, trans); - } - } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - halfStaveVol->SetLineColor(kYellow); - - return halfStaveVol; +TRKMLLayer::TRKMLLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode) + : TRKSegmentedLayer(layerNumber, layerName, rInn, numberOfModules, thickOrX2X0, mode) +{ } -TGeoVolume* TRKLayer::createStave(std::string type) +TGeoVolume* TRKMLLayer::createStave() { TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); std::string staveName = GeometryTGeo::getTRKStavePattern() + std::to_string(mLayerNumber); + TGeoShape* stave = new TGeoBBox(sStaveWidth / 2, mChipThickness / 2, mLength / 2); + TGeoVolume* staveVol = new TGeoVolume(staveName.c_str(), stave, medAir); + staveVol->SetLineColor(kYellow); - TGeoShape* stave; - TGeoVolume* staveVol; - - double staveLength = constants::moduleMLOT::length * mNumberOfModules; - - if (type == "cylinder") { - stave = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, staveLength / 2); - staveVol = new TGeoVolume(staveName.c_str(), stave, medAir); - - TGeoVolume* moduleVol = createModule("cylinder"); + for (int iModule = 0; iModule < mNumberOfModules; iModule++) { + TGeoVolume* moduleVol = createModule(); + double zPos = -0.5 * mNumberOfModules * sModuleLength + (iModule + 0.5) * sModuleLength; + TGeoCombiTrans* trans = new TGeoCombiTrans(); + trans->SetTranslation(0, 0, zPos); LOGP(debug, "Inserting {} in {} ", moduleVol->GetName(), staveVol->GetName()); - staveVol->AddNode(moduleVol, 1, nullptr); - } else if (type == "flat") { - double moduleLength = constants::moduleMLOT::length; - double staveWidth = constants::ML::width; - - stave = new TGeoBBox(staveWidth / 2, mChipThickness / 2, staveLength / 2); - staveVol = new TGeoVolume(staveName.c_str(), stave, medAir); + staveVol->AddNode(moduleVol, iModule, trans); + } - for (int iModule = 0; iModule < mNumberOfModules; iModule++) { - TGeoVolume* moduleVol = createModule("flat"); + return staveVol; +} - // Put the modules in the correct position - double zPos = -0.5 * mNumberOfModules * moduleLength + (iModule + 0.5) * moduleLength; +void TRKMLLayer::createLayer(TGeoVolume* motherVolume) +{ + TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); + TGeoTube* layer = new TGeoTube(mInnerRadius - 0.333 * sLogicalVolumeThickness, mInnerRadius + 0.667 * sLogicalVolumeThickness, mLength / 2); + TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); + layerVol->SetLineColor(kYellow); - TGeoCombiTrans* trans = new TGeoCombiTrans(); - trans->SetTranslation(0, 0, zPos); // TO BE CHECKED !!! + // Compute the number of staves + int nStaves = (int)std::ceil(mInnerRadius * 2 * TMath::Pi() / sStaveWidth); + nStaves += nStaves % 2; // Require an even number of staves + + // Compute the size of the overlap region + double theta = 2 * TMath::Pi() / nStaves; + double theta1 = std::atan(sStaveWidth / 2 / mInnerRadius); + double st = std::sin(theta); + double ct = std::cos(theta); + double theta2 = std::atan((mInnerRadius * st - sStaveWidth / 2 * ct) / (mInnerRadius * ct + sStaveWidth / 2 * st)); + double overlap = (theta1 - theta2) * mInnerRadius; + LOGP(info, "Creating a layer with {} staves and {} mm overlap", nStaves, overlap * 10); + + for (int iStave = 0; iStave < nStaves; iStave++) { + TGeoVolume* staveVol = createStave(); + TGeoCombiTrans* trans = new TGeoCombiTrans(); + double theta = 360. * iStave / nStaves; + // TGeoRotation* rot = new TGeoRotation("rot", theta - 90 + 4, 0, 0); + TGeoRotation* rot = new TGeoRotation("rot", theta + 90 + 4, 0, 0); + trans->SetRotation(rot); + trans->SetTranslation(mInnerRadius * std::cos(2. * TMath::Pi() * iStave / nStaves), mInnerRadius * std::sin(2 * TMath::Pi() * iStave / nStaves), 0); + LOGP(debug, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); + layerVol->AddNode(staveVol, iStave, trans); + } - LOGP(debug, "Inserting {} in {} ", moduleVol->GetName(), staveVol->GetName()); - staveVol->AddNode(moduleVol, iModule, trans); - } - } else if (type == "staggered") { - double overlap = constants::moduleMLOT::gaps::outerEdgeLongSide + constants::moduleMLOT::chip::passiveEdgeReadOut + 0.1; // 1.5mm outer-edge + 1mm deadzone + 1mm (true)overlap - double shift = overlap / 2; - double halfstaveWidth = constants::OT::halfstave::width; + LOGP(debug, "Inserting {} in {} ", layerVol->GetName(), motherVolume->GetName()); + motherVolume->AddNode(layerVol, 1, nullptr); +} - staveVol = new TGeoVolumeAssembly(staveName.c_str()); +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Put the half staves in the correct position - TGeoVolume* halfStaveVolLeft = createHalfStave("flat"); - TGeoVolume* halfStaveVolRight = createHalfStave("flat"); +TRKOTLayer::TRKOTLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode) + : TRKSegmentedLayer(layerNumber, layerName, rInn, numberOfModules, thickOrX2X0, mode) +{ +} - TGeoCombiTrans* transLeft = new TGeoCombiTrans(); - transLeft->SetTranslation(-halfstaveWidth / 2 + shift, 0, 0); // TO BE CHECKED !!! 1mm overlap between the modules - LOGP(debug, "Inserting {} in {} ", halfStaveVolLeft->GetName(), staveVol->GetName()); - staveVol->AddNode(halfStaveVolLeft, 0, transLeft); +TGeoVolume* TRKOTLayer::createHalfStave() +{ + TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + std::string halfStaveName = GeometryTGeo::getTRKHalfStavePattern() + std::to_string(mLayerNumber); + TGeoShape* halfStave = new TGeoBBox(sHalfStaveWidth / 2, mChipThickness / 2, mLength / 2); + TGeoVolume* halfStaveVol = new TGeoVolume(halfStaveName.c_str(), halfStave, medSi); + halfStaveVol->SetLineColor(kYellow); - TGeoCombiTrans* transRight = new TGeoCombiTrans(); - transRight->SetTranslation(halfstaveWidth / 2 - shift, 0.2, 0); // TO BE CHECKED !!! 1mm overlap between the modules - LOGP(debug, "Inserting {} in {} ", halfStaveVolRight->GetName(), staveVol->GetName()); - staveVol->AddNode(halfStaveVolRight, 1, transRight); - } else { - LOGP(fatal, "Chip of type '{}' is not implemented", type); + for (int iModule = 0; iModule < mNumberOfModules; iModule++) { + TGeoVolume* moduleVol = createModule(); + double zPos = -0.5 * mNumberOfModules * sModuleLength + (iModule + 0.5) * sModuleLength; + TGeoCombiTrans* trans = new TGeoCombiTrans(); + trans->SetTranslation(0, 0, zPos); + LOGP(debug, "Inserting {} in {} ", moduleVol->GetName(), halfStaveVol->GetName()); + halfStaveVol->AddNode(moduleVol, iModule, trans); } - staveVol->SetLineColor(kYellow); - - return staveVol; + return halfStaveVol; } -void TRKLayer::createLayer(TGeoVolume* motherVolume) +TGeoVolume* TRKOTLayer::createStave() { - TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); + std::string staveName = GeometryTGeo::getTRKStavePattern() + std::to_string(mLayerNumber); + TGeoVolume* staveVol = new TGeoVolumeAssembly(staveName.c_str()); - double layerThickness = mChipThickness; - if (mLayout != eLayout::kCylinder) { - layerThickness = mLogicalVolumeThickness; - } + TGeoVolume* halfStaveVolLeft = createHalfStave(); + TGeoCombiTrans* transLeft = new TGeoCombiTrans(); + transLeft->SetTranslation(-(sHalfStaveWidth - sInStaveOverlap) / 2, 0, 0); + LOGP(debug, "Inserting {} in {} ", halfStaveVolLeft->GetName(), staveVol->GetName()); + staveVol->AddNode(halfStaveVolLeft, 0, transLeft); - TGeoTube* layer; - TGeoVolume* layerVol; + TGeoVolume* halfStaveVolRight = createHalfStave(); + TGeoCombiTrans* transRight = new TGeoCombiTrans(); + transRight->SetTranslation((sHalfStaveWidth - sInStaveOverlap) / 2, 0.2, 0); + LOGP(debug, "Inserting {} in {} ", halfStaveVolRight->GetName(), staveVol->GetName()); + staveVol->AddNode(halfStaveVolRight, 1, transRight); - double layerLength = constants::moduleMLOT::length * mNumberOfModules; + return staveVol; +} - if (mLayout == eLayout::kCylinder) { - layer = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, layerLength / 2); - layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); +void TRKOTLayer::createLayer(TGeoVolume* motherVolume) +{ + TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); + TGeoTube* layer = new TGeoTube(mInnerRadius - 0.333 * sLogicalVolumeThickness, mInnerRadius + 0.667 * sLogicalVolumeThickness, mLength / 2); + TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); + layerVol->SetLineColor(kYellow); - TGeoVolume* staveVol = createStave("cylinder"); + // Compute the number of staves + int nStaves = (int)std::ceil(mInnerRadius * 2 * TMath::Pi() / sStaveWidth); + nStaves += nStaves % 2; // Require an even number of staves + + // Compute the size of the overlap region + double theta = 2 * TMath::Pi() / nStaves; + double theta1 = std::atan(sStaveWidth / 2 / mInnerRadius); + double st = std::sin(theta); + double ct = std::cos(theta); + double theta2 = std::atan((mInnerRadius * st - sStaveWidth / 2 * ct) / (mInnerRadius * ct + sStaveWidth / 2 * st)); + double overlap = (theta1 - theta2) * mInnerRadius; + LOGP(info, "Creating a layer with {} staves and {} mm overlap", nStaves, overlap * 10); + + for (int iStave = 0; iStave < nStaves; iStave++) { + TGeoVolume* staveVol = createStave(); + TGeoCombiTrans* trans = new TGeoCombiTrans(); + double theta = 360. * iStave / nStaves; + // TGeoRotation* rot = new TGeoRotation("rot", theta - 90, 0, 0); + TGeoRotation* rot = new TGeoRotation("rot", theta + 90, 0, 0); + trans->SetRotation(rot); + trans->SetTranslation(mInnerRadius * std::cos(2. * TMath::Pi() * iStave / nStaves), mInnerRadius * std::sin(2 * TMath::Pi() * iStave / nStaves), 0); LOGP(debug, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); - layerVol->AddNode(staveVol, 1, nullptr); - } else if (mLayout == eLayout::kTurboStaves) { - double staveWidth = constants::ML::width; // Each stave has two modules (based on the LOI design) - - if (mInnerRadius > 25) { - staveWidth = constants::OT::width; // Outer layers have two modules per stave - } - - layer = new TGeoTube(mInnerRadius - 0.333 * layerThickness, mInnerRadius + 0.667 * layerThickness, layerLength / 2); - layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); - - // Compute the number of staves - int nStaves = (int)std::ceil(mInnerRadius * 2 * TMath::Pi() / staveWidth); - nStaves += nStaves % 2; // Require an even number of staves - - // Compute the size of the overlap region - double theta = 2 * TMath::Pi() / nStaves; - double theta1 = std::atan(staveWidth / 2 / mInnerRadius); - double st = std::sin(theta); - double ct = std::cos(theta); - double theta2 = std::atan((mInnerRadius * st - staveWidth / 2 * ct) / (mInnerRadius * ct + staveWidth / 2 * st)); - double overlap = (theta1 - theta2) * mInnerRadius; - LOGP(info, "Creating a layer with {} staves and {} mm overlap", nStaves, overlap * 10); - - for (int iStave = 0; iStave < nStaves; iStave++) { - TGeoVolume* staveVol = createStave("flat"); - - // Put the staves in the correct position and orientation - TGeoCombiTrans* trans = new TGeoCombiTrans(); - double theta = 360. * iStave / nStaves; - TGeoRotation* rot = new TGeoRotation("rot", theta - 90 + 4, 0, 0); - trans->SetRotation(rot); - trans->SetTranslation(mInnerRadius * std::cos(2. * TMath::Pi() * iStave / nStaves), mInnerRadius * std::sin(2 * TMath::Pi() * iStave / nStaves), 0); - - LOGP(debug, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); - layerVol->AddNode(staveVol, iStave, trans); - } - } else if (mLayout == kStaggered) { - double overlapInStave = constants::moduleMLOT::gaps::outerEdgeLongSide + constants::moduleMLOT::chip::passiveEdgeReadOut + 0.1; // 1.5mm outer-edge + 1mm deadzone + 1mm (true)overlap - - double staveWidth = constants::OT::width - overlapInStave; - - layer = new TGeoTube(mInnerRadius - 0.333 * layerThickness, mInnerRadius + 0.667 * layerThickness, layerLength / 2); - layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); - - // Compute the number of staves - int nStaves = (int)std::ceil(mInnerRadius * 2 * TMath::Pi() / staveWidth); - nStaves += nStaves % 2; // Require an even number of staves - - // Compute the size of the overlap region - double theta = 2 * TMath::Pi() / nStaves; - double theta1 = std::atan(staveWidth / 2 / mInnerRadius); - double st = std::sin(theta); - double ct = std::cos(theta); - double theta2 = std::atan((mInnerRadius * st - staveWidth / 2 * ct) / (mInnerRadius * ct + staveWidth / 2 * st)); - double overlap = (theta1 - theta2) * mInnerRadius; - LOGP(info, "Creating a layer with {} staves and {} mm overlap", nStaves, overlap * 10); - - for (int iStave = 0; iStave < nStaves; iStave++) { - TGeoVolume* staveVol = createStave("staggered"); - - // Put the staves in the correct position and orientation - TGeoCombiTrans* trans = new TGeoCombiTrans(); - double theta = 360. * iStave / nStaves; - TGeoRotation* rot = new TGeoRotation("rot", theta - 90, 0, 0); - trans->SetRotation(rot); - trans->SetTranslation(mInnerRadius * std::cos(2. * TMath::Pi() * iStave / nStaves), mInnerRadius * std::sin(2 * TMath::Pi() * iStave / nStaves), 0); - - LOGP(debug, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); - layerVol->AddNode(staveVol, iStave, trans); - } - } else { - LOGP(fatal, "Layout not implemented"); + layerVol->AddNode(staveVol, iStave, trans); } - layerVol->SetLineColor(kYellow); LOGP(debug, "Inserting {} in {} ", layerVol->GetName(), motherVolume->GetName()); motherVolume->AddNode(layerVol, 1, nullptr); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx index bd27a5bc30f62..0394c59780141 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,8 @@ #include +using std::string; + namespace o2 { namespace trk @@ -126,9 +129,17 @@ void TRKServices::createServices(TGeoVolume* motherVolume) { createMaterials(); createVacuumCompositeShape(); - createMiddleServices(motherVolume); - createOuterDisksServices(motherVolume); - createOuterBarrelServices(motherVolume); + auto& trkPars = TRKBaseParam::Instance(); + if (trkPars.getLayoutSRV() == kLOISymm) { + LOGP(info, "TRK services: LoI version"); + createMiddleServices(motherVolume); + createOuterDisksServices(motherVolume); + createOuterBarrelServices(motherVolume); + } else { + LOGP(info, "TRK services: Peacock layout"); + createMLServicesPeacock(motherVolume); + createOTServicesPeacock(motherVolume); + } } void TRKServices::createVacuumCompositeShape() @@ -320,10 +331,10 @@ void TRKServices::createMiddleServices(TGeoVolume* motherVolume) const float rMaxMiddleBarrelDisk = 35.f; const float zLengthMiddleBarrel = 64.5f; for (auto& orientation : {Orientation::kASide, Orientation::kCSide}) { - TGeoTube* middleBarrelConnDiskSIO2 = new TGeoTube(Form("TRK_MIDBARCONN_DISK_SIO2sh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, siO2FiberThick / 2.); - TGeoTube* middleBarrelConnDiskPE = new TGeoTube(Form("TRK_MIDBARCONN_DISK_PEsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, peFiberThick / 2.); - TGeoVolume* middleBarrelConnDiskSIO2Volume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_SIO2_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskSIO2, medSiO2); - TGeoVolume* middleBarrelConnDiskPEVolume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_PE_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskPE, medPE); + TGeoTube* middleBarrelConnDiskSIO2 = new TGeoTube(Form("TRK_MIDBARCONN_DISK_FIBER_SIO2sh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, siO2FiberThick / 2.); + TGeoTube* middleBarrelConnDiskPE = new TGeoTube(Form("TRK_MIDBARCONN_DISK_FIBER_PEsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, peFiberThick / 2.); + TGeoVolume* middleBarrelConnDiskSIO2Volume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_FIBER_SIO2_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskSIO2, medSiO2); + TGeoVolume* middleBarrelConnDiskPEVolume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_FIBER_PE_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskPE, medPE); middleBarrelConnDiskSIO2Volume->SetLineColor(kGray); middleBarrelConnDiskPEVolume->SetLineColor(kGray); auto* rot = new TGeoRotation("", 0, 0, 180); @@ -332,10 +343,10 @@ void TRKServices::createMiddleServices(TGeoVolume* motherVolume) motherVolume->AddNode(middleBarrelConnDiskSIO2Volume, 1, combiTransSIO2); motherVolume->AddNode(middleBarrelConnDiskPEVolume, 1, combiTransPE); - TGeoTube* middleBarrelConnDiskCu = new TGeoTube(Form("TRK_MIDBARCONN_DISK_CUsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, cuPowerThick / 2.); - TGeoTube* middleBarrelConnDiskPEPower = new TGeoTube(Form("TRK_MIDBARCONN_DISK_PEsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, pePowerThick / 2.); - TGeoVolume* middleBarrelConnDiskCuVolume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_CU_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskCu, medCu); - TGeoVolume* middleBarrelConnDiskPEPowerVolume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_PE_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskPEPower, medPE); + TGeoTube* middleBarrelConnDiskCu = new TGeoTube(Form("TRK_MIDBARCONN_DISK_POWER_CUsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, cuPowerThick / 2.); + TGeoTube* middleBarrelConnDiskPEPower = new TGeoTube(Form("TRK_MIDBARCONN_DISK_POWER_PEsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, pePowerThick / 2.); + TGeoVolume* middleBarrelConnDiskCuVolume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_POWER_CU_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskCu, medCu); + TGeoVolume* middleBarrelConnDiskPEPowerVolume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_POWER_PE_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskPEPower, medPE); middleBarrelConnDiskCuVolume->SetLineColor(kGray); middleBarrelConnDiskPEPowerVolume->SetLineColor(kGray); auto* combiTransCu = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick / 2. + zLengthMiddleBarrel), rot); @@ -343,14 +354,16 @@ void TRKServices::createMiddleServices(TGeoVolume* motherVolume) motherVolume->AddNode(middleBarrelConnDiskCuVolume, 1, combiTransCu); motherVolume->AddNode(middleBarrelConnDiskPEPowerVolume, 1, combiTransPEPower); - TGeoTube* middleBarrelConnDiskPU = new TGeoTube(Form("TRK_MIDBARCONN_DISK_PUsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, puCoolingThick); - TGeoTube* middleBarrelConnDiskH2O = new TGeoTube(Form("TRK_MIDBARCONN_DISK_H2Osh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, h2oCoolingThick); + TGeoTube* middleBarrelConnDiskPU = new TGeoTube(Form("TRK_MIDBARCONN_DISK_PUsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, puCoolingThick / 2.); + TGeoTube* middleBarrelConnDiskH2O = new TGeoTube(Form("TRK_MIDBARCONN_DISK_H2Osh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, h2oCoolingThick / 2.); TGeoVolume* middleBarrelConnDiskPUVolume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_PU_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskPU, medPU); TGeoVolume* middleBarrelConnDiskH2OVolume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_H2O_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskH2O, medH2O); middleBarrelConnDiskPUVolume->SetLineColor(kGray); middleBarrelConnDiskH2OVolume->SetLineColor(kGray); - motherVolume->AddNode(middleBarrelConnDiskPUVolume, 1, combiTransCu); - motherVolume->AddNode(middleBarrelConnDiskH2OVolume, 1, combiTransPEPower); + auto* combiTransPU = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick + puCoolingThick / 2. + zLengthMiddleBarrel), rot); + auto* combiTransH2O = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick + puCoolingThick + h2oCoolingThick / 2. + zLengthMiddleBarrel), rot); + motherVolume->AddNode(middleBarrelConnDiskPUVolume, 1, combiTransPU); + motherVolume->AddNode(middleBarrelConnDiskH2OVolume, 1, combiTransH2O); } // Barrel to forward connection disks @@ -448,6 +461,10 @@ void TRKServices::createMiddleServices(TGeoVolume* motherVolume) void TRKServices::createOuterBarrelServices(TGeoVolume* motherVolume) { + // This implements a service barrel around the full outer tracker which is probably not needed: + // power, data and cooling should be implemented on the staves + // Used only for 'LOI' geometry + auto& matmgr = o2::base::MaterialManager::Instance(); TGeoMedium* medSiO2 = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_SILICONDIOXIDE"); @@ -500,5 +517,439 @@ void TRKServices::createOuterBarrelServices(TGeoVolume* motherVolume) motherVolume->AddNode(outerBarrelCoolingPUVolume, 1, nullptr); motherVolume->AddNode(outerBarrelCoolingH2OVolume, 1, nullptr); } + +void TRKServices::createMLServicesPeacock(TGeoVolume* motherVolume) +{ + // This method hardcoes the yellow shape for the middle services + auto& matmgr = o2::base::MaterialManager::Instance(); + + TGeoMedium* medSiO2 = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_SILICONDIOXIDE"); + TGeoMedium* medPE = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_POLYETHYLENE"); + TGeoMedium* medCu = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_COPPER"); + TGeoMedium* medPU = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_POLYURETHANE"); + TGeoMedium* medH2O = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_WATER"); + TGeoMedium* medCFiber = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_CARBONFIBERM55J6K"); + + // Barrel service constants + const int ITBarrelnFiber = 70; + const int ITBarrelnPower = 70; + float siO2FiberAreaB = ITBarrelnFiber * mFiberArea * mFiberComposition[0]; + float peFiberAreaB = ITBarrelnFiber * mFiberArea * mFiberComposition[1]; + + float puCoolingAreaB = 0; + float h2oCoolingAreaB = 0; + float cuPowerAreaB = ITBarrelnPower * mPowerBundleArea * mPowerBundleComposition[0]; + float pePowerAreaB = ITBarrelnPower * mPowerBundleArea * mPowerBundleComposition[1]; + + // Disk service constants + const int ITDisknFiber = 3 * 24; + const int ITDisknPower = 3 * 16; + float siO2FiberAreaD = ITDisknFiber * mFiberArea * mFiberComposition[0]; + float peFiberAreaD = ITDisknFiber * mFiberArea * mFiberComposition[1]; + + float puCoolingAreaD = 0; + float h2oCoolingAreaD = 0; + float cuPowerAreaD = ITDisknPower * mPowerBundleArea * mPowerBundleComposition[0]; + float pePowerAreaD = ITDisknPower * mPowerBundleArea * mPowerBundleComposition[1]; + + // Carbon Fiber Cylinder support for the middle tracker + float rMinMiddleCarbonSupport = 34.8f; // Arbitrary value + float rMaxMiddleCarbonSupport = 35.f; // 2 mm of carbon fiber + const float zLengthMiddleCarbon = 129.f; + TGeoTube* middleBarrelCarbonSupport = new TGeoTube("TRK_MID_CARBONSUPPORTsh", rMinMiddleCarbonSupport, rMaxMiddleCarbonSupport, zLengthMiddleCarbon / 2.); + TGeoVolume* middleBarrelCarbonSupportVolume = new TGeoVolume("TRK_MID_CARBONSUPPORT", middleBarrelCarbonSupport, medCFiber); + middleBarrelCarbonSupportVolume->SetLineColor(kGray); + LOGP(info, "Creating carbon fiber support for Middle Tracker"); + motherVolume->AddNode(middleBarrelCarbonSupportVolume, 1, nullptr); + + // Get geometry information from TRK which is already present + float rMinMiddleServices = 35.f; + float rMinMiddleBarrel = rMinMiddleServices; + const float zLengthCylinderMiddleServices = 40.5f; + const float zLengthMiddleServices = 143.f; + + // Middle layer barrel services are only on A side + rMinMiddleServices = 35.f; + LOGP(info, "Building services for Middle Tracker rminMiddleServices"); + + // Middle barrel connection disks + const float rMinMiddleBarrelDisk = 5.68f; + const float rMaxMiddleBarrelDisk = 35.f; + const float zLengthMiddleBarrel = 64.5f; + auto orientation = Orientation::kASide; + float diskCircumference = rMaxMiddleBarrelDisk * 3.14; // Use only half circumference + + double zCur = zLengthMiddleBarrel; + double dZ = siO2FiberAreaB / diskCircumference / 2.; + TGeoTube* middleBarrelConnDiskSIO2 = new TGeoTube("TRK_MIDBARCONN_DISK_FIBER_SIO2sh", rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, dZ); + TGeoVolume* middleBarrelConnDiskSIO2Volume = new TGeoVolume("TRK_MIDBARCONN_DISK_FIBER_SIO2", middleBarrelConnDiskSIO2, medSiO2); + middleBarrelConnDiskSIO2Volume->SetLineColor(kGray); + auto* rot = new TGeoRotation("", 0, 0, 180); // Why this? + auto* combiTransSIO2 = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), rot); + + zCur += 2. * dZ; + dZ = peFiberAreaB / diskCircumference / 2.; + TGeoTube* middleBarrelConnDiskPE = new TGeoTube("TRK_MIDBARCONN_DISK_FIBER_PEsh", rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, dZ); + TGeoVolume* middleBarrelConnDiskPEVolume = new TGeoVolume("TRK_MIDBARCONN_DISK_FIBER_PE", middleBarrelConnDiskPE, medPE); + middleBarrelConnDiskPEVolume->SetLineColor(kGray); + auto* combiTransPE = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), rot); + + motherVolume->AddNode(middleBarrelConnDiskSIO2Volume, 1, combiTransSIO2); + motherVolume->AddNode(middleBarrelConnDiskPEVolume, 1, combiTransPE); + + zCur += 2. * dZ; + dZ = cuPowerAreaB / diskCircumference / 2.; + TGeoTube* middleBarrelConnDiskCu = new TGeoTube("TRK_MIDBARCONN_DISK_POWER_CUsh", rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, dZ); + TGeoVolume* middleBarrelConnDiskCuVolume = new TGeoVolume("TRK_MIDBARCONN_DISK_POWER_CU", middleBarrelConnDiskCu, medCu); + middleBarrelConnDiskCuVolume->SetLineColor(kGray); + auto* combiTransCu = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), rot); + + zCur += 2. * dZ; + dZ = pePowerAreaB / diskCircumference / 2.; + TGeoTube* middleBarrelConnDiskPEPower = new TGeoTube("TRK_MIDBARCONN_DISK_POWER_PEsh", rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, dZ); + TGeoVolume* middleBarrelConnDiskPEPowerVolume = new TGeoVolume("TRK_MIDBARCONN_DISK_POWER_PE", middleBarrelConnDiskPEPower, medPE); + middleBarrelConnDiskPEPowerVolume->SetLineColor(kGray); + auto* combiTransPEPower = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), rot); + motherVolume->AddNode(middleBarrelConnDiskCuVolume, 1, combiTransCu); + motherVolume->AddNode(middleBarrelConnDiskPEPowerVolume, 1, combiTransPEPower); + + for (auto& orientation : {Orientation::kASide, Orientation::kCSide}) { + for (int iSide = 0; iSide < 2; iSide++) { // left/right or top/bottom + float refAngle = 0; + string orLabel("A"); + if (orientation == Orientation::kCSide) { + orLabel = "C"; + refAngle = 90; + } + // Add ML Disk services + // create data fiber volumes + double rCur = rMinMiddleServices; + double dR = siO2FiberAreaD / (3.14 * rCur); + TGeoTubeSeg* middleDiskFiberSIO2 = new TGeoTubeSeg(Form("TRK_MLD_FIBER_SIO2sh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthCylinderMiddleServices, -45, 45); + TGeoVolume* middleDiskFiberSIO2Volume = new TGeoVolume(Form("TRK_MLD_FIBER_SIO2_%s%d", orLabel.c_str(), iSide), middleDiskFiberSIO2, medSiO2); + middleDiskFiberSIO2Volume->SetLineColor(kGray); + + rCur += dR; + dR = peFiberAreaD / (3.14 * rCur); + TGeoTubeSeg* middleDiskFiberPE = new TGeoTubeSeg(Form("TRK_MLD_FIBER_PEsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthCylinderMiddleServices, -45, 45); + TGeoVolume* middleDiskFiberPEVolume = new TGeoVolume(Form("TRK_MLD_FIBER_PE_%s%d", orLabel.c_str(), iSide), middleDiskFiberPE, medPE); + middleDiskFiberPEVolume->SetLineColor(kGray); + auto* combiTrans = new TGeoCombiTrans(0, 0, (int)orientation * (zLengthMiddleServices - zLengthCylinderMiddleServices), new TGeoRotation("", refAngle + iSide * 180., 0, 0)); + motherVolume->AddNode(middleDiskFiberSIO2Volume, 1, combiTrans); + motherVolume->AddNode(middleDiskFiberPEVolume, 1, combiTrans); + + // Create powerlines + rCur += dR; + dR = cuPowerAreaD / (3.14 * rCur); + TGeoTubeSeg* middleDiskPowerCu = new TGeoTubeSeg(Form("TRK_MLD_POWER_CUsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthCylinderMiddleServices, -45, 45); + TGeoVolume* middleDiskPowerCuVolume = new TGeoVolume(Form("TRK_MLD_POWER_CU_%s%d", orLabel.c_str(), iSide), middleDiskPowerCu, medCu); + middleDiskPowerCuVolume->SetLineColor(kGray); + + rCur += dR; + dR = pePowerAreaD / (3.14 * rCur); + TGeoTubeSeg* middleDiskPowerPE = new TGeoTubeSeg(Form("TRK_MLD_POWER_PEsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthCylinderMiddleServices, -45, 45); + TGeoVolume* middleDiskPowerPEVolume = new TGeoVolume(Form("TRK_MLD_POWER_PE_%s%d", orLabel.c_str(), iSide), middleDiskPowerPE, medPE); + middleDiskPowerPEVolume->SetLineColor(kGray); + + motherVolume->AddNode(middleDiskPowerCuVolume, 1, combiTrans); + motherVolume->AddNode(middleDiskPowerPEVolume, 1, combiTrans); + + if (orientation == Orientation::kASide) { + // Add Barrel services + // create data fiber volumes + rCur += dR; + dR = siO2FiberAreaB / (3.14 * rCur); + TGeoTubeSeg* middleBarrelFiberSIO2 = new TGeoTubeSeg(Form("TRK_MLB_FIBER_SIO2sh_A%d", iSide), rCur, rCur + dR, zLengthCylinderMiddleServices, -45, 45); + TGeoVolume* middleBarrelFiberSIO2Volume = new TGeoVolume(Form("TRK_MLB_FIBER_SIO2_A%d", iSide), middleBarrelFiberSIO2, medSiO2); + middleBarrelFiberSIO2Volume->SetLineColor(kGray); + + rCur += dR; + dR = peFiberAreaB / (3.14 * rCur); + TGeoTubeSeg* middleBarrelFiberPE = new TGeoTubeSeg(Form("TRK_MLB_FIBER_PEsh_A%d", iSide), rCur, rCur + dR, zLengthCylinderMiddleServices, -45, 45); + TGeoVolume* middleBarrelFiberPEVolume = new TGeoVolume(Form("TRK_MLB_FIBER_PE_A%d", iSide), middleBarrelFiberPE, medPE); + middleBarrelFiberPEVolume->SetLineColor(kGray); + auto* combiTrans = new TGeoCombiTrans(0, 0, (int)orientation * (zLengthMiddleServices - zLengthCylinderMiddleServices), new TGeoRotation(nullptr, refAngle + iSide * 180., 0, 0)); + motherVolume->AddNode(middleBarrelFiberSIO2Volume, 1, combiTrans); + motherVolume->AddNode(middleBarrelFiberPEVolume, 1, combiTrans); + + // Create powerlines + rCur += dR; + dR = cuPowerAreaB / (3.14 * rCur); + TGeoTubeSeg* middleBarrelPowerCu = new TGeoTubeSeg(Form("TRK_MLB_POWER_CUsh_A%d", iSide), rCur, rCur + dR, zLengthCylinderMiddleServices, -45, 45); + TGeoVolume* middleBarrelPowerCuVolume = new TGeoVolume(Form("TRK_MLB_POWER_CU_A%d", iSide), middleBarrelPowerCu, medCu); + middleBarrelPowerCuVolume->SetLineColor(kGray); + + rCur += dR; + dR = pePowerAreaB / (3.14 * rCur); + TGeoTubeSeg* middleBarrelPowerPE = new TGeoTubeSeg(Form("TRK_MLB_POWER_PEsh_A%d", iSide), rCur, rCur + dR, zLengthCylinderMiddleServices, -45, 45); + TGeoVolume* middleBarrelPowerPEVolume = new TGeoVolume(Form("TRK_MLB_POWER_PE_A%d", iSide), middleBarrelPowerPE, medPE); + middleBarrelPowerPEVolume->SetLineColor(kGray); + + motherVolume->AddNode(middleBarrelPowerCuVolume, 1, combiTrans); + motherVolume->AddNode(middleBarrelPowerPEVolume, 1, combiTrans); + + // TODO: add cooling ducts/pipes + } + } + } + + // Barrel to forward connection disks + // A side: barrel + disk services + // C side: only disk services + float rMaxMiddleServicesBarFwd = 74.5f; // TODO: add thickness of service barrels + diskCircumference = rMaxMiddleServicesBarFwd * 3.14; // Only half of the area is used + for (auto& orientation : {Orientation::kASide, Orientation::kCSide}) { + float refAngle = 0; + string orLabel("A"); + if (orientation == Orientation::kCSide) { + refAngle = 90; + orLabel = "C"; + } + double totalThickness = 0; + for (int iSide = 0; iSide < 2; iSide++) { + // Create fibers + double zCur = zLengthMiddleServices; // Change to f + double dZ = siO2FiberAreaD / diskCircumference / 2.; + totalThickness += 2 * dZ; + if (orientation == Orientation::kASide) { + dZ += siO2FiberAreaB / diskCircumference / 2.; + } + TGeoTubeSeg* middleBarFwdFiberSIO2 = new TGeoTubeSeg(Form("TRK_MIDBARFWD_FIBER_SIO2sh_%s%d", orLabel.c_str(), iSide), rMinMiddleBarrel, rMaxMiddleServicesBarFwd, dZ, -45, 45); + TGeoVolume* middleBarFwdFiberSIO2Volume = new TGeoVolume(Form("TRK_MIDBARFWD_FIBER_SIO2_%s%d", orLabel.c_str(), iSide), middleBarFwdFiberSIO2, medSiO2); + auto* rot = new TGeoRotation("", refAngle + iSide * 180., 0, 180.); + auto* combiTransSIO2 = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), rot); + + zCur += 2 * dZ; + dZ = peFiberAreaD / diskCircumference / 2.; + totalThickness += 2 * dZ; + if (orientation == Orientation::kASide) { + dZ += peFiberAreaB / diskCircumference / 2.; + } + TGeoTubeSeg* middleBarFwdFiberPE = new TGeoTubeSeg(Form("TRK_MIDBARFWD_FIBER_PEsh_%s%d", orLabel.c_str(), iSide), rMinMiddleBarrel, rMaxMiddleServicesBarFwd, dZ, -45, 45); + TGeoVolume* middleBarFwdFiberPEVolume = new TGeoVolume(Form("TRK_MIDBARFWD_FIBER_PE_%s%d", orLabel.c_str(), iSide), middleBarFwdFiberPE, medPE); + middleBarFwdFiberSIO2Volume->SetLineColor(kGray); + middleBarFwdFiberPEVolume->SetLineColor(kGray); + auto* combiTransPE = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), rot); + motherVolume->AddNode(middleBarFwdFiberSIO2Volume, 1, combiTransSIO2); + motherVolume->AddNode(middleBarFwdFiberPEVolume, 1, combiTransPE); + + // Create powerlines + zCur += 2 * dZ; + dZ = cuPowerAreaD / diskCircumference / 2.; + totalThickness += 2 * dZ; + if (orientation == Orientation::kASide) { + dZ += cuPowerAreaB / diskCircumference / 2.; + } + TGeoTubeSeg* middleBarFwdPowerCu = new TGeoTubeSeg(Form("TRK_MIDBARFWD_POWER_CUsh_%s%d", orLabel.c_str(), iSide), rMinMiddleBarrel, rMaxMiddleServicesBarFwd, dZ, -45, 45); + TGeoVolume* middleBarFwdPowerCuVolume = new TGeoVolume(Form("TRK_MIDBARFWD_POWER_CU_%s%d", orLabel.c_str(), iSide), middleBarFwdPowerCu, medCu); + auto* combiTransCu = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), rot); + + zCur += 2 * dZ; + dZ = pePowerAreaD / diskCircumference / 2.; + totalThickness += 2 * dZ; + if (orientation == Orientation::kASide) { + dZ += pePowerAreaB / diskCircumference / 2.; + } + TGeoTubeSeg* middleBarFwdPowerPE = new TGeoTubeSeg(Form("TRK_MIDBARFWD_POWER_PEsh_%s%d", orLabel.c_str(), iSide), rMinMiddleBarrel, rMaxMiddleServicesBarFwd, dZ, -45, 45); + TGeoVolume* middleBarFwdPowerPEVolume = new TGeoVolume(Form("TRK_MIDBARFWD_POWER_PE_%s%d", orLabel.c_str(), iSide), middleBarFwdPowerPE, medPE); + middleBarFwdPowerCuVolume->SetLineColor(kGray); + middleBarFwdPowerPEVolume->SetLineColor(kGray); + auto* combiTransPEPower = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), rot); + motherVolume->AddNode(middleBarFwdPowerCuVolume, 1, combiTransCu); + motherVolume->AddNode(middleBarFwdPowerPEVolume, 1, combiTransPEPower); + + // TODO: add cooling ducts/pipes + } + + // Forward part + float zLengthMiddleServicesFwd = 350.f - (143.f + totalThickness); + + for (int iSide = 0; iSide < 2; iSide++) { + // Create fibers + float rMinMiddleServicesFwd = 74.5f; // 74.5cm + + float translation = (int)orientation * (143.f + totalThickness + zLengthMiddleServicesFwd / 2); + + double rCur = rMinMiddleServicesFwd; + double dR = siO2FiberAreaD / (3.14 * rCur); + if (orientation == Orientation::kASide) { + dR += siO2FiberAreaB / (3.14 * rCur); + } + TGeoTubeSeg* middleFwdFiberSIO2 = new TGeoTubeSeg(Form("TRK_MIDFWD_FIBER_SIO2sh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthMiddleServicesFwd / 2, -45, 45); + TGeoVolume* middleFwdFiberSIO2Volume = new TGeoVolume(Form("TRK_MIDFWD_FIBER_SIO2_%s%d", orLabel.c_str(), iSide), middleFwdFiberSIO2, medSiO2); + middleFwdFiberSIO2Volume->SetLineColor(kGray); + + rCur += dR; + dR = peFiberAreaD / (3.14 * rCur); + if (orientation == Orientation::kASide) { + dR += peFiberAreaB / (3.14 * rCur); + } + TGeoTubeSeg* middleFwdFiberPE = new TGeoTubeSeg(Form("TRK_MIDFWD_FIBER_PEsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthMiddleServicesFwd / 2, -45, 45); + TGeoVolume* middleFwdFiberPEVolume = new TGeoVolume(Form("TRK_MIDFWD_FIBER_PE_%s%d", orLabel.c_str(), iSide), middleFwdFiberPE, medPE); + middleFwdFiberPEVolume->SetLineColor(kGray); + + auto* rot = new TGeoRotation("", refAngle + iSide * 180., 0, 0.); + auto* combiTrans = new TGeoCombiTrans(0, 0, translation, rot); + motherVolume->AddNode(middleFwdFiberSIO2Volume, 1, combiTrans); + motherVolume->AddNode(middleFwdFiberPEVolume, 1, combiTrans); + + // Create powerlines + rCur += dR; + dR = cuPowerAreaD / (3.14 * rCur); + if (orientation == Orientation::kASide) { + dR += cuPowerAreaB / (3.14 * rCur); + } + TGeoTubeSeg* middleFwdPowerCu = new TGeoTubeSeg(Form("TRK_MIDFWD_POWER_CUsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthMiddleServicesFwd / 2, -45, 45); + TGeoVolume* middleFwdPowerCuVolume = new TGeoVolume(Form("TRK_MIDFWD_POWER_CU_%s%d", orLabel.c_str(), iSide), middleFwdPowerCu, medCu); + middleFwdPowerCuVolume->SetLineColor(kGray); + + rCur += dR; + dR = pePowerAreaD / (3.14 * rCur); + if (orientation == Orientation::kASide) { + dR += pePowerAreaB / (3.14 * rCur); + } + TGeoTubeSeg* middleFwdPowerPE = new TGeoTubeSeg(Form("TRK_MIDFWD_POWER_PEsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthMiddleServicesFwd / 2, -45, 45); + TGeoVolume* middleFwdPowerPEVolume = new TGeoVolume(Form("TRK_MIDFWD_POWER_PE_%s%d", orLabel.c_str(), iSide), middleFwdPowerPE, medPE); + middleFwdPowerPEVolume->SetLineColor(kGray); + motherVolume->AddNode(middleFwdPowerCuVolume, 1, combiTrans); + motherVolume->AddNode(middleFwdPowerPEVolume, 1, combiTrans); + + // TODO: add cooling ducts/pipes + } + } +} + +void TRKServices::createOTServicesPeacock(TGeoVolume* motherVolume) +{ + // This implments the service barrels for power + data for the OT barrels and disks + // TODO: add cooling + + auto& matmgr = o2::base::MaterialManager::Instance(); + + TGeoMedium* medSiO2 = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_SILICONDIOXIDE"); + TGeoMedium* medPE = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_POLYETHYLENE"); + TGeoMedium* medCu = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_COPPER"); + TGeoMedium* medPU = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_POLYURETHANE"); + TGeoMedium* medH2O = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_WATER"); + TGeoMedium* medCFiber = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_CARBONFIBERM55J6K"); + + // OT Disk service constants + const int OTDisknFiber = 3 * 51; + const int OTDisknPower = 3 * 34; + float siO2FiberAreaD = OTDisknFiber * mFiberArea * mFiberComposition[0]; + float peFiberAreaD = OTDisknFiber * mFiberArea * mFiberComposition[1]; + + float puCoolingAreaD = 0; + float h2oCoolingAreaD = 0; + float cuPowerAreaD = OTDisknPower * mPowerBundleArea * mPowerBundleComposition[0]; + float pePowerAreaD = OTDisknPower * mPowerBundleArea * mPowerBundleComposition[1]; + + // OT Barrel service constants + const int OTBarrelnFiber = 460; + const int OTBarrelnPower = 306; + float siO2FiberAreaB = OTBarrelnFiber * mFiberArea * mFiberComposition[0]; + float peFiberAreaB = OTBarrelnFiber * mFiberArea * mFiberComposition[1]; + + float puCoolingAreaB = 0; + float h2oCoolingAreaB = 0; + float cuPowerAreaB = OTBarrelnPower * mPowerBundleArea * mPowerBundleComposition[0]; + float pePowerAreaB = OTBarrelnPower * mPowerBundleArea * mPowerBundleComposition[1]; + + float rMinOuterServices = 68.5f; // 68.5cm + float zLengthOuterServices = 201.f; // 201cm + + // Carbon Fiber Cylinder support for the middle tracker + float rMinOuterCarbonSupport = 82.0f; // TODO: get more precise location + float rMaxOuterCarbonSupport = 82.4f; // 4 mm of carbon fiber + const float zLengthOuterCarbon = 280.0f; // Rough guess for now + TGeoTube* outerBarrelCarbonSupport = new TGeoTube("TRK_OT_CARBONSUPPORTsh", rMinOuterCarbonSupport, rMaxOuterCarbonSupport, zLengthOuterCarbon / 2.); + TGeoVolume* outerBarrelCarbonSupportVolume = new TGeoVolume("TRK_OT_CARBONSUPPORT", outerBarrelCarbonSupport, medCFiber); + outerBarrelCarbonSupportVolume->SetLineColor(kGray); + LOGP(info, "Creating carbon fiber support for Outer Tracker"); + motherVolume->AddNode(outerBarrelCarbonSupportVolume, 1, nullptr); + + for (auto& orientation : {Orientation::kASide, Orientation::kCSide}) { + string orLabel = "A"; + float refAngle = 0; + if (orientation == Orientation::kCSide) { + orLabel = "C"; + refAngle = 90; + } + // TODO: add cables/connections at ends of OT barrels + // Set rMin, rMax and dZ + + double rMin = 45.0; + double rMax = rMinOuterServices; + double zCur = 145.0; + double dZ = siO2FiberAreaB / (4 * 3.14 * rMax); + TGeoTube* outerBarrelFiberSIO2 = new TGeoTube(Form("TRK_OUTERBARREL_FIBER_SIO2sh_%s", orLabel.c_str()), rMin, rMax, dZ); + TGeoVolume* outerBarrelFiberSIO2Volume = new TGeoVolume(Form("TRK_OUTERBARREL_FIBER_SIO2_%s", orLabel.c_str()), outerBarrelFiberSIO2, medSiO2); + outerBarrelFiberSIO2Volume->SetLineColor(kGray); + auto* combiTrans = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), nullptr); + motherVolume->AddNode(outerBarrelFiberSIO2Volume, 1, combiTrans); + + zCur += 2 * dZ; + dZ = peFiberAreaB / (4 * 3.14 * rMax); + TGeoTube* outerBarrelFiberPE = new TGeoTube(Form("TRK_OUTERBARREL_FIBER_PEsh_%s", orLabel.c_str()), rMin, rMax, dZ); + TGeoVolume* outerBarrelFiberPEVolume = new TGeoVolume(Form("TRK_OUTERBARREL_FIBER_PE_%s", orLabel.c_str()), outerBarrelFiberPE, medPE); + outerBarrelFiberPEVolume->SetLineColor(kGray); + combiTrans = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), nullptr); + motherVolume->AddNode(outerBarrelFiberPEVolume, 1, combiTrans); + + zCur += 2 * dZ; + dZ = cuPowerAreaB / (4 * 3.14 * rMax); + TGeoTube* outerBarrelPowerCu = new TGeoTube(Form("TRK_OUTERBARREL_POWER_CUsh_%s", orLabel.c_str()), rMin, rMax, dZ); + TGeoVolume* outerBarrelPowerCuVolume = new TGeoVolume(Form("TRK_OUTERBARREL_POWER_CU_%s", orLabel.c_str()), outerBarrelPowerCu, medCu); + outerBarrelFiberSIO2Volume->SetLineColor(kGray); + combiTrans = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), nullptr); + motherVolume->AddNode(outerBarrelPowerCuVolume, 1, combiTrans); + + zCur += 2 * dZ; + dZ = pePowerAreaB / (4 * 3.14 * rMax); + TGeoTube* outerBarrelPowerPE = new TGeoTube(Form("TRK_OUTERBARREL_POWER_PEsh_%s", orLabel.c_str()), rMin, rMax, dZ); + TGeoVolume* outerBarrelPowerPEVolume = new TGeoVolume(Form("TRK_OUTERBARREL_POWER_PE_%s", orLabel.c_str()), outerBarrelPowerPE, medPE); + outerBarrelPowerPEVolume->SetLineColor(kGray); + combiTrans = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), nullptr); + motherVolume->AddNode(outerBarrelPowerPEVolume, 1, combiTrans); + + for (int iSide = 0; iSide < 2; iSide++) { + // Create fibers + double rCur = rMinOuterServices; + double dR = (siO2FiberAreaD + siO2FiberAreaB) / (3.14 * rCur); + TGeoTubeSeg* outerDisksFiberSIO2 = new TGeoTubeSeg(Form("TRK_OUTERDISKS_FIBER_SIO2sh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthOuterServices / 2, -45, 45); + TGeoVolume* outerDisksFiberSIO2Volume = new TGeoVolume(Form("TRK_OUTERDISKS_FIBER_SIO2_%s%d", orLabel.c_str(), iSide), outerDisksFiberSIO2, medSiO2); + outerDisksFiberSIO2Volume->SetLineColor(kGray); + + rCur += dR; + dR = (peFiberAreaD + peFiberAreaB) / (3.14 * rCur); + TGeoTubeSeg* outerDisksFiberPE = new TGeoTubeSeg(Form("TRK_OUTERDISKS_FIBER_PEsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthOuterServices / 2, -45, 45); + TGeoVolume* outerDisksFiberPEVolume = new TGeoVolume(Form("TRK_OUTERDISKS_FIBER_PE_%s%d", orLabel.c_str(), iSide), outerDisksFiberPE, medPE); + outerDisksFiberPEVolume->SetLineColor(kGray); + + float translation = (int)orientation * (149.f + zLengthOuterServices / 2); // ±149cm + auto* combiTrans = new TGeoCombiTrans(0, 0, translation, new TGeoRotation("", refAngle + iSide * 180., 0, 0)); + motherVolume->AddNode(outerDisksFiberSIO2Volume, 1, combiTrans); + motherVolume->AddNode(outerDisksFiberPEVolume, 1, combiTrans); + + // Create power lines + rCur += dR; + dR = (cuPowerAreaD + cuPowerAreaB) / (3.14 * rCur); + TGeoTubeSeg* outerDisksPowerCu = new TGeoTubeSeg(Form("TRK_OUTERDISKS_POWER_CUsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthOuterServices / 2, -45, 45); + TGeoVolume* outerDisksPowerCuVolume = new TGeoVolume(Form("TRK_OUTERDISKS_POWER_CU_%s%d", orLabel.c_str(), iSide), outerDisksPowerCu, medCu); + outerDisksPowerCuVolume->SetLineColor(kGray); + + rCur += dR; + dR = (pePowerAreaD + pePowerAreaB) / (3.14 * rCur); + TGeoTubeSeg* outerDisksPowerPE = new TGeoTubeSeg(Form("TRK_OUTERDISKS_POWER_PEsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthOuterServices / 2, -45, 45); + TGeoVolume* outerDisksPowerPEVolume = new TGeoVolume(Form("TRK_OUTERDISKS_POWER_PE_%s%d", orLabel.c_str(), iSide), outerDisksPowerPE, medPE); + outerDisksPowerPEVolume->SetLineColor(kGray); + motherVolume->AddNode(outerDisksPowerCuVolume, 1, combiTrans); + motherVolume->AddNode(outerDisksPowerPEVolume, 1, combiTrans); + + // TODO: add cooling ducts/pipes + } + } +} + } // namespace trk } // namespace o2 diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h index fec9cb6631a6f..282fc72becc52 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h @@ -18,7 +18,10 @@ #pragma link C++ class o2::trk::Hit + ; #pragma link C++ class std::vector < o2::trk::Hit> + ; -#pragma link C++ class o2::trk::TRKLayer + ; +#pragma link C++ class o2::trk::TRKCylindricalLayer + ; +#pragma link C++ class o2::trk::TRKSegmentedLayer + ; +#pragma link C++ class o2::trk::TRKMLLayer + ; +#pragma link C++ class o2::trk::TRKOTLayer + ; #pragma link C++ class o2::trk::VDLayer + ; #pragma link C++ class o2::trk::TRKServices + ; #pragma link C++ class o2::trk::Detector + ; diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx index f487d7602619f..48cd0f37d2eb5 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx @@ -84,6 +84,9 @@ inline bool isSolidToCut(const TGeoVolume* v) if (TString(nm).BeginsWith("VD_InclinedWall")) { return true; } + if (TString(nm).Contains("_ZCap")) { + return true; + } return false; } @@ -252,10 +255,11 @@ static const double diskZ_cm[6] = {-34.0f, -30.0f, -26.0f, 26.0f, 30.0f, 34.0f}; // Petal walls specifications (cm) static constexpr double kPetalZ_cm = 70.0f; // full wall height -static constexpr double kWallThick_cm = 0.015f; // 0.15 mm +static constexpr double kWallThick_cm = 0.02f; // 0.2 mm static constexpr double kInnerWallRadius_cm = 0.48f; // 4.8 mm (ALWAYS cylindrical) static constexpr double kOuterWallRadius_cm = 4.8f; // 48 mm (can be changed) static constexpr double kEps_cm = 2.5e-4f; +static constexpr double kEps_100um = 0.01f; // 100 microns in cm // 3 inclined walls ("walls") specs for the full-cylinder option // Thickness in-plane (cm). This is the short half-dimension of the TGeoBBox in XY. @@ -604,8 +608,8 @@ static void addIRISServiceModulesSegmented(TGeoVolume* petalAsm, int nPetals) // --- Vacuum vessel window around z∈[-L/2, +L/2] with wall thickness on +Z side // Keep these in sync with TRKServices::createVacuumCompositeShape() - constexpr double vacuumVesselLength = 76.0; // cm - constexpr double vacuumVesselThickness = 0.08; // cm (0.8 mm) + constexpr double vacuumVesselLength = kPetalZ_cm; // cm + constexpr double vacuumVesselThickness = kWallThick_cm; // cm (0.2 mm) const double halfVess = 0.5 * vacuumVesselLength; // 38.0 cm const double gapStart = halfVess; // 38.00 const double gapEnd = halfVess + vacuumVesselThickness; // 38.08 @@ -783,8 +787,8 @@ static TGeoVolume* buildFullCylAssembly(int petalID, bool withDisks) // --- Z end-cap walls to close the petal in Z --- { - const double zMin = -0.5 * kLenZ_cm; - const double zMax = +0.5 * kLenZ_cm; + const double zMin = -0.5 * kPetalZ_cm - 2 * kWallThick_cm; + const double zMax = +0.5 * kPetalZ_cm + 2 * kWallThick_cm; const double rIn = kInnerWallRadius_cm; const double rOut = kOuterWallRadius_cm + kWallThick_cm; diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx index 411dd485684b9..a92dcd24d6038 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx @@ -27,6 +27,13 @@ namespace o2 { namespace trk { + +// Helper function for floating point comparison +inline bool isFullCircle(double phiSpanDeg, double epsilon = 0.005) +{ + return (std::fabs(phiSpanDeg - 360.0) < epsilon); +} + // Base layer constructor VDLayer::VDLayer(int layerNumber, const std::string& layerName, double layerX2X0) : mLayerNumber(layerNumber), mLayerName(layerName), mX2X0(layerX2X0), mModuleWidth(4.54) @@ -88,8 +95,13 @@ TGeoVolume* VDCylindricalLayer::createSensor() const const double rIn = mRadius; const double rOut = mRadius + mSensorThickness; const double halfZ = 0.5 * mLengthSensZ; - const double halfPhi = 0.5 * mPhiSpanDeg; // degrees - auto* shape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); + TGeoShape* shape; + if (isFullCircle(mPhiSpanDeg)) { + shape = new TGeoTube(rIn, rOut, halfZ); + } else { + const double halfPhi = 0.5 * mPhiSpanDeg; // degrees + shape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); + } auto* vol = new TGeoVolume(sensName.c_str(), shape, medSi); vol->SetLineColor(kYellow); vol->SetTransparency(30); @@ -138,10 +150,15 @@ TGeoVolume* VDDiskLayer::createSensor() const } std::string sensName = Form("%s_%s%d", this->mLayerName.c_str(), GeometryTGeo::getTRKSensorPattern(), this->mLayerNumber); const double halfThickness = 0.5 * mSensorThickness; // active sensor thickness along Z - const double halfPhi = 0.5 * mPhiSpanDeg; // degrees // Same geometry as the layer (identical radii + phi span + thickness) - auto* shape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + TGeoShape* shape; + if (isFullCircle(mPhiSpanDeg)) { + shape = new TGeoTube(mRMin, mRMax, halfThickness); + } else { + const double halfPhi = 0.5 * mPhiSpanDeg; // degrees + shape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + } auto* sensVol = new TGeoVolume(sensName.c_str(), shape, medSi); sensVol->SetLineColor(kYellow); @@ -177,9 +194,14 @@ TGeoVolume* VDCylindricalLayer::createMetalStack() const const double rIn = mRadius + mSensorThickness; const double rOut = mRadius + mChipThickness; const double halfZ = 0.5 * mLengthSensZ; - const double halfPhi = 0.5 * mPhiSpanDeg; - auto* shape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); + TGeoShape* shape; + if (isFullCircle(mPhiSpanDeg)) { + shape = new TGeoTube(rIn, rOut, halfZ); + } else { + const double halfPhi = 0.5 * mPhiSpanDeg; + shape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); + } auto* vol = new TGeoVolume(name.c_str(), shape, medSi); vol->SetLineColor(kGray); vol->SetTransparency(30); @@ -244,9 +266,14 @@ TGeoVolume* VDDiskLayer::createMetalStack() const GeometryTGeo::getTRKMetalStackPattern(), mLayerNumber); const double halfThickness = 0.5 * metalT; - const double halfPhi = 0.5 * mPhiSpanDeg; - auto* shape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + TGeoShape* shape; + if (isFullCircle(mPhiSpanDeg)) { + shape = new TGeoTube(mRMin, mRMax, halfThickness); + } else { + const double halfPhi = 0.5 * mPhiSpanDeg; + shape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + } auto* vol = new TGeoVolume(name.c_str(), shape, medSi); vol->SetLineColor(kGray); vol->SetTransparency(30); @@ -275,9 +302,14 @@ TGeoVolume* VDCylindricalLayer::createChip() const const double rIn = mRadius; const double rOut = mRadius + mChipThickness; const double halfZ = 0.5 * mLengthSensZ; - const double halfPhi = 0.5 * mPhiSpanDeg; - auto* chipShape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); + TGeoShape* chipShape; + if (isFullCircle(mPhiSpanDeg)) { + chipShape = new TGeoTube(rIn, rOut, halfZ); + } else { + const double halfPhi = 0.5 * mPhiSpanDeg; + chipShape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); + } auto* chipVol = new TGeoVolume(chipName.c_str(), chipShape, medSi); // sensor @@ -361,9 +393,14 @@ TGeoVolume* VDDiskLayer::createChip() const GeometryTGeo::getTRKChipPattern(), mLayerNumber); const double halfThickness = 0.5 * mChipThickness; - const double halfPhi = 0.5 * mPhiSpanDeg; - auto* chipShape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + TGeoShape* chipShape; + if (isFullCircle(mPhiSpanDeg)) { + chipShape = new TGeoTube(mRMin, mRMax, halfThickness); + } else { + const double halfPhi = 0.5 * mPhiSpanDeg; + chipShape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + } auto* chipVol = new TGeoVolume(chipName.c_str(), chipShape, medSi); chipVol->SetLineColor(kYellow); chipVol->SetTransparency(30); @@ -417,9 +454,14 @@ void VDCylindricalLayer::createLayer(TGeoVolume* motherVolume, TGeoMatrix* combi const double rIn = mRadius; const double rOut = mRadius + mChipThickness; const double halfZ = 0.5 * mLengthZ; - const double halfPhi = 0.5 * mPhiSpanDeg; // degrees - auto* layerShape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); + TGeoShape* layerShape; + if (isFullCircle(mPhiSpanDeg)) { + layerShape = new TGeoTube(rIn, rOut, halfZ); + } else { + const double halfPhi = 0.5 * mPhiSpanDeg; // degrees + layerShape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); + } auto* layerVol = new TGeoVolume(mLayerName.c_str(), layerShape, medAir); layerVol->SetLineColor(kYellow); layerVol->SetTransparency(30); @@ -523,10 +565,15 @@ void VDDiskLayer::createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans) // For disks the thickness is along Z and equals mChipThickness const double halfThickness = 0.5 * mChipThickness; - const double halfPhi = 0.5 * mPhiSpanDeg; // AIR container (layer) - auto* layerShape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + TGeoShape* layerShape; + if (isFullCircle(mPhiSpanDeg)) { + layerShape = new TGeoTube(mRMin, mRMax, halfThickness); + } else { + const double halfPhi = 0.5 * mPhiSpanDeg; + layerShape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + } auto* layerVol = new TGeoVolume(mLayerName.c_str(), layerShape, medAir); layerVol->SetLineColor(kYellow); layerVol->SetTransparency(30); diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt index d6c8ea85c2bbd..42402fe6b62dc 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt @@ -13,6 +13,8 @@ o2_add_library(TRKWorkflow TARGETVARNAME targetName SOURCES src/DigitReaderSpec.cxx src/DigitWriterSpec.cxx + src/ClustererSpec.cxx + src/ClusterWriterSpec.cxx src/TrackerSpec.cxx src/TrackWriterSpec.cxx src/RecoWorkflow.cxx @@ -20,6 +22,7 @@ o2_add_library(TRKWorkflow O2::GPUWorkflow O2::SimConfig O2::DataFormatsITSMFT + O2::DataFormatsTRK O2::SimulationDataFormat O2::DPLUtils O2::TRKBase diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClusterWriterSpec.h b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClusterWriterSpec.h new file mode 100644 index 0000000000000..50d823b497bb9 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClusterWriterSpec.h @@ -0,0 +1,24 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRK_CLUSTERWRITER +#define O2_TRK_CLUSTERWRITER + +#include "Framework/DataProcessorSpec.h" + +namespace o2::trk +{ + +framework::DataProcessorSpec getClusterWriterSpec(bool useMC); + +} // namespace o2::trk + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h new file mode 100644 index 0000000000000..bacc1057c7b07 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h @@ -0,0 +1,39 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRK_CLUSTERERDPL +#define O2_TRK_CLUSTERERDPL + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "TRKReconstruction/Clusterer.h" + +namespace o2::trk +{ + +class ClustererDPL : public o2::framework::Task +{ + public: + ClustererDPL(bool useMC) : mUseMC(useMC) {} + void init(o2::framework::InitContext& ic) final; + void run(o2::framework::ProcessingContext& pc) final; + + private: + bool mUseMC = true; + int mNThreads = 1; + o2::trk::Clusterer mClusterer; +}; + +o2::framework::DataProcessorSpec getClustererSpec(bool useMC); + +} // namespace o2::trk + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClusterWriterSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClusterWriterSpec.cxx new file mode 100644 index 0000000000000..bc3a75c646198 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClusterWriterSpec.cxx @@ -0,0 +1,65 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file ClusterWriterSpec.cxx + +#include + +#include "TRKWorkflow/ClusterWriterSpec.h" +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "DataFormatsTRK/Cluster.h" +#include "DataFormatsTRK/ROFRecord.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" + +using namespace o2::framework; + +namespace o2::trk +{ + +template +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; +using ClustersType = std::vector; +using PatternsType = std::vector; +using ROFrameType = std::vector; +using LabelsType = o2::dataformats::MCTruthContainer; +using ROFRecLblType = std::vector; + +DataProcessorSpec getClusterWriterSpec(bool useMC) +{ + auto clustersSize = std::make_shared(0); + auto clustersSizeGetter = [clustersSize](ClustersType const& clusters) { + *clustersSize = clusters.size(); + }; + auto logger = [clustersSize](ROFrameType const& rofs) { + LOG(info) << "TRKClusterWriter pulled " << *clustersSize << " clusters, in " << rofs.size() << " RO frames"; + }; + + return MakeRootTreeWriterSpec("trk-cluster-writer", + "o2clus_trk.root", + MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Tree with TRK clusters"}, + BranchDefinition{InputSpec{"compclus", "TRK", "COMPCLUSTERS", 0}, + "TRKClusterComp", + clustersSizeGetter}, + BranchDefinition{InputSpec{"patterns", "TRK", "PATTERNS", 0}, + "TRKClusterPatt"}, + BranchDefinition{InputSpec{"ROframes", "TRK", "CLUSTERSROF", 0}, + "TRKClustersROF", + logger}, + BranchDefinition{InputSpec{"labels", "TRK", "CLUSTERSMCTR", 0}, + "TRKClusterMCTruth", + (useMC ? 1 : 0)}, + BranchDefinition{InputSpec{"MC2ROframes", "TRK", "CLUSTERSMC2ROF", 0}, + "TRKClustersMC2ROF", + (useMC ? 1 : 0)})(); +} + +} // namespace o2::trk diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClustererSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClustererSpec.cxx new file mode 100644 index 0000000000000..8aec63d69206b --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClustererSpec.cxx @@ -0,0 +1,99 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "TRKWorkflow/ClustererSpec.h" +#include "DetectorsBase/GeometryManager.h" +#include "DataFormatsTRK/Cluster.h" +#include "DataFormatsTRK/ROFRecord.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/Logger.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" + +namespace o2::trk +{ + +void ClustererDPL::init(o2::framework::InitContext& ic) +{ + mNThreads = std::max(1, ic.options().get("nthreads")); +} + +void ClustererDPL::run(o2::framework::ProcessingContext& pc) +{ + auto digits = pc.inputs().get>("digits"); + auto rofs = pc.inputs().get>("ROframes"); + + gsl::span mc2rofs; + gsl::span labelbuffer; + if (mUseMC) { + labelbuffer = pc.inputs().get>("labels"); + mc2rofs = pc.inputs().get>("MC2ROframes"); + } + o2::dataformats::ConstMCTruthContainerView labels(labelbuffer); + + std::vector clusters; + std::vector patterns; + std::vector clusterROFs; + std::unique_ptr> clusterLabels; + std::vector clusterMC2ROFs; + if (mUseMC) { + clusterLabels = std::make_unique>(); + } + o2::base::GeometryManager::loadGeometry("o2sim_geometry.root", false, true); + + mClusterer.process(digits, + rofs, + clusters, + patterns, + clusterROFs, + mUseMC ? &labels : nullptr, + clusterLabels.get(), + mc2rofs, + mUseMC ? &clusterMC2ROFs : nullptr); + + pc.outputs().snapshot(o2::framework::Output{"TRK", "COMPCLUSTERS", 0}, clusters); + pc.outputs().snapshot(o2::framework::Output{"TRK", "PATTERNS", 0}, patterns); + pc.outputs().snapshot(o2::framework::Output{"TRK", "CLUSTERSROF", 0}, clusterROFs); + + if (mUseMC) { + pc.outputs().snapshot(o2::framework::Output{"TRK", "CLUSTERSMCTR", 0}, *clusterLabels); + pc.outputs().snapshot(o2::framework::Output{"TRK", "CLUSTERSMC2ROF", 0}, clusterMC2ROFs); + } + + LOGP(info, "TRKClusterer pushed {} clusters in {} ROFs", clusters.size(), clusterROFs.size()); +} + +o2::framework::DataProcessorSpec getClustererSpec(bool useMC) +{ + std::vector inputs; + inputs.emplace_back("digits", "TRK", "DIGITS", 0, o2::framework::Lifetime::Timeframe); + inputs.emplace_back("ROframes", "TRK", "DIGITSROF", 0, o2::framework::Lifetime::Timeframe); + + std::vector outputs; + outputs.emplace_back("TRK", "COMPCLUSTERS", 0, o2::framework::Lifetime::Timeframe); + outputs.emplace_back("TRK", "PATTERNS", 0, o2::framework::Lifetime::Timeframe); + outputs.emplace_back("TRK", "CLUSTERSROF", 0, o2::framework::Lifetime::Timeframe); + + if (useMC) { + inputs.emplace_back("labels", "TRK", "DIGITSMCTR", 0, o2::framework::Lifetime::Timeframe); + inputs.emplace_back("MC2ROframes", "TRK", "DIGITSMC2ROF", 0, o2::framework::Lifetime::Timeframe); + outputs.emplace_back("TRK", "CLUSTERSMCTR", 0, o2::framework::Lifetime::Timeframe); + outputs.emplace_back("TRK", "CLUSTERSMC2ROF", 0, o2::framework::Lifetime::Timeframe); + } + + return o2::framework::DataProcessorSpec{ + "trk-clusterer", + inputs, + outputs, + o2::framework::AlgorithmSpec{o2::framework::adaptFromTask(useMC)}, + o2::framework::Options{{"nthreads", o2::framework::VariantType::Int, 1, {"Number of clustering threads"}}}}; +} + +} // namespace o2::trk diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx index 5f6cbe2f96b04..d10feb4214f38 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx @@ -10,6 +10,9 @@ // or submit itself to any jurisdiction. #include "TRKWorkflow/RecoWorkflow.h" +#include "TRKWorkflow/ClustererSpec.h" +#include "TRKWorkflow/ClusterWriterSpec.h" +#include "TRKWorkflow/DigitReaderSpec.h" #include "TRKWorkflow/TrackerSpec.h" #include "TRKWorkflow/TrackWriterSpec.h" #include "Framework/CCDBParamSpec.h" @@ -28,10 +31,24 @@ framework::WorkflowSpec getWorkflow(bool useMC, o2::gpu::gpudatatypes::DeviceType dtype) { framework::WorkflowSpec specs; - specs.emplace_back(o2::trk::getTrackerSpec(useMC, hitRecoConfig, dtype)); + + if (!(upstreamDigits || upstreamClusters)) { + specs.emplace_back(o2::trk::getTRKDigitReaderSpec(useMC, false, "trkdigits.root")); + } + if (!upstreamClusters) { + specs.emplace_back(o2::trk::getClustererSpec(useMC)); + } if (!disableRootOutput) { - specs.emplace_back(o2::trk::getTrackWriterSpec(useMC)); + specs.emplace_back(o2::trk::getClusterWriterSpec(useMC)); + } + + if (!hitRecoConfig.empty()) { + LOGP(info, "Using hit reco config from file {}", hitRecoConfig); + specs.emplace_back(o2::trk::getTrackerSpec(useMC, hitRecoConfig, dtype)); + if (!disableRootOutput) { + specs.emplace_back(o2::trk::getTrackWriterSpec(useMC)); + } } return specs; diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx index 8fc67f0fa5567..6b98fcf74b583 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx @@ -37,7 +37,6 @@ namespace o2 using namespace framework; namespace trk { -using Vertex = o2::dataformats::Vertex>; TrackerDPL::TrackerDPL(std::shared_ptr gr, bool isMC, @@ -84,18 +83,12 @@ std::vector TrackerDPL::createTrackingParamsFromCon if (paramConfig.contains("NLayers")) { params.NLayers = paramConfig["NLayers"].get(); } - if (paramConfig.contains("DeltaROF")) { - params.DeltaROF = paramConfig["DeltaROF"].get(); - } if (paramConfig.contains("ZBins")) { params.ZBins = paramConfig["ZBins"].get(); } if (paramConfig.contains("PhiBins")) { params.PhiBins = paramConfig["PhiBins"].get(); } - if (paramConfig.contains("nROFsPerIterations")) { - params.nROFsPerIterations = paramConfig["nROFsPerIterations"].get(); - } if (paramConfig.contains("ClusterSharing")) { params.ClusterSharing = paramConfig["ClusterSharing"].get(); } @@ -119,27 +112,21 @@ std::vector TrackerDPL::createTrackingParamsFromCon if (paramConfig.contains("TrackletMinPt")) { params.TrackletMinPt = paramConfig["TrackletMinPt"].get(); } - if (paramConfig.contains("TrackletsPerClusterLimit")) { - params.TrackletsPerClusterLimit = paramConfig["TrackletsPerClusterLimit"].get(); - } if (paramConfig.contains("CellDeltaTanLambdaSigma")) { params.CellDeltaTanLambdaSigma = paramConfig["CellDeltaTanLambdaSigma"].get(); } - if (paramConfig.contains("CellsPerClusterLimit")) { - params.CellsPerClusterLimit = paramConfig["CellsPerClusterLimit"].get(); - } if (paramConfig.contains("MaxChi2ClusterAttachment")) { params.MaxChi2ClusterAttachment = paramConfig["MaxChi2ClusterAttachment"].get(); } if (paramConfig.contains("MaxChi2NDF")) { params.MaxChi2NDF = paramConfig["MaxChi2NDF"].get(); } - if (paramConfig.contains("TrackFollowerNSigmaCutZ")) { - params.TrackFollowerNSigmaCutZ = paramConfig["TrackFollowerNSigmaCutZ"].get(); - } - if (paramConfig.contains("TrackFollowerNSigmaCutPhi")) { - params.TrackFollowerNSigmaCutPhi = paramConfig["TrackFollowerNSigmaCutPhi"].get(); - } + // if (paramConfig.contains("TrackFollowerNSigmaCutZ")) { + // params.TrackFollowerNSigmaCutZ = paramConfig["TrackFollowerNSigmaCutZ"].get(); + // } + // if (paramConfig.contains("TrackFollowerNSigmaCutPhi")) { + // params.TrackFollowerNSigmaCutPhi = paramConfig["TrackFollowerNSigmaCutPhi"].get(); + // } // Parse boolean parameters if (paramConfig.contains("UseDiamond")) { @@ -154,9 +141,9 @@ std::vector TrackerDPL::createTrackingParamsFromCon if (paramConfig.contains("ShiftRefToCluster")) { params.ShiftRefToCluster = paramConfig["ShiftRefToCluster"].get(); } - if (paramConfig.contains("FindShortTracks")) { - params.FindShortTracks = paramConfig["FindShortTracks"].get(); - } + // if (paramConfig.contains("FindShortTracks")) { + // params.FindShortTracks = paramConfig["FindShortTracks"].get(); + // } if (paramConfig.contains("PerPrimaryVertexProcessing")) { params.PerPrimaryVertexProcessing = paramConfig["PerPrimaryVertexProcessing"].get(); } @@ -169,18 +156,18 @@ std::vector TrackerDPL::createTrackingParamsFromCon if (paramConfig.contains("FataliseUponFailure")) { params.FataliseUponFailure = paramConfig["FataliseUponFailure"].get(); } - if (paramConfig.contains("UseTrackFollower")) { - params.UseTrackFollower = paramConfig["UseTrackFollower"].get(); - } - if (paramConfig.contains("UseTrackFollowerTop")) { - params.UseTrackFollowerTop = paramConfig["UseTrackFollowerTop"].get(); - } - if (paramConfig.contains("UseTrackFollowerBot")) { - params.UseTrackFollowerBot = paramConfig["UseTrackFollowerBot"].get(); - } - if (paramConfig.contains("UseTrackFollowerMix")) { - params.UseTrackFollowerMix = paramConfig["UseTrackFollowerMix"].get(); - } + // if (paramConfig.contains("UseTrackFollower")) { + // params.UseTrackFollower = paramConfig["UseTrackFollower"].get(); + // } + // if (paramConfig.contains("UseTrackFollowerTop")) { + // params.UseTrackFollowerTop = paramConfig["UseTrackFollowerTop"].get(); + // } + // if (paramConfig.contains("UseTrackFollowerBot")) { + // params.UseTrackFollowerBot = paramConfig["UseTrackFollowerBot"].get(); + // } + // if (paramConfig.contains("UseTrackFollowerMix")) { + // params.UseTrackFollowerMix = paramConfig["UseTrackFollowerMix"].get(); + // } if (paramConfig.contains("createArtefactLabels")) { params.createArtefactLabels = paramConfig["createArtefactLabels"].get(); } @@ -297,44 +284,37 @@ void TrackerDPL::run(ProcessingContext& pc) for (size_t iter{0}; iter < trackingParams.size(); ++iter) { LOGP(info, "{}", trackingParams[iter].asString()); timeFrame.initialise(iter, trackingParams[iter], 11, false); - itsTrackerTraits.computeLayerTracklets(iter, -1, -1); + itsTrackerTraits.computeLayerTracklets(iter, -1); LOGP(info, "Number of tracklets in iteration {}: {}", iter, timeFrame.getNumberOfTracklets()); itsTrackerTraits.computeLayerCells(iter); LOGP(info, "Number of cells in iteration {}: {}", iter, timeFrame.getNumberOfCells()); itsTrackerTraits.findCellsNeighbours(iter); LOGP(info, "Number of cell neighbours in iteration {}: {}", iter, timeFrame.getNumberOfNeighbours()); itsTrackerTraits.findRoads(iter); - LOGP(info, "Number of roads in iteration {}: {}", iter, timeFrame.getNumberOfTracks()); - itsTrackerTraits.extendTracks(iter); + LOGP(info, "Number of tracks in iteration {}: {}", iter, timeFrame.getNumberOfTracks()); } const auto trackingLoopElapsedMs = std::chrono::duration_cast(std::chrono::steady_clock::now() - trackingLoopStart).count(); LOGP(info, "Tracking iterations block took {} ms", trackingLoopElapsedMs); itsTracker.computeTracksMClabels(); - // Stream tracks and their MC labels to the output - // Collect all tracks and labels from all ROFs - std::vector allTracks; - std::vector allLabels; + // Collect tracks and labels (flat vectors in the new interface) + const auto& tracks = timeFrame.getTracks(); + const auto& labels = timeFrame.getTracksLabel(); + + // Copy to output vectors (TrackITSExt -> TrackITS slicing for output compatibility) + std::vector allTracks(tracks.begin(), tracks.end()); + std::vector allLabels(labels.begin(), labels.end()); - int totalTracks = 0; + int totalTracks = allTracks.size(); int goodTracks = 0; int fakeTracks = 0; - for (int iRof = 0; iRof < nRofs; ++iRof) { - const auto& rofTracks = timeFrame.getTracks(iRof); - const auto& rofLabels = timeFrame.getTracksLabel(iRof); - - allTracks.insert(allTracks.end(), rofTracks.begin(), rofTracks.end()); - allLabels.insert(allLabels.end(), rofLabels.begin(), rofLabels.end()); - - totalTracks += rofTracks.size(); - for (const auto& label : rofLabels) { - if (label.isFake()) { - fakeTracks++; - } else { - goodTracks++; - } + for (const auto& label : allLabels) { + if (label.isFake()) { + fakeTracks++; + } else { + goodTracks++; } } @@ -396,9 +376,13 @@ DataProcessorSpec getTrackerSpec(bool useMC, const std::string& hitRecoConfig, o inputs.emplace_back("dummy", "TRK", "DUMMY", 0, Lifetime::Timeframe); - // inputs.emplace_back("compClusters", "TRK", "COMPCLUSTERS", 0, Lifetime::Timeframe); - // inputs.emplace_back("patterns", "TRK", "PATTERNS", 0, Lifetime::Timeframe); - // inputs.emplace_back("ROframes", "TRK", "CLUSTERSROF", 0, Lifetime::Timeframe); + constexpr bool expectClusterInputs = false; + if (expectClusterInputs) { + inputs.pop_back(); + inputs.emplace_back("compClusters", "TRK", "COMPCLUSTERS", 0, Lifetime::Timeframe); + inputs.emplace_back("patterns", "TRK", "PATTERNS", 0, Lifetime::Timeframe); + inputs.emplace_back("ROframes", "TRK", "CLUSTERSROF", 0, Lifetime::Timeframe); + } // inputs.emplace_back("itscldict", "TRK", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/ClusterDictionary")); // inputs.emplace_back("itsalppar", "TRK", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam")); diff --git a/Detectors/Vertexing/src/SVertexer.cxx b/Detectors/Vertexing/src/SVertexer.cxx index d9206fe54e068..2c625c9cfaf0a 100644 --- a/Detectors/Vertexing/src/SVertexer.cxx +++ b/Detectors/Vertexing/src/SVertexer.cxx @@ -459,7 +459,6 @@ void SVertexer::buildT2V(const o2::globaltracking::RecoContainer& recoData) // a mTPCRefitterShMap = recoData.clusterShMapTPC; mTPCRefitterOccMap = mRecoCont->occupancyMapTPC; mTPCRefitter = std::make_unique(mTPCClusterIdxStruct, mTPCCorrMapsHelper, o2::base::Propagator::Instance()->getNominalBz(), mTPCTrackClusIdx.data(), 0, mTPCRefitterShMap.data(), mTPCRefitterOccMap.data(), mTPCRefitterOccMap.size(), nullptr, o2::base::Propagator::Instance()); - mTPCRefitter->setTrackReferenceX(900); // disable propagation after refit by setting reference to value > 500 } std::unordered_map> tmap; diff --git a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx index ea13d412cd0b8..413adfddecf04 100644 --- a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx +++ b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx @@ -84,6 +84,7 @@ AlgorithmSpec AnalysisCCDBHelpers::fetchFromCCDB(ConfigContext const& /*ctx*/) if (m.name.starts_with("input:")) { auto name = m.name.substr(6); schemaMetadata->Append("sourceTable", name); + schemaMetadata->Append("sourceMatcher", DataSpecUtils::describe(std::get(DataSpecUtils::fromMetadataString(m.defaultValue.get()).matcher))); continue; } // Ignore the non ccdb: entries diff --git a/Framework/Core/src/OptionsHelpers.h b/Framework/Core/src/OptionsHelpers.h index 578ccc4935e69..3ca2dc1df330e 100644 --- a/Framework/Core/src/OptionsHelpers.h +++ b/Framework/Core/src/OptionsHelpers.h @@ -13,7 +13,7 @@ #define BOOST_BIND_GLOBAL_PLACEHOLDERS #include -#include +#include namespace boost::program_options { diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index dfee81b398a79..c276bf59af40b 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -283,6 +283,7 @@ foreach(GPU_PARAM_JSON_FILE IN LISTS GPU_PARAM_JSON) execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/Definitions/Parameters/csv_to_json.sh "${GPU_PARAM_JSON_FILE}" OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/${CONVOUTFILE} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND_ERROR_IS_FATAL ANY ) message(STATUS "Converted ${GPU_PARAM_JSON_FILE} to ${CONVOUTFILE}") list(APPEND GPU_PARAM_JSON_FILES ${CMAKE_CURRENT_BINARY_DIR}/${CONVOUTFILE}) diff --git a/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.cxx b/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.cxx index 60fdbe8042c2d..f3b7e07c4c43b 100644 --- a/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.cxx +++ b/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.cxx @@ -29,7 +29,7 @@ using namespace o2::gpu; #define AddOptionArray(name, type, count, default, optname, optnameshort, help, ...) #define AddOptionArrayRTC(...) AddOptionArray(__VA_ARGS__) #define AddSubConfig(name, instance) -#define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr, o2prefix) O2ParamImpl(GPUCA_M_CAT(GPUConfigurableParam, name)) +#define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr, o2prefix) O2ParamImpl(internal::GPUCA_M_CAT(GPUConfigurableParam, name)) #define BeginHiddenConfig(...) #define EndConfig() #define AddCustomCPP(...) @@ -73,7 +73,7 @@ GPUSettingsO2 GPUO2InterfaceConfiguration::ReadConfigurableParam(GPUO2InterfaceC #define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr, o2prefix) \ name instance; \ { \ - const auto& src = GPUCA_M_CAT(GPUConfigurableParam, name)::Instance(); \ + const auto& src = internal::GPUCA_M_CAT(GPUConfigurableParam, name)::Instance(); \ name& dst = instance; #define BeginHiddenConfig(name, instance) { #define EndConfig() } @@ -119,9 +119,6 @@ GPUSettingsO2 GPUO2InterfaceConfiguration::ReadConfigurableParam(GPUO2InterfaceC if (global.gpuDisplayfilterMacro != "") { obj.configDisplay.filterMacros.emplace_back(global.gpuDisplayfilterMacro); } - if (obj.configReconstruction.tpc.trackReferenceX == 1000.f) { - obj.configReconstruction.tpc.trackReferenceX = 83.f; - } obj.configDeviceBackend.deviceType = gpudatatypes::GetDeviceType(global.deviceType.c_str()); obj.configDeviceBackend.forceDeviceType = global.forceDeviceType; return global; diff --git a/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.h b/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.h index 8bc0d98910f54..503ba3e0d51ae 100644 --- a/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.h +++ b/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.h @@ -29,6 +29,7 @@ #include "GPUDefMacros.h" #include +// clang-format off #define BeginNamespace(name) \ namespace name \ { @@ -42,12 +43,17 @@ #define AddOptionArray(name, type, count, default, optname, optnameshort, help, ...) type name[count] = {GPUCA_M_STRIP(default)}; #define AddSubConfig(name, instance) #define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr, o2prefix) \ + namespace internal \ + { \ struct GPUCA_M_CAT(GPUConfigurableParam, name) : public o2::conf::ConfigurableParamHelper { \ O2ParamDef(GPUCA_M_CAT(GPUConfigurableParam, name), GPUCA_M_STR(GPUCA_M_CAT(GPU_, o2prefix))) public: -#define BeginHiddenConfig(name, instance) struct GPUCA_M_CAT(GPUConfigurableParam, name) { +#define BeginHiddenConfig(name, instance) \ + namespace internal \ + { \ + struct GPUCA_M_CAT(GPUConfigurableParam, name) { #define EndConfig() \ - } \ - ; + }; \ + } // namespace internal #define AddCustomCPP(...) __VA_ARGS__ #define AddHelp(...) #define AddShortcut(...) @@ -71,5 +77,6 @@ #undef AddCustomCPP #undef AddHelp #undef AddShortcut +// clang-format on #endif diff --git a/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx b/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx index 674b7a317b477..f7adc2401df79 100644 --- a/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx +++ b/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx @@ -13,6 +13,7 @@ /// \brief Error parameterizations and helper functions for TRD reconstruction /// \author Ole Schmidt +#include "GPUO2InterfaceConfiguration.inc" #include "GPUSettings.h" #include "GPUTRDRecoParam.h" #include "GPUCommonLogger.h" @@ -23,7 +24,16 @@ using namespace o2::gpu; // error parameterizations taken from http://cds.cern.ch/record/2724259 Appendix A void GPUTRDRecoParam::init(float bz, const GPUSettingsRec* rec) { - float resRPhiIdeal2 = rec ? rec->trd.trkltResRPhiIdeal * rec->trd.trkltResRPhiIdeal : 1.6e-3f; + float resRPhiIdeal2 = 1.6e-3f; + if (rec) { + resRPhiIdeal2 = rec->trd.trkltResRPhiIdeal * rec->trd.trkltResRPhiIdeal; + } +#ifndef GPUCA_STANDALONE + else { + const auto& rtrd = GPU_GET_CONFIG(GPUSettingsRecTRD); + resRPhiIdeal2 = rtrd.trkltResRPhiIdeal * rtrd.trkltResRPhiIdeal; + } +#endif if (CAMath::Abs(CAMath::Abs(bz) - 2) < 0.1) { if (bz > 0) { diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index cf6b913551ab5..e34af48d7a85e 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -66,7 +66,7 @@ AddOptionRTC(sysClusErrorC12Box, float, 1.1e-05f, "", 0, "Systematic cluster for AddOptionRTC(minNClustersTrackSeed, int32_t, -1, "", 0, "required min number of clusters on the track after track following (before merging)") AddOptionRTC(minNClustersFinalTrack, int32_t, -1, "", 0, "required min number of clusters on the final track") AddOptionRTC(searchWindowDZDR, float, 2.5f, "", 0, "Use DZDR window for seeding instead of neighboursSearchArea") -AddOptionRTC(trackReferenceX, float, 1000.f, "", 0, "Transport all tracks to this X after tracking (disabled if > 500, auto = 1000)") +AddOptionRTC(trackReferenceX, float, 1000.f, "", 0, "Transport all tracks to this X after tracking (disabled if > 500)") AddOptionRTC(zsThreshold, float, 2.0f, "", 0, "Zero-Suppression threshold") AddOptionRTC(tubeProtectSigma2, float, 4.f * 4.f, "", 0, "Max sigma2 to mark adjacent cluster for protection") AddOptionRTC(tubeProtectMaxSize2, float, 2.f * 2.f, "", 0, "Square of max tube size (if smaller than tubeProtectChi2)") diff --git a/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh b/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh index 373bd18ba7cd4..d064c4f6b58d9 100755 --- a/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh +++ b/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh @@ -2,7 +2,10 @@ [[ -z $1 ]] && { echo "Usage: csv_to_json.sh CSV_FILE"; exit 1; } +LANG=C +LC_ALL=C DELIM=$'\xFF' +set -o pipefail sed -E \ ':loop s/^(([^"]*"[^"]*")*[^"]*),/\1'$DELIM'/; diff --git a/GPU/GPUTracking/GPUTrackingLinkDef_O2_DataTypes.h b/GPU/GPUTracking/GPUTrackingLinkDef_O2_DataTypes.h index 7bd2c689c5354..5318e23e7d10f 100644 --- a/GPU/GPUTracking/GPUTrackingLinkDef_O2_DataTypes.h +++ b/GPU/GPUTracking/GPUTrackingLinkDef_O2_DataTypes.h @@ -22,22 +22,22 @@ #pragma link C++ class o2::gpu::GPUTRDTrack_t < o2::gpu::trackInterface < o2::track::TrackParCov>> + ; #pragma link C++ class std::vector < o2::gpu::GPUTRDTrack_t < o2::gpu::trackInterface < o2::track::TrackParCov>>> + ; #ifdef GPUCA_O2_LIB -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsO2 + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsRec + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsRecTPC + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsRecTRD + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsRecDynamic + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsProcessing + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsProcessingParam + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsProcessingRTC + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsProcessingRTCtechnical + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsProcessingNNclusterizer + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsDisplay + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsDisplayLight + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsDisplayHeavy + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsDisplayRenderer + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsDisplayVulkan + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsQA + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsO2 + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsRec + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsRecTPC + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsRecTRD + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsRecDynamic + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsProcessing + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsProcessingParam + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsProcessingRTC + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsProcessingRTCtechnical + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsProcessingNNclusterizer + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsDisplay + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsDisplayLight + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsDisplayHeavy + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsDisplayRenderer + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsDisplayVulkan + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsQA + ; #endif #pragma link C++ class o2::gpu::GPUTPCGMMergedTrackHit + ; #pragma link C++ class o2::tpc::CalibdEdxTrackTopologyPol + ; diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.inc b/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.inc new file mode 100644 index 0000000000000..30f6d56a17f1a --- /dev/null +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.inc @@ -0,0 +1,37 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file GPUO2InterfaceConfiguration.inc +/// \author David Rohr + +// Note, this must potentionally be included first, before other GPU headers! +// Do not include in .h files! + +#ifndef GPUO2INTERFACECONFIGURATIONINC_H +#define GPUO2INTERFACECONFIGURATIONINC_H + +#ifdef GPUCA_STANDALONE +#define GPU_GET_CONFIG(configName) static_assert(false, "GPU_GET_CONFIG not available in standalone benchmark") +#else +#include "GPUO2ExternalUser.h" +#include "GPUCommonRtypes.h" +#include "GPUDefMacros.h" +#include "GPUO2InterfaceConfiguration.h" +#include "GPUO2ConfigurableParam.h" +#include + +#define GPU_GET_CONFIG(configName) []() -> decltype(auto) { \ + static_assert(!std::is_same_v); \ + return o2::gpu::internal::GPUCA_M_CAT(GPUConfigurableParam, configName)::Instance(); \ +}() +#endif + +#endif diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index 48210c440d01e..f7fc760b99125 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -281,6 +281,9 @@ void GPURecoWorkflowSpec::init(InitContext& ic) mConfig->configProcessing.willProvideO2PropagatorLate = true; mConfig->configProcessing.o2PropagatorUseGPUField = true; + if (mConfig->configReconstruction.tpc.trackReferenceX == 1000.f) { + mConfig->configReconstruction.tpc.trackReferenceX = 83.f; + } if (mConfParam->printSettings && (mConfParam->printSettings > 1 || ic.services().get().inputTimesliceId == 0)) { mConfig->configProcessing.printSettings = true; @@ -517,7 +520,7 @@ int32_t GPURecoWorkflowSpec::runMain(o2::framework::ProcessingContext* pc, GPUTr static bool first = true; if (first) { first = false; - if (pc->services().get().inputTimesliceId == 0) { // TPC ConfigurableCarams are somewhat special, need to construct by hand + if (pc && pc->services().get().inputTimesliceId == 0) { // TPC ConfigurableCarams are somewhat special, need to construct by hand o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc->services().get().name, "rec_tpc"), "GPU_rec_tpc,GPU_rec,GPU_proc_param,GPU_proc,GPU_global,trackTuneParams"); } } diff --git a/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx b/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx index c45c746064101..6f956efe79304 100644 --- a/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx +++ b/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx @@ -53,6 +53,7 @@ // for TOF #include "TOFDigitizerSpec.h" #include "TOFWorkflowIO/TOFDigitWriterSpec.h" +#include "TOFBase/CalibTOFapi.h" // for FT0 #include "FT0DigitizerSpec.h" @@ -202,6 +203,7 @@ void customize(std::vector& workflowOptions) // option to use/not use CCDB for TOF workflowOptions.push_back(ConfigParamSpec{"use-ccdb-tof", o2::framework::VariantType::Bool, false, {"enable access to ccdb tof calibration objects"}}); workflowOptions.push_back(ConfigParamSpec{"ccdb-tof-sa", o2::framework::VariantType::Bool, false, {"enable access to ccdb tof calibration objects via CCDBManager (obsolete remap to use-ccdb-tof)"}}); + workflowOptions.push_back(ConfigParamSpec{"tof-drm-bitmask", o2::framework::VariantType::Int, (int)o2::tof::CalibTOFapi::DRM_ORBIT_MISMATCH, {"bit mask of DRM critical errors"}}); // option to use/not use CCDB for FT0 workflowOptions.push_back(ConfigParamSpec{"use-ccdb-ft0", o2::framework::VariantType::Bool, false, {"enable access to ccdb ft0 calibration objects"}}); @@ -677,10 +679,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto ccdb_url_tof = o2::base::NameConf::getCCDBServer(); auto timestamp = o2::raw::HBFUtils::Instance().startTime / 1000; detList.emplace_back(o2::detectors::DetID::TOF); + auto maskDRM = (uint32_t)configcontext.options().get("tof-drm-bitmask"); // connect the TOF digitization // printf("TOF Setting: use-ccdb = %d ---- ccdb url=%s ---- timestamp=%ld\n", useCCDB, ccdb_url_tof.c_str(), timestamp); - digitizerSpecs.emplace_back(o2::tof::getTOFDigitizerSpec(fanoutsize++, useCCDB, mctruth, ccdb_url_tof.c_str(), timestamp)); + digitizerSpecs.emplace_back(o2::tof::getTOFDigitizerSpec(fanoutsize++, useCCDB, mctruth, ccdb_url_tof.c_str(), timestamp, maskDRM)); // add TOF digit writer writerSpecs.emplace_back(o2::tof::getTOFDigitWriterSpec(mctruth)); } diff --git a/Steer/DigitizerWorkflow/src/TOFDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/TOFDigitizerSpec.cxx index e512659686c86..2a7800985fc1f 100644 --- a/Steer/DigitizerWorkflow/src/TOFDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/TOFDigitizerSpec.cxx @@ -48,7 +48,7 @@ namespace tof class TOFDPLDigitizerTask : public o2::base::BaseDPLDigitizer { public: - TOFDPLDigitizerTask(bool useCCDB, std::string ccdb_url, int timestamp) : mUseCCDB{useCCDB}, mCCDBurl(ccdb_url), mTimestamp(timestamp), o2::base::BaseDPLDigitizer(o2::base::InitServices::FIELD | o2::base::InitServices::GEOM), mPass(o2::conf::DigiParams::Instance().passName){}; + TOFDPLDigitizerTask(bool useCCDB, std::string ccdb_url, int timestamp, uint32_t mask) : mUseCCDB{useCCDB}, mCCDBurl(ccdb_url), mTimestamp(timestamp), o2::base::BaseDPLDigitizer(o2::base::InitServices::FIELD | o2::base::InitServices::GEOM), mPass(o2::conf::DigiParams::Instance().passName), mMaskDRM(mask){}; void initDigitizerTask(framework::InitContext& ic) override { @@ -77,6 +77,10 @@ class TOFDPLDigitizerTask : public o2::base::BaseDPLDigitizer mUpdateCCDB = true; return; } + if (matcher == ConcreteDataMatcher("TOF", "DiagnosticDRM", 0)) { + mUpdateCCDB = true; + return; + } if (matcher == ConcreteDataMatcher("TOF", "LHCphaseCal", 0)) { mUpdateCCDB = true; return; @@ -127,6 +131,7 @@ class TOFDPLDigitizerTask : public o2::base::BaseDPLDigitizer const auto lhcPhaseIn = pc.inputs().get("tofccdbLHCphase"); const auto channelCalibIn = pc.inputs().get("tofccdbChannelCalib"); const auto diagnosticIn = pc.inputs().get("tofccdbDia"); + const auto diagnosticDRM = pc.inputs().get("tofccdbDrm"); const auto statusIn = pc.inputs().get("tofccdbStatus"); const auto tofParams = pc.inputs().get("tofccdbParams"); @@ -165,11 +170,15 @@ class TOFDPLDigitizerTask : public o2::base::BaseDPLDigitizer o2::dataformats::CalibLHCphaseTOF* lhcPhase = new o2::dataformats::CalibLHCphaseTOF(std::move(*lhcPhaseIn)); o2::dataformats::CalibTimeSlewingParamTOF* channelCalib = new o2::dataformats::CalibTimeSlewingParamTOF(std::move(*channelCalibIn)); o2::tof::Diagnostic* diagnostic = new o2::tof::Diagnostic(std::move(*diagnosticIn)); + o2::tof::Diagnostic* diagnosticDRMerr = new o2::tof::Diagnostic(std::move(*diagnosticDRM)); o2::tof::TOFFEElightInfo* status = new o2::tof::TOFFEElightInfo(std::move(*statusIn)); - mCalibApi = new o2::tof::CalibTOFapi(long(0), lhcPhase, channelCalib, diagnostic); + mCalibApi = new o2::tof::CalibTOFapi(long(0), lhcPhase, channelCalib, diagnostic, diagnosticDRMerr); + mCalibApi->setDRMCriticalErrorMask(mMaskDRM); mCalibApi->loadDiagnosticFrequencies(); + mCalibApi->loadDiagnosticDRMFrequencies(); mCalibApi->loadActiveMap(status); + mUpdateCCDB = false; } else { // update if necessary if (mUpdateCCDB) { @@ -178,10 +187,14 @@ class TOFDPLDigitizerTask : public o2::base::BaseDPLDigitizer o2::dataformats::CalibLHCphaseTOF* lhcPhase = new o2::dataformats::CalibLHCphaseTOF(*lhcPhaseIn); o2::dataformats::CalibTimeSlewingParamTOF* channelCalib = new o2::dataformats::CalibTimeSlewingParamTOF(*channelCalibIn); o2::tof::Diagnostic* diagnostic = new o2::tof::Diagnostic(std::move(*diagnosticIn)); + o2::tof::Diagnostic* diagnosticDRMerr = new o2::tof::Diagnostic(std::move(*diagnosticDRM)); o2::tof::TOFFEElightInfo* status = new o2::tof::TOFFEElightInfo(std::move(*statusIn)); mCalibApi = new o2::tof::CalibTOFapi(long(0), lhcPhase, channelCalib, diagnostic); + mCalibApi->setDRMCriticalErrorMask(mMaskDRM); mCalibApi->loadDiagnosticFrequencies(); + mCalibApi->loadDiagnosticDRMFrequencies(); mCalibApi->loadActiveMap(status); + mUpdateCCDB = false; } else { // do nothing @@ -201,10 +214,12 @@ class TOFDPLDigitizerTask : public o2::base::BaseDPLDigitizer channelCalibDummy->setFractionUnderPeak(sector, channelInSector, 1); } mCalibApi = new o2::tof::CalibTOFapi(long(mTimestamp), lhcPhaseDummy, channelCalibDummy); + mCalibApi->setDRMCriticalErrorMask(mMaskDRM); if (mUseCCDB) { mCalibApi->setURL(mCCDBurl); mCalibApi->readDiagnosticFrequencies(); + mCalibApi->readDiagnosticDRMFrequencies(); mCalibApi->readLHCphase(); mCalibApi->readActiveMap(); mCalibApi->readTimeSlewingParam(); @@ -300,9 +315,10 @@ class TOFDPLDigitizerTask : public o2::base::BaseDPLDigitizer bool mUpdateCCDB = false; o2::tof::CalibTOFapi* mCalibApi = nullptr; std::string mPass; + uint32_t mMaskDRM = 0; }; -DataProcessorSpec getTOFDigitizerSpec(int channel, bool useCCDB, bool mctruth, std::string ccdb_url, int timestamp) +DataProcessorSpec getTOFDigitizerSpec(int channel, bool useCCDB, bool mctruth, std::string ccdb_url, int timestamp, uint32_t maskDRM) { // create the full data processor spec using // a name identifier @@ -319,6 +335,7 @@ DataProcessorSpec getTOFDigitizerSpec(int channel, bool useCCDB, bool mctruth, s if (useCCDB) { inputs.emplace_back("tofccdbStatus", "TOF", "StatusTOF", 0, Lifetime::Condition, ccdbParamSpec("TOF/Calib/FEELIGHT")); inputs.emplace_back("tofccdbDia", "TOF", "DiagnosticCal", 0, Lifetime::Condition, ccdbParamSpec("TOF/Calib/Diagnostic")); + inputs.emplace_back("tofccdbDrm", "TOF", "DiagnosticDRM", 0, Lifetime::Condition, ccdbParamSpec("TOF/Calib/DRMerrors")); inputs.emplace_back("tofccdbLHCphase", "TOF", "LHCphaseCal", 0, Lifetime::Condition, ccdbParamSpec("TOF/Calib/LHCphase")); inputs.emplace_back("tofccdbChannelCalib", "TOF", "ChannelCalibCal", 0, Lifetime::Condition, ccdbParamSpec("TOF/Calib/ChannelCalib")); inputs.emplace_back("tofccdbParams", "TOF", "parameters", 0, Lifetime::Condition, ccdbParamSpec("TOF/Calib/Params")); @@ -337,7 +354,7 @@ DataProcessorSpec getTOFDigitizerSpec(int channel, bool useCCDB, bool mctruth, s "TOFDigitizer", inputs, outputs, - AlgorithmSpec{adaptFromTask(useCCDB, ccdb_url, timestamp)}, + AlgorithmSpec{adaptFromTask(useCCDB, ccdb_url, timestamp, maskDRM)}, Options{{"pileup", VariantType::Int, 1, {"whether to run in continuous time mode"}}} // I can't use VariantType::Bool as it seems to have a problem }; diff --git a/Steer/DigitizerWorkflow/src/TOFDigitizerSpec.h b/Steer/DigitizerWorkflow/src/TOFDigitizerSpec.h index f5841313c1cb0..7951b93eb1419 100644 --- a/Steer/DigitizerWorkflow/src/TOFDigitizerSpec.h +++ b/Steer/DigitizerWorkflow/src/TOFDigitizerSpec.h @@ -19,7 +19,7 @@ namespace o2 namespace tof { -o2::framework::DataProcessorSpec getTOFDigitizerSpec(int channel, bool useCCDB, bool mctruth, std::string ccdb_url, int timestamp); +o2::framework::DataProcessorSpec getTOFDigitizerSpec(int channel, bool useCCDB, bool mctruth, std::string ccdb_url, int timestamp, uint32_t maskDRM = 1 << 1); } // end namespace tof } // end namespace o2 diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index a8f01a3ef1822..a3048a494796e 100755 --- a/prodtests/full-system-test/dpl-workflow.sh +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -332,7 +332,9 @@ done ! has_detector CTP && [[ ${CTPLUMY_DISABLED:-} != 1 ]] && TPC_CORR_OPT+=" --disable-ctp-lumi-request" } -parse_TPC_CORR_SCALING $TPC_CORR_SCALING +if has_detector TPC; then + parse_TPC_CORR_SCALING $TPC_CORR_SCALING +fi if [[ $GPUTYPE != "CPU" && $(ulimit -e) -ge 25 && ${O2_GPU_WORKFLOW_NICE:-} == 1 ]]; then GPU_CONFIG_SELF+=" --child-driver 'nice -n -5'" @@ -363,7 +365,7 @@ if [[ ${O2_GPU_RTC:-$EPNSYNCMODE} == 1 ]] && [[ ( ${ALICE_O2_FST:-0} == 1 && ${F [[ ${EPN_NODE_MI100:-0} == 1 ]] && GPU_CONFIG_KEY+="GPU_proc_rtctech.overrideArchitecture=--offload-arch=gfx908;" fi -( workflow_has_parameter AOD || [[ -z "$DISABLE_ROOT_OUTPUT" ]] || needs_root_output o2-emcal-cell-writer-workflow ) && has_detector EMC && RAW_EMC_SUBSPEC=" --subspecification 1 " +( workflow_has_parameter AOD || [[ -z "$DISABLE_ROOT_OUTPUT" ]] || needs_root_output o2-emcal-cell-writer-workflow ) && has_detector EMC && RAW_EMC_SUBSPEC=" --subspecificationOut 1 " has_detector_reco MID && has_detector_matching MCHMID && MFTMCHConf="FwdMatching.useMIDMatch=true;" || MFTMCHConf="FwdMatching.useMIDMatch=false;" [[ -n ${MFTMCH_NCANDIDATES_OPT:-} ]] && MFTMCHConf+="${MFTMCH_NCANDIDATES_OPT}" @@ -568,7 +570,7 @@ if [[ $CTFINPUT == 0 && $DIGITINPUT == 0 ]]; then has_detector CTP && ! has_detector_from_global_reader CTP && add_W o2-ctp-reco-workflow "$DISABLE_ROOT_OUTPUT $CTP_CONFIG --ntf-to-average 1 --pipeline $(get_N ctp-raw-decoder CTP RAW 1)" has_detector PHS && ! has_detector_from_global_reader PHS && ! has_detector_flp_processing PHS && add_W o2-phos-reco-workflow "--input-type raw --output-type cells $DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT --pipeline $(get_N PHOSRawToCellConverterSpec PHS REST 1) $DISABLE_MC" has_detector CPV && ! has_detector_from_global_reader CPV && add_W o2-cpv-reco-workflow "--input-type $CPV_INPUT --output-type clusters $DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT --pipeline $(get_N CPVRawToDigitConverterSpec CPV REST 1),$(get_N CPVClusterizerSpec CPV REST 1) $DISABLE_MC" - has_detector EMC && ! has_detector_from_global_reader EMC && ! has_detector_flp_processing EMC && add_W o2-emcal-reco-workflow "--input-type raw --output-type cells ${RAW_EMC_SUBSPEC:-} $EMCRAW2C_CONFIG $DISABLE_ROOT_OUTPUT $DISABLE_MC --pipeline $(get_N EMCALRawToCellConverterSpec EMC REST 1 EMCREC)" + has_detector EMC && ! has_detector_from_global_reader EMC && ! has_detector_flp_processing EMC && add_W o2-emcal-reco-workflow "--input-type raw --output-type cells ${RAW_EMC_SUBSPEC:-} $EMCRAW2C_CONFIG --disable-root-output $DISABLE_MC --pipeline $(get_N EMCALRawToCellConverterSpec EMC REST 1 EMCREC)" fi has_detector_gpu ITS && GPU_INPUT+=",its-clusters" diff --git a/prodtests/full_system_test.sh b/prodtests/full_system_test.sh index 07ccdf01d4566..24215276fd463 100755 --- a/prodtests/full_system_test.sh +++ b/prodtests/full_system_test.sh @@ -169,7 +169,7 @@ SIMOPTKEY+="GenTPCLoopers.colsys=${BEAMTYPE};" taskwrapper sim.log o2-sim ${FST_BFIELD+--field=}${FST_BFIELD} --vertexMode kCollContext --seed $O2SIMSEED -n $NEvents --configKeyValues "\"$SIMOPTKEY\"" -g ${FST_GENERATOR} -e ${FST_MC_ENGINE} -j $NJOBS --run ${RUNNUMBER} -o o2sim --fromCollContext collisioncontext.root:o2sim # Test MCTracks to AO2D conversion tool -taskwrapper kine2aod.log "o2-sim-kine-publisher -b --kineFileName o2sim --aggregate-timeframe $NEvents | o2-sim-mctracks-to-aod -b --aod-writer-keep dangling | o2-analysis-mctracks-to-aod-simple-task -b" +taskwrapper kine2aod.log "o2-sim-kine-publisher --shm-segment-size $SHMSIZE -b --kineFileName o2sim --aggregate-timeframe $NEvents | o2-sim-mctracks-to-aod --shm-segment-size $SHMSIZE -b --aod-writer-keep dangling | o2-analysis-mctracks-to-aod-simple-task --shm-segment-size $SHMSIZE -b" if [[ ! -s AnalysisResults_trees.root ]] || [[ ! -s AnalysisResults.root ]]; then echo "Error: AnalysisResults_trees.root (AO2D from Kine file) or AnalysisResults.root (simple analysis task output) missing or empty" exit 1 diff --git a/scripts/geometry/O2_CADtoTGeo.py b/scripts/geometry/O2_CADtoTGeo.py index d564cdc6124a8..3de2fd75973df 100644 --- a/scripts/geometry/O2_CADtoTGeo.py +++ b/scripts/geometry/O2_CADtoTGeo.py @@ -4,6 +4,14 @@ For now, all CAD solids are simply meshed. The ROOT geometry is build as a C++ ROOT macro and facet data is stored in binary form to keep disc space minimal. +NEW (03/2026): + - Optional material/medium emission from a BOM (bill of materials) CSV file. + The CSV is expected to contain lines like: + CAD, Mechanical/Part, , , , , , ... + - If both a part mass and a CAD volume are available, an effective density is computed + and used in the emitted TGeoMaterial. Otherwise a reasonable default density is used + for a few common materials, or 1.0 g/cm^3 as fallback. + Generates (into --output-folder): - geom.C (small ROOT macro) - facets__.bin for each leaf logical volume (float32 triangles) @@ -28,15 +36,21 @@ Author: - Sandro Wenzel, CERN (02/2026) + - Material/BOM integration patch (03/2026) """ import warnings warnings.filterwarnings("ignore", message=".*all to deprecated function.*", category=DeprecationWarning) import argparse +import csv +import json +import math import re import struct +from dataclasses import dataclass from pathlib import Path as _Path +from typing import Dict, List, Optional, Tuple from OCC.Core.Bnd import Bnd_Box from OCC.Core.BRepBndLib import brepbndlib @@ -55,6 +69,14 @@ from OCC.Core.TCollection import TCollection_AsciiString from OCC.Core.gp import gp_Trsf +# volume properties for density calcs (may not be present in older pythonOCC builds) +try: + from OCC.Core.GProp import GProp_GProps + from OCC.Core.BRepGProp import brepgprop_VolumeProperties + _HAS_VOLPROPS = True +except Exception: + _HAS_VOLPROPS = False + # ------------------------------- # STEP/XCAF loading @@ -221,6 +243,30 @@ def triangulate_CAD_solid(my_solid, meshparam, scale_to_cm: float = 1.0): return _scale_triangles(triangles, scale_to_cm) +# ------------------------------- +# Volume helpers (for density) +# ------------------------------- + +def volume_cm3_of_shape(shape, scale_to_cm: float) -> float: + """Compute CAD solid volume in cm^3 (using STEP->cm scale).""" + if _HAS_VOLPROPS: + try: + props = GProp_GProps() + brepgprop_VolumeProperties(shape, props) + # volume returned in STEP length units^3 + v = float(props.Mass()) + return v * (scale_to_cm ** 3) + except Exception: + pass + + # Fallback: bounding-box volume (rough but always defined) + box = Bnd_Box() + brepbndlib.Add(shape, box) + xmin, ymin, zmin, xmax, ymax, zmax = box.Get() + dx, dy, dz = (xmax - xmin) * scale_to_cm, (ymax - ymin) * scale_to_cm, (zmax - zmin) * scale_to_cm + return max(dx, 0.0) * max(dy, 0.0) * max(dz, 0.0) + + # ------------------------------- # Naming helpers # ------------------------------- @@ -256,6 +302,472 @@ def write_facets_bin(path: _Path, triangles): )) +# ------------------------------- +# BOM / material mapping +# ------------------------------- + +@dataclass(frozen=True) +class BomEntry: + part_number: str + revision: str + name: str + mass_value: float # as in CSV + material: str + + @property + def part_number_key(self) -> str: + return (self.part_number or "").strip() + + @property + def name_key(self) -> str: + return (self.name or "").strip() + + +def _to_float(s: str) -> Optional[float]: + try: + if s is None: + return None + s = str(s).strip() + if not s: + return None + return float(s) + except Exception: + return None + + +def read_bom_csv(csv_path: str) -> List[BomEntry]: + """ + Reads a BOM CSV in the format provided by design team. + + We look for rows whose first column is 'CAD' and second is 'Mechanical/Part'. + Columns (0-based): + 0 CAD + 1 type + 2 part number + 3 revision + 4 name/description + 5 mass + 6 material + """ + entries: List[BomEntry] = [] + with open(csv_path, newline="", encoding="utf-8", errors="ignore") as f: + reader = csv.reader(f) + for row in reader: + if not row: + continue + if len(row) < 7: + continue + if row[0].strip() != "CAD": + continue + if row[1].strip() != "Mechanical/Part": + continue + + part_no = (row[2] or "").strip() + rev = (row[3] or "").strip() + name = (row[4] or "").strip() + mass = _to_float(row[5]) + mat = (row[6] or "").strip() + + if not (part_no or name): + continue + if mass is None: + mass = float("nan") + if not mat: + mat = "Default" + + entries.append(BomEntry(part_no, rev, name, float(mass), mat)) + return entries + + + +def normalize_material_name(mat: str) -> str: + """ + Normalizes a BOM material string for matching / caching. + + Note: We keep the *original* string for ROOT object names; this is only used + internally for robust matching and dictionary keys. + """ + mat = (mat or "Default").strip() + mat = re.sub(r"\s+", " ", mat) + return mat + + +def _norm_tokens(s: str) -> List[str]: + s = (s or "").lower() + # common grade/format noise + s = re.sub(r"\(.*?\)", " ", s) + s = s.replace("en aw", " ") + s = s.replace("en-aw", " ") + s = s.replace("en", " ") + s = s.replace("aw", " ") + s = s.replace("_", " ").replace("-", " ") + s = re.sub(r"[^a-z0-9]+", " ", s) + s = re.sub(r"\s+", " ", s).strip() + if not s: + return [] + toks = s.split(" ") + + # small synonym normalization + syn = { + "alu": "al", + "aluminium": "aluminum", + "silicium": "silicon", + "inox": "stainless", + "ss": "stainless", + "cu": "copper", + "fe": "iron", + "ptfe": "teflon", + "ti": "titanium", + "be": "beryllium", + } + + # Expand common element symbols to names and vice-versa so that e.g. "G4_Si" can match "silicon". + elem_alias = { + "h": "hydrogen", "he": "helium", "c": "carbon", "n": "nitrogen", "o": "oxygen", + "al": "aluminum", "si": "silicon", "fe": "iron", "cu": "copper", "be": "beryllium", + "mg": "magnesium", "mn": "manganese", "cr": "chromium", "ni": "nickel", "zn": "zinc", + "ti": "titanium", "w": "tungsten", "pb": "lead", "sn": "tin", + } + name_to_sym = {v: k for k, v in elem_alias.items()} + + out: List[str] = [] + for t in toks: + t2 = syn.get(t, t) + out.append(t2) + if t2 in elem_alias: + out.append(elem_alias[t2]) + if t2 in name_to_sym: + out.append(name_to_sym[t2]) + + # de-dup while preserving order + seen = set() + out2: List[str] = [] + for t in out: + if t and t not in seen: + seen.add(t) + out2.append(t) + return out2 + + +def _density_score(rho_part: Optional[float], rho_ref: Optional[float]) -> float: + if rho_part is None or rho_ref is None or not (rho_part > 0.0) or not (rho_ref > 0.0): + return 0.0 + # symmetric score in log-space; 1.0 is perfect match + d = abs(math.log(rho_ref / rho_part)) + return 1.0 / (1.0 + d) + + +def _token_score(tokens_a: List[str], tokens_b: List[str]) -> float: + if not tokens_a or not tokens_b: + return 0.0 + sa = set(tokens_a) + sb = set(tokens_b) + inter = len(sa & sb) + union = len(sa | sb) + if union == 0: + return 0.0 + return inter / union + + +def load_g4_nist_db(json_path: str) -> Dict[str, dict]: + """ + Loads a JSON dump created by the 'nist_export_all' tool. + Returns a dict: nist_name -> material record. + """ + with open(json_path, "r", encoding="utf-8") as f: + data = json.load(f) + mats = data.get("materials", {}) + if not isinstance(mats, dict) or not mats: + raise RuntimeError(f"G4 NIST DB JSON seems empty or malformed: {json_path}") + return mats + +# Minimal periodic table for parsing custom alloys not present in NIST. +# Values: Z (atomic number), A (g/mol) +_ELEMENT_TABLE = { + "H": (1, 1.00794), + "C": (6, 12.0107), + "N": (7, 14.0067), + "O": (8, 15.9994), + "Al": (13, 26.9815385), + "Si": (14, 28.0855), + "Fe": (26, 55.845), + "Cu": (29, 63.546), + "Be": (4, 9.0121831), + "Mg": (12, 24.305), + "Mn": (25, 54.938044), + "Cr": (24, 51.9961), + "Ni": (28, 58.6934), + "Zn": (30, 65.38), + "Ti": (22, 47.867), + "W": (74, 183.84), + "Pb": (82, 207.2), + "Sn": (50, 118.71), +} + + +@dataclass +class ResolvedMaterial: + bom_name: str + nist_name: Optional[str] # e.g. "G4_Al" + score: float + rho_used_g_cm3: Optional[float] # density used in ROOT definition + radlen_cm: Optional[float] + intlen_cm: Optional[float] + elements: Optional[List[dict]] # list of {symbol,Z,A_g_mol,mass_fraction} + note: str # for comments in geom.C (warnings/FIXME) + +@dataclass +class MatMatchConfig: + # Minimum combined score to accept a match. + min_score: float = 0.35 + # If (best - second_best) < ambiguity_delta, treat as ambiguous/unresolved. + ambiguity_delta: float = 0.05 + # Weights for the combined score = w_token * token_score + w_density * density_score + w_token: float = 0.75 + w_density: float = 0.25 + # Optional hard filter on density proximity (in log-space). If <=0, disabled. + # Example: max_log_density_diff=0.8 means accept within exp(0.8)~2.2x in either direction. + max_log_density_diff: float = 0.0 + # Penalize compound matches (oxide/dioxide/carbide/...) when BOM doesn't mention those tokens. + compound_penalty: float = 0.25 + + +def resolve_bom_material( + bom_material: str, + rho_part_g_cm3: Optional[float], + g4db: Optional[Dict[str, dict]], + cfg: MatMatchConfig, +) -> ResolvedMaterial: + """ + Resolves an arbitrary BOM material string to a Geant4 NIST material name using: + - exact key match (BOM already uses e.g. "G4_Al") + - token overlap scoring on names + - density proximity scoring (if rho_part_g_cm3 available) + + If unresolved/ambiguous, tries to parse element symbols from the BOM string (e.g. "Cu Be") + and emits a placeholder mixture (equal mass fractions) annotated with FIXME. + """ + raw_bom_material = (bom_material or "").strip() + bom_material = normalize_material_name(bom_material) + + if not g4db: + return ResolvedMaterial( + bom_name=bom_material, + nist_name=None, + score=0.0, + rho_used_g_cm3=rho_part_g_cm3, + radlen_cm=None, + intlen_cm=None, + elements=None, + note="FIXME: No Geant4 NIST DB provided; using dummy material.", + ) + + # Trivial: BOM already provides an exact Geant4 material key + if bom_material in g4db: + rec = g4db[bom_material] + rho_ref = rec.get("density_g_cm3") + # Use NIST density for emission; CAD-derived density is used only for matching. + rho_used = rho_ref + + rad = rec.get("radlen_cm") + itl = rec.get("intlen_cm") + + return ResolvedMaterial( + bom_name=bom_material, + nist_name=bom_material, + score=1.0, + rho_used_g_cm3=rho_used, + radlen_cm=rad, + intlen_cm=itl, + elements=rec.get("elements", []), + note="Resolved by exact Geant4 NIST name from BOM.", + ) + + bom_toks = _norm_tokens(bom_material) + if not bom_toks: + return ResolvedMaterial( + bom_name=bom_material, + nist_name=None, + score=0.0, + rho_used_g_cm3=rho_part_g_cm3, + radlen_cm=None, + intlen_cm=None, + elements=None, + note="FIXME: Empty/unknown BOM material string; using dummy material.", + ) + + def _build_custom_from_elements(note_prefix: str) -> Optional[ResolvedMaterial]: + s = raw_bom_material + if not s: + return None + + symbols = set(re.findall(r"\b([A-Z][a-z]?)\b", s)) + name_to_symbol = { + "aluminum": "Al", "aluminium": "Al", "silicon": "Si", "iron": "Fe", "copper": "Cu", + "beryllium": "Be", "magnesium": "Mg", "manganese": "Mn", "chromium": "Cr", "nickel": "Ni", + "zinc": "Zn", "titanium": "Ti", "tungsten": "W", "lead": "Pb", "tin": "Sn", + } + for t in bom_toks: + if t in name_to_symbol: + symbols.add(name_to_symbol[t]) + + symbols = [sym for sym in sorted(symbols) if sym in _ELEMENT_TABLE] + if not symbols: + return None + + frac = 1.0 / float(len(symbols)) + elems: List[dict] = [] + for sym in symbols: + Z, A = _ELEMENT_TABLE[sym] + elems.append({"symbol": sym, "Z": Z, "A_g_mol": A, "mass_fraction": frac}) + + return ResolvedMaterial( + bom_name=bom_material, + nist_name=None, + score=0.0, + rho_used_g_cm3=rho_part_g_cm3, + radlen_cm=None, + intlen_cm=None, + elements=elems, + note=f"FIXME: {note_prefix} No suitable Geant4 NIST material. Emitting placeholder mixture from parsed elements {symbols} with equal mass fractions; please adjust fractions/material.", + ) + + best = (None, -1.0, 0.0, 0.0) # (nist_name, score, dens_score, token_score) + second = (None, -1.0, 0.0, 0.0) + + bom_has_compound = any(t in bom_toks for t in ( + "oxide", "dioxide", "carbide", "nitride", "fluoride", "chloride", + "sulfate", "phosphate", "glass", "dioxyde" + )) + + for nist_name, rec in g4db.items(): + nist_toks = _norm_tokens(nist_name) + ts = _token_score(bom_toks, nist_toks) + if ts <= 0.0: + continue + + ds = _density_score(rho_part_g_cm3, rec.get("density_g_cm3")) + + # Optional hard density filter + if cfg.max_log_density_diff and cfg.max_log_density_diff > 0.0 and rho_part_g_cm3 and rec.get("density_g_cm3"): + try: + if abs(math.log(float(rec.get("density_g_cm3")) / float(rho_part_g_cm3))) > cfg.max_log_density_diff: + continue + except Exception: + pass + + nist_has_compound = any(t in nist_toks for t in ( + "oxide", "dioxide", "carbide", "nitride", "fluoride", "chloride", + "sulfate", "phosphate", "glass", "dioxyde" + )) + compound_pen = cfg.compound_penalty if (nist_has_compound and not bom_has_compound) else 0.0 + + score = cfg.w_token * ts + cfg.w_density * ds - compound_pen + + if score > best[1]: + second = best + best = (nist_name, score, ds, ts) + elif score > second[1]: + second = (nist_name, score, ds, ts) + + nist_best, score_best, ds_best, ts_best = best + nist_second, score_second, _, _ = second + + if nist_best is None or score_best < cfg.min_score: + custom = _build_custom_from_elements("Could not resolve with enough confidence.") + if custom is not None: + return custom + return ResolvedMaterial( + bom_name=bom_material, + nist_name=None, + score=float(score_best if score_best > 0 else 0.0), + rho_used_g_cm3=rho_part_g_cm3, + radlen_cm=None, + intlen_cm=None, + elements=None, + note="FIXME: Could not resolve BOM material to a Geant4 NIST material with enough confidence; using dummy material.", + ) + + if score_second > 0 and (score_best - score_second) < cfg.ambiguity_delta: + custom = _build_custom_from_elements( + f"Ambiguous material match (best '{nist_best}' score={score_best:.3f}, second '{nist_second}' score={score_second:.3f})." + ) + if custom is not None: + return custom + return ResolvedMaterial( + bom_name=bom_material, + nist_name=None, + score=float(score_best), + rho_used_g_cm3=rho_part_g_cm3, + radlen_cm=None, + intlen_cm=None, + elements=None, + note=f"FIXME: Ambiguous material match (best '{nist_best}' score={score_best:.3f}, second '{nist_second}' score={score_second:.3f}); using dummy material.", + ) + + rec = g4db[nist_best] + rho_ref = rec.get("density_g_cm3") + # Use NIST density for emission; CAD-derived density is used only for matching. + rho_used = rho_ref + + rad = rec.get("radlen_cm") + itl = rec.get("intlen_cm") + + return ResolvedMaterial( + bom_name=bom_material, + nist_name=nist_best, + score=float(score_best), + rho_used_g_cm3=rho_used, + radlen_cm=rad, + intlen_cm=itl, + elements=rec.get("elements", []), + note=f"Resolved to '{nist_best}' (token={ts_best:.3f}, density={ds_best:.3f}, score={score_best:.3f}).", + ) + + +def build_volume_to_material_map( + bom_entries: List[BomEntry], + def_names: Dict[str, str], +) -> Dict[str, BomEntry]: + """ + Builds a mapping def_lid -> BomEntry by matching the XCAF display name to: + - exact part_number match + - exact description/name match + - substring match on part_number within the XCAF name + + This is heuristic; if nothing matches we keep no assignment for that volume. + """ + # lookup tables + by_part: Dict[str, BomEntry] = {} + by_name: Dict[str, BomEntry] = {} + for e in bom_entries: + if e.part_number_key: + by_part[e.part_number_key] = e + if e.name_key and e.name_key not in by_name: + by_name[e.name_key] = e + + out: Dict[str, BomEntry] = {} + for lid, disp in def_names.items(): + key = (disp or "").strip() + if not key: + continue + + # 1) exact part number + if key in by_part: + out[lid] = by_part[key] + continue + # 2) exact name/description + if key in by_name: + out[lid] = by_name[key] + continue + # 3) substring match on any part number + for pn, e in by_part.items(): + if pn and pn in key: + out[lid] = e + break + return out + + # ------------------------------- # C++ emission helpers # ------------------------------- @@ -306,27 +818,100 @@ def emit_cpp_prelude() -> str: """ -def emit_materials_cpp() -> str: - return """ // Default material/medium (placeholder; can be replaced later) - TGeoMaterial *mat_Default = new TGeoMaterial("Default", 0., 0., 0.); - TGeoMedium *med_Default = new TGeoMedium("Default", 1, mat_Default); -""" +def emit_materials_cpp( + used_materials: Dict[str, ResolvedMaterial], + # key: BOM material string as used in CSV after normalization +) -> Tuple[str, Dict[str, str]]: + """ + Emits C++ code defining TGeoMaterial/TGeoMixture + TGeoMedium for all used materials. + + - If a material resolved to a Geant4 NIST entry, emit a physically correct mixture + (element mass fractions) and set RadLen/IntLen (from Geant4) when available. + - If unresolved/ambiguous, emit a dummy material and annotate with FIXME comments. + """ + cpp: List[str] = [] + cpp.append(" // Default material/medium (placeholder; can be replaced later)") + cpp.append(" TGeoMaterial *mat_Default = new TGeoMaterial(\"Default\", 0., 0., 0.);") + cpp.append(" TGeoMedium *med_Default = new TGeoMedium(\"Default\", 1, mat_Default);") + cpp.append("") + + emitted_el: Dict[str, str] = {} + + def _emit_element(el: dict) -> str: + sym = el.get("symbol", "X") + Z = int(el.get("Z", 0)) + A = float(el.get("A_g_mol", 0.0)) + if sym in emitted_el: + return emitted_el[sym] + safe = sanitize_cpp_name(sym) + var = f"el_{safe}" + cpp.append(f" TGeoElement *{var} = new TGeoElement(\"{sym}\", \"{sym}\", {Z}, {A:.10g});") + emitted_el[sym] = var + return var + + medium_var: Dict[str, str] = {"Default": "med_Default"} + next_id = 2 + + for bom_mat in sorted(used_materials.keys(), key=lambda s: s.lower()): + rm = used_materials[bom_mat] + safe = sanitize_cpp_name(bom_mat) + base = safe + k = 2 + while f"med_{safe}" in medium_var.values(): + safe = f"{base}_{k}" + k += 1 + + rho = rm.rho_used_g_cm3 if (rm.rho_used_g_cm3 and rm.rho_used_g_cm3 > 0.0) else 0.0 + + cpp.append(f" // BOM material: {rm.bom_name}") + cpp.append(f" // {rm.note}") + + if rm.elements: + elems = rm.elements + if len(elems) == 1 and abs(float(elems[0].get('mass_fraction', 1.0)) - 1.0) < 1e-6: + el = elems[0] + A = float(el.get("A_g_mol", 0.0)) + Z = float(el.get("Z", 0)) + cpp.append(f" TGeoMaterial *mat_{safe} = new TGeoMaterial(\"{bom_mat}\", {A:.10g}, {Z:.10g}, {rho:.10g});") + else: + cpp.append(f" TGeoMixture *mat_{safe} = new TGeoMixture(\"{bom_mat}\", {len(elems)}, {rho:.10g});") + for el in elems: + elvar = _emit_element(el) + w = float(el.get("mass_fraction", 0.0)) + cpp.append(f" mat_{safe}->AddElement({elvar}, {w:.10g});") + + if rm.radlen_cm is not None and rm.intlen_cm is not None: + cpp.append(f" mat_{safe}->SetRadLen({float(rm.radlen_cm):.10g}, {float(rm.intlen_cm):.10g});") + elif rm.radlen_cm is not None: + cpp.append(f" mat_{safe}->SetRadLen({float(rm.radlen_cm):.10g});") + else: + cpp.append(" // FIXME: Unresolved material. Replace with a proper TGeoMaterial/TGeoMixture.") + cpp.append(f" TGeoMaterial *mat_{safe} = new TGeoMaterial(\"{bom_mat}\", 0., 0., {rho:.10g});") + + cpp.append(f" TGeoMedium *med_{safe} = new TGeoMedium(\"{bom_mat}\", {next_id}, mat_{safe});") + cpp.append("") + medium_var[bom_mat] = f"med_{safe}" + next_id += 1 + + return "\n".join(cpp), medium_var + + -def emit_tessellated_cpp(lid: str, vol_display_name: str, facet_abspath: str, ntriangles: int) -> str: +def emit_tessellated_cpp(lid: str, vol_display_name: str, facet_abspath: str, ntriangles: int, medium_var: str) -> str: safe = sanitize_cpp_name(lid) shape_name = vol_display_name if vol_display_name else lid if ntriangles <= 0: out = [] out.append(f' TGeoBBox *solid_{safe} = new TGeoBBox("{shape_name}", 0.001, 0.001, 0.001);') - out.append(f' TGeoVolume *vol_{safe} = new TGeoVolume("{shape_name}", solid_{safe}, med_Default);') + out.append(f' TGeoVolume *vol_{safe} = new TGeoVolume("{shape_name}", solid_{safe}, {medium_var});') return "\n".join(out) out = [] out.append(f' TGeoTessellated *solid_{safe} = new TGeoTessellated("{shape_name}", {ntriangles});') out.append(f' LoadFacets("{facet_abspath}", solid_{safe}, check);') - out.append(f' TGeoVolume *vol_{safe} = new TGeoVolume("{shape_name}", solid_{safe}, med_Default);') + out.append(f' TGeoVolume *vol_{safe} = new TGeoVolume("{shape_name}", solid_{safe}, {medium_var});') return "\n".join(out) @@ -340,12 +925,13 @@ def emit_assembly_cpp(lid: str, asm_display_name: str) -> str: # Definition graph extraction # ------------------------------- -logical_volumes = {} # def_lid -> triangles -def_names = {} # def_lid -> human display name (may be "") -assemblies = set() # def_lid -placements = [] # (parent_def_lid, child_def_lid, gp_Trsf local) -top_defs = set() # top definition lids -visited_defs = set() # expanded defs +logical_volumes: Dict[str, list] = {} # def_lid -> triangles +def_names: Dict[str, str] = {} # def_lid -> human display name (may be "") +def_volumes_cm3: Dict[str, float] = {} # def_lid -> volume in cm^3 (leaf only) +assemblies = set() # def_lid +placements = [] # (parent_def_lid, child_def_lid, gp_Trsf local) +top_defs = set() # top definition lids +visited_defs = set() # expanded defs def cpp_var_for_def(lid: str) -> str: @@ -393,6 +979,13 @@ def expand_definition(def_label: TDF_Label, shape_tool, meshparam=None, scale_to if shape_tool.IsSimpleShape(def_label): if def_lid not in logical_volumes: shape = shape_tool.GetShape(def_label) + + # store volume (for density estimation) + try: + def_volumes_cm3[def_lid] = volume_cm3_of_shape(shape, scale_to_cm=scale_to_cm) + except Exception: + def_volumes_cm3[def_lid] = 0.0 + do_meshing = (meshparam is not None) and meshparam.get("do_meshing", None) is True logical_volumes[def_lid] = triangulate_CAD_solid(shape, meshparam=meshparam, scale_to_cm=scale_to_cm) if do_meshing else triangulate_asbbox(shape, scale_to_cm=scale_to_cm) return @@ -401,9 +994,10 @@ def expand_definition(def_label: TDF_Label, shape_tool, meshparam=None, scale_to def extract_graph(step_path: str, meshparam=None, scale_to_cm: float = 1.0): - global logical_volumes, def_names, assemblies, placements, top_defs, visited_defs + global logical_volumes, def_names, def_volumes_cm3, assemblies, placements, top_defs, visited_defs logical_volumes = {} def_names = {} + def_volumes_cm3 = {} assemblies = set() placements = [] top_defs = set() @@ -439,7 +1033,52 @@ def emit_placement_cpp(parent_def: str, child_def: str, trsf: gp_Trsf, copy_no: return trsf_to_tgeo(trsf, tr_name, scale_to_cm) + f" {parent_cpp}->AddNode({child_cpp}, {copy_no}, {tr_name});\n" -def emit_root_macro(step_path: str, out_folder: _Path, meshparam=None, step_unit: str = "auto"): + +def _compute_density_g_cm3( + volume_cm3: float, + mass_value: float, + mass_unit: str, +) -> Tuple[Optional[float], str]: + """ + Computes an effective part density from (mass, CAD volume). + + Returns (rho_g_cm3 or None, comment). If rho is None, caller should fall back + to the Geant4 NIST density (if resolved) or to a dummy density. + """ + if not volume_cm3 or volume_cm3 <= 0: + return None, "no CAD volume available for density" + + if (mass_value is None) or (isinstance(mass_value, float) and math.isnan(mass_value)): + return None, "no BOM mass available for density" + + mass_g = float(mass_value) + mu = (mass_unit or "kg").lower() + if mu == "kg": + mass_g *= 1000.0 + elif mu == "g": + pass + else: + # unknown unit: assume kg + mass_g *= 1000.0 + + rho = mass_g / float(volume_cm3) + # Guard against obvious unit/volume issues + if not (0.01 < rho < 50.0): + return None, f"computed density {rho:.3g} g/cm3 rejected (unit mismatch?)" + + return rho, "density from BOM mass and CAD volume" + + +def emit_root_macro( + step_path: str, + out_folder: _Path, + meshparam=None, + step_unit: str = "auto", + materials_csv: Optional[str] = None, + bom_mass_unit: str = "kg", + g4_nist_json: Optional[str] = None, + mat_cfg: Optional[MatMatchConfig] = None, +): if (step_unit or "auto").lower() == "auto": detected = detect_step_length_unit(step_path) scale_to_cm = step_unit_scale_to_cm(detected) @@ -453,6 +1092,28 @@ def emit_root_macro(step_path: str, out_folder: _Path, meshparam=None, step_unit out_folder = out_folder.expanduser().resolve() out_folder.mkdir(parents=True, exist_ok=True) + + # --- Geant4 NIST material DB (optional but recommended) --- + g4db: Optional[Dict[str, dict]] = None + if g4_nist_json: + g4db = load_g4_nist_db(g4_nist_json) + print(f"Loaded Geant4 NIST DB with {len(g4db)} materials from: {g4_nist_json}") + else: + print("No --g4-nist-json provided: unresolved materials will fall back to dummy ROOT materials.") + mat_cfg = mat_cfg or MatMatchConfig() + + + # --- BOM: map volumes to materials (heuristic) --- + lid_to_bom: Dict[str, BomEntry] = {} + if materials_csv: + bom_entries = read_bom_csv(materials_csv) + lid_to_bom = build_volume_to_material_map(bom_entries, def_names) + print(f"Loaded {len(bom_entries)} BOM entries from: {materials_csv}") + print(f"Matched {len(lid_to_bom)} CAD logical volumes to BOM entries (by name/part-number heuristics)") + else: + print("No --materials-csv provided: emitting Default medium for all logical volumes") + + # --- facet files --- facet_files = {} # def_lid -> absolute path string for lid, tris in logical_volumes.items(): disp = def_names.get(lid, "") @@ -463,16 +1124,63 @@ def emit_root_macro(step_path: str, out_folder: _Path, meshparam=None, step_unit write_facets_bin(fpath, tris) facet_files[lid] = str(fpath).replace("\\", "\\\\") # C++ string literal safety - cpp = [] + # --- which materials do we need to emit? --- + + # --- materials: collect unique BOM material strings actually used by leaf volumes --- + # We resolve each unique BOM string to a Geant4 NIST material using string + density scoring. + used_materials: Dict[str, ResolvedMaterial] = {} + + # Precompute one representative part density per BOM material (first good value wins) + mat_to_rho: Dict[str, Optional[float]] = {} + mat_to_rho_note: Dict[str, str] = {} + + for lid in logical_volumes.keys(): + if lid not in lid_to_bom: + continue + bom = lid_to_bom[lid] + mat_name = normalize_material_name(bom.material) + + if mat_name not in mat_to_rho: + rho_part, rho_note = _compute_density_g_cm3( + def_volumes_cm3.get(lid, 0.0), + bom.mass_value, + bom_mass_unit, + ) + mat_to_rho[mat_name] = rho_part + mat_to_rho_note[mat_name] = rho_note + + for mat_name in sorted(mat_to_rho.keys(), key=lambda s: s.lower()): + rho_part = mat_to_rho.get(mat_name) + rm = resolve_bom_material(mat_name, rho_part, g4db, mat_cfg) + + # Fold density provenance into the note for geom.C comments + rm.note = f"{rm.note} (density: {mat_to_rho_note.get(mat_name, 'n/a')})" + + if rm.nist_name is None: + print(f"WARNING: Unresolved/ambiguous material '{mat_name}'. See FIXME in generated geom.C.") + + used_materials[mat_name] = rm + + materials_cpp, medium_var_map = emit_materials_cpp(used_materials) + + # --- emit C++ macro --- + cpp: List[str] = [] cpp.append(emit_cpp_prelude()) cpp.append("TGeoVolume* build(bool check=true) {") cpp.append(' if (!gGeoManager) { throw std::runtime_error("gGeoManager is null. Call build_and_export() or create a TGeoManager first."); }') - cpp.append(emit_materials_cpp()) + cpp.append(materials_cpp) for lid in logical_volumes.keys(): ntriangles = len(logical_volumes[lid]) - cpp.append(emit_tessellated_cpp(lid, def_names.get(lid, ""), facet_files[lid], ntriangles)) + + # choose medium for this volume + med = "med_Default" + if lid in lid_to_bom: + mat_name = normalize_material_name(lid_to_bom[lid].material) + med = medium_var_map.get(mat_name, "med_Default") + + cpp.append(emit_tessellated_cpp(lid, def_names.get(lid, ""), facet_files[lid], ntriangles, med)) for lid in sorted(assemblies): cpp.append(emit_assembly_cpp(lid, def_names.get(lid, ""))) @@ -527,7 +1235,7 @@ def traverse_print(label, shape_tool, depth=0): indent = " " * depth name = label.GetLabelName() entry = label_entry(label) - print(f"{indent}- {name} =>[{entry}]") + print(f"{indent}- {name} =>[{entry}]") if shape_tool.IsReference(label): ref_label = TDF_Label() @@ -569,7 +1277,21 @@ def main(): ap.add_argument("--mesh", action="store_true", help="Use full BRepMesh triangulation instead of bounding boxes") ap.add_argument("--print-tree", action="store_true", help="Just prints the geometry tree") ap.add_argument("--mesh-prec", default=0.1, help="meshing precision. lower --> slower") - ap.add_argument("--step-unit", default="auto", choices=["auto", "mm", "cm", "m", "in", "ft"], help="STEP length unit override (default: auto-detect)") + ap.add_argument("--step-unit", default="auto", choices=["auto", "mm", "cm", "m", "in", "ft"], help="STEP length unit override (default: auto-detect); TGeo expects cm") + + # NEW: BOM / material support + ap.add_argument("--materials-csv", default=None, help="BOM CSV file providing material + mass per part (optional)") + ap.add_argument("--bom-mass-unit", default="kg", choices=["kg", "g"], help="Unit of the BOM mass column (default: kg)") + ap.add_argument("--g4-nist-json", default=None, help="Path to Geant4 NIST DB JSON dump (from nist_export_all). Enables TGeoMixture emission + RadLen/IntLen.") + + + # Material matching scoring knobs (only used if --g4-nist-json is provided) + ap.add_argument("--mat-min-score", type=float, default=0.35, help="Minimum combined score to accept a G4 NIST material match (default: 0.35)") + ap.add_argument("--mat-ambiguity-delta", type=float, default=0.05, help="If best-second < delta, treat match as ambiguous/unresolved (default: 0.05)") + ap.add_argument("--mat-w-token", type=float, default=0.75, help="Weight for token/name similarity score (default: 0.75)") + ap.add_argument("--mat-w-density", type=float, default=0.25, help="Weight for density proximity score (default: 0.25)") + ap.add_argument("--mat-max-log-density-diff", type=float, default=0.0, help="Optional hard density filter in log-space (0 disables). Example 0.8 ~ within 2.2x (default: 0.0)") + ap.add_argument("--mat-compound-penalty", type=float, default=0.25, help="Penalty for matching to oxides/carbides/etc. when BOM doesn't mention them (default: 0.25)") args = ap.parse_args() @@ -584,11 +1306,30 @@ def main(): meshparam = {"do_meshing": args.mesh, "lin_defl": args.mesh_prec, "ang_defl": args.mesh_prec} + + mat_cfg = MatMatchConfig( + min_score=args.mat_min_score, + ambiguity_delta=args.mat_ambiguity_delta, + w_token=args.mat_w_token, + w_density=args.mat_w_density, + max_log_density_diff=args.mat_max_log_density_diff, + compound_penalty=args.mat_compound_penalty, + ) + out_folder = out_folder.expanduser().resolve() out_folder.mkdir(parents=True, exist_ok=True) out_macro = (out_folder / _Path(args.out).name).resolve() - code = emit_root_macro(step_path, out_folder, meshparam=meshparam, step_unit=args.step_unit) + code = emit_root_macro( + step_path, + out_folder, + meshparam=meshparam, + step_unit=args.step_unit, + materials_csv=args.materials_csv, + bom_mass_unit=args.bom_mass_unit, + g4_nist_json=args.g4_nist_json, + mat_cfg=mat_cfg, + ) out_macro.write_text(code) print(f"Wrote ROOT macro: {out_macro}") diff --git a/scripts/geometry/g4_nist_database/G4_NIST_DB.json b/scripts/geometry/g4_nist_database/G4_NIST_DB.json new file mode 100644 index 0000000000000..3489beb16bad3 --- /dev/null +++ b/scripts/geometry/g4_nist_database/G4_NIST_DB.json @@ -0,0 +1,7160 @@ +{ + "schema": "g4_nist_export_v1", + "count_requested": 309, + "materials": { + "G4_1,2-DICHLOROBENZENE": { + "name": "G4_1,2-DICHLOROBENZENE", + "density_g_cm3": 1.3048000000, + "radlen_cm": 20.7489144362, + "intlen_cm": 69.0775200428, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4902297089 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0274267115 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.4823435796 + } + ] + }, + "G4_1,2-DICHLOROETHANE": { + "name": "G4_1,2-DICHLOROETHANE", + "density_g_cm3": 1.2351000000, + "radlen_cm": 18.6131823209, + "intlen_cm": 77.6700314494, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.2427431829 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0407420059 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.7165148112 + } + ] + }, + "G4_A-150_TISSUE": { + "name": "G4_A-150_TISSUE", + "density_g_cm3": 1.1270000000, + "radlen_cm": 37.1439852154, + "intlen_cm": 63.7486912428, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1013268987 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7755002245 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0350569649 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.0523159477 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.0174219826 + }, + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.0183779816 + } + ] + }, + "G4_ACETONE": { + "name": "G4_ACETONE", + "density_g_cm3": 0.7899000000, + "radlen_cm": 52.2534185395, + "intlen_cm": 91.4814595809, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6203973505 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1041274661 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2754751834 + } + ] + }, + "G4_ACETYLENE": { + "name": "G4_ACETYLENE", + "density_g_cm3": 0.0010967000, + "radlen_cm": 39930.0227629710, + "intlen_cm": 66449.2307704659, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.9225773293 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0774226707 + } + ] + }, + "G4_ADENINE": { + "name": "G4_ADENINE", + "density_g_cm3": 1.3500000000, + "radlen_cm": 30.0581586935, + "intlen_cm": 58.0823779196, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4444232424 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0372959895 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.5182807681 + } + ] + }, + "G4_ADIPOSE_TISSUE_ICRP": { + "name": "G4_ADIPOSE_TISSUE_ICRP", + "density_g_cm3": 0.9500000000, + "radlen_cm": 43.3949297995, + "intlen_cm": 75.2919618730, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1140000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5980000000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0070000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2780000000 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.0010000000 + } + ] + }, + "G4_AIR": { + "name": "G4_AIR", + "density_g_cm3": 0.0012047900, + "radlen_cm": 30392.0700501740, + "intlen_cm": 71009.5012707064, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.0001240001 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.7552677553 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2317812318 + }, + { + "symbol": "Ar", + "Z": 18, + "A_g_mol": 39.9476933511, + "mass_fraction": 0.0128270128 + } + ] + }, + "G4_ALANINE": { + "name": "G4_ALANINE", + "density_g_cm3": 1.4200000000, + "radlen_cm": 27.7725516260, + "intlen_cm": 53.1723594099, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4044321096 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0791931803 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1572145382 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3591601720 + } + ] + }, + "G4_ALUMINUM_OXIDE": { + "name": "G4_ALUMINUM_OXIDE", + "density_g_cm3": 3.9700000000, + "radlen_cm": 7.0377543639, + "intlen_cm": 24.2683456290, + "elements": [ + { + "symbol": "Al", + "Z": 13, + "A_g_mol": 26.9815000000, + "mass_fraction": 0.5292504916 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4707495084 + } + ] + }, + "G4_AMBER": { + "name": "G4_AMBER", + "density_g_cm3": 1.1000000000, + "radlen_cm": 39.1372915409, + "intlen_cm": 64.6512140234, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1059301059 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7889737890 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1050961051 + } + ] + }, + "G4_AMMONIA": { + "name": "G4_AMMONIA", + "density_g_cm3": 0.0008260190, + "radlen_cm": 49481.0183957974, + "intlen_cm": 81682.0756970836, + "elements": [ + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.8224476051 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1775523949 + } + ] + }, + "G4_ANILINE": { + "name": "G4_ANILINE", + "density_g_cm3": 1.0235000000, + "radlen_cm": 41.9603888047, + "intlen_cm": 71.8321544827, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7738313735 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0757632320 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1504053945 + } + ] + }, + "G4_ANTHRACENE": { + "name": "G4_ANTHRACENE", + "density_g_cm3": 1.2830000000, + "radlen_cm": 33.8977543296, + "intlen_cm": 58.2256880136, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.9434470990 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0565529010 + } + ] + }, + "G4_Ac": { + "name": "G4_Ac", + "density_g_cm3": 10.0700000000, + "radlen_cm": 0.6015581907, + "intlen_cm": 21.2030538527, + "elements": [ + { + "symbol": "Ac", + "Z": 89, + "A_g_mol": 227.0280000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Ag": { + "name": "G4_Ag", + "density_g_cm3": 10.5000000000, + "radlen_cm": 0.8542919107, + "intlen_cm": 15.8675527542, + "elements": [ + { + "symbol": "Ag", + "Z": 47, + "A_g_mol": 107.8682200000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Al": { + "name": "G4_Al", + "density_g_cm3": 2.6990000000, + "radlen_cm": 8.8963176127, + "intlen_cm": 38.8944132871, + "elements": [ + { + "symbol": "Al", + "Z": 13, + "A_g_mol": 26.9815000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Am": { + "name": "G4_Am", + "density_g_cm3": 13.6700000000, + "radlen_cm": 0.4243095287, + "intlen_cm": 15.9785730103, + "elements": [ + { + "symbol": "Am", + "Z": 95, + "A_g_mol": 243.0610000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Ar": { + "name": "G4_Ar", + "density_g_cm3": 0.0016620100, + "radlen_cm": 11762.1436719519, + "intlen_cm": 71988.8135208583, + "elements": [ + { + "symbol": "Ar", + "Z": 18, + "A_g_mol": 39.9476933511, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_As": { + "name": "G4_As", + "density_g_cm3": 5.7300000000, + "radlen_cm": 2.0837957777, + "intlen_cm": 25.7503105909, + "elements": [ + { + "symbol": "As", + "Z": 33, + "A_g_mol": 74.9216000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_At": { + "name": "G4_At", + "density_g_cm3": 9.3200000000, + "radlen_cm": 0.6507992580, + "intlen_cm": 22.3211364933, + "elements": [ + { + "symbol": "At", + "Z": 85, + "A_g_mol": 209.9870000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Au": { + "name": "G4_Au", + "density_g_cm3": 19.3200000000, + "radlen_cm": 0.3344364418, + "intlen_cm": 10.5404409730, + "elements": [ + { + "symbol": "Au", + "Z": 79, + "A_g_mol": 196.9670000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_B": { + "name": "G4_B", + "density_g_cm3": 2.3700000000, + "radlen_cm": 22.2307454494, + "intlen_cm": 32.6544150557, + "elements": [ + { + "symbol": "B", + "Z": 5, + "A_g_mol": 10.8110164000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_B-100_BONE": { + "name": "G4_B-100_BONE", + "density_g_cm3": 1.4500000000, + "radlen_cm": 22.1470650946, + "intlen_cm": 55.3715324980, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0654709345 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5369444631 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0214999785 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.0320849679 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.1674108326 + }, + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.1765888234 + } + ] + }, + "G4_BAKELITE": { + "name": "G4_BAKELITE", + "density_g_cm3": 1.2500000000, + "radlen_cm": 33.3909731372, + "intlen_cm": 60.5624222030, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0574410000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7745910000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1679680000 + } + ] + }, + "G4_BARIUM_FLUORIDE": { + "name": "G4_BARIUM_FLUORIDE", + "density_g_cm3": 4.8900000000, + "radlen_cm": 2.0272211742, + "intlen_cm": 30.7133072542, + "elements": [ + { + "symbol": "Ba", + "Z": 56, + "A_g_mol": 137.3267993000, + "mass_fraction": 0.7832761810 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.2167238190 + } + ] + }, + "G4_BARIUM_SULFATE": { + "name": "G4_BARIUM_SULFATE", + "density_g_cm3": 4.5000000000, + "radlen_cm": 2.5872675325, + "intlen_cm": 29.2271363557, + "elements": [ + { + "symbol": "Ba", + "Z": 56, + "A_g_mol": 137.3267993000, + "mass_fraction": 0.5883993303 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.1373925574 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2742081123 + } + ] + }, + "G4_BENZENE": { + "name": "G4_BENZENE", + "density_g_cm3": 0.8786500000, + "radlen_cm": 49.8392488069, + "intlen_cm": 82.9395907198, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.9225773293 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0774226707 + } + ] + }, + "G4_BERYLLIUM_OXIDE": { + "name": "G4_BERYLLIUM_OXIDE", + "density_g_cm3": 3.0100000000, + "radlen_cm": 13.7223989677, + "intlen_cm": 27.2312420821, + "elements": [ + { + "symbol": "Be", + "Z": 4, + "A_g_mol": 9.0121800000, + "mass_fraction": 0.3603204378 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.6396795622 + } + ] + }, + "G4_BGO": { + "name": "G4_BGO", + "density_g_cm3": 7.1300000000, + "radlen_cm": 1.1180299951, + "intlen_cm": 22.7101337225, + "elements": [ + { + "symbol": "Bi", + "Z": 83, + "A_g_mol": 208.9800000000, + "mass_fraction": 0.6710168961 + }, + { + "symbol": "Ge", + "Z": 32, + "A_g_mol": 72.6127869100, + "mass_fraction": 0.1748650836 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1541180203 + } + ] + }, + "G4_BLOOD_ICRP": { + "name": "G4_BLOOD_ICRP", + "density_g_cm3": 1.0600000000, + "radlen_cm": 34.4916255606, + "intlen_cm": 71.4008796952, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1020000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1100000000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0330000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.7450000000 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.0030000000 + }, + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "Fe", + "Z": 26, + "A_g_mol": 55.8451107980, + "mass_fraction": 0.0010000000 + } + ] + }, + "G4_BONE_COMPACT_ICRU": { + "name": "G4_BONE_COMPACT_ICRU", + "density_g_cm3": 1.8500000000, + "radlen_cm": 16.4792529255, + "intlen_cm": 44.4244163422, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0640000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.2780000000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0270000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4100000000 + }, + { + "symbol": "Mg", + "Z": 12, + "A_g_mol": 24.3050157600, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.0700000000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.1470000000 + } + ] + }, + "G4_BONE_CORTICAL_ICRP": { + "name": "G4_BONE_CORTICAL_ICRP", + "density_g_cm3": 1.9200000000, + "radlen_cm": 14.0594998747, + "intlen_cm": 46.4710446539, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0340000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1550000000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0420000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4350000000 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "Mg", + "Z": 12, + "A_g_mol": 24.3050157600, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.1030000000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0030000000 + }, + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.2250000000 + } + ] + }, + "G4_BORON_CARBIDE": { + "name": "G4_BORON_CARBIDE", + "density_g_cm3": 2.5200000000, + "radlen_cm": 19.8956330875, + "intlen_cm": 30.9425465737, + "elements": [ + { + "symbol": "B", + "Z": 5, + "A_g_mol": 10.8110164000, + "mass_fraction": 0.7826299987 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.2173700013 + } + ] + }, + "G4_BORON_OXIDE": { + "name": "G4_BORON_OXIDE", + "density_g_cm3": 1.8120000000, + "radlen_cm": 21.2007815469, + "intlen_cm": 46.6495645684, + "elements": [ + { + "symbol": "B", + "Z": 5, + "A_g_mol": 10.8110164000, + "mass_fraction": 0.3105712358 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.6894287642 + } + ] + }, + "G4_BRAIN_ICRP": { + "name": "G4_BRAIN_ICRP", + "density_g_cm3": 1.0400000000, + "radlen_cm": 35.4025933979, + "intlen_cm": 72.1555402096, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1070000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1450000000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0220000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.7120000000 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.0040000000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.0030000000 + }, + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 0.0030000000 + } + ] + }, + "G4_BRASS": { + "name": "G4_BRASS", + "density_g_cm3": 8.5200000000, + "radlen_cm": 1.3674058172, + "intlen_cm": 16.9476882148, + "elements": [ + { + "symbol": "Cu", + "Z": 29, + "A_g_mol": 63.5456450600, + "mass_fraction": 0.5751304341 + }, + { + "symbol": "Zn", + "Z": 30, + "A_g_mol": 65.3955232900, + "mass_fraction": 0.3341218915 + }, + { + "symbol": "Pb", + "Z": 82, + "A_g_mol": 207.2170000000, + "mass_fraction": 0.0907476744 + } + ] + }, + "G4_BRONZE": { + "name": "G4_BRONZE", + "density_g_cm3": 8.8200000000, + "radlen_cm": 1.3674305230, + "intlen_cm": 16.1768594449, + "elements": [ + { + "symbol": "Cu", + "Z": 29, + "A_g_mol": 63.5456450600, + "mass_fraction": 0.8493676870 + }, + { + "symbol": "Zn", + "Z": 30, + "A_g_mol": 65.3955232900, + "mass_fraction": 0.0883914919 + }, + { + "symbol": "Pb", + "Z": 82, + "A_g_mol": 207.2170000000, + "mass_fraction": 0.0622408211 + } + ] + }, + "G4_BUTANE": { + "name": "G4_BUTANE", + "density_g_cm3": 0.0024934300, + "radlen_cm": 18139.0227601157, + "intlen_cm": 26268.9556216470, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8265829410 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1734170590 + } + ] + }, + "G4_Ba": { + "name": "G4_Ba", + "density_g_cm3": 3.5000000000, + "radlen_cm": 2.3733248313, + "intlen_cm": 51.5923290927, + "elements": [ + { + "symbol": "Ba", + "Z": 56, + "A_g_mol": 137.3267993000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Be": { + "name": "G4_Be", + "density_g_cm3": 1.8480000000, + "radlen_cm": 35.2759751356, + "intlen_cm": 39.4132938630, + "elements": [ + { + "symbol": "Be", + "Z": 4, + "A_g_mol": 9.0121800000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Bi": { + "name": "G4_Bi", + "density_g_cm3": 9.7470000000, + "radlen_cm": 0.6453882442, + "intlen_cm": 21.3091121330, + "elements": [ + { + "symbol": "Bi", + "Z": 83, + "A_g_mol": 208.9800000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Bk": { + "name": "G4_Bk", + "density_g_cm3": 14.0000000000, + "radlen_cm": 0.4064786913, + "intlen_cm": 15.6872462983, + "elements": [ + { + "symbol": "Bk", + "Z": 97, + "A_g_mol": 247.0700000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Br": { + "name": "G4_Br", + "density_g_cm3": 0.0070721000, + "radlen_cm": 1615.1154699324, + "intlen_cm": 21316.1276450533, + "elements": [ + { + "symbol": "Br", + "Z": 35, + "A_g_mol": 79.9035138000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_C": { + "name": "G4_C", + "density_g_cm3": 2.0000000000, + "radlen_cm": 21.3485184336, + "intlen_cm": 40.0769468390, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_C-552": { + "name": "G4_C-552", + "density_g_cm3": 1.7600000000, + "radlen_cm": 21.3755217174, + "intlen_cm": 47.2178616072, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0246800247 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5016105016 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.0045270045 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.4652094652 + }, + { + "symbol": "Si", + "Z": 14, + "A_g_mol": 28.0853614555, + "mass_fraction": 0.0039730040 + } + ] + }, + "G4_CADMIUM_TELLURIDE": { + "name": "G4_CADMIUM_TELLURIDE", + "density_g_cm3": 6.2000000000, + "radlen_cm": 1.4363029675, + "intlen_cm": 27.8572965803, + "elements": [ + { + "symbol": "Cd", + "Z": 48, + "A_g_mol": 112.4114464000, + "mass_fraction": 0.4683531856 + }, + { + "symbol": "Te", + "Z": 52, + "A_g_mol": 127.6028203000, + "mass_fraction": 0.5316468144 + } + ] + }, + "G4_CADMIUM_TUNGSTATE": { + "name": "G4_CADMIUM_TUNGSTATE", + "density_g_cm3": 7.9000000000, + "radlen_cm": 1.0975367504, + "intlen_cm": 19.6990488848, + "elements": [ + { + "symbol": "Cd", + "Z": 48, + "A_g_mol": 112.4114464000, + "mass_fraction": 0.3120367899 + }, + { + "symbol": "W", + "Z": 74, + "A_g_mol": 183.8416100000, + "mass_fraction": 0.5103158768 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1776473334 + } + ] + }, + "G4_CALCIUM_CARBONATE": { + "name": "G4_CALCIUM_CARBONATE", + "density_g_cm3": 2.8000000000, + "radlen_cm": 8.5806303005, + "intlen_cm": 34.7483442364, + "elements": [ + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.4004321837 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1200030341 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4795647823 + } + ] + }, + "G4_CALCIUM_FLUORIDE": { + "name": "G4_CALCIUM_FLUORIDE", + "density_g_cm3": 3.1800000000, + "radlen_cm": 6.7515445571, + "intlen_cm": 33.1126825134, + "elements": [ + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.5133284414 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.4866715586 + } + ] + }, + "G4_CALCIUM_OXIDE": { + "name": "G4_CALCIUM_OXIDE", + "density_g_cm3": 3.3000000000, + "radlen_cm": 5.7605472772, + "intlen_cm": 32.9311668963, + "elements": [ + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.7146910499 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2853089501 + } + ] + }, + "G4_CALCIUM_SULFATE": { + "name": "G4_CALCIUM_SULFATE", + "density_g_cm3": 2.9600000000, + "radlen_cm": 7.6700891493, + "intlen_cm": 34.1039310668, + "elements": [ + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.2943846699 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.2355348323 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4700804977 + } + ] + }, + "G4_CALCIUM_TUNGSTATE": { + "name": "G4_CALCIUM_TUNGSTATE", + "density_g_cm3": 6.0620000000, + "radlen_cm": 1.5061183605, + "intlen_cm": 23.9389476671, + "elements": [ + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.1391998505 + }, + { + "symbol": "W", + "Z": 74, + "A_g_mol": 183.8416100000, + "mass_fraction": 0.6385224915 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2222776580 + } + ] + }, + "G4_CARBON_DIOXIDE": { + "name": "G4_CARBON_DIOXIDE", + "density_g_cm3": 0.0018421200, + "radlen_cm": 19648.6261057218, + "intlen_cm": 46600.4101036886, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.2729122504 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.7270877496 + } + ] + }, + "G4_CARBON_TETRACHLORIDE": { + "name": "G4_CARBON_TETRACHLORIDE", + "density_g_cm3": 1.5940000000, + "radlen_cm": 12.6353035438, + "intlen_cm": 69.7653934353, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.0780825377 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.9219174623 + } + ] + }, + "G4_CELLULOSE_BUTYRATE": { + "name": "G4_CELLULOSE_BUTYRATE", + "density_g_cm3": 1.2000000000, + "radlen_cm": 33.1272771295, + "intlen_cm": 63.5368792659, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0671250000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5454030000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3874720000 + } + ] + }, + "G4_CELLULOSE_CELLOPHANE": { + "name": "G4_CELLULOSE_CELLOPHANE", + "density_g_cm3": 1.4200000000, + "radlen_cm": 27.2894013482, + "intlen_cm": 54.5257863934, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4444558564 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0621645440 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4933795996 + } + ] + }, + "G4_CELLULOSE_NITRATE": { + "name": "G4_CELLULOSE_NITRATE", + "density_g_cm3": 1.4900000000, + "radlen_cm": 24.9514919127, + "intlen_cm": 54.9526743102, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0292160000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.2712960000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1212760000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.5782120000 + } + ] + }, + "G4_CERIC_SULFATE": { + "name": "G4_CERIC_SULFATE", + "density_g_cm3": 1.0300000000, + "radlen_cm": 34.3245544164, + "intlen_cm": 73.8457863342, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1075960000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0008000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.8749760000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0146270000 + }, + { + "symbol": "Ce", + "Z": 58, + "A_g_mol": 140.1153107700, + "mass_fraction": 0.0020010000 + } + ] + }, + "G4_CESIUM_FLUORIDE": { + "name": "G4_CESIUM_FLUORIDE", + "density_g_cm3": 4.1150000000, + "radlen_cm": 2.2265267957, + "intlen_cm": 38.9591822729, + "elements": [ + { + "symbol": "Cs", + "Z": 55, + "A_g_mol": 132.9050000000, + "mass_fraction": 0.8749310417 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.1250689583 + } + ] + }, + "G4_CESIUM_IODIDE": { + "name": "G4_CESIUM_IODIDE", + "density_g_cm3": 4.5100000000, + "radlen_cm": 1.8602879809, + "intlen_cm": 39.3059850354, + "elements": [ + { + "symbol": "Cs", + "Z": 55, + "A_g_mol": 132.9050000000, + "mass_fraction": 0.5115488686 + }, + { + "symbol": "I", + "Z": 53, + "A_g_mol": 126.9040000000, + "mass_fraction": 0.4884511314 + } + ] + }, + "G4_CHLOROBENZENE": { + "name": "G4_CHLOROBENZENE", + "density_g_cm3": 1.1058000000, + "radlen_cm": 28.2213601504, + "intlen_cm": 75.3196655676, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6402499469 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0447748021 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.3149752510 + } + ] + }, + "G4_CHLOROFORM": { + "name": "G4_CHLOROFORM", + "density_g_cm3": 1.4832000000, + "radlen_cm": 13.8426852338, + "intlen_cm": 72.9258168862, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1006123208 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0084433839 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.8909442953 + } + ] + }, + "G4_CONCRETE": { + "name": "G4_CONCRETE", + "density_g_cm3": 2.3000000000, + "radlen_cm": 11.5527147841, + "intlen_cm": 41.2115550512, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0100000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.5291070000 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0160000000 + }, + { + "symbol": "Mg", + "Z": 12, + "A_g_mol": 24.3050157600, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "Al", + "Z": 13, + "A_g_mol": 26.9815000000, + "mass_fraction": 0.0338720000 + }, + { + "symbol": "Si", + "Z": 14, + "A_g_mol": 28.0853614555, + "mass_fraction": 0.3370210000 + }, + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 0.0130000000 + }, + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.0440000000 + }, + { + "symbol": "Fe", + "Z": 26, + "A_g_mol": 55.8451107980, + "mass_fraction": 0.0140000000 + } + ] + }, + "G4_CR39": { + "name": "G4_CR39", + "density_g_cm3": 1.3200000000, + "radlen_cm": 29.9630720578, + "intlen_cm": 57.9349397872, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0661505040 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5255046077 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4083448883 + } + ] + }, + "G4_CYCLOHEXANE": { + "name": "G4_CYCLOHEXANE", + "density_g_cm3": 0.7790000000, + "radlen_cm": 57.4759804747, + "intlen_cm": 86.7995841505, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8562817123 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1437182877 + } + ] + }, + "G4_CYTOSINE": { + "name": "G4_CYTOSINE", + "density_g_cm3": 1.3000000000, + "radlen_cm": 30.7578506265, + "intlen_cm": 60.0651401038, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0453609120 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4324206194 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.3782125956 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1440058730 + } + ] + }, + "G4_Ca": { + "name": "G4_Ca", + "density_g_cm3": 1.5500000000, + "radlen_cm": 10.4151095198, + "intlen_cm": 77.2749101845, + "elements": [ + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Cd": { + "name": "G4_Cd", + "density_g_cm3": 8.6500000000, + "radlen_cm": 1.0399387577, + "intlen_cm": 19.5278973588, + "elements": [ + { + "symbol": "Cd", + "Z": 48, + "A_g_mol": 112.4114464000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Ce": { + "name": "G4_Ce", + "density_g_cm3": 6.6570000000, + "radlen_cm": 1.1950616986, + "intlen_cm": 27.3076746781, + "elements": [ + { + "symbol": "Ce", + "Z": 58, + "A_g_mol": 140.1153107700, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Cf": { + "name": "G4_Cf", + "density_g_cm3": 10.0000000000, + "radlen_cm": 0.5683275438, + "intlen_cm": 22.0803245446, + "elements": [ + { + "symbol": "Cf", + "Z": 98, + "A_g_mol": 251.0800000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Cl": { + "name": "G4_Cl", + "density_g_cm3": 0.0029947300, + "radlen_cm": 6437.3408608729, + "intlen_cm": 38393.6729355327, + "elements": [ + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Cm": { + "name": "G4_Cm", + "density_g_cm3": 13.5100000000, + "radlen_cm": 0.4287060756, + "intlen_cm": 16.2562137806, + "elements": [ + { + "symbol": "Cm", + "Z": 96, + "A_g_mol": 247.0700000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Co": { + "name": "G4_Co", + "density_g_cm3": 8.9000000000, + "radlen_cm": 1.5300516989, + "intlen_cm": 15.3037576765, + "elements": [ + { + "symbol": "Co", + "Z": 27, + "A_g_mol": 58.9332000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Cr": { + "name": "G4_Cr", + "density_g_cm3": 7.1800000000, + "radlen_cm": 2.0814040144, + "intlen_cm": 18.1942423649, + "elements": [ + { + "symbol": "Cr", + "Z": 24, + "A_g_mol": 51.9961301370, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Cs": { + "name": "G4_Cs", + "density_g_cm3": 1.8730000000, + "radlen_cm": 4.4342020259, + "intlen_cm": 95.3624518046, + "elements": [ + { + "symbol": "Cs", + "Z": 55, + "A_g_mol": 132.9050000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Cu": { + "name": "G4_Cu", + "density_g_cm3": 8.9600000000, + "radlen_cm": 1.4355780238, + "intlen_cm": 15.5879379043, + "elements": [ + { + "symbol": "Cu", + "Z": 29, + "A_g_mol": 63.5456450600, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_DACRON": { + "name": "G4_DACRON", + "density_g_cm3": 1.4000000000, + "radlen_cm": 28.5364043256, + "intlen_cm": 55.9231513594, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6250108323 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0419607171 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3330284506 + } + ] + }, + "G4_DEOXYRIBOSE": { + "name": "G4_DEOXYRIBOSE", + "density_g_cm3": 1.5000000000, + "radlen_cm": 26.0277793263, + "intlen_cm": 50.7245404788, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0751461910 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4477252695 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4771285395 + } + ] + }, + "G4_DICHLORODIETHYL_ETHER": { + "name": "G4_DICHLORODIETHYL_ETHER", + "density_g_cm3": 1.2199000000, + "radlen_cm": 21.7159252335, + "intlen_cm": 72.0157030853, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.3359387920 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0563839532 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1118752364 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.4958020185 + } + ] + }, + "G4_DIETHYL_ETHER": { + "name": "G4_DIETHYL_ETHER", + "density_g_cm3": 0.7137800000, + "radlen_cm": 59.2587073492, + "intlen_cm": 97.1612602951, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6481626481 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1359844906 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2158528613 + } + ] + }, + "G4_DIMETHYL_SULFOXIDE": { + "name": "G4_DIMETHYL_SULFOXIDE", + "density_g_cm3": 1.1014000000, + "radlen_cm": 25.6056153635, + "intlen_cm": 75.2873092978, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.3074369871 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0774003171 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2047669780 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.4103957178 + } + ] + }, + "G4_DNA_ADENINE": { + "name": "G4_DNA_ADENINE", + "density_g_cm3": 1.0000000000, + "radlen_cm": 40.4701354302, + "intlen_cm": 79.1489834113, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0300610227 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4477631967 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.5221757806 + } + ] + }, + "G4_DNA_CYTOSINE": { + "name": "G4_DNA_CYTOSINE", + "density_g_cm3": 1.0000000000, + "radlen_cm": 39.8517602235, + "intlen_cm": 78.9747286021, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0366209616 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4363795341 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.3816752229 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1453242814 + } + ] + }, + "G4_DNA_DEOXYRIBOSE": { + "name": "G4_DNA_DEOXYRIBOSE", + "density_g_cm3": 1.0000000000, + "radlen_cm": 41.8531490771, + "intlen_cm": 73.4025938940, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0848959119 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7225923706 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1925117175 + } + ] + }, + "G4_DNA_GUANINE": { + "name": "G4_DNA_GUANINE", + "density_g_cm3": 1.0000000000, + "radlen_cm": 39.6999832548, + "intlen_cm": 80.0236241154, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0268571707 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4000413663 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.4665231851 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1065782779 + } + ] + }, + "G4_DNA_PHOSPHATE": { + "name": "G4_DNA_PHOSPHATE", + "density_g_cm3": 1.0000000000, + "radlen_cm": 28.5212126943, + "intlen_cm": 94.2697781537, + "elements": [ + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.3261383165 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.6738616835 + } + ] + }, + "G4_DNA_THYMINE": { + "name": "G4_DNA_THYMINE", + "density_g_cm3": 1.0000000000, + "radlen_cm": 39.6095818095, + "intlen_cm": 78.7777563069, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0402835649 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4800235304 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.2239189496 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2557739551 + } + ] + }, + "G4_DNA_URACIL": { + "name": "G4_DNA_URACIL", + "density_g_cm3": 1.0000000000, + "radlen_cm": 39.0409633092, + "intlen_cm": 80.4546879807, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0272222464 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4325111686 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.2521945291 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2880720559 + } + ] + }, + "G4_Dy": { + "name": "G4_Dy", + "density_g_cm3": 8.5500000000, + "radlen_cm": 0.8561401252, + "intlen_cm": 22.3383201875, + "elements": [ + { + "symbol": "Dy", + "Z": 66, + "A_g_mol": 162.4971100000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_ETHANE": { + "name": "G4_ETHANE", + "density_g_cm3": 0.0012532400, + "radlen_cm": 36434.2860647397, + "intlen_cm": 50781.0189376312, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7988752227 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.2011247773 + } + ] + }, + "G4_ETHYLENE": { + "name": "G4_ETHYLENE", + "density_g_cm3": 0.0011749700, + "radlen_cm": 38106.3250889615, + "intlen_cm": 57547.7467962794, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8562817123 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1437182877 + } + ] + }, + "G4_ETHYL_ALCOHOL": { + "name": "G4_ETHYL_ALCOHOL", + "density_g_cm3": 0.7893000000, + "radlen_cm": 51.8429704156, + "intlen_cm": 89.2594985493, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5214293661 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1312750254 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3472956085 + } + ] + }, + "G4_ETHYL_CELLULOSE": { + "name": "G4_ETHYL_CELLULOSE", + "density_g_cm3": 1.1300000000, + "radlen_cm": 35.9450301822, + "intlen_cm": 65.2831880981, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0900270000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5851820000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3247910000 + } + ] + }, + "G4_EYE_LENS_ICRP": { + "name": "G4_EYE_LENS_ICRP", + "density_g_cm3": 1.0700000000, + "radlen_cm": 34.9413412839, + "intlen_cm": 70.6360062417, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0960000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1950000000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0570000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.6460000000 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0030000000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.0010000000 + } + ] + }, + "G4_Er": { + "name": "G4_Er", + "density_g_cm3": 9.0660000000, + "radlen_cm": 0.7880939310, + "intlen_cm": 21.2705940900, + "elements": [ + { + "symbol": "Er", + "Z": 68, + "A_g_mol": 167.2560232000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Eu": { + "name": "G4_Eu", + "density_g_cm3": 5.2430000000, + "radlen_cm": 1.4186770984, + "intlen_cm": 35.6234054321, + "elements": [ + { + "symbol": "Eu", + "Z": 63, + "A_g_mol": 151.9643219000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_F": { + "name": "G4_F", + "density_g_cm3": 0.0015802900, + "radlen_cm": 20838.1744350084, + "intlen_cm": 59097.6615288435, + "elements": [ + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_FERRIC_OXIDE": { + "name": "G4_FERRIC_OXIDE", + "density_g_cm3": 5.2000000000, + "radlen_cm": 3.2418173343, + "intlen_cm": 22.2675201920, + "elements": [ + { + "symbol": "Fe", + "Z": 26, + "A_g_mol": 55.8451107980, + "mass_fraction": 0.6994260486 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3005739514 + } + ] + }, + "G4_FERROBORIDE": { + "name": "G4_FERROBORIDE", + "density_g_cm3": 7.1500000000, + "radlen_cm": 2.1983578257, + "intlen_cm": 16.7331909572, + "elements": [ + { + "symbol": "Fe", + "Z": 26, + "A_g_mol": 55.8451107980, + "mass_fraction": 0.8378091129 + }, + { + "symbol": "B", + "Z": 5, + "A_g_mol": 10.8110164000, + "mass_fraction": 0.1621908871 + } + ] + }, + "G4_FERROUS_OXIDE": { + "name": "G4_FERROUS_OXIDE", + "density_g_cm3": 5.7000000000, + "radlen_cm": 2.7992227484, + "intlen_cm": 21.0475937407, + "elements": [ + { + "symbol": "Fe", + "Z": 26, + "A_g_mol": 55.8451107980, + "mass_fraction": 0.7773052893 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2226947107 + } + ] + }, + "G4_FERROUS_SULFATE": { + "name": "G4_FERROUS_SULFATE", + "density_g_cm3": 1.0240000000, + "radlen_cm": 34.8125553802, + "intlen_cm": 74.1303341800, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1082590000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0000270000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.8786360000 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0000220000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0129680000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.0000340000 + }, + { + "symbol": "Fe", + "Z": 26, + "A_g_mol": 55.8451107980, + "mass_fraction": 0.0000540000 + } + ] + }, + "G4_FREON-12": { + "name": "G4_FREON-12", + "density_g_cm3": 1.1200000000, + "radlen_cm": 21.1136459442, + "intlen_cm": 92.0056500582, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.0993350000 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.3142470000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.5864180000 + } + ] + }, + "G4_FREON-12B2": { + "name": "G4_FREON-12B2", + "density_g_cm3": 1.8000000000, + "radlen_cm": 7.5563082797, + "intlen_cm": 72.0960738549, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.0572450000 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.1810960000 + }, + { + "symbol": "Br", + "Z": 35, + "A_g_mol": 79.9035138000, + "mass_fraction": 0.7616590000 + } + ] + }, + "G4_FREON-13": { + "name": "G4_FREON-13", + "density_g_cm3": 0.9500000000, + "radlen_cm": 28.5519879814, + "intlen_cm": 102.9101489011, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1149828850 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.5456214544 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.3393956606 + } + ] + }, + "G4_FREON-13B1": { + "name": "G4_FREON-13B1", + "density_g_cm3": 1.5000000000, + "radlen_cm": 11.0211416256, + "intlen_cm": 76.9456788799, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.0806579862 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.3827507249 + }, + { + "symbol": "Br", + "Z": 35, + "A_g_mol": 79.9035138000, + "mass_fraction": 0.5365912889 + } + ] + }, + "G4_FREON-13I1": { + "name": "G4_FREON-13I1", + "density_g_cm3": 1.8000000000, + "radlen_cm": 6.4111664137, + "intlen_cm": 73.4578642805, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.0613090000 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.2909240000 + }, + { + "symbol": "I", + "Z": 53, + "A_g_mol": 126.9040000000, + "mass_fraction": 0.6477670000 + } + ] + }, + "G4_Fe": { + "name": "G4_Fe", + "density_g_cm3": 7.8740000000, + "radlen_cm": 1.7574934651, + "intlen_cm": 16.9903002759, + "elements": [ + { + "symbol": "Fe", + "Z": 26, + "A_g_mol": 55.8451107980, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Fr": { + "name": "G4_Fr", + "density_g_cm3": 1.0000000000, + "radlen_cm": 6.1882573776, + "intlen_cm": 212.2508067736, + "elements": [ + { + "symbol": "Fr", + "Z": 87, + "A_g_mol": 223.0200000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_GADOLINIUM_OXYSULFIDE": { + "name": "G4_GADOLINIUM_OXYSULFIDE", + "density_g_cm3": 7.4400000000, + "radlen_cm": 1.1407035079, + "intlen_cm": 21.9702398250, + "elements": [ + { + "symbol": "Gd", + "Z": 64, + "A_g_mol": 157.2521250000, + "mass_fraction": 0.8307709545 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.0845255913 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0847034542 + } + ] + }, + "G4_GALLIUM_ARSENIDE": { + "name": "G4_GALLIUM_ARSENIDE", + "density_g_cm3": 5.3100000000, + "radlen_cm": 2.2959768793, + "intlen_cm": 27.4658727793, + "elements": [ + { + "symbol": "Ga", + "Z": 31, + "A_g_mol": 69.7230809720, + "mass_fraction": 0.4820300374 + }, + { + "symbol": "As", + "Z": 33, + "A_g_mol": 74.9216000000, + "mass_fraction": 0.5179699626 + } + ] + }, + "G4_GEL_PHOTO_EMULSION": { + "name": "G4_GEL_PHOTO_EMULSION", + "density_g_cm3": 1.2914000000, + "radlen_cm": 30.2058086009, + "intlen_cm": 58.4748172458, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0811800000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4160600000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1112400000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3806400000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0108800000 + } + ] + }, + "G4_GLASS_LEAD": { + "name": "G4_GLASS_LEAD", + "density_g_cm3": 6.2200000000, + "radlen_cm": 1.2655477423, + "intlen_cm": 25.7388388952, + "elements": [ + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1564530000 + }, + { + "symbol": "Si", + "Z": 14, + "A_g_mol": 28.0853614555, + "mass_fraction": 0.0808660000 + }, + { + "symbol": "Ti", + "Z": 22, + "A_g_mol": 47.8667173300, + "mass_fraction": 0.0080920000 + }, + { + "symbol": "As", + "Z": 33, + "A_g_mol": 74.9216000000, + "mass_fraction": 0.0026510000 + }, + { + "symbol": "Pb", + "Z": 82, + "A_g_mol": 207.2170000000, + "mass_fraction": 0.7519380000 + } + ] + }, + "G4_GLASS_PLATE": { + "name": "G4_GLASS_PLATE", + "density_g_cm3": 2.4000000000, + "radlen_cm": 10.6921640656, + "intlen_cm": 40.6857797379, + "elements": [ + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4598004598 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0964410964 + }, + { + "symbol": "Si", + "Z": 14, + "A_g_mol": 28.0853614555, + "mass_fraction": 0.3365533366 + }, + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.1072051072 + } + ] + }, + "G4_GLUTAMINE": { + "name": "G4_GLUTAMINE", + "density_g_cm3": 1.4600000000, + "radlen_cm": 27.0121584298, + "intlen_cm": 52.3124063449, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4109190507 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0689686368 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1916834413 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3284288712 + } + ] + }, + "G4_GLYCEROL": { + "name": "G4_GLYCEROL", + "density_g_cm3": 1.2613000000, + "radlen_cm": 30.7600170394, + "intlen_cm": 59.6449295333, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.3912550846 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0875576500 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.5211872654 + } + ] + }, + "G4_GRAPHITE": { + "name": "G4_GRAPHITE", + "density_g_cm3": 2.2100000000, + "radlen_cm": 19.3199261842, + "intlen_cm": 36.2687301711, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_GRAPHITE_POROUS": { + "name": "G4_GRAPHITE_POROUS", + "density_g_cm3": 1.7000000000, + "radlen_cm": 25.1159040395, + "intlen_cm": 47.1493492224, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_GUANINE": { + "name": "G4_GUANINE", + "density_g_cm3": 1.5800000000, + "radlen_cm": 25.1887769514, + "intlen_cm": 50.2170220603, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.3973732857 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0333475581 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.4634117033 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1058674529 + } + ] + }, + "G4_GYPSUM": { + "name": "G4_GYPSUM", + "density_g_cm3": 2.3200000000, + "radlen_cm": 10.6092208180, + "intlen_cm": 40.6273547785, + "elements": [ + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.2327786930 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.1862443803 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.5575598954 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0234170314 + } + ] + }, + "G4_Ga": { + "name": "G4_Ga", + "density_g_cm3": 5.9040000000, + "radlen_cm": 2.1127975858, + "intlen_cm": 24.3994809094, + "elements": [ + { + "symbol": "Ga", + "Z": 31, + "A_g_mol": 69.7230809720, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Galactic": { + "name": "G4_Galactic", + "density_g_cm3": 0.0000000000, + "radlen_cm": 630435090422683690204135424.0000000000, + "intlen_cm": 350000028082484913811488768.0000000000, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Gd": { + "name": "G4_Gd", + "density_g_cm3": 7.9004000000, + "radlen_cm": 0.9472083827, + "intlen_cm": 23.9121066950, + "elements": [ + { + "symbol": "Gd", + "Z": 64, + "A_g_mol": 157.2521250000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Ge": { + "name": "G4_Ge", + "density_g_cm3": 5.3230000000, + "radlen_cm": 2.3012998808, + "intlen_cm": 27.4314847558, + "elements": [ + { + "symbol": "Ge", + "Z": 32, + "A_g_mol": 72.6127869100, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_H": { + "name": "G4_H", + "density_g_cm3": 0.0000837480, + "radlen_cm": 752776.2936699188, + "intlen_cm": 417920.4614826443, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_He": { + "name": "G4_He", + "density_g_cm3": 0.0001663220, + "radlen_cm": 567113.1420929121, + "intlen_cm": 334118.5985088379, + "elements": [ + { + "symbol": "He", + "Z": 2, + "A_g_mol": 4.0026425851, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Hf": { + "name": "G4_Hf", + "density_g_cm3": 13.3100000000, + "radlen_cm": 0.5177172521, + "intlen_cm": 14.8055339056, + "elements": [ + { + "symbol": "Hf", + "Z": 72, + "A_g_mol": 178.4851746000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Hg": { + "name": "G4_Hg", + "density_g_cm3": 13.5460000000, + "radlen_cm": 0.4752411427, + "intlen_cm": 15.1251608352, + "elements": [ + { + "symbol": "Hg", + "Z": 80, + "A_g_mol": 200.5991002000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Ho": { + "name": "G4_Ho", + "density_g_cm3": 8.7950000000, + "radlen_cm": 0.8224469594, + "intlen_cm": 21.8238878765, + "elements": [ + { + "symbol": "Ho", + "Z": 67, + "A_g_mol": 164.9300000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_I": { + "name": "G4_I", + "density_g_cm3": 4.9300000000, + "radlen_cm": 1.7201640735, + "intlen_cm": 35.6762827299, + "elements": [ + { + "symbol": "I", + "Z": 53, + "A_g_mol": 126.9040000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_In": { + "name": "G4_In", + "density_g_cm3": 7.3100000000, + "radlen_cm": 1.2105450732, + "intlen_cm": 23.2713161873, + "elements": [ + { + "symbol": "In", + "Z": 49, + "A_g_mol": 114.8182000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Ir": { + "name": "G4_Ir", + "density_g_cm3": 22.4200000000, + "radlen_cm": 0.2941415950, + "intlen_cm": 9.0093994089, + "elements": [ + { + "symbol": "Ir", + "Z": 77, + "A_g_mol": 192.2162540000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_K": { + "name": "G4_K", + "density_g_cm3": 0.8620000000, + "radlen_cm": 20.0870675609, + "intlen_cm": 137.8097927497, + "elements": [ + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_KAPTON": { + "name": "G4_KAPTON", + "density_g_cm3": 1.4200000000, + "radlen_cm": 28.5747754063, + "intlen_cm": 55.8169092105, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6911278143 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0263633782 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0732713202 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2092374873 + } + ] + }, + "G4_KEVLAR": { + "name": "G4_KEVLAR", + "density_g_cm3": 1.4400000000, + "radlen_cm": 28.6728455313, + "intlen_cm": 53.7041703894, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7057961409 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0423074270 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1343120694 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1175843627 + } + ] + }, + "G4_Kr": { + "name": "G4_Kr", + "density_g_cm3": 0.0034783200, + "radlen_cm": 3269.4392743928, + "intlen_cm": 44033.0436533239, + "elements": [ + { + "symbol": "Kr", + "Z": 36, + "A_g_mol": 83.7993175100, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_LANTHANUM_OXYBROMIDE": { + "name": "G4_LANTHANUM_OXYBROMIDE", + "density_g_cm3": 6.2800000000, + "radlen_cm": 1.5241532522, + "intlen_cm": 25.3014351184, + "elements": [ + { + "symbol": "La", + "Z": 57, + "A_g_mol": 138.9051009000, + "mass_fraction": 0.5915688472 + }, + { + "symbol": "Br", + "Z": 35, + "A_g_mol": 79.9035138000, + "mass_fraction": 0.3402929715 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.0681381812 + } + ] + }, + "G4_LANTHANUM_OXYSULFIDE": { + "name": "G4_LANTHANUM_OXYSULFIDE", + "density_g_cm3": 5.8600000000, + "radlen_cm": 1.5889258344, + "intlen_cm": 26.7145523078, + "elements": [ + { + "symbol": "La", + "Z": 57, + "A_g_mol": 138.9051009000, + "mass_fraction": 0.8126073070 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.0935978698 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0937948232 + } + ] + }, + "G4_LEAD_OXIDE": { + "name": "G4_LEAD_OXIDE", + "density_g_cm3": 9.5300000000, + "radlen_cm": 0.7098556946, + "intlen_cm": 19.8173808964, + "elements": [ + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.0716820000 + }, + { + "symbol": "Pb", + "Z": 82, + "A_g_mol": 207.2170000000, + "mass_fraction": 0.9283180000 + } + ] + }, + "G4_LITHIUM_AMIDE": { + "name": "G4_LITHIUM_AMIDE", + "density_g_cm3": 1.1780000000, + "radlen_cm": 40.2298585529, + "intlen_cm": 59.5078880837, + "elements": [ + { + "symbol": "Li", + "Z": 3, + "A_g_mol": 6.9400332080, + "mass_fraction": 0.3022309285 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.6099796156 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0877894559 + } + ] + }, + "G4_LITHIUM_CARBONATE": { + "name": "G4_LITHIUM_CARBONATE", + "density_g_cm3": 2.1100000000, + "radlen_cm": 18.9197918579, + "intlen_cm": 38.8235211926, + "elements": [ + { + "symbol": "Li", + "Z": 3, + "A_g_mol": 6.9400332080, + "mass_fraction": 0.1878503065 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1625511321 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.6495985614 + } + ] + }, + "G4_LITHIUM_FLUORIDE": { + "name": "G4_LITHIUM_FLUORIDE", + "density_g_cm3": 2.6350000000, + "radlen_cm": 14.8973607769, + "intlen_cm": 32.0247499094, + "elements": [ + { + "symbol": "Li", + "Z": 3, + "A_g_mol": 6.9400332080, + "mass_fraction": 0.2675579189 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.7324420811 + } + ] + }, + "G4_LITHIUM_HYDRIDE": { + "name": "G4_LITHIUM_HYDRIDE", + "density_g_cm3": 0.8200000000, + "radlen_cm": 97.0850620364, + "intlen_cm": 73.0132624194, + "elements": [ + { + "symbol": "Li", + "Z": 3, + "A_g_mol": 6.9400332080, + "mass_fraction": 0.8731826806 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1268173194 + } + ] + }, + "G4_LITHIUM_IODIDE": { + "name": "G4_LITHIUM_IODIDE", + "density_g_cm3": 3.4940000000, + "radlen_cm": 2.5456045592, + "intlen_cm": 46.4058302608, + "elements": [ + { + "symbol": "Li", + "Z": 3, + "A_g_mol": 6.9400332080, + "mass_fraction": 0.0518516443 + }, + { + "symbol": "I", + "Z": 53, + "A_g_mol": 126.9040000000, + "mass_fraction": 0.9481483557 + } + ] + }, + "G4_LITHIUM_OXIDE": { + "name": "G4_LITHIUM_OXIDE", + "density_g_cm3": 2.0130000000, + "radlen_cm": 23.3753863602, + "intlen_cm": 38.1260963105, + "elements": [ + { + "symbol": "Li", + "Z": 3, + "A_g_mol": 6.9400332080, + "mass_fraction": 0.4645354330 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.5354645670 + } + ] + }, + "G4_LITHIUM_TETRABORATE": { + "name": "G4_LITHIUM_TETRABORATE", + "density_g_cm3": 2.4400000000, + "radlen_cm": 16.2719894171, + "intlen_cm": 34.0334121576, + "elements": [ + { + "symbol": "Li", + "Z": 3, + "A_g_mol": 6.9400332080, + "mass_fraction": 0.0820723599 + }, + { + "symbol": "B", + "Z": 5, + "A_g_mol": 10.8110164000, + "mass_fraction": 0.2557006868 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.6622269533 + } + ] + }, + "G4_LUCITE": { + "name": "G4_LUCITE", + "density_g_cm3": 1.1900000000, + "radlen_cm": 34.0748652335, + "intlen_cm": 62.6704780225, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0805380000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5998480000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3196140000 + } + ] + }, + "G4_LUNG_ICRP": { + "name": "G4_LUNG_ICRP", + "density_g_cm3": 1.0400000000, + "radlen_cm": 35.0156133645, + "intlen_cm": 72.6726342239, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1050000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.0830000000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0230000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.7790000000 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.0030000000 + }, + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 0.0020000000 + } + ] + }, + "G4_La": { + "name": "G4_La", + "density_g_cm3": 6.1540000000, + "radlen_cm": 1.3223836276, + "intlen_cm": 29.4543867747, + "elements": [ + { + "symbol": "La", + "Z": 57, + "A_g_mol": 138.9051009000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Li": { + "name": "G4_Li", + "density_g_cm3": 0.5340000000, + "radlen_cm": 154.9972904774, + "intlen_cm": 125.0203388568, + "elements": [ + { + "symbol": "Li", + "Z": 3, + "A_g_mol": 6.9400332080, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Lu": { + "name": "G4_Lu", + "density_g_cm3": 9.8400000000, + "radlen_cm": 0.7036514007, + "intlen_cm": 19.8941317320, + "elements": [ + { + "symbol": "Lu", + "Z": 71, + "A_g_mol": 174.9669518000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_M3_WAX": { + "name": "G4_M3_WAX", + "density_g_cm3": 1.0500000000, + "radlen_cm": 37.4523271935, + "intlen_cm": 68.7782440198, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1143181143 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6558236558 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.0921830922 + }, + { + "symbol": "Mg", + "Z": 12, + "A_g_mol": 24.3050157600, + "mass_fraction": 0.1347921348 + }, + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.0028830029 + } + ] + }, + "G4_MAGNESIUM_CARBONATE": { + "name": "G4_MAGNESIUM_CARBONATE", + "density_g_cm3": 2.9580000000, + "radlen_cm": 10.7392076017, + "intlen_cm": 30.5238324080, + "elements": [ + { + "symbol": "Mg", + "Z": 12, + "A_g_mol": 24.3050157600, + "mass_fraction": 0.2882681150 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1424525855 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.5692792995 + } + ] + }, + "G4_MAGNESIUM_FLUORIDE": { + "name": "G4_MAGNESIUM_FLUORIDE", + "density_g_cm3": 3.0000000000, + "radlen_cm": 9.7736131065, + "intlen_cm": 32.1181946756, + "elements": [ + { + "symbol": "Mg", + "Z": 12, + "A_g_mol": 24.3050157600, + "mass_fraction": 0.3901172937 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.6098827063 + } + ] + }, + "G4_MAGNESIUM_OXIDE": { + "name": "G4_MAGNESIUM_OXIDE", + "density_g_cm3": 3.5800000000, + "radlen_cm": 7.8275822876, + "intlen_cm": 26.7323051305, + "elements": [ + { + "symbol": "Mg", + "Z": 12, + "A_g_mol": 24.3050157600, + "mass_fraction": 0.6030361955 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3969638045 + } + ] + }, + "G4_MAGNESIUM_TETRABORATE": { + "name": "G4_MAGNESIUM_TETRABORATE", + "density_g_cm3": 2.5300000000, + "radlen_cm": 14.0171151884, + "intlen_cm": 34.3098315005, + "elements": [ + { + "symbol": "Mg", + "Z": 12, + "A_g_mol": 24.3050157600, + "mass_fraction": 0.1353701908 + }, + { + "symbol": "B", + "Z": 5, + "A_g_mol": 10.8110164000, + "mass_fraction": 0.2408538825 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.6237759267 + } + ] + }, + "G4_MERCURIC_IODIDE": { + "name": "G4_MERCURIC_IODIDE", + "density_g_cm3": 6.3600000000, + "radlen_cm": 1.1695626925, + "intlen_cm": 29.4979745172, + "elements": [ + { + "symbol": "Hg", + "Z": 80, + "A_g_mol": 200.5991002000, + "mass_fraction": 0.4414523895 + }, + { + "symbol": "I", + "Z": 53, + "A_g_mol": 126.9040000000, + "mass_fraction": 0.5585476105 + } + ] + }, + "G4_METHANE": { + "name": "G4_METHANE", + "density_g_cm3": 0.0006671510, + "radlen_cm": 69648.1895684307, + "intlen_cm": 90727.2666787782, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7486823647 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.2513176353 + } + ] + }, + "G4_METHANOL": { + "name": "G4_METHANOL", + "density_g_cm3": 0.7914000000, + "radlen_cm": 49.8277776201, + "intlen_cm": 90.6875121309, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.3748448189 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1258278783 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4993273028 + } + ] + }, + "G4_MIX_D_WAX": { + "name": "G4_MIX_D_WAX", + "density_g_cm3": 0.9900000000, + "radlen_cm": 42.4388764028, + "intlen_cm": 70.0170389774, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1340400000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7779600000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.0350200000 + }, + { + "symbol": "Mg", + "Z": 12, + "A_g_mol": 24.3050157600, + "mass_fraction": 0.0385940000 + }, + { + "symbol": "Ti", + "Z": 22, + "A_g_mol": 47.8667173300, + "mass_fraction": 0.0143860000 + } + ] + }, + "G4_MS20_TISSUE": { + "name": "G4_MS20_TISSUE", + "density_g_cm3": 1.0000000000, + "radlen_cm": 38.2901787990, + "intlen_cm": 75.6659085615, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0811920000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5834420000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0177980000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1863810000 + }, + { + "symbol": "Mg", + "Z": 12, + "A_g_mol": 24.3050157600, + "mass_fraction": 0.1302870000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.0009000000 + } + ] + }, + "G4_MUSCLE_SKELETAL_ICRP": { + "name": "G4_MUSCLE_SKELETAL_ICRP", + "density_g_cm3": 1.0500000000, + "radlen_cm": 35.0573564270, + "intlen_cm": 71.8808850701, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1020000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1430000000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0340000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.7100000000 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0030000000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 0.0040000000 + } + ] + }, + "G4_MUSCLE_STRIATED_ICRU": { + "name": "G4_MUSCLE_STRIATED_ICRU", + "density_g_cm3": 1.0400000000, + "radlen_cm": 35.2882455673, + "intlen_cm": 72.6659485329, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1021021021 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1231231231 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0350350350 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.7297297297 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0010010010 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.0020020020 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0040040040 + }, + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 0.0030030030 + } + ] + }, + "G4_MUSCLE_WITHOUT_SUCROSE": { + "name": "G4_MUSCLE_WITHOUT_SUCROSE", + "density_g_cm3": 1.0700000000, + "radlen_cm": 34.5507134115, + "intlen_cm": 70.5306459594, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1019690000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1200580000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0354510000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.7425220000 + } + ] + }, + "G4_MUSCLE_WITH_SUCROSE": { + "name": "G4_MUSCLE_WITH_SUCROSE", + "density_g_cm3": 1.1100000000, + "radlen_cm": 33.5030358691, + "intlen_cm": 68.1084691034, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0982340982 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1562141562 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0354510355 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.7101007101 + } + ] + }, + "G4_MYLAR": { + "name": "G4_MYLAR", + "density_g_cm3": 1.4000000000, + "radlen_cm": 28.5364043256, + "intlen_cm": 55.9231513594, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6250108323 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0419607171 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3330284506 + } + ] + }, + "G4_Mg": { + "name": "G4_Mg", + "density_g_cm3": 1.7400000000, + "radlen_cm": 14.3859171086, + "intlen_cm": 58.2663034870, + "elements": [ + { + "symbol": "Mg", + "Z": 12, + "A_g_mol": 24.3050157600, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Mn": { + "name": "G4_Mn", + "density_g_cm3": 7.4400000000, + "radlen_cm": 1.9677221865, + "intlen_cm": 17.8835098670, + "elements": [ + { + "symbol": "Mn", + "Z": 25, + "A_g_mol": 54.9380000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Mo": { + "name": "G4_Mo", + "density_g_cm3": 10.2200000000, + "radlen_cm": 0.9591074077, + "intlen_cm": 15.6772760730, + "elements": [ + { + "symbol": "Mo", + "Z": 42, + "A_g_mol": 95.9312864600, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_N": { + "name": "G4_N", + "density_g_cm3": 0.0011652000, + "radlen_cm": 32602.2350168044, + "intlen_cm": 72406.9506998844, + "elements": [ + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_N,N-DIMETHYL_FORMAMIDE": { + "name": "G4_N,N-DIMETHYL_FORMAMIDE", + "density_g_cm3": 0.9487000000, + "radlen_cm": 42.9986906208, + "intlen_cm": 77.1577428431, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4929574510 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0965276183 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1916269163 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2188880144 + } + ] + }, + "G4_N-BUTYL_ALCOHOL": { + "name": "G4_N-BUTYL_ALCOHOL", + "density_g_cm3": 0.8098000000, + "radlen_cm": 52.2322550404, + "intlen_cm": 85.6406080185, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6481626481 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1359844906 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2158528613 + } + ] + }, + "G4_N-HEPTANE": { + "name": "G4_N-HEPTANE", + "density_g_cm3": 0.6837600000, + "radlen_cm": 65.8657382833, + "intlen_cm": 97.0698845209, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8390549213 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1609450787 + } + ] + }, + "G4_N-HEXANE": { + "name": "G4_N-HEXANE", + "density_g_cm3": 0.6603000000, + "radlen_cm": 68.2710686267, + "intlen_cm": 100.2185093356, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8362509531 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1637490469 + } + ] + }, + "G4_N-PENTANE": { + "name": "G4_N-PENTANE", + "density_g_cm3": 0.6262000000, + "radlen_cm": 72.0844513772, + "intlen_cm": 105.2394466274, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8323567353 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1676432647 + } + ] + }, + "G4_N-PROPYL_ALCOHOL": { + "name": "G4_N-PROPYL_ALCOHOL", + "density_g_cm3": 0.8035000000, + "radlen_cm": 51.9709503464, + "intlen_cm": 86.8320985664, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5995862193 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1341793689 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2662344118 + } + ] + }, + "G4_NAPHTHALENE": { + "name": "G4_NAPHTHALENE", + "density_g_cm3": 1.1450000000, + "radlen_cm": 38.0628209006, + "intlen_cm": 64.7481877565, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.9370876957 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0629123043 + } + ] + }, + "G4_NEOPRENE": { + "name": "G4_NEOPRENE", + "density_g_cm3": 1.2300000000, + "radlen_cm": 23.6452775257, + "intlen_cm": 68.4404103466, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5426421718 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0569231500 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.4004346781 + } + ] + }, + "G4_NITROBENZENE": { + "name": "G4_NITROBENZENE", + "density_g_cm3": 1.1986700000, + "radlen_cm": 33.4429484913, + "intlen_cm": 65.3377994376, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5853676418 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0409367005 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1137747242 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2599209335 + } + ] + }, + "G4_NITROUS_OXIDE": { + "name": "G4_NITROUS_OXIDE", + "density_g_cm3": 0.0018309400, + "radlen_cm": 19953.4404326249, + "intlen_cm": 46817.4565215888, + "elements": [ + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.6364843009 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3635156991 + } + ] + }, + "G4_NYLON-11_RILSAN": { + "name": "G4_NYLON-11_RILSAN", + "density_g_cm3": 1.4250000000, + "radlen_cm": 30.1506780146, + "intlen_cm": 49.4620432566, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1154758845 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7208182792 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0764169236 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.0872889127 + } + ] + }, + "G4_NYLON-6-10": { + "name": "G4_NYLON-6-10", + "density_g_cm3": 1.1400000000, + "radlen_cm": 37.2399939034, + "intlen_cm": 62.6184623928, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1070620000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6804490000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0991890000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1133000000 + } + ] + }, + "G4_NYLON-6-6": { + "name": "G4_NYLON-6-6", + "density_g_cm3": 1.1400000000, + "radlen_cm": 36.7677016597, + "intlen_cm": 63.4952151640, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6368481720 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0979811903 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1237807148 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1413899228 + } + ] + }, + "G4_NYLON-8062": { + "name": "G4_NYLON-8062", + "density_g_cm3": 1.0800000000, + "radlen_cm": 38.9258725316, + "intlen_cm": 66.5604267341, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1035091035 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6484156484 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0995360995 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1485391485 + } + ] + }, + "G4_Na": { + "name": "G4_Na", + "density_g_cm3": 0.9710000000, + "radlen_cm": 28.5646359402, + "intlen_cm": 102.4929311883, + "elements": [ + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Nb": { + "name": "G4_Nb", + "density_g_cm3": 8.5700000000, + "radlen_cm": 1.1578315182, + "intlen_cm": 18.4970498911, + "elements": [ + { + "symbol": "Nb", + "Z": 41, + "A_g_mol": 92.9064000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Nd": { + "name": "G4_Nd", + "density_g_cm3": 6.9000000000, + "radlen_cm": 1.1166740406, + "intlen_cm": 26.6017647572, + "elements": [ + { + "symbol": "Nd", + "Z": 60, + "A_g_mol": 144.2362360000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Ne": { + "name": "G4_Ne", + "density_g_cm3": 0.0008385050, + "radlen_cm": 34504.7957012515, + "intlen_cm": 113641.3080885588, + "elements": [ + { + "symbol": "Ne", + "Z": 10, + "A_g_mol": 20.1800112800, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Ni": { + "name": "G4_Ni", + "density_g_cm3": 8.9020000000, + "radlen_cm": 1.4242208745, + "intlen_cm": 15.2795322887, + "elements": [ + { + "symbol": "Ni", + "Z": 28, + "A_g_mol": 58.6933251009, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Np": { + "name": "G4_Np", + "density_g_cm3": 20.2500000000, + "radlen_cm": 0.2896763497, + "intlen_cm": 10.6968313874, + "elements": [ + { + "symbol": "Np", + "Z": 93, + "A_g_mol": 237.0480000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_O": { + "name": "G4_O", + "density_g_cm3": 0.0013315100, + "radlen_cm": 25713.7634595345, + "intlen_cm": 66235.5975584616, + "elements": [ + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_OCTADECANOL": { + "name": "G4_OCTADECANOL", + "density_g_cm3": 0.8120000000, + "radlen_cm": 54.2695775063, + "intlen_cm": 83.8467764328, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1415990478 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7992522572 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.0591486950 + } + ] + }, + "G4_OCTANE": { + "name": "G4_OCTANE", + "density_g_cm3": 0.7026000000, + "radlen_cm": 64.0534438285, + "intlen_cm": 94.6809469695, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8411702684 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1588297316 + } + ] + }, + "G4_Os": { + "name": "G4_Os", + "density_g_cm3": 22.5700000000, + "radlen_cm": 0.2958609866, + "intlen_cm": 8.9185046979, + "elements": [ + { + "symbol": "Os", + "Z": 76, + "A_g_mol": 190.2245546000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_P": { + "name": "G4_P", + "density_g_cm3": 2.2000000000, + "radlen_cm": 9.6387902637, + "intlen_cm": 49.9624310154, + "elements": [ + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_PARAFFIN": { + "name": "G4_PARAFFIN", + "density_g_cm3": 0.9300000000, + "radlen_cm": 48.2237383379, + "intlen_cm": 72.3210868975, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8513873152 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1486126848 + } + ] + }, + "G4_PHOSPHORIC_ACID": { + "name": "G4_PHOSPHORIC_ACID", + "density_g_cm3": 1.8700000000, + "radlen_cm": 15.5141283621, + "intlen_cm": 47.9082638597, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0308568456 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.3160747168 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.6530684376 + } + ] + }, + "G4_PHOTO_EMULSION": { + "name": "G4_PHOTO_EMULSION", + "density_g_cm3": 3.8150000000, + "radlen_cm": 2.9706503474, + "intlen_cm": 35.0478714990, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0141000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.0722610000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0193200000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.0661010000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0018900000 + }, + { + "symbol": "Br", + "Z": 35, + "A_g_mol": 79.9035138000, + "mass_fraction": 0.3491030000 + }, + { + "symbol": "Ag", + "Z": 47, + "A_g_mol": 107.8682200000, + "mass_fraction": 0.4741050000 + }, + { + "symbol": "I", + "Z": 53, + "A_g_mol": 126.9040000000, + "mass_fraction": 0.0031200000 + } + ] + }, + "G4_PLASTIC_SC_VINYLTOLUENE": { + "name": "G4_PLASTIC_SC_VINYLTOLUENE", + "density_g_cm3": 1.0320000000, + "radlen_cm": 42.5441996486, + "intlen_cm": 69.9693874192, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.9147085318 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0852914682 + } + ] + }, + "G4_PLEXIGLASS": { + "name": "G4_PLEXIGLASS", + "density_g_cm3": 1.1900000000, + "radlen_cm": 34.0748806544, + "intlen_cm": 62.6702055110, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5998410709 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0805418407 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3196170884 + } + ] + }, + "G4_PLUTONIUM_DIOXIDE": { + "name": "G4_PLUTONIUM_DIOXIDE", + "density_g_cm3": 11.4600000000, + "radlen_cm": 0.5723242927, + "intlen_cm": 16.2912350677, + "elements": [ + { + "symbol": "Pu", + "Z": 94, + "A_g_mol": 244.0640000000, + "mass_fraction": 0.8840887543 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1159112457 + } + ] + }, + "G4_POLYACRYLONITRILE": { + "name": "G4_POLYACRYLONITRILE", + "density_g_cm3": 1.1700000000, + "radlen_cm": 35.9776646539, + "intlen_cm": 64.6096169435, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6790483898 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0569857271 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.2639658832 + } + ] + }, + "G4_POLYCARBONATE": { + "name": "G4_POLYCARBONATE", + "density_g_cm3": 1.2000000000, + "radlen_cm": 34.5873361608, + "intlen_cm": 63.3495193368, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7557453702 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0554943691 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1887602607 + } + ] + }, + "G4_POLYCHLOROSTYRENE": { + "name": "G4_POLYCHLOROSTYRENE", + "density_g_cm3": 1.3000000000, + "radlen_cm": 25.3754585368, + "intlen_cm": 62.3930757123, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6932901610 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0509082842 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.2558015548 + } + ] + }, + "G4_POLYETHYLENE": { + "name": "G4_POLYETHYLENE", + "density_g_cm3": 0.9400000000, + "radlen_cm": 47.6316902019, + "intlen_cm": 71.9328468651, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8562817123 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1437182877 + } + ] + }, + "G4_POLYOXYMETHYLENE": { + "name": "G4_POLYOXYMETHYLENE", + "density_g_cm3": 1.4250000000, + "radlen_cm": 26.9940588984, + "intlen_cm": 54.1869646318, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4000110924 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0671378455 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.5328510621 + } + ] + }, + "G4_POLYPROPYLENE": { + "name": "G4_POLYPROPYLENE", + "density_g_cm3": 0.9000000000, + "radlen_cm": 49.7486542109, + "intlen_cm": 75.1298622814, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8562817123 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1437182877 + } + ] + }, + "G4_POLYSTYRENE": { + "name": "G4_POLYSTYRENE", + "density_g_cm3": 1.0600000000, + "radlen_cm": 41.3125056266, + "intlen_cm": 68.7498786660, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.9225773293 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0774226707 + } + ] + }, + "G4_POLYTRIFLUOROCHLOROETHYLENE": { + "name": "G4_POLYTRIFLUOROCHLOROETHYLENE", + "density_g_cm3": 2.1000000000, + "radlen_cm": 13.4211774188, + "intlen_cm": 45.5231505449, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.2062473447 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.4893583661 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.3043942892 + } + ] + }, + "G4_POLYVINYLIDENE_CHLORIDE": { + "name": "G4_POLYVINYLIDENE_CHLORIDE", + "density_g_cm3": 1.7000000000, + "radlen_cm": 13.3466912811, + "intlen_cm": 58.5490734291, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.2477909327 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0207946100 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.7314144572 + } + ] + }, + "G4_POLYVINYLIDENE_FLUORIDE": { + "name": "G4_POLYVINYLIDENE_FLUORIDE", + "density_g_cm3": 1.7600000000, + "radlen_cm": 20.8089540539, + "intlen_cm": 47.6128231526, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.3751353170 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0314813482 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.5933833348 + } + ] + }, + "G4_POLYVINYL_ACETATE": { + "name": "G4_POLYVINYL_ACETATE", + "density_g_cm3": 1.1900000000, + "radlen_cm": 33.5589610921, + "intlen_cm": 63.7392761655, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5580589687 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0702484460 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3716925853 + } + ] + }, + "G4_POLYVINYL_ALCOHOL": { + "name": "G4_POLYVINYL_ALCOHOL", + "density_g_cm3": 1.3000000000, + "radlen_cm": 30.9791700346, + "intlen_cm": 56.8283796029, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5452903684 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0915215132 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3631881183 + } + ] + }, + "G4_POLYVINYL_BUTYRAL": { + "name": "G4_POLYVINYL_BUTYRAL", + "density_g_cm3": 1.1200000000, + "radlen_cm": 37.2445284119, + "intlen_cm": 64.6185702523, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6757292578 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0992375747 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2250331675 + } + ] + }, + "G4_POLYVINYL_CHLORIDE": { + "name": "G4_POLYVINYL_CHLORIDE", + "density_g_cm3": 1.3000000000, + "radlen_cm": 19.6259713893, + "intlen_cm": 69.2301257138, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.3843566728 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0483828062 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.5672605210 + } + ] + }, + "G4_POLYVINYL_PYRROLIDONE": { + "name": "G4_POLYVINYL_PYRROLIDONE", + "density_g_cm3": 1.2500000000, + "radlen_cm": 33.3295432994, + "intlen_cm": 59.0516806099, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6483992503 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0816204778 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1260258351 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1439544368 + } + ] + }, + "G4_POTASSIUM_IODIDE": { + "name": "G4_POTASSIUM_IODIDE", + "density_g_cm3": 3.1300000000, + "radlen_cm": 3.0794662292, + "intlen_cm": 50.4789672731, + "elements": [ + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 0.2355286329 + }, + { + "symbol": "I", + "Z": 53, + "A_g_mol": 126.9040000000, + "mass_fraction": 0.7644713671 + } + ] + }, + "G4_POTASSIUM_OXIDE": { + "name": "G4_POTASSIUM_OXIDE", + "density_g_cm3": 2.3200000000, + "radlen_cm": 8.1473889734, + "intlen_cm": 48.3539578260, + "elements": [ + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 0.8301478368 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1698521632 + } + ] + }, + "G4_PROPANE": { + "name": "G4_PROPANE", + "density_g_cm3": 0.0018793900, + "radlen_cm": 24143.4343892002, + "intlen_cm": 34507.9467355684, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8171359205 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1828640795 + } + ] + }, + "G4_PYRIDINE": { + "name": "G4_PYRIDINE", + "density_g_cm3": 0.9819000000, + "radlen_cm": 43.4238519327, + "intlen_cm": 76.0528817127, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7592106765 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0637129445 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1770763790 + } + ] + }, + "G4_Pa": { + "name": "G4_Pa", + "density_g_cm3": 15.3700000000, + "radlen_cm": 0.3860695338, + "intlen_cm": 13.9729283036, + "elements": [ + { + "symbol": "Pa", + "Z": 91, + "A_g_mol": 231.0360000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Pb": { + "name": "G4_Pb", + "density_g_cm3": 11.3500000000, + "radlen_cm": 0.5612532628, + "intlen_cm": 18.2479470310, + "elements": [ + { + "symbol": "Pb", + "Z": 82, + "A_g_mol": 207.2170000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_PbWO4": { + "name": "G4_PbWO4", + "density_g_cm3": 8.2800000000, + "radlen_cm": 0.8924531919, + "intlen_cm": 20.7397427149, + "elements": [ + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1406366195 + }, + { + "symbol": "Pb", + "Z": 82, + "A_g_mol": 207.2170000000, + "mass_fraction": 0.4553657612 + }, + { + "symbol": "W", + "Z": 74, + "A_g_mol": 183.8416100000, + "mass_fraction": 0.4039976193 + } + ] + }, + "G4_Pd": { + "name": "G4_Pd", + "density_g_cm3": 12.0200000000, + "radlen_cm": 0.7657167657, + "intlen_cm": 13.7984874589, + "elements": [ + { + "symbol": "Pd", + "Z": 46, + "A_g_mol": 106.4151876000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Pm": { + "name": "G4_Pm", + "density_g_cm3": 7.2200000000, + "radlen_cm": 1.0408459865, + "intlen_cm": 25.4624387556, + "elements": [ + { + "symbol": "Pm", + "Z": 61, + "A_g_mol": 144.9130000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Po": { + "name": "G4_Po", + "density_g_cm3": 9.3200000000, + "radlen_cm": 0.6610916001, + "intlen_cm": 22.2854698005, + "elements": [ + { + "symbol": "Po", + "Z": 84, + "A_g_mol": 208.9820000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Pr": { + "name": "G4_Pr", + "density_g_cm3": 6.7100000000, + "radlen_cm": 1.1562026576, + "intlen_cm": 27.1429747446, + "elements": [ + { + "symbol": "Pr", + "Z": 59, + "A_g_mol": 140.9080000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Pt": { + "name": "G4_Pt", + "density_g_cm3": 21.4500000000, + "radlen_cm": 0.3050532706, + "intlen_cm": 9.4633205278, + "elements": [ + { + "symbol": "Pt", + "Z": 78, + "A_g_mol": 195.0780035700, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Pu": { + "name": "G4_Pu", + "density_g_cm3": 19.8400000000, + "radlen_cm": 0.2989048704, + "intlen_cm": 11.0245529144, + "elements": [ + { + "symbol": "Pu", + "Z": 94, + "A_g_mol": 244.0640000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Pyrex_Glass": { + "name": "G4_Pyrex_Glass", + "density_g_cm3": 2.2300000000, + "radlen_cm": 12.6325375693, + "intlen_cm": 42.2910635276, + "elements": [ + { + "symbol": "B", + "Z": 5, + "A_g_mol": 10.8110164000, + "mass_fraction": 0.0400639199 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.5395609209 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0281909436 + }, + { + "symbol": "Al", + "Z": 13, + "A_g_mol": 26.9815000000, + "mass_fraction": 0.0116439767 + }, + { + "symbol": "Si", + "Z": 14, + "A_g_mol": 28.0853614555, + "mass_fraction": 0.3772192456 + }, + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 0.0033209934 + } + ] + }, + "G4_RUBBER_BUTYL": { + "name": "G4_RUBBER_BUTYL", + "density_g_cm3": 0.9200000000, + "radlen_cm": 48.6670416944, + "intlen_cm": 73.4971873425, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1437110000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8562890000 + } + ] + }, + "G4_RUBBER_NATURAL": { + "name": "G4_RUBBER_NATURAL", + "density_g_cm3": 0.9200000000, + "radlen_cm": 48.2532262024, + "intlen_cm": 75.5816000621, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1183710000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8816290000 + } + ] + }, + "G4_RUBBER_NEOPRENE": { + "name": "G4_RUBBER_NEOPRENE", + "density_g_cm3": 1.2300000000, + "radlen_cm": 23.6452744201, + "intlen_cm": 68.4406876941, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0569200000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5426460000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.4004340000 + } + ] + }, + "G4_Ra": { + "name": "G4_Ra", + "density_g_cm3": 5.0000000000, + "radlen_cm": 1.2298658749, + "intlen_cm": 42.6399710179, + "elements": [ + { + "symbol": "Ra", + "Z": 88, + "A_g_mol": 226.0250000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Rb": { + "name": "G4_Rb", + "density_g_cm3": 1.5320000000, + "radlen_cm": 7.1977407506, + "intlen_cm": 100.6336626726, + "elements": [ + { + "symbol": "Rb", + "Z": 37, + "A_g_mol": 85.4676764200, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Re": { + "name": "G4_Re", + "density_g_cm3": 21.0200000000, + "radlen_cm": 0.3182831922, + "intlen_cm": 9.5082503276, + "elements": [ + { + "symbol": "Re", + "Z": 75, + "A_g_mol": 186.2068780000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Rh": { + "name": "G4_Rh", + "density_g_cm3": 12.4100000000, + "radlen_cm": 0.7466192395, + "intlen_cm": 13.2162992461, + "elements": [ + { + "symbol": "Rh", + "Z": 45, + "A_g_mol": 102.9060000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Rn": { + "name": "G4_Rn", + "density_g_cm3": 0.0090066200, + "radlen_cm": 697.7766671691, + "intlen_cm": 23530.7426900032, + "elements": [ + { + "symbol": "Rn", + "Z": 86, + "A_g_mol": 222.0180000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Ru": { + "name": "G4_Ru", + "density_g_cm3": 12.4100000000, + "radlen_cm": 0.7640666774, + "intlen_cm": 13.1370029745, + "elements": [ + { + "symbol": "Ru", + "Z": 44, + "A_g_mol": 101.0648187900, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_S": { + "name": "G4_S", + "density_g_cm3": 2.0000000000, + "radlen_cm": 9.7482931365, + "intlen_cm": 55.5972779233, + "elements": [ + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_SILICON_DIOXIDE": { + "name": "G4_SILICON_DIOXIDE", + "density_g_cm3": 2.3200000000, + "radlen_cm": 11.6577276645, + "intlen_cm": 41.3174239290, + "elements": [ + { + "symbol": "Si", + "Z": 14, + "A_g_mol": 28.0853614555, + "mass_fraction": 0.4674338418 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.5325661582 + } + ] + }, + "G4_SILVER_BROMIDE": { + "name": "G4_SILVER_BROMIDE", + "density_g_cm3": 6.4730000000, + "radlen_cm": 1.5250930816, + "intlen_cm": 24.6362003753, + "elements": [ + { + "symbol": "Ag", + "Z": 47, + "A_g_mol": 107.8682200000, + "mass_fraction": 0.5744646322 + }, + { + "symbol": "Br", + "Z": 35, + "A_g_mol": 79.9035138000, + "mass_fraction": 0.4255353678 + } + ] + }, + "G4_SILVER_CHLORIDE": { + "name": "G4_SILVER_CHLORIDE", + "density_g_cm3": 5.5600000000, + "radlen_cm": 1.8592358479, + "intlen_cm": 26.9699324435, + "elements": [ + { + "symbol": "Ag", + "Z": 47, + "A_g_mol": 107.8682200000, + "mass_fraction": 0.7526348232 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.2473651768 + } + ] + }, + "G4_SILVER_HALIDES": { + "name": "G4_SILVER_HALIDES", + "density_g_cm3": 6.4700000000, + "radlen_cm": 1.5245239630, + "intlen_cm": 24.6583577333, + "elements": [ + { + "symbol": "Br", + "Z": 35, + "A_g_mol": 79.9035138000, + "mass_fraction": 0.4228950000 + }, + { + "symbol": "Ag", + "Z": 47, + "A_g_mol": 107.8682200000, + "mass_fraction": 0.5737480000 + }, + { + "symbol": "I", + "Z": 53, + "A_g_mol": 126.9040000000, + "mass_fraction": 0.0033570000 + } + ] + }, + "G4_SILVER_IODIDE": { + "name": "G4_SILVER_IODIDE", + "density_g_cm3": 6.0100000000, + "radlen_cm": 1.4473505292, + "intlen_cm": 28.5353855145, + "elements": [ + { + "symbol": "Ag", + "Z": 47, + "A_g_mol": 107.8682200000, + "mass_fraction": 0.4594590450 + }, + { + "symbol": "I", + "Z": 53, + "A_g_mol": 126.9040000000, + "mass_fraction": 0.5405409550 + } + ] + }, + "G4_SKIN_ICRP": { + "name": "G4_SKIN_ICRP", + "density_g_cm3": 1.0900000000, + "radlen_cm": 34.3047957676, + "intlen_cm": 69.0045449724, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1000000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.2040000000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0420000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.6450000000 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.0030000000 + }, + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 0.0010000000 + } + ] + }, + "G4_SODIUM_CARBONATE": { + "name": "G4_SODIUM_CARBONATE", + "density_g_cm3": 2.5320000000, + "radlen_cm": 12.5293001203, + "intlen_cm": 36.2077642513, + "elements": [ + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.4338168452 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1133211199 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4528620349 + } + ] + }, + "G4_SODIUM_IODIDE": { + "name": "G4_SODIUM_IODIDE", + "density_g_cm3": 3.6670000000, + "radlen_cm": 2.5882212769, + "intlen_cm": 42.9136935370, + "elements": [ + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.1533739221 + }, + { + "symbol": "I", + "Z": 53, + "A_g_mol": 126.9040000000, + "mass_fraction": 0.8466260779 + } + ] + }, + "G4_SODIUM_MONOXIDE": { + "name": "G4_SODIUM_MONOXIDE", + "density_g_cm3": 2.2700000000, + "radlen_cm": 12.8484696099, + "intlen_cm": 42.4347669192, + "elements": [ + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.7418578408 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2581421592 + } + ] + }, + "G4_SODIUM_NITRATE": { + "name": "G4_SODIUM_NITRATE", + "density_g_cm3": 2.2610000000, + "radlen_cm": 14.4612324186, + "intlen_cm": 39.9375053268, + "elements": [ + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.2704849729 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1647957147 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.5647193123 + } + ] + }, + "G4_STAINLESS-STEEL": { + "name": "G4_STAINLESS-STEEL", + "density_g_cm3": 8.0000000000, + "radlen_cm": 1.7380670645, + "intlen_cm": 16.6780570974, + "elements": [ + { + "symbol": "Fe", + "Z": 26, + "A_g_mol": 55.8451107980, + "mass_fraction": 0.7462128746 + }, + { + "symbol": "Cr", + "Z": 24, + "A_g_mol": 51.9961301370, + "mass_fraction": 0.1690010443 + }, + { + "symbol": "Ni", + "Z": 28, + "A_g_mol": 58.6933251009, + "mass_fraction": 0.0847860811 + } + ] + }, + "G4_STILBENE": { + "name": "G4_STILBENE", + "density_g_cm3": 0.9707000000, + "radlen_cm": 44.9595141781, + "intlen_cm": 75.9942943910, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.9328955096 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0671044904 + } + ] + }, + "G4_SUCROSE": { + "name": "G4_SUCROSE", + "density_g_cm3": 1.5805000000, + "radlen_cm": 24.4231162245, + "intlen_cm": 48.9185997692, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4210638981 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0647820686 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.5141540333 + } + ] + }, + "G4_Sb": { + "name": "G4_Sb", + "density_g_cm3": 6.6910000000, + "radlen_cm": 1.3040127628, + "intlen_cm": 25.9265676022, + "elements": [ + { + "symbol": "Sb", + "Z": 51, + "A_g_mol": 121.7598000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Sc": { + "name": "G4_Sc", + "density_g_cm3": 2.9890000000, + "radlen_cm": 5.5354470594, + "intlen_cm": 41.6361977932, + "elements": [ + { + "symbol": "Sc", + "Z": 21, + "A_g_mol": 44.9559000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Se": { + "name": "G4_Se", + "density_g_cm3": 4.5000000000, + "radlen_cm": 2.6462517652, + "intlen_cm": 33.3674841789, + "elements": [ + { + "symbol": "Se", + "Z": 34, + "A_g_mol": 78.9593734300, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Si": { + "name": "G4_Si", + "density_g_cm3": 2.3300000000, + "radlen_cm": 9.3660702922, + "intlen_cm": 45.6603073704, + "elements": [ + { + "symbol": "Si", + "Z": 14, + "A_g_mol": 28.0853614555, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Sm": { + "name": "G4_Sm", + "density_g_cm3": 7.4600000000, + "radlen_cm": 1.0152448223, + "intlen_cm": 24.9485982224, + "elements": [ + { + "symbol": "Sm", + "Z": 62, + "A_g_mol": 150.3663619000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Sn": { + "name": "G4_Sn", + "density_g_cm3": 7.3100000000, + "radlen_cm": 1.2063713058, + "intlen_cm": 23.5313378422, + "elements": [ + { + "symbol": "Sn", + "Z": 50, + "A_g_mol": 118.7101218000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Sr": { + "name": "G4_Sr", + "density_g_cm3": 2.5400000000, + "radlen_cm": 4.2369989713, + "intlen_cm": 61.2016634807, + "elements": [ + { + "symbol": "Sr", + "Z": 38, + "A_g_mol": 87.6166395000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_TEFLON": { + "name": "G4_TEFLON", + "density_g_cm3": 2.2000000000, + "radlen_cm": 15.8385014262, + "intlen_cm": 40.8310561331, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.2401785261 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.7598214739 + } + ] + }, + "G4_TERPHENYL": { + "name": "G4_TERPHENYL", + "density_g_cm3": 1.2400000000, + "radlen_cm": 35.1277340003, + "intlen_cm": 59.9049067935, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.9387281833 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0612718167 + } + ] + }, + "G4_TESTIS_ICRP": { + "name": "G4_TESTIS_ICRP", + "density_g_cm3": 1.0400000000, + "radlen_cm": 35.1692276422, + "intlen_cm": 72.4725513802, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1060000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.0990000000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0200000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.7660000000 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 0.0020000000 + } + ] + }, + "G4_TETRACHLOROETHYLENE": { + "name": "G4_TETRACHLOROETHYLENE", + "density_g_cm3": 1.6250000000, + "radlen_cm": 12.8873634663, + "intlen_cm": 66.5667035399, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1448544708 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.8551455292 + } + ] + }, + "G4_THALLIUM_CHLORIDE": { + "name": "G4_THALLIUM_CHLORIDE", + "density_g_cm3": 7.0040000000, + "radlen_cm": 1.0166713986, + "intlen_cm": 26.3467114309, + "elements": [ + { + "symbol": "Tl", + "Z": 81, + "A_g_mol": 204.3829295200, + "mass_fraction": 0.8521796274 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.1478203726 + } + ] + }, + "G4_THYMINE": { + "name": "G4_THYMINE", + "density_g_cm3": 1.4800000000, + "radlen_cm": 26.8429768410, + "intlen_cm": 52.7013719035, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0479539269 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4761870282 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.2221293174 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2537297275 + } + ] + }, + "G4_TISSUE-METHANE": { + "name": "G4_TISSUE-METHANE", + "density_g_cm3": 0.0010640900, + "radlen_cm": 37431.0260905174, + "intlen_cm": 68943.1775981619, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1018690000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4561790000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0351720000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4067800000 + } + ] + }, + "G4_TISSUE-PROPANE": { + "name": "G4_TISSUE-PROPANE", + "density_g_cm3": 0.0018262800, + "radlen_cm": 22400.6777976263, + "intlen_cm": 39755.8870930692, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1026720000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5689400000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0350220000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2933660000 + } + ] + }, + "G4_TISSUE_SOFT_ICRP": { + "name": "G4_TISSUE_SOFT_ICRP", + "density_g_cm3": 1.0300000000, + "radlen_cm": 36.6945920182, + "intlen_cm": 72.2954858829, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1050000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.2560000000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0270000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.6020000000 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0030000000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 0.0020000000 + } + ] + }, + "G4_TISSUE_SOFT_ICRU-4": { + "name": "G4_TISSUE_SOFT_ICRU-4", + "density_g_cm3": 1.0000000000, + "radlen_cm": 36.8431452797, + "intlen_cm": 75.6496725291, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1010000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1110000000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0260000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.7620000000 + } + ] + }, + "G4_TITANIUM_DIOXIDE": { + "name": "G4_TITANIUM_DIOXIDE", + "density_g_cm3": 4.2600000000, + "radlen_cm": 4.8120066363, + "intlen_cm": 25.3523138121, + "elements": [ + { + "symbol": "Ti", + "Z": 22, + "A_g_mol": 47.8667173300, + "mass_fraction": 0.5993416236 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4006583764 + } + ] + }, + "G4_TOLUENE": { + "name": "G4_TOLUENE", + "density_g_cm3": 0.8669000000, + "radlen_cm": 50.6840912009, + "intlen_cm": 83.0802585391, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.9124848982 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0875151018 + } + ] + }, + "G4_TRICHLOROETHYLENE": { + "name": "G4_TRICHLOROETHYLENE", + "density_g_cm3": 1.4600000000, + "radlen_cm": 14.7632677276, + "intlen_cm": 71.7912794212, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1828297192 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0076715332 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.8094987477 + } + ] + }, + "G4_TRIETHYL_PHOSPHATE": { + "name": "G4_TRIETHYL_PHOSPHATE", + "density_g_cm3": 1.0700000000, + "radlen_cm": 32.3802005558, + "intlen_cm": 72.7982710675, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.3956216481 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0830014017 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3513359494 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.1700410008 + } + ] + }, + "G4_TUNGSTEN_HEXAFLUORIDE": { + "name": "G4_TUNGSTEN_HEXAFLUORIDE", + "density_g_cm3": 2.4000000000, + "radlen_cm": 4.0495275513, + "intlen_cm": 57.8720027568, + "elements": [ + { + "symbol": "W", + "Z": 74, + "A_g_mol": 183.8416100000, + "mass_fraction": 0.6172661226 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.3827338774 + } + ] + }, + "G4_Ta": { + "name": "G4_Ta", + "density_g_cm3": 16.6540000000, + "radlen_cm": 0.4093920370, + "intlen_cm": 11.8868655868, + "elements": [ + { + "symbol": "Ta", + "Z": 73, + "A_g_mol": 180.9478798800, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Tb": { + "name": "G4_Tb", + "density_g_cm3": 8.2290000000, + "radlen_cm": 0.8939773235, + "intlen_cm": 23.0383704280, + "elements": [ + { + "symbol": "Tb", + "Z": 65, + "A_g_mol": 158.9250000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Tc": { + "name": "G4_Tc", + "density_g_cm3": 11.5000000000, + "radlen_cm": 0.8331486453, + "intlen_cm": 14.0273332814, + "elements": [ + { + "symbol": "Tc", + "Z": 43, + "A_g_mol": 97.9072000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Te": { + "name": "G4_Te", + "density_g_cm3": 6.2400000000, + "radlen_cm": 1.4145736790, + "intlen_cm": 28.2381937661, + "elements": [ + { + "symbol": "Te", + "Z": 52, + "A_g_mol": 127.6028203000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Th": { + "name": "G4_Th", + "density_g_cm3": 11.7200000000, + "radlen_cm": 0.5182303174, + "intlen_cm": 18.3510184568, + "elements": [ + { + "symbol": "Th", + "Z": 90, + "A_g_mol": 232.0380000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Ti": { + "name": "G4_Ti", + "density_g_cm3": 4.5400000000, + "radlen_cm": 3.5601976931, + "intlen_cm": 27.9913240031, + "elements": [ + { + "symbol": "Ti", + "Z": 22, + "A_g_mol": 47.8667173300, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Tl": { + "name": "G4_Tl", + "density_g_cm3": 11.7200000000, + "radlen_cm": 0.5476649213, + "intlen_cm": 17.5909248845, + "elements": [ + { + "symbol": "Tl", + "Z": 81, + "A_g_mol": 204.3829295200, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Tm": { + "name": "G4_Tm", + "density_g_cm3": 9.3210000000, + "radlen_cm": 0.7544283053, + "intlen_cm": 20.7576376405, + "elements": [ + { + "symbol": "Tm", + "Z": 69, + "A_g_mol": 168.9340000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_U": { + "name": "G4_U", + "density_g_cm3": 18.9500000000, + "radlen_cm": 0.3166296193, + "intlen_cm": 11.4463995326, + "elements": [ + { + "symbol": "U", + "Z": 92, + "A_g_mol": 238.0291290500, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_URACIL": { + "name": "G4_URACIL", + "density_g_cm3": 1.3200000000, + "radlen_cm": 29.6780964598, + "intlen_cm": 60.2469223893, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0359699343 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4286218190 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.2499266740 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2854815727 + } + ] + }, + "G4_URANIUM_DICARBIDE": { + "name": "G4_URANIUM_DICARBIDE", + "density_g_cm3": 11.2800000000, + "radlen_cm": 0.5774187445, + "intlen_cm": 16.6288137602, + "elements": [ + { + "symbol": "U", + "Z": 92, + "A_g_mol": 238.0291290500, + "mass_fraction": 0.9083326938 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.0916673062 + } + ] + }, + "G4_URANIUM_MONOCARBIDE": { + "name": "G4_URANIUM_MONOCARBIDE", + "density_g_cm3": 13.6300000000, + "radlen_cm": 0.4591719671, + "intlen_cm": 14.7086462814, + "elements": [ + { + "symbol": "U", + "Z": 92, + "A_g_mol": 238.0291290500, + "mass_fraction": 0.9519647143 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.0480352857 + } + ] + }, + "G4_URANIUM_OXIDE": { + "name": "G4_URANIUM_OXIDE", + "density_g_cm3": 10.9600000000, + "radlen_cm": 0.6067585859, + "intlen_cm": 16.8728319003, + "elements": [ + { + "symbol": "U", + "Z": 92, + "A_g_mol": 238.0291290500, + "mass_fraction": 0.8814982465 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1185017535 + } + ] + }, + "G4_UREA": { + "name": "G4_UREA", + "density_g_cm3": 1.3230000000, + "radlen_cm": 29.2864284039, + "intlen_cm": 58.3095550771, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1999941860 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0671340321 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.4664613838 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2664103982 + } + ] + }, + "G4_V": { + "name": "G4_V", + "density_g_cm3": 6.1100000000, + "radlen_cm": 2.5928540732, + "intlen_cm": 21.2349284162, + "elements": [ + { + "symbol": "V", + "Z": 23, + "A_g_mol": 50.9415080000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_VALINE": { + "name": "G4_VALINE", + "density_g_cm3": 1.2300000000, + "radlen_cm": 33.0046908252, + "intlen_cm": 59.7177245788, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5126370904 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0946450872 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1195661791 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2731516433 + } + ] + }, + "G4_VITON": { + "name": "G4_VITON", + "density_g_cm3": 1.8000000000, + "radlen_cm": 19.6436366979, + "intlen_cm": 48.8530589173, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0094170000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.2805550000 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.7100280000 + } + ] + }, + "G4_W": { + "name": "G4_W", + "density_g_cm3": 19.3000000000, + "radlen_cm": 0.3504180177, + "intlen_cm": 10.3115837893, + "elements": [ + { + "symbol": "W", + "Z": 74, + "A_g_mol": 183.8416100000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_WATER": { + "name": "G4_WATER", + "density_g_cm3": 1.0000000000, + "radlen_cm": 36.0829774640, + "intlen_cm": 75.3747894121, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1118984778 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.8881015222 + } + ] + }, + "G4_WATER_VAPOR": { + "name": "G4_WATER_VAPOR", + "density_g_cm3": 0.0007561820, + "radlen_cm": 47717.3186666997, + "intlen_cm": 99678.1058159553, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1118984778 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.8881015222 + } + ] + }, + "G4_XYLENE": { + "name": "G4_XYLENE", + "density_g_cm3": 0.8700000000, + "radlen_cm": 50.6283508279, + "intlen_cm": 82.0777032085, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.9050593022 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0949406978 + } + ] + }, + "G4_Xe": { + "name": "G4_Xe", + "density_g_cm3": 0.0054853600, + "radlen_cm": 1546.2047849966, + "intlen_cm": 32429.6946749956, + "elements": [ + { + "symbol": "Xe", + "Z": 54, + "A_g_mol": 131.2924485000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Y": { + "name": "G4_Y", + "density_g_cm3": 4.4690000000, + "radlen_cm": 2.3294263573, + "intlen_cm": 34.9543386305, + "elements": [ + { + "symbol": "Y", + "Z": 39, + "A_g_mol": 88.9058000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Yb": { + "name": "G4_Yb", + "density_g_cm3": 6.7300000000, + "radlen_cm": 1.0433231211, + "intlen_cm": 28.9800996269, + "elements": [ + { + "symbol": "Yb", + "Z": 70, + "A_g_mol": 173.0376377000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Zn": { + "name": "G4_Zn", + "density_g_cm3": 7.1330000000, + "radlen_cm": 1.7428596191, + "intlen_cm": 19.7687190024, + "elements": [ + { + "symbol": "Zn", + "Z": 30, + "A_g_mol": 65.3955232900, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Zr": { + "name": "G4_Zr", + "density_g_cm3": 6.5060000000, + "radlen_cm": 1.5670742707, + "intlen_cm": 24.2171559753, + "elements": [ + { + "symbol": "Zr", + "Z": 40, + "A_g_mol": 91.2236313100, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_lAr": { + "name": "G4_lAr", + "density_g_cm3": 1.3960000000, + "radlen_cm": 14.0034386850, + "intlen_cm": 85.7063953867, + "elements": [ + { + "symbol": "Ar", + "Z": 18, + "A_g_mol": 39.9476933511, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_lBr": { + "name": "G4_lBr", + "density_g_cm3": 3.1028000000, + "radlen_cm": 3.6812743699, + "intlen_cm": 48.5850800305, + "elements": [ + { + "symbol": "Br", + "Z": 35, + "A_g_mol": 79.9035138000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_lH2": { + "name": "G4_lH2", + "density_g_cm3": 0.0708000000, + "radlen_cm": 890.4450429699, + "intlen_cm": 494.3503221504, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_lKr": { + "name": "G4_lKr", + "density_g_cm3": 2.4180000000, + "radlen_cm": 4.7031249036, + "intlen_cm": 63.3420249794, + "elements": [ + { + "symbol": "Kr", + "Z": 36, + "A_g_mol": 83.7993175100, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_lN2": { + "name": "G4_lN2", + "density_g_cm3": 0.8070000000, + "radlen_cm": 47.0732642399, + "intlen_cm": 104.5459466611, + "elements": [ + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_lO2": { + "name": "G4_lO2", + "density_g_cm3": 1.1410000000, + "radlen_cm": 30.0071281192, + "intlen_cm": 77.2947944830, + "elements": [ + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_lPROPANE": { + "name": "G4_lPROPANE", + "density_g_cm3": 0.4300000000, + "radlen_cm": 105.5230910621, + "intlen_cm": 150.8230000357, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8171359205 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1828640795 + } + ] + }, + "G4_lXe": { + "name": "G4_lXe", + "density_g_cm3": 2.9530000000, + "radlen_cm": 2.8721604739, + "intlen_cm": 60.2399424255, + "elements": [ + { + "symbol": "Xe", + "Z": 54, + "A_g_mol": 131.2924485000, + "mass_fraction": 1.0000000000 + } + ] + } + }, + "count_built_ok": 309, + "count_built_fail": 0 +} diff --git a/scripts/geometry/g4_nist_database/compile.sh b/scripts/geometry/g4_nist_database/compile.sh new file mode 100755 index 0000000000000..27d9cb0d87450 --- /dev/null +++ b/scripts/geometry/g4_nist_database/compile.sh @@ -0,0 +1,11 @@ +echo "Compiling using geant4-config..." + +g++ -std=c++20 nist_export_all.cxx \ + $(geant4-config --cflags) \ + $(geant4-config --libs) \ + -O2 -o nist_export_all + +echo "" +echo "Build complete." +echo "Run with:" +echo " ./nist_export_all nist_db_all.json" \ No newline at end of file diff --git a/scripts/geometry/g4_nist_database/nist_export_all.cxx b/scripts/geometry/g4_nist_database/nist_export_all.cxx new file mode 100644 index 0000000000000..709b3da261fbf --- /dev/null +++ b/scripts/geometry/g4_nist_database/nist_export_all.cxx @@ -0,0 +1,136 @@ +#include +#include +#include +#include +#include +#include + +// Geant4 +#include "G4NistManager.hh" +#include "G4Material.hh" +#include "G4Element.hh" +#include "G4SystemOfUnits.hh" + +static std::string json_escape(const std::string& s) +{ + std::string out; + out.reserve(s.size() + 8); + for (char c : s) { + switch (c) { + case '\\': + out += "\\\\"; + break; + case '"': + out += "\\\""; + break; + case '\n': + out += "\\n"; + break; + case '\r': + out += "\\r"; + break; + case '\t': + out += "\\t"; + break; + default: + out += c; + break; + } + } + return out; +} + +int main(int argc, char** argv) +{ + if (argc < 2) { + std::cerr << "Usage:\n " << argv[0] << " out.json\n"; + return 2; + } + + const std::string out_json = argv[1]; + + auto* nist = G4NistManager::Instance(); + + // This returns all known NIST material names. + std::vector names = nist->GetNistMaterialNames(); + std::sort(names.begin(), names.end()); + + std::ofstream out(out_json); + if (!out) { + std::cerr << "Cannot write: " << out_json << "\n"; + return 2; + } + + out << std::fixed << std::setprecision(10); + out << "{\n" + << " \"schema\": \"g4_nist_export_v1\",\n" + << " \"count_requested\": " << names.size() << ",\n" + << " \"materials\": {\n"; + + bool first_mat = true; + size_t built_ok = 0; + size_t built_fail = 0; + + for (const auto& g4name : names) { + // Build the material (some may fail depending on Geant4 build/config). + G4Material* mat = nist->FindOrBuildMaterial(g4name, /*warning=*/false, /*isotopes=*/false); + if (!mat) { + ++built_fail; + continue; + } + ++built_ok; + + const std::string name = g4name; // convert G4String -> std::string + + // Export in convenient units + const double density_g_cm3 = mat->GetDensity() / (g / cm3); + const double radlen_cm = mat->GetRadlen() / cm; + const double intlen_cm = mat->GetNuclearInterLength() / cm; + + const size_t ne = mat->GetNumberOfElements(); + const auto* elems = mat->GetElementVector(); + const auto* fracs = mat->GetFractionVector(); // mass fractions (nullptr for some edge cases) + + if (!first_mat) + out << ",\n"; + first_mat = false; + + out << " \"" << json_escape(name) << "\": {\n"; + out << " \"name\": \"" << json_escape(name) << "\",\n"; + out << " \"density_g_cm3\": " << density_g_cm3 << ",\n"; + out << " \"radlen_cm\": " << radlen_cm << ",\n"; + out << " \"intlen_cm\": " << intlen_cm << ",\n"; + out << " \"elements\": [\n"; + + for (size_t i = 0; i < ne; ++i) { + const G4Element* el = (*elems)[i]; + const int Z = static_cast(el->GetZ()); + const double A_g_mol = el->GetA() / (g / mole); + const double w = fracs ? fracs[i] : 0.0; + + out << " {" + << "\"symbol\": \"" << json_escape(el->GetSymbol()) << "\", " + << "\"Z\": " << Z << ", " + << "\"A_g_mol\": " << A_g_mol << ", " + << "\"mass_fraction\": " << w + << "}"; + + if (i + 1 != ne) + out << ","; + out << "\n"; + } + + out << " ]\n"; + out << " }"; + } + + out << "\n },\n" + << " \"count_built_ok\": " << built_ok << ",\n" + << " \"count_built_fail\": " << built_fail << "\n" + << "}\n"; + + std::cerr << "Wrote: " << out_json << "\n" + << "NIST names: " << names.size() << ", built ok: " << built_ok + << ", failed: " << built_fail << "\n"; + return 0; +} \ No newline at end of file diff --git a/scripts/geometry/simulating_CAD_modules.md b/scripts/geometry/simulating_CAD_modules.md index ccd59a3523781..fe30456332ff6 100644 --- a/scripts/geometry/simulating_CAD_modules.md +++ b/scripts/geometry/simulating_CAD_modules.md @@ -6,7 +6,8 @@ These are a few notes related to the inclusion of external (CAD-described) detec In principle, such integration is now possible and requires the following steps: -1. The CAD geometry needs to be exported to STEP format and must contain only the final geometry (no artificial eta-cut elements). Ideally, the geometry should be fully hierarchical with proper solid reuse. The solids should retain their proper surface representation for detailed analysis. +1. The CAD geometry needs to be exported to STEP format and must contain only the final geometry (no artificial eta-cut elements). Ideally, the geometry should be fully hierarchical with proper solid reuse. The solids should retain their proper surface representation for detailed analysis. Materials can be treated by providing a CSV file that map STEP part names to a material name. The conversion code will do it's best to find a corresponding material definition from a G4 NIST database JSON file (which can be expanded by users with custom definitions). + 2. A tool `O2-CADtoTGeo.py` is provided to convert the STEP geometry into TGeo format. The tool is part of AliceO2 and is based on Python bindings (OCC) for OpenCascade. The tool can be used as follows: @@ -17,7 +18,14 @@ In principle, such integration is now possible and requires the following steps: This will create a ROOT macro file `geom.C` containing the geometry description in ROOT format, as well as several binary files describing the TGeo solids. The `geom.C` file can either be used directly in ROOT to inspect the geometry or be provided to ALICE-O2 for inclusion in the geometry. -3. Introduction of materials/media in the file `geom.C`. Currently, the file `geom.C` needs to be patched or edited to properly include `TGeoMaterial`/`TGeoMedium` definitions and connect them to the relevant `TGeoVolume` objects. At present, every solid has the same dummy material attached, which is not realistic. It may be a good idea to create a new file `geom_withMaterials.C`, which differs from `geom.C` by the addition of these material definitions. + When materials are included the conversion process looks like this + ```bash + python O2-CADtoTGeo.py STEP_FILE --output-folder my_detector -o geom.C --mesh \ + --mesh-prec 0.2 \ + --materials-csv MATERIALS.csv \ --g4-nist-json ../g4_nist_database/G4_NIST_DB.json + ``` + +3. Inspection of the created geom.C file and possible manual editing/fixing of the code, in particular materials and medium objects. 4. Once the conversion is complete, the module can be inserted into the O2 geometry via the `ExternalModule` class. To do so, follow this pattern in `build_geometry.C`: