diff --git a/DataFormats/Detectors/FIT/FT0/CMakeLists.txt b/DataFormats/Detectors/FIT/FT0/CMakeLists.txt index f7d6a111f4348..8f06da6ac4a08 100644 --- a/DataFormats/Detectors/FIT/FT0/CMakeLists.txt +++ b/DataFormats/Detectors/FIT/FT0/CMakeLists.txt @@ -19,6 +19,7 @@ o2_add_library(DataFormatsFT0 src/CTF.cxx src/LookUpTable.cxx src/SlewingCoef.cxx + src/PMLookupTable.cxx PUBLIC_LINK_LIBRARIES O2::FT0Base O2::DataFormatsFIT diff --git a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/PMLookupTable.h b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/PMLookupTable.h new file mode 100755 index 0000000000000..a507494a691d3 --- /dev/null +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/PMLookupTable.h @@ -0,0 +1,58 @@ +// 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_FT0_PMLOOKUPTABLE_H_ +#define O2_FT0_PMLOOKUPTABLE_H_ + +#include +#include +#include +#include + +#include "FT0Base/Constants.h" + +namespace o2::ft0 +{ + +/// Cached mapping between FT0 channels and PM modules. +/// +/// The mapping is constructed once from SingleLUT and can then be reused by +/// digitization, reconstruction, trigger and QA code without reparsing the LUT. +class PMLookupTable +{ + public: + using ChannelID = uint16_t; + using PMHash = uint8_t; + using PMMap = std::map; // PM hash -> true for A side, false for C side + + /// Return the process-wide immutable lookup table. + static const PMLookupTable& Instance(); + + /// Return the PM hash assigned to a detector channel. + PMHash getPMHash(ChannelID channelID) const; + + /// Return true for an A-side PM and false for a C-side PM. + bool isASide(PMHash pmHash) const; + + /// Return all PM hashes and their sides. TCM entries are not included. + const PMMap& getPMs() const noexcept { return mPMHash2IsASide; } + + private: + PMLookupTable(); + + std::array mChannelID2PMHash{}; + std::array mChannelIsMapped{}; + PMMap mPMHash2IsASide; +}; + +} // namespace o2::ft0 + +#endif // O2_FT0_PMLOOKUPTABLE_H_ diff --git a/DataFormats/Detectors/FIT/FT0/src/PMLookupTable.cxx b/DataFormats/Detectors/FIT/FT0/src/PMLookupTable.cxx new file mode 100644 index 0000000000000..206d94b36ac44 --- /dev/null +++ b/DataFormats/Detectors/FIT/FT0/src/PMLookupTable.cxx @@ -0,0 +1,109 @@ +// 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. + +#include "DataFormatsFT0/PMLookupTable.h" + +#include "DataFormatsFT0/LookUpTable.h" +#include "Framework/Logger.h" + +#include +#include +#include +#include +#include +#include + +namespace o2::ft0 +{ + +const PMLookupTable& PMLookupTable::Instance() +{ + static const PMLookupTable table; + return table; +} + +PMLookupTable::PMLookupTable() +{ + std::map moduleName2Hash; + + auto lut = SingleLUT::Instance().getVecMetadataFEE(); + std::sort(lut.begin(), lut.end(), [](const auto& lhs, const auto& rhs) { + return lhs.mModuleName < rhs.mModuleName; + }); + + unsigned int nextHash = 0; + + for (const auto& entry : lut) { + const auto& moduleName = entry.mModuleName; + const auto& moduleType = entry.mModuleType; + const auto& channelString = entry.mChannelID; + + if (nextHash > static_cast(std::numeric_limits::max())) { + LOG(fatal) << "Too many FT0 FEE modules to represent with PMHash"; + } + + auto [moduleIt, inserted] = moduleName2Hash.emplace(moduleName, static_cast(nextHash)); + + if (inserted) { + const auto moduleHash = moduleIt->second; + if (moduleName.find("PMA") != std::string::npos) { + mPMHash2IsASide.emplace(moduleHash, true); + } else if (moduleName.find("PMC") != std::string::npos) { + mPMHash2IsASide.emplace(moduleHash, false); + } else if (moduleType != "TCM") { + LOG(fatal) << "Unknown FT0 module in LUT: " << moduleName + << " (type " << moduleType << ")"; + } + ++nextHash; + } + + int channelID = -1; + const char* begin = channelString.data(); + const char* end = begin + channelString.size(); + const auto [ptr, error] = std::from_chars(begin, end, channelID); + const bool isNumericChannel = error == std::errc{} && ptr == end; + + if (isNumericChannel) { + if (channelID < 0 || channelID >= Constants::sNCHANNELS_PM) { + LOG(fatal) << "Incorrect FT0 LUT entry: channel " << channelString + << " | module " << moduleName; + } + + mChannelID2PMHash[channelID] = moduleIt->second; + mChannelIsMapped[channelID] = true; + } else if (moduleType != "TCM") { + LOG(fatal) << "Non-TCM FT0 module without numerical channel ID: channel " + << channelString << " | module " << moduleName; + } + } +} + +PMLookupTable::PMHash PMLookupTable::getPMHash(ChannelID channelID) const +{ + if (channelID >= Constants::sNCHANNELS_PM) { + LOG(fatal) << "FT0 channel ID outside valid range: " << channelID; + } + if (!mChannelIsMapped[channelID]) { + LOG(fatal) << "FT0 channel is not mapped to a PM: " << channelID; + } + return mChannelID2PMHash[channelID]; +} + +bool PMLookupTable::isASide(PMHash pmHash) const +{ + const auto it = mPMHash2IsASide.find(pmHash); + if (it == mPMHash2IsASide.end()) { + LOG(fatal) << "Unknown FT0 PM hash: " << static_cast(pmHash); + } + return it->second; +} + +} // namespace o2::ft0 diff --git a/Detectors/FIT/FT0/simulation/src/Digitizer.cxx b/Detectors/FIT/FT0/simulation/src/Digitizer.cxx old mode 100755 new mode 100644 index 6f270216a0267..61633a237f693 --- a/Detectors/FIT/FT0/simulation/src/Digitizer.cxx +++ b/Detectors/FIT/FT0/simulation/src/Digitizer.cxx @@ -16,13 +16,9 @@ #include "CommonConstants/PhysicsConstants.h" #include "CommonDataFormat/InteractionRecord.h" -#include "DataFormatsFT0/LookUpTable.h" +#include "DataFormatsFT0/PMLookupTable.h" #include "FT0Base/Constants.h" #include -#include -#include -#include -#include #include "TMath.h" #include "TRandom.h" @@ -317,52 +313,7 @@ void Digitizer::storeBC(BCCache& bc, if (bc.hits.empty()) { return; } - // Initialize mapping channelID -> PM hash and PM side (A/C) using FT0 LUT - static bool pmLutInitialized = false; - static std::array mChID2PMhash{}; - static std::map mMapPMhash2isAside; // hashed PM -> is A side - - if (!pmLutInitialized) { - std::map mapFEE2hash; // module name -> hashed PM id - uint8_t tcmHash = 0; - - const auto& lut = o2::ft0::SingleLUT::Instance().getVecMetadataFEE(); - auto lutSorted = lut; - std::sort(lutSorted.begin(), lutSorted.end(), - [](const auto& first, const auto& second) { return first.mModuleName < second.mModuleName; }); - - uint8_t binPos = 0; - for (const auto& lutEntry : lutSorted) { - const auto& moduleName = lutEntry.mModuleName; - const auto& moduleType = lutEntry.mModuleType; - const auto& strChID = lutEntry.mChannelID; - - auto [it, inserted] = mapFEE2hash.insert({moduleName, binPos}); - if (inserted) { - if (moduleName.find("PMA") != std::string::npos) { - mMapPMhash2isAside.insert({binPos, true}); - } else if (moduleName.find("PMC") != std::string::npos) { - mMapPMhash2isAside.insert({binPos, false}); - } - ++binPos; - } - - if (std::regex_match(strChID, std::regex("^[0-9]{1,3}$"))) { - int chID = std::stoi(strChID); - if (chID < o2::ft0::Constants::sNCHANNELS_PM) { - mChID2PMhash[chID] = mapFEE2hash[moduleName]; - } else { - LOG(fatal) << "Incorrect LUT entry: chID " << strChID << " | " << moduleName; - } - } else if (moduleType != "TCM") { - LOG(fatal) << "Non-TCM module w/o numerical chID: chID " << strChID << " | " << moduleName; - } else { // TCM - tcmHash = mapFEE2hash[moduleName]; - } - } - - pmLutInitialized = true; - } + const auto& pmLookupTable = PMLookupTable::Instance(); int n_hit_A = 0, n_hit_C = 0, mean_time_A = 0, mean_time_C = 0; int summ_ampl_A = 0, summ_ampl_C = 0; @@ -371,57 +322,25 @@ void Digitizer::storeBC(BCCache& bc, std::vector sum_ampl_ipmt(nPMTs, 0); // Per-PM summed charge (like in digits2trgFT0) std::map mapPMhash2sumAmpl; - for (const auto& entry : mMapPMhash2isAside) { + for (const auto& entry : pmLookupTable.getPMs()) { mapPMhash2sumAmpl.insert({entry.first, 0}); } int vertex_time; const auto& params = FT0DigParam::Instance(); - - static bool pmGroupsInitialized = false; - static std::vector> pmtChannelGroups; - if (!pmGroupsInitialized) { - std::unordered_map> tmpGroups; - for (int ch = 0; ch < o2::ft0::Constants::sNCHANNELS_PM; ++ch) { - tmpGroups[mChID2PMhash[static_cast(ch)]].push_back(ch); - } - - for (auto& [pmHash, chVec] : tmpGroups) { - std::sort(chVec.begin(), chVec.end()); - if (chVec.size() % 4 != 0) { - LOG(fatal) << "PM hash " << int(pmHash) << " has " << chVec.size() - << " channels in LUT, expected multiplicity of 4"; - } - for (size_t i = 0; i < chVec.size(); i += 4) { - std::array arr = {chVec[i + 0], chVec[i + 1], chVec[i + 2], chVec[i + 3]}; - pmtChannelGroups.push_back(arr); - } - } - pmGroupsInitialized = true; - } - int first = digitsCh.size(), nStored = 0; auto& particles = bc.hits; std::sort(std::begin(particles), std::end(particles)); auto channel_end = particles.begin(); std::vector channel_times; - std::vector baseAmp(params.mMCPs, 0.f); - std::vector finalAmp(params.mMCPs, 0.f); - std::vector chTime(params.mMCPs, -5000); - std::vector chChain(params.mMCPs, 0); - std::vector chValid(params.mMCPs, false); - - static const std::array, 4> localNeighbours = {{{{1, 2, 3}}, - {{0, 3, 2}}, - {{0, 3, 1}}, - {{1, 2, 0}}}}; - // std::set disabledChannels = {40, 41, 42, 43, 88, 89, 90, 91, 56, 57, 58, 59, 60, 61, 62, 63, 72, 73, 74, 75, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 164, 165, 166, 167, 184, 185, 186, 187, 160, 161, 162, 163, 188, 189, 190, 191, 156, 157, 158, 159, 192, 193, 194, 195, 152, 153, 154, 155, 196, 197, 198, 199, 148, 149, 150, 151, 144, 145, 146, 147, 204, 205, 206, 207, 200, 201, 202, 203}; // przykładowe kanały for (Int_t ipmt = 0; ipmt < params.mMCPs; ++ipmt) { auto channel_begin = channel_end; channel_end = std::find_if(channel_begin, particles.end(), [ipmt](BCCache::particle const& p) { return p.hit_ch != ipmt; }); + // The hits between 'channel_begin' and 'channel_end' now contains all hits for channel 'ipmt' + if (channel_end - channel_begin < params.mAmp_trsh) { continue; } @@ -442,98 +361,31 @@ void Digitizer::storeBC(BCCache& bc, if (mCalibOffset) { miscalib = mCalibOffset->mTimeOffsets[ipmt]; } - int smeared_time = 1000. * (*cfd.particle - params.mCfdShift) * params.mChannelWidthInverse + miscalib; + int smeared_time = 1000. * (*cfd.particle - params.mCfdShift) * params.mChannelWidthInverse + miscalib; // + int(1000. * mIntRecord.getTimeOffsetWrtBC() * params.mChannelWidthInverse); bool is_time_in_signal_gate = (smeared_time > -params.mTime_trg_gate && smeared_time < params.mTime_trg_gate); float charge = measure_amplitude(channel_times) * params.mCharge2amp; - float amp = is_time_in_signal_gate ? params.mMV_2_Nchannels * charge : 0.f; - if (amp > 4095.f) { - amp = 4095.f; + float amp = is_time_in_signal_gate ? params.mMV_2_Nchannels * charge : 0; + if (amp > 4095) { + amp = 4095; } // if (!disabledChannels.count(ipmt)) { // continue; // } - LOG(debug) << mEventID << " bc " << firstBCinDeque.bc << " orbit " << firstBCinDeque.orbit - << ", ipmt " << ipmt << ", smeared_time " << smeared_time - << " nStored " << nStored << " offset " << miscalib - << " base amp " << amp; + LOG(debug) << mEventID << " bc " << firstBCinDeque.bc << " orbit " << firstBCinDeque.orbit << ", ipmt " << ipmt << ", smeared_time " << smeared_time << " nStored " << nStored << " offset " << miscalib; if (is_time_in_signal_gate) { chain |= (1 << o2::ft0::ChannelData::EEventDataBit::kIsCFDinADCgate); chain |= (1 << o2::ft0::ChannelData::EEventDataBit::kIsEventInTVDC); - } - - baseAmp[ipmt] = amp; - finalAmp[ipmt] = amp; - chTime[ipmt] = smeared_time; - chChain[ipmt] = chain; - chValid[ipmt] = true; - } - - for (const auto& channels : pmtChannelGroups) { - for (int localIdx = 0; localIdx < 4; ++localIdx) { - const int src = channels[localIdx]; - if (!chValid[src] || baseAmp[src] <= 0.f) { - continue; - } - - const int nb1 = channels[localNeighbours[localIdx][0]]; - const int nb2 = channels[localNeighbours[localIdx][1]]; - const int diag = channels[localNeighbours[localIdx][2]]; - - const float directXtalk = baseAmp[src] * params.Cross_Talk_Frac; - const float diagXtalk = baseAmp[src] * (params.Cross_Talk_Frac / 3.f); - - finalAmp[nb1] += directXtalk; - finalAmp[nb2] += directXtalk; - finalAmp[diag] += diagXtalk; - - if (!chValid[nb1] && directXtalk >= params.mAmpThresholdForCrossTalkDigit) { - chValid[nb1] = true; - chTime[nb1] = chTime[src]; - chChain[nb1] = chChain[src]; - } - - if (!chValid[nb2] && directXtalk >= params.mAmpThresholdForCrossTalkDigit) { - chValid[nb2] = true; - chTime[nb2] = chTime[src]; - chChain[nb2] = chChain[src]; - } - - if (!chValid[diag] && diagXtalk >= params.mAmpThresholdForCrossTalkDigit) { - chValid[diag] = true; - chTime[diag] = chTime[src]; - chChain[diag] = chChain[src]; + // Sum channel charge per PM (similar logic as in digits2trgFT0) + if (ipmt < o2::ft0::Constants::sNCHANNELS_PM) { + mapPMhash2sumAmpl[pmLookupTable.getPMHash(static_cast(ipmt))] += static_cast(amp); } } - } - - for (Int_t ipmt = 0; ipmt < params.mMCPs; ++ipmt) { - if (!chValid[ipmt]) { - continue; - } - - float amp = finalAmp[ipmt]; - if (amp > 4095.f) { - amp = 4095.f; - } - const bool hasPrimarySignal = (baseAmp[ipmt] > 0.f); - const bool isCrossTalkOnly = (!hasPrimarySignal && amp > 0.f); - - if (isCrossTalkOnly && amp < params.mAmpThresholdForCrossTalkDigit) { - continue; - } - - const int smeared_time = chTime[ipmt]; - const int chain = chChain[ipmt]; - const bool is_time_in_signal_gate = (smeared_time > -params.mTime_trg_gate && smeared_time < params.mTime_trg_gate); - - if (is_time_in_signal_gate && ipmt < o2::ft0::Constants::sNCHANNELS_PM) { - mapPMhash2sumAmpl[mChID2PMhash[static_cast(ipmt)]] += static_cast(amp); - } - digitsCh.emplace_back(ipmt, smeared_time, int(amp), chain); nStored++; + // fill triggers + Bool_t is_A_side = (ipmt < 4 * mGeometry.NCellsA); if (!is_time_in_signal_gate) { continue; @@ -568,13 +420,10 @@ void Digitizer::storeBC(BCCache& bc, for (const auto& entry : mapPMhash2sumAmpl) { int pmAmpl = (entry.second >> 3); sum_PM_ampl_debug += pmAmpl; - auto itSide = mMapPMhash2isAside.find(entry.first); - if (itSide != mMapPMhash2isAside.end()) { - if (itSide->second) { - sum_PM_ampl_A_debug += pmAmpl; - } else { - sum_PM_ampl_C_debug += pmAmpl; - } + if (pmLookupTable.isASide(entry.first)) { + sum_PM_ampl_A_debug += pmAmpl; + } else { + sum_PM_ampl_C_debug += pmAmpl; } } LOG(debug) << "Sum PM amplitude (LUT-based): total=" << sum_PM_ampl_debug