File indexing completed on 2025-09-18 08:17:41
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include "algorithms/calorimetry/CalorimeterHitsMerger.h"
0012
0013 #include <DD4hep/Alignments.h>
0014 #include <DD4hep/DetElement.h>
0015 #include <DD4hep/Objects.h>
0016 #include <DD4hep/Readout.h>
0017 #include <DD4hep/VolumeManager.h>
0018 #include <DDSegmentation/BitFieldCoder.h>
0019 #include <Evaluator/DD4hepUnits.h>
0020 #include <Math/GenVector/Cartesian3D.h>
0021 #include <Math/GenVector/DisplacementVector3D.h>
0022 #include <algorithms/service.h>
0023 #include <edm4hep/RawCalorimeterHit.h>
0024 #include <edm4hep/Vector3f.h>
0025 #include <fmt/core.h>
0026 #include <algorithm>
0027 #include <cmath>
0028 #include <cstddef>
0029 #include <gsl/pointers>
0030 #include <string>
0031 #include <unordered_map>
0032 #include <utility>
0033 #include <vector>
0034
0035 #include "algorithms/calorimetry/CalorimeterHitsMergerConfig.h"
0036 #include "services/evaluator/EvaluatorSvc.h"
0037
0038 namespace eicrecon {
0039
0040 void CalorimeterHitsMerger::init() {
0041
0042 if (m_cfg.readout.empty()) {
0043 error("readoutClass is not provided, it is needed to know the fields in readout ids");
0044 return;
0045 }
0046
0047
0048
0049 std::vector<std::string> fields;
0050 std::vector<std::string> transforms;
0051 for (const std::string& field_transform : m_cfg.fieldTransformations) {
0052
0053 const std::size_t isplit = field_transform.find_first_of(':');
0054 if (isplit == std::string::npos) {
0055 warning("transform '{}' ill-formatted. Format is <field>:<transformation>.", field_transform);
0056 }
0057
0058 fields.emplace_back(field_transform.substr(0, isplit));
0059 transforms.emplace_back(field_transform.substr(isplit + 1));
0060 }
0061
0062
0063
0064 try {
0065 id_desc = m_detector->readout(m_cfg.readout).idSpec();
0066 } catch (...) {
0067 warning("Failed to get idSpec for {}", m_cfg.readout);
0068 return;
0069 }
0070 try {
0071 id_decoder = id_desc.decoder();
0072 for (const std::string& field : fields) {
0073 const short index [[maybe_unused]] = id_decoder->index(field);
0074 }
0075 } catch (...) {
0076 auto mess = fmt::format("Failed to load ID decoder for {}", m_cfg.readout);
0077 warning(mess);
0078 return;
0079 }
0080
0081
0082 std::function hit_transform = [this](const edm4eic::CalorimeterHit& hit) {
0083 std::unordered_map<std::string, double> params;
0084 for (const auto& name_field : id_desc.fields()) {
0085 params.emplace(name_field.first, name_field.second->value(hit.getCellID()));
0086 trace("{} = {}", name_field.first, name_field.second->value(hit.getCellID()));
0087 }
0088 return params;
0089 };
0090
0091
0092 auto& svc = algorithms::ServiceSvc::instance();
0093 std::size_t iField = 0;
0094 for (std::string& field : fields) {
0095
0096
0097 const std::string field_transform = transforms.at(iField);
0098
0099
0100 ref_maps[field] =
0101 svc.service<EvaluatorSvc>("EvaluatorSvc")->compile(field_transform, hit_transform);
0102 trace("{}: using transformation '{}'", field, field_transform);
0103 ++iField;
0104 }
0105 }
0106
0107 void CalorimeterHitsMerger::process(const CalorimeterHitsMerger::Input& input,
0108 const CalorimeterHitsMerger::Output& output) const {
0109
0110 const auto [in_hits] = input;
0111 auto [out_hits] = output;
0112
0113
0114 MergeMap merge_map;
0115 build_merge_map(in_hits, merge_map);
0116 debug("Merge map built: merging {} hits into {} merged hits", in_hits->size(), merge_map.size());
0117
0118
0119 for (auto& it : merge_map) {
0120 std::sort(it.second.begin(), it.second.end(), [&](std::size_t ix1, std::size_t ix2) {
0121 return (*in_hits)[ix1].getEnergy() > (*in_hits)[ix2].getEnergy();
0122 });
0123 }
0124
0125
0126
0127 auto volman = m_detector->volumeManager();
0128
0129 for (const auto& [id, ixs] : merge_map) {
0130
0131 const uint64_t ref_id = id | ref_mask;
0132
0133 const auto gpos = m_converter->position(ref_id);
0134
0135 auto alignment = volman.lookupDetElement(ref_id).nominal();
0136 const auto pos = alignment.worldToLocal(dd4hep::Position(gpos.x(), gpos.y(), gpos.z()));
0137 debug("{}, {}", volman.lookupDetElement(ref_id).path(), volman.lookupDetector(ref_id).path());
0138
0139 float energy = 0.;
0140 float energyError = 0.;
0141 float time = 0;
0142 float timeError = 0;
0143 for (auto ix : ixs) {
0144 auto hit = (*in_hits)[ix];
0145 energy += hit.getEnergy();
0146 energyError += hit.getEnergyError() * hit.getEnergyError();
0147 time += hit.getTime();
0148 timeError += hit.getTimeError() * hit.getTimeError();
0149 }
0150 energyError = sqrt(energyError);
0151 time /= ixs.size();
0152 timeError = sqrt(timeError) / ixs.size();
0153
0154 const auto href = (*in_hits)[ixs.front()];
0155
0156
0157 const decltype(edm4eic::CalorimeterHitData::position) position(
0158 gpos.x() / dd4hep::mm, gpos.y() / dd4hep::mm, gpos.z() / dd4hep::mm);
0159 const decltype(edm4eic::CalorimeterHitData::local) local(
0160 pos.x() / dd4hep::mm, pos.y() / dd4hep::mm, pos.z() / dd4hep::mm);
0161
0162 auto out_hit = out_hits->create(
0163 href.getCellID(), energy, energyError, time, timeError, position, href.getDimension(),
0164 href.getSector(), href.getLayer(),
0165 local);
0166
0167
0168
0169
0170 out_hit.setRawHit(href.getRawHit());
0171 }
0172
0173 debug("Size before = {}, after = {}", in_hits->size(), out_hits->size());
0174 }
0175
0176 void CalorimeterHitsMerger::build_merge_map(const edm4eic::CalorimeterHitCollection* in_hits,
0177 MergeMap& merge_map) const {
0178
0179 std::vector<RefField> ref_fields;
0180 for (std::size_t iHit = 0; const auto& hit : *in_hits) {
0181
0182 ref_fields.clear();
0183 for (const auto& name_field : id_desc.fields()) {
0184
0185
0186
0187 if (ref_maps.contains(name_field.first)) {
0188 ref_fields.emplace_back(name_field.first, ref_maps[name_field.first](hit));
0189 } else {
0190 ref_fields.emplace_back(name_field.first,
0191 id_decoder->get(hit.getCellID(), name_field.first));
0192 }
0193 }
0194
0195
0196 const uint64_t ref_id = id_desc.encode(ref_fields);
0197 merge_map[ref_id].push_back(iHit);
0198 ++iHit;
0199
0200 }
0201
0202 }
0203
0204 }