File indexing completed on 2025-07-01 07:56:21
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #include "CalorimeterHitDigi.h"
0014
0015 #include <DD4hep/Detector.h>
0016 #include <DD4hep/IDDescriptor.h>
0017 #include <DD4hep/Readout.h>
0018 #include <DD4hep/config.h>
0019 #include <DDSegmentation/BitFieldCoder.h>
0020 #include <Evaluator/DD4hepUnits.h>
0021 #include <algorithms/service.h>
0022 #include <edm4eic/EDM4eicVersion.h>
0023 #if EDM4EIC_VERSION_MAJOR >= 7
0024 #include <edm4eic/MCRecoCalorimeterHitAssociationCollection.h>
0025 #endif
0026 #include <edm4hep/CaloHitContributionCollection.h>
0027 #include <fmt/core.h>
0028 #include <podio/RelationRange.h>
0029 #include <algorithm>
0030 #include <cmath>
0031 #include <cstddef>
0032 #include <gsl/pointers>
0033 #include <limits>
0034 #include <map>
0035 #include <stdexcept>
0036 #include <string>
0037 #include <unordered_map>
0038 #include <utility>
0039 #include <vector>
0040
0041 #include "algorithms/calorimetry/CalorimeterHitDigiConfig.h"
0042 #include "services/evaluator/EvaluatorSvc.h"
0043
0044 using namespace dd4hep;
0045
0046 namespace eicrecon {
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056 void CalorimeterHitDigi::init() {
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073 if (m_cfg.eRes.empty()) {
0074 m_cfg.eRes.resize(3);
0075 } else if (m_cfg.eRes.size() != 3) {
0076 error("Invalid m_cfg.eRes.size()");
0077 throw std::runtime_error("Invalid m_cfg.eRes.size()");
0078 }
0079
0080
0081 tRes = m_cfg.tRes / dd4hep::ns;
0082 stepTDC = dd4hep::ns / m_cfg.resolutionTDC;
0083
0084
0085 if (m_cfg.readout.empty()) {
0086 error("readoutClass is not provided, it is needed to know the fields in readout ids");
0087 throw std::runtime_error("readoutClass is not provided");
0088 }
0089
0090
0091 try {
0092 id_spec = m_geo.detector()->readout(m_cfg.readout).idSpec();
0093 } catch (...) {
0094
0095
0096
0097 debug("Failed to load ID decoder for {}", m_cfg.readout);
0098 throw std::runtime_error(fmt::format("Failed to load ID decoder for {}", m_cfg.readout));
0099 }
0100
0101 decltype(id_mask) id_inverse_mask = 0;
0102
0103 if (!m_cfg.fields.empty()) {
0104 for (auto& field : m_cfg.fields) {
0105 id_inverse_mask |= id_spec.field(field)->mask();
0106 }
0107 debug("ID mask in {:s}: {:#064b}", m_cfg.readout, id_mask);
0108 }
0109 id_mask = ~id_inverse_mask;
0110
0111 std::function hit_to_map = [this](const edm4hep::SimCalorimeterHit& h) {
0112 std::unordered_map<std::string, double> params;
0113 for (const auto& p : id_spec.fields()) {
0114 const std::string& name = p.first;
0115 const dd4hep::IDDescriptor::Field* field = p.second;
0116 params.emplace(name, field->value(h.getCellID()));
0117 trace("{} = {}", name, field->value(h.getCellID()));
0118 }
0119 return params;
0120 };
0121
0122 auto& serviceSvc = algorithms::ServiceSvc::instance();
0123 corrMeanScale =
0124 serviceSvc.service<EvaluatorSvc>("EvaluatorSvc")->compile(m_cfg.corrMeanScale, hit_to_map);
0125
0126 std::map<std::string, readout_enum> readoutTypes{{"simple", kSimpleReadout},
0127 {"poisson_photon", kPoissonPhotonReadout},
0128 {"sipm", kSipmReadout}};
0129 if (not readoutTypes.count(m_cfg.readoutType)) {
0130 error("Invalid readoutType \"{}\"", m_cfg.readoutType);
0131 throw std::runtime_error(fmt::format("Invalid readoutType \"{}\"", m_cfg.readoutType));
0132 }
0133 readoutType = readoutTypes.at(m_cfg.readoutType);
0134 }
0135
0136 void CalorimeterHitDigi::process(const CalorimeterHitDigi::Input& input,
0137 const CalorimeterHitDigi::Output& output) const {
0138
0139 const auto [simhits] = input;
0140 #if EDM4EIC_VERSION_MAJOR >= 7
0141 auto [rawhits, rawassocs] = output;
0142 #else
0143 auto [rawhits] = output;
0144 #endif
0145
0146
0147 std::unordered_map<uint64_t, std::vector<std::size_t>> merge_map;
0148 std::size_t ix = 0;
0149 for (const auto& ahit : *simhits) {
0150 uint64_t hid = ahit.getCellID() & id_mask;
0151
0152 trace("org cell ID in {:s}: {:#064b}", m_cfg.readout, ahit.getCellID());
0153 trace("new cell ID in {:s}: {:#064b}", m_cfg.readout, hid);
0154
0155 merge_map[hid].push_back(ix);
0156
0157 ix++;
0158 }
0159
0160
0161
0162 for (const auto& [id, ixs] : merge_map) {
0163
0164
0165 edm4hep::MutableRawCalorimeterHit rawhit;
0166 #if EDM4EIC_VERSION_MAJOR >= 7
0167 std::vector<edm4eic::MutableMCRecoCalorimeterHitAssociation> rawassocs_staging;
0168 #endif
0169
0170 double edep = 0;
0171 double time = std::numeric_limits<double>::max();
0172 double max_edep = 0;
0173 auto leading_hit = (*simhits)[ixs[0]];
0174
0175 for (unsigned long i : ixs) {
0176 auto hit = (*simhits)[i];
0177
0178 double timeC = std::numeric_limits<double>::max();
0179 for (const auto& c : hit.getContributions()) {
0180 if (c.getTime() <= timeC) {
0181 timeC = c.getTime();
0182 }
0183 }
0184 if (timeC > m_cfg.capTime) {
0185 continue;
0186 }
0187 edep += hit.getEnergy();
0188 trace("adding {} \t total: {}", hit.getEnergy(), edep);
0189
0190
0191 if (hit.getEnergy() > max_edep) {
0192 max_edep = hit.getEnergy();
0193 leading_hit = hit;
0194 if (timeC <= time) {
0195 time = timeC;
0196 }
0197 }
0198
0199 #if EDM4EIC_VERSION_MAJOR >= 7
0200 edm4eic::MutableMCRecoCalorimeterHitAssociation assoc;
0201 assoc.setRawHit(rawhit);
0202 assoc.setSimHit(hit);
0203 assoc.setWeight(hit.getEnergy());
0204 rawassocs_staging.push_back(assoc);
0205 #endif
0206 }
0207 if (time > m_cfg.capTime) {
0208 continue;
0209 }
0210
0211
0212 const double eResRel =
0213 (edep > m_cfg.threshold)
0214 ? m_gaussian(m_generator) *
0215 std::sqrt(std::pow(m_cfg.eRes[0] / std::sqrt(edep), 2) +
0216 std::pow(m_cfg.eRes[1], 2) + std::pow(m_cfg.eRes[2] / (edep), 2))
0217 : 0;
0218
0219 double corrMeanScale_value = corrMeanScale(leading_hit);
0220
0221 double ped = m_cfg.pedMeanADC + m_gaussian(m_generator) * m_cfg.pedSigmaADC;
0222
0223
0224 unsigned long long adc;
0225 unsigned long long tdc = std::llround((time + m_gaussian(m_generator) * tRes) * stepTDC);
0226
0227 if (readoutType == kSimpleReadout) {
0228 adc = std::max(std::llround(ped + edep * corrMeanScale_value * (1.0 + eResRel) /
0229 m_cfg.dyRangeADC * m_cfg.capADC),
0230 0LL);
0231 } else if (readoutType == kPoissonPhotonReadout) {
0232 const long long int n_photons_mean =
0233 edep * m_cfg.lightYield * m_cfg.photonDetectionEfficiency;
0234 std::poisson_distribution<> n_photons_detected_dist(n_photons_mean);
0235 const long long int n_photons_detected = n_photons_detected_dist(m_generator);
0236 const long long int n_max_photons =
0237 m_cfg.dyRangeADC * m_cfg.lightYield * m_cfg.photonDetectionEfficiency;
0238 trace("n_photons_detected {}", n_photons_detected);
0239 adc = std::max(std::llround(ped + n_photons_detected * corrMeanScale_value * (1.0 + eResRel) /
0240 n_max_photons * m_cfg.capADC),
0241 0LL);
0242 } else if (readoutType == kSipmReadout) {
0243 const long long int n_photons = edep * m_cfg.lightYield;
0244 std::binomial_distribution<> n_photons_detected_dist(n_photons,
0245 m_cfg.photonDetectionEfficiency);
0246 const long long int n_photons_detected = n_photons_detected_dist(m_generator);
0247 const long long int n_pixels_fired =
0248 m_cfg.numEffectiveSipmPixels *
0249 (1 - exp(-n_photons_detected / (double)m_cfg.numEffectiveSipmPixels));
0250 const long long int n_max_photons =
0251 m_cfg.dyRangeADC * m_cfg.lightYield * m_cfg.photonDetectionEfficiency;
0252 trace("n_photons_detected {}, n_pixels_fired {}, n_max_photons {}", n_photons_detected,
0253 n_pixels_fired, n_max_photons);
0254 adc = std::max(std::llround(ped + n_pixels_fired * corrMeanScale_value * (1.0 + eResRel) /
0255 n_max_photons * m_cfg.capADC),
0256 0LL);
0257 }
0258
0259 if (edep > 1.e-3)
0260 trace("E sim {} \t adc: {} \t time: {}\t maxtime: {} \t tdc: {} \t corrMeanScale: {}", edep,
0261 adc, time, m_cfg.capTime, tdc, corrMeanScale_value);
0262
0263 rawhit.setCellID(leading_hit.getCellID());
0264 rawhit.setAmplitude(adc > m_cfg.capADC ? m_cfg.capADC : adc);
0265 rawhit.setTimeStamp(tdc);
0266 rawhits->push_back(rawhit);
0267
0268 #if EDM4EIC_VERSION_MAJOR >= 7
0269 for (auto& assoc : rawassocs_staging) {
0270 assoc.setWeight(assoc.getWeight() / edep);
0271 rawassocs->push_back(assoc);
0272 }
0273 #endif
0274 }
0275 }
0276
0277 }