File indexing completed on 2024-06-29 07:05:54
0001
0002
0003
0004
0005
0006
0007
0008 #include <DD4hep/Readout.h>
0009 #include <Evaluator/DD4hepUnits.h>
0010 #include <TInterpreter.h>
0011 #include <TInterpreterValue.h>
0012 #include <edm4hep/Vector2f.h>
0013 #include <edm4hep/Vector3f.h>
0014 #include <fmt/format.h>
0015 #include <algorithm>
0016 #include <gsl/pointers>
0017 #include <map>
0018 #include <memory>
0019 #include <set>
0020 #include <sstream>
0021 #include <stdexcept>
0022 #include <string>
0023 #include <tuple>
0024 #include <utility>
0025 #include <vector>
0026
0027 #include "CalorimeterIslandCluster.h"
0028 #include "algorithms/calorimetry/CalorimeterIslandClusterConfig.h"
0029
0030 using namespace edm4eic;
0031
0032 namespace eicrecon {
0033
0034 unsigned int CalorimeterIslandCluster::function_id = 0;
0035
0036 static double Phi_mpi_pi(double phi) {
0037 return std::remainder(phi, 2 * M_PI);
0038 }
0039
0040 static edm4hep::Vector2f localDistXY(const CaloHit &h1, const CaloHit &h2) {
0041 const auto delta =h1.getLocal() - h2.getLocal();
0042 return {delta.x, delta.y};
0043 }
0044 static edm4hep::Vector2f localDistXZ(const CaloHit &h1, const CaloHit &h2) {
0045 const auto delta = h1.getLocal() - h2.getLocal();
0046 return {delta.x, delta.z};
0047 }
0048 static edm4hep::Vector2f localDistYZ(const CaloHit &h1, const CaloHit &h2) {
0049 const auto delta = h1.getLocal() - h2.getLocal();
0050 return {delta.y, delta.z};
0051 }
0052 static edm4hep::Vector2f dimScaledLocalDistXY(const CaloHit &h1, const CaloHit &h2) {
0053 const auto delta = h1.getLocal() - h2.getLocal();
0054
0055 const auto dimsum = h1.getDimension() + h2.getDimension();
0056
0057 return {2 * delta.x / dimsum.x, 2 * delta.y / dimsum.y};
0058 }
0059 static edm4hep::Vector2f globalDistRPhi(const CaloHit &h1, const CaloHit &h2) {
0060 using vector_type = decltype(edm4hep::Vector2f::a);
0061 return {
0062 static_cast<vector_type>(
0063 edm4hep::utils::magnitude(h1.getPosition()) - edm4hep::utils::magnitude(h2.getPosition())
0064 ),
0065 static_cast<vector_type>(
0066 Phi_mpi_pi(edm4hep::utils::angleAzimuthal(h1.getPosition()) - edm4hep::utils::angleAzimuthal(h2.getPosition()))
0067 )
0068 };
0069 }
0070 static edm4hep::Vector2f globalDistEtaPhi(const CaloHit &h1, const CaloHit &h2) {
0071 using vector_type = decltype(edm4hep::Vector2f::a);
0072 return {
0073 static_cast<vector_type>(
0074 edm4hep::utils::eta(h1.getPosition()) - edm4hep::utils::eta(h2.getPosition())
0075 ),
0076 static_cast<vector_type>(
0077 Phi_mpi_pi(edm4hep::utils::angleAzimuthal(h1.getPosition()) - edm4hep::utils::angleAzimuthal(h2.getPosition()))
0078 )
0079 };
0080 }
0081
0082
0083
0084
0085 void CalorimeterIslandCluster::init() {
0086
0087 static std::map<std::string,
0088 std::tuple<std::function<edm4hep::Vector2f(const CaloHit&, const CaloHit&)>, std::vector<double>>>
0089 distMethods{
0090 {"localDistXY", {localDistXY, {dd4hep::mm, dd4hep::mm}}}, {"localDistXZ", {localDistXZ, {dd4hep::mm, dd4hep::mm}}},
0091 {"localDistYZ", {localDistYZ, {dd4hep::mm, dd4hep::mm}}}, {"dimScaledLocalDistXY", {dimScaledLocalDistXY, {1., 1.}}},
0092 {"globalDistRPhi", {globalDistRPhi, {dd4hep::mm, dd4hep::rad}}}, {"globalDistEtaPhi", {globalDistEtaPhi, {1., dd4hep::rad}}}
0093 };
0094
0095
0096
0097 auto set_dist_method = [this](std::pair<std::string, std::vector<double>> uprop) {
0098 if (uprop.second.size() == 0) {
0099 return false;
0100 }
0101 auto& [method, units] = distMethods[uprop.first];
0102 if (uprop.second.size() != units.size()) {
0103 warning("Expect {} values from {}, received {}. ignored it.", units.size(), uprop.first, uprop.second.size());
0104 return false;
0105 } else {
0106 for (size_t i = 0; i < units.size(); ++i) {
0107 neighbourDist[i] = uprop.second[i] / units[i];
0108 }
0109 hitsDist = method;
0110 info("Clustering uses {} with distances <= [{}]", uprop.first, fmt::join(neighbourDist, ","));
0111 }
0112 return true;
0113 };
0114
0115 std::map<std::string, std::vector<double>> uprops{
0116 {"localDistXY", m_cfg.localDistXY},
0117 {"localDistXZ", m_cfg.localDistXZ},
0118 {"localDistYZ", m_cfg.localDistYZ},
0119 {"globalDistRPhi", m_cfg.globalDistRPhi},
0120 {"globalDistEtaPhi", m_cfg.globalDistEtaPhi},
0121
0122 {"dimScaledLocalDistXY", m_cfg.dimScaledLocalDistXY}
0123 };
0124
0125 bool method_found = false;
0126
0127
0128 if (!m_cfg.adjacencyMatrix.empty()) {
0129
0130 if (m_cfg.readout.empty()) {
0131 error("readoutClass is not provided, it is needed to know the fields in readout ids");
0132 }
0133 m_idSpec = m_detector->readout(m_cfg.readout).idSpec();
0134
0135 std::string func_name = fmt::format("_CalorimeterIslandCluster_{}", function_id++);
0136 std::ostringstream sstr;
0137 sstr << "bool " << func_name << "(double params[]){";
0138 unsigned int param_ix = 0;
0139 for(const auto &p : m_idSpec.fields()) {
0140 const std::string &name = p.first;
0141 const dd4hep::IDDescriptor::Field* field = p.second;
0142 sstr << "double " << name << "_1 = params[" << (param_ix++) << "];";
0143 sstr << "double " << name << "_2 = params[" << (param_ix++) << "];";
0144 }
0145 sstr << "return " << m_cfg.adjacencyMatrix << ";";
0146 sstr << "}";
0147 debug("Compiling {}", sstr.str());
0148
0149 TInterpreter *interp = TInterpreter::Instance();
0150 interp->ProcessLine(sstr.str().c_str());
0151 std::unique_ptr<TInterpreterValue> func_val { gInterpreter->MakeInterpreterValue() };
0152 interp->Evaluate(func_name.c_str(), *func_val);
0153 typedef bool (*func_t)(double params[]);
0154 func_t func = ((func_t)(func_val->GetAsPointer()));
0155
0156 is_neighbour = [this, func, param_ix](const CaloHit &h1, const CaloHit &h2) {
0157 std::vector<double> params;
0158 params.reserve(param_ix);
0159 for(const auto &p : m_idSpec.fields()) {
0160 const std::string &name = p.first;
0161 const dd4hep::IDDescriptor::Field* field = p.second;
0162 params.push_back(field->value(h1.getCellID()));
0163 params.push_back(field->value(h2.getCellID()));
0164 trace("{}_1 = {}", name, field->value(h1.getCellID()));
0165 trace("{}_2 = {}", name, field->value(h2.getCellID()));
0166 }
0167 return func(params.data());
0168 };
0169 method_found = true;
0170 }
0171
0172
0173 if (not method_found) {
0174 for (auto& uprop : uprops) {
0175 if (set_dist_method(uprop)) {
0176 method_found = true;
0177
0178 is_neighbour = [this](const CaloHit &h1, const CaloHit &h2) {
0179
0180 if (h1.getSector() == h2.getSector()) {
0181 auto dist = hitsDist(h1, h2);
0182 return (fabs(dist.a) <= neighbourDist[0]) && (fabs(dist.b) <= neighbourDist[1]);
0183
0184 } else {
0185
0186
0187 return (edm4hep::utils::magnitude(h1.getPosition() - h2.getPosition()) <= m_cfg.sectorDist / dd4hep::mm);
0188 }
0189 };
0190
0191 info("Using clustering method: {}", uprop.first);
0192 break;
0193 }
0194 }
0195 }
0196
0197 if (not method_found) {
0198 throw std::runtime_error("Cannot determine the clustering coordinates");
0199 }
0200
0201 if (m_cfg.splitCluster) {
0202 auto transverseEnergyProfileMetric_it = std::find_if(distMethods.begin(), distMethods.end(), [&](auto &p) { return m_cfg.transverseEnergyProfileMetric == p.first; });
0203 if (transverseEnergyProfileMetric_it == distMethods.end()) {
0204 throw std::runtime_error(fmt::format("Unsupported value \"{}\" for \"transverseEnergyProfileMetric\"", m_cfg.transverseEnergyProfileMetric));
0205 }
0206 transverseEnergyProfileMetric = std::get<0>(transverseEnergyProfileMetric_it->second);
0207 std::vector<double> &units = std::get<1>(transverseEnergyProfileMetric_it->second);
0208 for (auto unit : units) {
0209 if (unit != units[0]) {
0210 throw std::runtime_error(fmt::format("Metric {} has incompatible dimension units", m_cfg.transverseEnergyProfileMetric));
0211 }
0212 }
0213 transverseEnergyProfileScaleUnits = units[0];
0214 }
0215
0216 return;
0217 }
0218
0219
0220 void CalorimeterIslandCluster::process(
0221 const CalorimeterIslandCluster::Input& input,
0222 const CalorimeterIslandCluster::Output& output) const {
0223
0224 const auto [hits] = input;
0225 auto [proto_clusters] = output;
0226
0227
0228 std::vector<std::set<std::size_t>> groups;
0229
0230 std::vector<bool> visits(hits->size(), false);
0231 for (size_t i = 0; i < hits->size(); ++i) {
0232
0233 {
0234 const auto& hit = (*hits)[i];
0235 debug("hit {:d}: energy = {:.4f} MeV, local = ({:.4f}, {:.4f}) mm, global=({:.4f}, {:.4f}, {:.4f}) mm", i, hit.getEnergy() * 1000., hit.getLocal().x, hit.getLocal().y, hit.getPosition().x, hit.getPosition().y, hit.getPosition().z);
0236 }
0237
0238 if (visits[i]) {
0239 continue;
0240 }
0241 groups.emplace_back();
0242
0243 bfs_group(*hits, groups.back(), i, visits);
0244 }
0245
0246 for (auto& group : groups) {
0247 if (group.empty()) {
0248 continue;
0249 }
0250 auto maxima = find_maxima(*hits, group, !m_cfg.splitCluster);
0251 split_group(*hits, group, maxima, proto_clusters);
0252
0253 debug("hits in a group: {}, local maxima: {}", group.size(), maxima.size());
0254 }
0255 }
0256
0257 }