Warning, file /EICrecon/src/algorithms/fardetectors/FarDetectorLinearTracking.cc was not indexed
or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001
0002
0003
0004 #include <DD4hep/VolumeManager.h>
0005 #include <Evaluator/DD4hepUnits.h>
0006 #include <Math/GenVector/Cartesian3D.h>
0007 #include <Math/GenVector/DisplacementVector3D.h>
0008 #include <algorithms/geo.h>
0009 #include <edm4eic/Cov6f.h>
0010 #include <edm4eic/MCRecoTrackParticleAssociationCollection.h>
0011 #include <edm4eic/MCRecoTrackerHitAssociationCollection.h>
0012 #include <edm4eic/MCRecoTrackerHitLinkCollection.h>
0013 #include <edm4eic/Measurement2DCollection.h>
0014 #include <edm4eic/RawTrackerHit.h>
0015 #include <edm4eic/TrackCollection.h>
0016 #include <edm4eic/TrackerHit.h>
0017 #include <edm4hep/MCParticle.h>
0018 #include <edm4hep/SimTrackerHit.h>
0019 #include <edm4hep/Vector2f.h>
0020 #include <edm4hep/Vector3d.h>
0021 #include <edm4hep/Vector3f.h>
0022 #include <edm4hep/utils/vector_utils.h>
0023 #include <podio/LinkNavigator.h>
0024 #include <podio/RelationRange.h>
0025 #include <podio/detail/Link.h>
0026 #include <Eigen/Geometry>
0027 #include <Eigen/Householder>
0028 #include <Eigen/Jacobi>
0029 #include <Eigen/SVD>
0030 #include <algorithm>
0031 #include <cmath>
0032 #include <cstddef>
0033 #include <cstdint>
0034 #include <memory>
0035 #include <new>
0036 #include <tuple>
0037 #include <unordered_map>
0038 #include <utility>
0039
0040 #include "FarDetectorLinearTracking.h"
0041 #include "algorithms/fardetectors/FarDetectorLinearTrackingConfig.h"
0042
0043 namespace eicrecon {
0044
0045 void FarDetectorLinearTracking::init() {
0046
0047
0048 m_layerWeights = Eigen::VectorXd::Constant(m_cfg.n_layer, 1);
0049
0050 for (std::size_t i = 0; i < std::min(m_cfg.layer_weights.size(), m_cfg.n_layer); i++) {
0051 m_layerWeights(i) = m_cfg.layer_weights[i];
0052 }
0053
0054
0055 m_optimumDirection = Eigen::Vector3d::UnitZ();
0056 m_optimumDirection =
0057 Eigen::AngleAxisd(m_cfg.optimum_theta, Eigen::Vector3d::UnitY()) * m_optimumDirection;
0058 m_optimumDirection =
0059 Eigen::AngleAxisd(m_cfg.optimum_phi, Eigen::Vector3d::UnitZ()) * m_optimumDirection;
0060
0061 m_cellid_converter = algorithms::GeoSvc::instance().cellIDPositionConverter();
0062 }
0063
0064 void FarDetectorLinearTracking::process(const FarDetectorLinearTracking::Input& input,
0065 const FarDetectorLinearTracking::Output& output) const {
0066
0067 const auto [inputhits, hitLinks, assocHits] = input;
0068 auto [outputTracks, trackLinks, assocTracks] = output;
0069
0070
0071 std::size_t nCollections = inputhits.size();
0072 if (nCollections != m_cfg.n_layer) {
0073 error("Wrong number of input collections passed to algorithm");
0074 return;
0075 }
0076
0077
0078 const bool do_assoc = hitLinks != nullptr && !hitLinks->empty();
0079 if (!do_assoc) {
0080 debug("Provided MCRecoTrackerHitLink collection is empty. No truth associations "
0081 "will be performed.");
0082 }
0083
0084 std::optional<podio::LinkNavigator<edm4eic::MCRecoTrackerHitLinkCollection>> link_nav;
0085 if (do_assoc) {
0086 link_nav.emplace(*hitLinks);
0087 }
0088
0089 std::vector<std::vector<Eigen::Vector3d>> convertedHits;
0090 std::vector<std::vector<edm4hep::MCParticle>> assocParts;
0091 convertedHits.reserve(m_cfg.n_layer);
0092 assocParts.reserve(m_cfg.n_layer);
0093
0094
0095
0096
0097 for (const auto& layerHits : inputhits) {
0098 if ((*layerHits).size() > m_cfg.layer_hits_max) {
0099 info("Too many hits in layer");
0100 return;
0101 }
0102 if ((*layerHits).empty()) {
0103 trace("No hits in layer");
0104 return;
0105 }
0106 ConvertClusters(*layerHits, *link_nav, *assocHits, convertedHits, assocParts);
0107 }
0108
0109
0110 Eigen::MatrixXd hitMatrix(3, m_cfg.n_layer);
0111
0112
0113 std::vector<std::size_t> layerHitIndex(m_cfg.n_layer, 0);
0114
0115 int layer = 0;
0116
0117
0118 while (true) {
0119 hitMatrix.col(layer) << convertedHits[layer][layerHitIndex[layer]];
0120
0121 bool isValid = true;
0122
0123 if (layer > 0 && m_cfg.restrict_direction) {
0124 isValid = checkHitPair(hitMatrix.col(layer - 1), hitMatrix.col(layer));
0125 }
0126
0127
0128 if (isValid) {
0129 if (layer == static_cast<long>(m_cfg.n_layer) - 1) {
0130
0131 checkHitCombination(&hitMatrix, outputTracks, trackLinks, assocTracks, inputhits,
0132 assocParts, layerHitIndex);
0133 } else {
0134 layer++;
0135 continue;
0136 }
0137 }
0138
0139
0140 layerHitIndex[layer]++;
0141
0142 bool doBreak = false;
0143
0144 while (layerHitIndex[layer] >= convertedHits[layer].size()) {
0145 layerHitIndex[layer] = 0;
0146 if (layer == 0) {
0147 doBreak = true;
0148 break;
0149 }
0150 layer--;
0151
0152 layerHitIndex[layer]++;
0153 }
0154 if (doBreak) {
0155 break;
0156 }
0157 }
0158 }
0159
0160 void FarDetectorLinearTracking::checkHitCombination(
0161 Eigen::MatrixXd* hitMatrix, edm4eic::TrackCollection* outputTracks,
0162 edm4eic::MCRecoTrackParticleLinkCollection* trackLinks,
0163 edm4eic::MCRecoTrackParticleAssociationCollection* assocTracks,
0164 const std::vector<gsl::not_null<const edm4eic::Measurement2DCollection*>>& inputHits,
0165 const std::vector<std::vector<edm4hep::MCParticle>>& assocParts,
0166 const std::vector<std::size_t>& layerHitIndex) const {
0167
0168 Eigen::Vector3d weightedAnchor = (*hitMatrix) * m_layerWeights / (m_layerWeights.sum());
0169
0170 auto localMatrix = (*hitMatrix).colwise() - weightedAnchor;
0171
0172 Eigen::BDCSVD<Eigen::MatrixXd> svd(localMatrix.transpose(),
0173 Eigen::ComputeThinU | Eigen::ComputeThinV);
0174
0175 auto V = svd.matrixV();
0176
0177
0178 auto rotatedMatrix = localMatrix.transpose() * V;
0179 auto residuals = rotatedMatrix.rightCols(2);
0180 double chi2 = (residuals.array() * residuals.array()).sum() / (2 * m_cfg.n_layer);
0181
0182 if (chi2 > m_cfg.chi2_max) {
0183 return;
0184 }
0185
0186 edm4hep::Vector3d outPos = weightedAnchor.data();
0187 edm4hep::Vector3d outVec = V.col(0).data();
0188
0189
0190 if (outVec.z > 0) {
0191 outVec = outVec * -1;
0192 }
0193
0194 int32_t type{0};
0195 edm4hep::Vector3f position(outPos.x, outPos.y, outPos.z);
0196 edm4hep::Vector3f momentum(outVec.x, outVec.y, outVec.z);
0197 edm4eic::Cov6f positionMomentumCovariance;
0198 float time{0};
0199 float timeError{0};
0200 float charge{-1};
0201 int32_t ndf{static_cast<int32_t>(m_cfg.n_layer) - 1};
0202 int32_t pdg{11};
0203
0204
0205 auto track = (*outputTracks)
0206 .create(type, position, momentum, positionMomentumCovariance, time, timeError,
0207 charge, chi2, ndf, pdg);
0208
0209
0210 std::unordered_map<edm4hep::MCParticle, int> particleCount;
0211 for (std::size_t layer = 0; layer < layerHitIndex.size(); layer++) {
0212 track.addToMeasurements((*inputHits[layer])[layerHitIndex[layer]]);
0213 const auto& assocParticle = assocParts[layer][layerHitIndex[layer]];
0214 particleCount[assocParticle]++;
0215 }
0216
0217
0218 for (const auto& [particle, count] : particleCount) {
0219 auto trackLink = trackLinks->create();
0220 trackLink.setFrom(track);
0221 trackLink.setTo(particle);
0222 trackLink.setWeight(count / static_cast<double>(m_cfg.n_layer));
0223 auto trackAssoc = assocTracks->create();
0224 trackAssoc.setRec(track);
0225 trackAssoc.setSim(particle);
0226 trackAssoc.setWeight(count / static_cast<double>(m_cfg.n_layer));
0227 }
0228 }
0229
0230
0231 bool FarDetectorLinearTracking::checkHitPair(const Eigen::Vector3d& hit1,
0232 const Eigen::Vector3d& hit2) const {
0233
0234 Eigen::Vector3d hitDiff = hit2 - hit1;
0235 hitDiff.normalize();
0236
0237 double angle = std::acos(hitDiff.dot(m_optimumDirection));
0238
0239 debug("Vector: x={}, y={}, z={}", hitDiff.x(), hitDiff.y(), hitDiff.z());
0240 debug("Optimum: x={}, y={}, z={}", m_optimumDirection.x(), m_optimumDirection.y(),
0241 m_optimumDirection.z());
0242 debug("Angle: {}, Tolerance {}", angle, m_cfg.step_angle_tolerance);
0243
0244 return angle <= m_cfg.step_angle_tolerance;
0245 }
0246
0247
0248 void FarDetectorLinearTracking::ConvertClusters(
0249 const edm4eic::Measurement2DCollection& clusters,
0250 const podio::LinkNavigator<edm4eic::MCRecoTrackerHitLinkCollection>& link_nav,
0251 [[maybe_unused]] const edm4eic::MCRecoTrackerHitAssociationCollection& assoc_hits,
0252 std::vector<std::vector<Eigen::Vector3d>>& pointPositions,
0253 std::vector<std::vector<edm4hep::MCParticle>>& assoc_parts) const {
0254
0255
0256 const dd4hep::VolumeManagerContext* context =
0257 m_cellid_converter->findContext(clusters[0].getSurface());
0258
0259 std::vector<Eigen::Vector3d> layerPositions;
0260 std::vector<edm4hep::MCParticle> assocParticles;
0261
0262 for (auto cluster : clusters) {
0263
0264 auto globalPos = context->localToWorld({cluster.getLoc()[0], cluster.getLoc()[1], 0});
0265 layerPositions.emplace_back(globalPos.x() / dd4hep::mm, globalPos.y() / dd4hep::mm,
0266 globalPos.z() / dd4hep::mm);
0267
0268
0269
0270 float maxWeight = 0;
0271 std::size_t maxIndex = cluster.getWeights().size();
0272 for (std::size_t i = 0; i < cluster.getWeights().size(); ++i) {
0273 if (cluster.getWeights()[i] > maxWeight) {
0274 maxWeight = cluster.getWeights()[i];
0275 maxIndex = i;
0276 }
0277 }
0278 if (maxIndex == cluster.getWeights().size()) {
0279
0280 continue;
0281 }
0282 auto maxHit = cluster.getHits()[maxIndex];
0283
0284 auto rawHit = maxHit.getRawHit();
0285
0286 const auto sim_hits = link_nav.getLinked(rawHit);
0287 if (!sim_hits.empty()) {
0288 auto particle = sim_hits[0].o.getParticle();
0289 assocParticles.push_back(particle);
0290 }
0291 }
0292
0293 pointPositions.push_back(layerPositions);
0294 assoc_parts.push_back(assocParticles);
0295 }
0296
0297 }