File indexing completed on 2025-07-12 07:55:37
0001
0002
0003
0004
0005
0006
0007
0008 #include "CalorimeterHitReco.h"
0009
0010 #include <DD4hep/Alignments.h>
0011 #include <DD4hep/Handle.h>
0012 #include <DD4hep/IDDescriptor.h>
0013 #include <DD4hep/Objects.h>
0014 #include <DD4hep/Readout.h>
0015 #include <DD4hep/Segmentations.h>
0016 #include <DD4hep/Shapes.h>
0017 #include <DD4hep/VolumeManager.h>
0018 #include <DD4hep/Volumes.h>
0019 #include <DD4hep/config.h>
0020 #include <DD4hep/detail/SegmentationsInterna.h>
0021 #include <DDSegmentation/BitFieldCoder.h>
0022 #include <DDSegmentation/MultiSegmentation.h>
0023 #include <DDSegmentation/Segmentation.h>
0024 #include <Evaluator/DD4hepUnits.h>
0025 #include <Math/GenVector/Cartesian3D.h>
0026 #include <Math/GenVector/DisplacementVector3D.h>
0027 #include <algorithms/service.h>
0028 #include <fmt/core.h>
0029 #include <fmt/format.h>
0030 #include <algorithm>
0031 #include <cctype>
0032 #include <gsl/pointers>
0033 #include <map>
0034 #include <ostream>
0035 #include <string>
0036 #include <unordered_map>
0037 #include <utility>
0038 #include <vector>
0039
0040 #include "algorithms/calorimetry/CalorimeterHitRecoConfig.h"
0041 #include "services/evaluator/EvaluatorSvc.h"
0042
0043 using namespace dd4hep;
0044
0045 namespace eicrecon {
0046
0047 void CalorimeterHitReco::init() {
0048
0049
0050
0051 if (m_cfg.thresholdFactor * m_cfg.thresholdValue != 0) {
0052 error("thresholdFactor = {}, thresholdValue = {}. Only one of these should be non-zero.",
0053 m_cfg.thresholdFactor, m_cfg.thresholdValue);
0054 throw;
0055 }
0056 thresholdADC = m_cfg.thresholdFactor * m_cfg.pedSigmaADC + m_cfg.thresholdValue;
0057
0058 stepTDC = dd4hep::ns / m_cfg.resolutionTDC;
0059
0060
0061 if (m_cfg.readout.empty()) {
0062 return;
0063 }
0064
0065
0066 try {
0067 id_spec = m_detector->readout(m_cfg.readout).idSpec();
0068 } catch (...) {
0069 warning("Failed to get idSpec for {}", m_cfg.readout);
0070 return;
0071 }
0072
0073 try {
0074 id_dec = id_spec.decoder();
0075 if (!m_cfg.sectorField.empty()) {
0076 sector_idx = id_dec->index(m_cfg.sectorField);
0077 debug("Find sector field {}, index = {}", m_cfg.sectorField, sector_idx);
0078 }
0079 if (!m_cfg.layerField.empty()) {
0080 layer_idx = id_dec->index(m_cfg.layerField);
0081 debug("Find layer field {}, index = {}", m_cfg.layerField, sector_idx);
0082 }
0083 if (!m_cfg.maskPosFields.empty()) {
0084 std::size_t tmp_mask = 0;
0085 for (auto& field : m_cfg.maskPosFields) {
0086 tmp_mask |= id_spec.field(field)->mask();
0087 }
0088
0089 gpos_mask = tmp_mask;
0090 }
0091 } catch (...) {
0092 if (id_dec == nullptr) {
0093 warning("Failed to load ID decoder for {}", m_cfg.readout);
0094 std::stringstream readouts;
0095 for (auto r : m_detector->readouts()) {
0096 readouts << "\"" << r.first << "\", ";
0097 }
0098 warning("Available readouts: {}", readouts.str());
0099 } else {
0100 warning("Failed to find field index for {}.", m_cfg.readout);
0101 if (!m_cfg.sectorField.empty()) {
0102 warning(" -- looking for sector field \"{}\".", m_cfg.sectorField);
0103 }
0104 if (!m_cfg.layerField.empty()) {
0105 warning(" -- looking for layer field \"{}\".", m_cfg.layerField);
0106 }
0107 if (!m_cfg.maskPosFields.empty()) {
0108 warning(" -- looking for masking fields \"{}\".", fmt::join(m_cfg.maskPosFields, ", "));
0109 }
0110 std::stringstream fields;
0111 for (auto field : id_spec.decoder()->fields()) {
0112 fields << "\"" << field.name() << "\", ";
0113 }
0114 warning("Available fields: {}", fields.str());
0115 warning("n.b. The local position, sector id and layer id will not be correct for this.");
0116 warning("Position masking may not be applied.");
0117 warning("however, the position, energy, and time values should still be good.");
0118 }
0119
0120 return;
0121 }
0122
0123 id_spec = m_detector->readout(m_cfg.readout).idSpec();
0124
0125 std::function hit_to_map = [this](const edm4hep::RawCalorimeterHit& h) {
0126 std::unordered_map<std::string, double> params;
0127 for (const auto& p : id_spec.fields()) {
0128 const std::string& name = p.first;
0129 const dd4hep::IDDescriptor::Field* field = p.second;
0130 params.emplace(name, field->value(h.getCellID()));
0131 trace("{} = {}", name, field->value(h.getCellID()));
0132 }
0133 return params;
0134 };
0135
0136 auto& serviceSvc = algorithms::ServiceSvc::instance();
0137 sampFrac = serviceSvc.service<EvaluatorSvc>("EvaluatorSvc")->compile(m_cfg.sampFrac, hit_to_map);
0138
0139
0140 if (!m_cfg.localDetElement.empty()) {
0141 try {
0142 m_local = m_detector->detector(m_cfg.localDetElement);
0143 info("local coordinate system from DetElement {}", m_cfg.localDetElement);
0144 } catch (...) {
0145 error("failed to load local coordinate system from DetElement {}", m_cfg.localDetElement);
0146 return;
0147 }
0148 } else {
0149 std::vector<std::pair<std::string, int>> fields;
0150 for (auto f : m_cfg.localDetFields) {
0151 fields.emplace_back(f, 0);
0152 }
0153 local_mask = id_spec.get_mask(fields);
0154
0155 if (fields.empty()) {
0156 local_mask = ~static_cast<decltype(local_mask)>(0);
0157 }
0158 }
0159 }
0160
0161 void CalorimeterHitReco::process(const CalorimeterHitReco::Input& input,
0162 const CalorimeterHitReco::Output& output) const {
0163
0164 const auto [rawhits] = input;
0165 auto [recohits] = output;
0166
0167
0168
0169
0170
0171
0172
0173
0174
0175 if (NcellIDerrors >= MaxCellIDerrors) {
0176 return;
0177 }
0178
0179 for (const auto& rh : *rawhits) {
0180
0181
0182 const auto cellID = rh.getCellID();
0183 if (rh.getAmplitude() < m_cfg.pedMeanADC + thresholdADC) {
0184 continue;
0185 }
0186
0187 if (rh.getAmplitude() > static_cast<int>(m_cfg.capADC)) {
0188 error("Encountered hit with amplitude {} outside of ADC capacity {}", rh.getAmplitude(),
0189 m_cfg.capADC);
0190 continue;
0191 }
0192
0193
0194 const int lid = id_dec != nullptr && !m_cfg.layerField.empty()
0195 ? static_cast<int>(id_dec->get(cellID, layer_idx))
0196 : -1;
0197 const int sid = id_dec != nullptr && !m_cfg.sectorField.empty()
0198 ? static_cast<int>(id_dec->get(cellID, sector_idx))
0199 : -1;
0200
0201
0202 float sampFrac_value = sampFrac(rh);
0203 float energy = (((signed)rh.getAmplitude() - (signed)m_cfg.pedMeanADC)) /
0204 static_cast<float>(m_cfg.capADC) * m_cfg.dyRangeADC / sampFrac_value;
0205
0206 const float time = rh.getTimeStamp() / stepTDC;
0207 trace("cellID {}, \t energy: {}, TDC: {}, time: {}, sampFrac: {}", cellID, energy,
0208 rh.getTimeStamp(), time, sampFrac_value);
0209
0210 dd4hep::DetElement local;
0211 dd4hep::Position gpos;
0212 try {
0213
0214 gpos = m_converter->position(cellID);
0215
0216
0217 if (gpos_mask != 0) {
0218 auto mpos = m_converter->position(cellID & ~gpos_mask);
0219
0220 for (const char& c : m_cfg.maskPos) {
0221 switch (std::tolower(c)) {
0222 case 'x':
0223 gpos.SetX(mpos.X());
0224 break;
0225 case 'y':
0226 gpos.SetY(mpos.Y());
0227 break;
0228 case 'z':
0229 gpos.SetZ(mpos.Z());
0230 break;
0231 default:
0232 break;
0233 }
0234 }
0235 }
0236
0237
0238 if (m_cfg.localDetElement.empty()) {
0239 auto volman = m_detector->volumeManager();
0240 local = volman.lookupDetElement(cellID & local_mask);
0241 } else {
0242 local = m_local;
0243 }
0244 } catch (...) {
0245
0246
0247 if (++NcellIDerrors >= MaxCellIDerrors) {
0248 error("Maximum number of errors reached: {}", MaxCellIDerrors);
0249 error("This is likely an issue with the cellID being unknown.");
0250 error("Note: local_mask={:X} example cellID={:x}", local_mask, cellID);
0251 error("Disabling this algorithm since it requires a valid cellID.");
0252 error("(See {}:{})", __FILE__, __LINE__);
0253 }
0254 continue;
0255 }
0256
0257 const auto pos = local.nominal().worldToLocal(gpos);
0258 std::vector<double> cdim;
0259
0260
0261 const dd4hep::DDSegmentation::Segmentation* segmentation =
0262 m_converter->findReadout(local).segmentation()->segmentation;
0263 auto segmentation_type = segmentation->type();
0264
0265 while (segmentation_type == "MultiSegmentation") {
0266 const auto* multi_segmentation =
0267 dynamic_cast<const dd4hep::DDSegmentation::MultiSegmentation*>(segmentation);
0268 const dd4hep::DDSegmentation::Segmentation& sub_segmentation =
0269 multi_segmentation->subsegmentation(cellID);
0270
0271 segmentation = &sub_segmentation;
0272 segmentation_type = segmentation->type();
0273 }
0274
0275 if (segmentation_type == "CartesianGridXY" || segmentation_type == "HexGridXY") {
0276 auto cell_dim = m_converter->cellDimensions(cellID);
0277 cdim.resize(3);
0278 cdim[0] = cell_dim[0];
0279 cdim[1] = cell_dim[1];
0280 debug("Using segmentation for cell dimensions: {}", fmt::join(cdim, ", "));
0281 } else if (segmentation_type == "CartesianStripZ") {
0282 auto cell_dim = m_converter->cellDimensions(cellID);
0283 cdim.resize(3);
0284 cdim[2] = cell_dim[0];
0285 debug("Using segmentation for cell dimensions: {}", fmt::join(cdim, ", "));
0286 } else {
0287 if ((segmentation_type != "NoSegmentation") && (!warned_unsupported_segmentation)) {
0288 warning("Unsupported segmentation type \"{}\"", segmentation_type);
0289 warned_unsupported_segmentation = true;
0290 }
0291
0292
0293 cdim =
0294 m_converter->findContext(cellID)->volumePlacement().volume().boundingBox().dimensions();
0295 std::transform(cdim.begin(), cdim.end(), cdim.begin(), [](auto&& PH1) {
0296 return std::multiplies<double>()(std::forward<decltype(PH1)>(PH1), 2);
0297 });
0298 debug("Using bounding box for cell dimensions: {}", fmt::join(cdim, ", "));
0299 }
0300
0301
0302
0303 const decltype(edm4eic::CalorimeterHitData::position) position(
0304 gpos.x() / dd4hep::mm, gpos.y() / dd4hep::mm, gpos.z() / dd4hep::mm);
0305 const decltype(edm4eic::CalorimeterHitData::dimension) dimension(
0306 cdim.at(0) / dd4hep::mm, cdim.at(1) / dd4hep::mm, cdim.at(2) / dd4hep::mm);
0307 const decltype(edm4eic::CalorimeterHitData::local) local_position(
0308 pos.x() / dd4hep::mm, pos.y() / dd4hep::mm, pos.z() / dd4hep::mm);
0309
0310 auto recohit = recohits->create(rh.getCellID(), energy, 0, time, 0, position, dimension, sid,
0311 lid, local_position);
0312 recohit.setRawHit(rh);
0313 }
0314 }
0315
0316 }