1#include "Tracking/Reco/LinearSeedFinder.h"
13 truth_matching_tool_ = std::make_shared<tracking::sim::TruthMatchingTool>();
19 "out_seed_collection",
getName() +
"LinearRecoilSeedTracks");
23 parameters.
get<std::string>(
"input_hits_collection",
"DigiRecoilSimHits");
25 parameters.
get<std::string>(
"input_rec_hits_collection",
"EcalRecHits");
27 input_pass_name_ = parameters.
get<std::string>(
"input_pass_name",
"");
29 sim_particles_passname_ =
30 parameters.
get<std::string>(
"sim_particles_passname");
32 sim_particles_events_passname_ =
33 parameters.
get<std::string>(
"sim_particles_events_passname");
37 parameters.
get<std::vector<double>>(
"recoil_uncertainty", {0.006, 0.085});
38 ecal_uncertainty_ = parameters.
get<
double>(
"ecal_uncertainty", {3.87});
39 ecal_distance_threshold_ = parameters.
get<
double>(
"ecal_distance_threshold");
40 ecal_first_layer_z_threshold_ =
41 parameters.
get<
double>(
"ecal_first_layer_z_threshold");
43 layer12_midpoint_ = parameters.
get<
double>(
"layer12_midpoint");
44 layer23_midpoint_ = parameters.
get<
double>(
"layer23_midpoint");
45 layer34_midpoint_ = parameters.
get<
double>(
"layer34_midpoint");
49 auto start = std::chrono::high_resolution_clock::now();
50 std::vector<ldmx::StraightTrack> straight_seed_tracks;
54 const std::vector<ldmx::Measurement> recoil_hits =
57 const std::vector<ldmx::EcalHit> ecal_rec_hit =
61 std::vector<std::array<double, 3>> first_layer_ecal_rec_hits;
64 for (
const auto& x_ecal : ecal_rec_hit) {
65 if (x_ecal.getZPos() < ecal_first_layer_z_threshold_) {
66 first_layer_ecal_rec_hits.push_back(
67 {x_ecal.getZPos(), x_ecal.getXPos(), x_ecal.getYPos()});
72 if ((recoil_hits.size() < 2) || (first_layer_ecal_rec_hits.empty()) ||
73 (uniqueLayersHit(recoil_hits) < 2)) {
75 n_seeds_ += straight_seed_tracks.size();
81 std::map<int, ldmx::SimParticle> particle_map;
82 if (event.
exists(
"SimParticles", sim_particles_events_passname_)) {
84 "SimParticles", sim_particles_passname_);
85 truth_matching_tool_->setup(particle_map, recoil_hits);
89 std::tuple<ldmx::Measurement, ldmx::SimTrackerHit, ldmx::SimTrackerHit>>
92 std::tuple<ldmx::Measurement, ldmx::SimTrackerHit, ldmx::SimTrackerHit>>
95 const std::vector<ldmx::SimTrackerHit> recoil_sim_hits =
98 const std::vector<ldmx::SimTrackerHit> scoring_hits =
103 std::unordered_map<int, std::vector<const ldmx::SimTrackerHit*>>
104 sim_hits_by_track_id;
105 for (
const auto& hit : recoil_sim_hits) {
106 sim_hits_by_track_id[hit.getTrackID()].push_back(&hit);
110 std::unordered_map<int, const ldmx::SimTrackerHit*> scoring_hit_map;
111 for (
const auto& sp_hit : scoring_hits) {
112 if (sp_hit.getPosition()[2] > 0)
113 scoring_hit_map[sp_hit.getTrackID()] = &sp_hit;
116 for (
const auto& point : recoil_hits) {
118 float x = point.getGlobalPosition()[0];
119 int track_id = point.getTrackIds()[0];
122 auto sim_range_it = sim_hits_by_track_id.find(track_id);
123 if (sim_range_it == sim_hits_by_track_id.end())
continue;
126 const auto& sim_hits = sim_range_it->second;
128 for (
const auto* sim_hit : sim_hits) {
129 float z = sim_hit->getPosition()[2];
131 if (x < layer12_midpoint_) {
132 if (z < layer12_midpoint_) {
133 auto sp_it = scoring_hit_map.find(track_id);
134 if (sp_it != scoring_hit_map.end()) {
135 first_two_layers.emplace_back(point, *sim_hit, *sp_it->second);
141 else if (x < layer23_midpoint_) {
142 if (z > layer12_midpoint_ && z < layer23_midpoint_) {
147 else if (x < layer34_midpoint_) {
148 if (z > layer23_midpoint_ && z < layer34_midpoint_) {
149 auto sp_it = scoring_hit_map.find(track_id);
150 if (sp_it != scoring_hit_map.end()) {
151 second_two_layers.emplace_back(point, *sim_hit, *sp_it->second);
158 if (z > layer34_midpoint_) {
159 second_two_layers.emplace_back(point, *sim_hit,
169 auto first_sensor_combos = processMeasurements(first_two_layers, tg);
170 auto second_sensor_combos = processMeasurements(second_two_layers, tg);
172 for (
const auto& [first_combo_3d_point, first_layer_one, first_layer_two] :
173 first_sensor_combos) {
175 std::optional<ldmx::Measurement>>
178 if (first_layer_two.has_value()) {
179 first_sensor_point = {first_combo_3d_point,
180 std::get<ldmx::Measurement>(first_layer_one),
181 std::get<ldmx::Measurement>(*first_layer_two)};
184 first_sensor_point = {first_combo_3d_point,
185 std::get<ldmx::Measurement>(first_layer_one),
189 for (
const auto& [second_combo_3d_point, second_layer_one,
190 second_layer_two] : second_sensor_combos) {
192 std::optional<ldmx::Measurement>>
194 if (second_layer_two.has_value()) {
195 second_sensor_point = {second_combo_3d_point,
196 std::get<ldmx::Measurement>(second_layer_one),
197 std::get<ldmx::Measurement>(*second_layer_two)};
200 second_sensor_point = {second_combo_3d_point,
201 std::get<ldmx::Measurement>(second_layer_one),
205 for (
const auto& rec_hit : first_layer_ecal_rec_hits) {
209 seedTracker(first_sensor_point, second_sensor_point, rec_hit);
212 if (seed_track.getChi2() > 0.0) {
213 straight_seed_tracks.push_back(seed_track);
219 n_seeds_ += straight_seed_tracks.size();
222 auto end = std::chrono::high_resolution_clock::now();
224 auto diff = end - start;
225 processing_time_ += std::chrono::duration<double, std::milli>(diff).count();
227 first_layer_ecal_rec_hits.clear();
228 straight_seed_tracks.clear();
234 std::optional<ldmx::Measurement>>
237 std::optional<ldmx::Measurement>>
239 const std::array<double, 3> ecal_one) {
240 auto [sensor1, layer1, layer2] = recoil_one;
241 auto [sensor2, layer3, layer4] = recoil_two;
242 std::vector<ldmx::Measurement> all_points;
251 all_points.push_back(layer1);
255 if (layer2.has_value()) {
256 all_points.push_back(*layer2);
259 all_points.push_back(layer3);
263 if (layer4.has_value()) {
264 all_points.push_back(*layer4);
270 auto [m_x, b_x, m_y, b_y, seed_cov] = fit3DLine(sensor1, sensor2, ecal_one);
271 std::array<double, 3> temp_extrapolated_point = {
272 ecal_one[0], m_x * ecal_one[0] + b_x, m_y * ecal_one[0] + b_y};
273 double temp_distance = calculateDistance(temp_extrapolated_point, ecal_one);
277 if (temp_distance < ecal_distance_threshold_) {
279 trk.setInterceptX(b_x);
281 trk.setInterceptY(b_y);
282 trk.setTheta(std::atan2(m_y, std::sqrt(1 + m_x * m_x)));
283 trk.setPhi(std::atan2(m_x, 1.0));
285 trk.setAllSensorPoints(all_points);
286 trk.setFirstSensorPosition(sensor1);
287 trk.setSecondSensorPosition(sensor2);
288 trk.setFirstLayerEcalRecHit(ecal_one);
289 trk.setDistancetoRecHit(temp_distance);
291 trk.setTargetLocation(0.0, b_x, b_y);
292 trk.setEcalLayer1Location(temp_extrapolated_point);
294 globalChiSquare(sensor1, sensor2, ecal_one, m_x, m_y, b_x, b_y));
298 trk.setCov(seed_cov);
301 if (truth_matching_tool_->configured()) {
302 auto truth_info = truth_matching_tool_->truthMatch(all_points);
303 trk.setTrackID(truth_info.track_id_);
304 trk.setPdgID(truth_info.pdg_id_);
305 trk.setTruthProb(truth_info.truth_prob_);
318 ldmx_log(info) <<
"AVG Time/Event: " << std::fixed << std::setprecision(1)
319 << processing_time_ / n_events_ <<
" ms";
320 ldmx_log(info) <<
"Total Seeds/Events: " << n_seeds_ <<
"/" << n_events_;
321 ldmx_log(info) <<
"not enough seed points " << n_missing_;
325std::array<double, 3> LinearSeedFinder::getPointAtZ(
326 std::array<double, 3> target, std::array<double, 3> measurement,
328 double slope_x = (measurement[1] - target[0]) / (measurement[0] - target[2]);
329 double slope_y = (measurement[2] - target[1]) / (measurement[0] - target[2]);
331 double intercept_x = target[0] - slope_x * target[2];
332 double intercept_y = target[1] - slope_y * target[2];
334 double x_target = slope_x * z_target + intercept_x;
335 double y_target = slope_y * z_target + intercept_y;
337 return {z_target, x_target, y_target};
340std::vector<std::tuple<
341 std::array<double, 3>,
342 std::tuple<ldmx::Measurement, ldmx::SimTrackerHit, ldmx::SimTrackerHit>,
345LinearSeedFinder::processMeasurements(
348 const geo::TrackersTrackingGeometry& tg) {
350 std::tuple<ldmx::Measurement, ldmx::SimTrackerHit, ldmx::SimTrackerHit>>
353 std::tuple<ldmx::Measurement, ldmx::SimTrackerHit, ldmx::SimTrackerHit>>
355 std::vector<std::tuple<
356 std::array<double, 3>,
357 std::tuple<ldmx::Measurement, ldmx::SimTrackerHit, ldmx::SimTrackerHit>,
360 points_with_measurement;
361 Acts::Vector3 dummy{0., 0., 0.};
364 for (
const auto& [measurement, sim_hit, scoring_hit] : measurements) {
365 if (measurement.getLayerID() % 2 == 0) {
366 axial_measurements.emplace_back(measurement, sim_hit, scoring_hit);
368 stereo_measurements.emplace_back(measurement, sim_hit, scoring_hit);
374 if (!axial_measurements.empty() && !stereo_measurements.empty()) {
375 for (
const auto& axial : axial_measurements) {
376 const auto& [axial_meas, axial_hit, axial_sp] = axial;
378 for (
const auto& stereo : stereo_measurements) {
379 const auto& [stereo_meas, stereo_hit, stereo_sp] = stereo;
381 const Acts::Surface* axial_surface =
382 tg.getSurface(axial_meas.getLayerID());
383 const Acts::Surface* stereo_surface =
384 tg.getSurface(stereo_meas.getLayerID());
386 if (!axial_surface || !stereo_surface)
continue;
388 std::vector<ldmx::SimTrackerHit> sim_hits = {axial_hit, stereo_hit};
389 Acts::Vector3 space_point =
390 simple3DHitV2(axial_meas, *axial_surface, stereo_meas,
391 *stereo_surface, axial_sp, sim_hits);
393 points_with_measurement.push_back(
394 {convertToLdmxStdArray(space_point), axial, stereo});
397 }
else if (!axial_measurements.empty()) {
399 for (
const auto& axial : axial_measurements) {
400 const auto& [axial_meas, axial_hit, axial_sp] = axial;
401 const Acts::Surface* axial_surface =
402 tg.getSurface(axial_meas.getLayerID());
404 Acts::Vector3 axial_meas_hit = axial_surface->localToGlobal(
406 Acts::Vector2(axial_meas.getLocalPosition()[0], 0.0), dummy);
408 points_with_measurement.push_back(
409 {convertToLdmxStdArray(axial_meas_hit), axial, std::nullopt});
411 }
else if (!stereo_measurements.empty()) {
412 for (
const auto& stereo : stereo_measurements) {
413 const auto& [stereo_meas, stereo_hit, stereo_sp] = stereo;
414 const Acts::Surface* stereo_surface =
415 tg.getSurface(stereo_meas.getLayerID());
417 Acts::Vector3 stereo_meas_hit = stereo_surface->localToGlobal(
419 Acts::Vector2(stereo_meas.getLocalPosition()[0], 0.0), dummy);
421 points_with_measurement.push_back(
422 {convertToLdmxStdArray(stereo_meas_hit), stereo, std::nullopt});
425 return points_with_measurement;
429std::array<double, 3> LinearSeedFinder::convertToLdmxStdArray(
430 const Acts::Vector3& vec) {
431 return {vec.x(), vec.y(), vec.z()};
436std::tuple<Acts::Vector3, Acts::Vector3, Acts::Vector3>
437LinearSeedFinder::getSurfaceVectors(
const Acts::Surface& surface) {
438 Acts::Vector3 dummy{0., 0., 0.};
440 surface.localToGlobal(geometryContext(), Acts::Vector2(1, 0), dummy) -
441 surface.center(geometryContext());
443 surface.localToGlobal(geometryContext(), Acts::Vector2(0, 1), dummy) -
444 surface.center(geometryContext());
445 Acts::Vector3 w = u.cross(v).normalized();
446 return {u.normalized(), v.normalized(), w};
458Acts::Vector3 LinearSeedFinder::simple3DHitV2(
462 std::vector<ldmx::SimTrackerHit> pair_sim_hits) {
463 Acts::Vector3 dummy{0., 0., 0.};
464 Acts::Vector3 hit_on_target{target_sp.
getPosition()[0],
468 Acts::Vector3 axial_true_global{pair_sim_hits[0].getPosition()[0],
469 pair_sim_hits[0].getPosition()[1],
470 pair_sim_hits[0].getPosition()[2]};
472 Acts::Vector3 stereo_true_global{pair_sim_hits[1].getPosition()[0],
473 pair_sim_hits[1].getPosition()[1],
474 pair_sim_hits[1].getPosition()[2]};
476 Acts::Vector3 simpart_path = axial_true_global - hit_on_target;
477 Acts::Vector3 simpart_unit = simpart_path.normalized();
481 Acts::Vector3 axial_origin = axial_surface.center(geometryContext());
482 Acts::Vector3 stereo_origin = stereo_surface.center(geometryContext());
486 Acts::Vector3 delta_sensors = stereo_origin - axial_origin;
490 double dx_proj = (simpart_unit[0] / simpart_unit[2]) *
496 auto [axial_u, axial_v, axial_w] = getSurfaceVectors(axial_surface);
497 auto [stereo_u, stereo_v, stereo_w] = getSurfaceVectors(stereo_surface);
498 double salpha = dotProduct(axial_v, stereo_u);
499 double cosalpha = dotProduct(axial_u, stereo_u);
508 stereo_v_value = 0.0;
513 double v_intercept_useproj =
514 (stereo_u_value - (axial_u_value - dx_proj) * cosalpha) / salpha;
515 double u_intercept_useproj = axial_u_value - dx_proj;
518 Acts::Vector3 axst_global_useproj = axial_surface.localToGlobal(
520 Acts::Vector2(u_intercept_useproj, v_intercept_useproj), dummy);
521 Acts::Vector3 dummy_stereo_proj = stereo_surface.localToGlobal(
523 Acts::Vector2(u_intercept_useproj, v_intercept_useproj), dummy);
526 Acts::Vector3 reconstructed_hit{dummy_stereo_proj[0], axst_global_useproj[1],
527 axst_global_useproj[2]};
529 ldmx_log(debug) <<
"The particle projected axst measured position is "
530 "(compare with stereo sim position): "
531 << reconstructed_hit[0] <<
", " << reconstructed_hit[1]
532 <<
", " << reconstructed_hit[2] <<
"\n";
534 return reconstructed_hit;
537double LinearSeedFinder::dotProduct(
const Acts::Vector3& v1,
538 const Acts::Vector3& v2) {
542std::tuple<double, double, double, double, std::vector<double>>
543LinearSeedFinder::fit3DLine(
const std::array<double, 3>& first_recoil,
544 const std::array<double, 3>& second_recoil,
545 const std::array<double, 3>& ecal) {
546 double z_pos1 = first_recoil[0], x_pos1 = first_recoil[1],
547 y_pos1 = first_recoil[2];
548 double z_pos2 = second_recoil[0], x_pos2 = second_recoil[1],
549 y_pos2 = second_recoil[2];
550 double z_pos3 = ecal[0], x_pos3 = ecal[1], y_pos3 = ecal[2];
552 std::array<double, 6> weights = {
553 1 / pow(recoil_uncertainty_[0], 2), 1 / pow(recoil_uncertainty_[1], 2),
554 1 / pow(recoil_uncertainty_[0], 2), 1 / pow(recoil_uncertainty_[1], 2),
555 1 / pow(ecal_uncertainty_, 2), 1 / pow(ecal_uncertainty_, 2)};
557 Eigen::Matrix<double, 6, 4> a_mat;
558 Eigen::Matrix<double, 6, 1> d_vec, w_vec;
561 a_mat << z_pos1, 1, 0, 0, 0, 0, z_pos1, 1, z_pos2, 1, 0, 0, 0, 0, z_pos2, 1,
562 z_pos3, 1, 0, 0, 0, 0, z_pos3, 1;
565 d_vec << x_pos1, y_pos1, x_pos2, y_pos2, x_pos3, y_pos3;
568 w_vec = Eigen::Matrix<double, 6, 1>(weights.data());
571 Eigen::MatrixXd at_w_a = a_mat.transpose() * w_vec.asDiagonal() * a_mat;
572 Eigen::MatrixXd at_w_d = a_mat.transpose() * w_vec.asDiagonal() * d_vec;
573 Eigen::VectorXd param_vec = at_w_a.ldlt().solve(at_w_d);
575 Eigen::Matrix4d covariance_matrix = at_w_a.inverse();
579 std::vector<double> covariance_vector = {
580 covariance_matrix(0, 0), covariance_matrix(0, 1), covariance_matrix(0, 2),
581 covariance_matrix(0, 3), covariance_matrix(1, 1), covariance_matrix(1, 2),
582 covariance_matrix(1, 3), covariance_matrix(2, 2), covariance_matrix(2, 3),
583 covariance_matrix(3, 3)};
586 return {param_vec(0), param_vec(1), param_vec(2), param_vec(3),
590double LinearSeedFinder::calculateDistance(
591 const std::array<double, 3>& point1,
const std::array<double, 3>& point2) {
592 return sqrt(pow(point1[1] - point2[1], 2) + pow(point1[2] - point2[2], 2));
595double LinearSeedFinder::globalChiSquare(
596 const std::array<double, 3>& first_sensor,
597 const std::array<double, 3>& second_sensor,
598 const std::array<double, 3>& ecal_hit,
double m_x,
double m_y,
double b_x,
600 double chi2_x = 0, chi2_y = 0;
602 (m_x * first_sensor[0] + b_x - first_sensor[1]) / recoil_uncertainty_[0],
605 (m_y * first_sensor[0] + b_y - first_sensor[2]) / recoil_uncertainty_[1],
608 chi2_x += pow((m_x * second_sensor[0] + b_x - second_sensor[1]) /
609 recoil_uncertainty_[0],
611 chi2_y += pow((m_y * second_sensor[0] + b_y - second_sensor[2]) /
612 recoil_uncertainty_[1],
615 chi2_x += pow((m_x * ecal_hit[0] + b_x - ecal_hit[1]) / ecal_uncertainty_, 2);
616 chi2_y += pow((m_y * ecal_hit[0] + b_y - ecal_hit[2]) / ecal_uncertainty_, 2);
618 return chi2_x + chi2_y;
621int LinearSeedFinder::uniqueLayersHit(
622 const std::vector<ldmx::Measurement>& digi_points) {
623 std::vector<ldmx::Measurement> sorted_points = digi_points;
626 std::sort(sorted_points.begin(), sorted_points.end(),
628 return meas1.getGlobalPosition()[0] <
629 meas2.getGlobalPosition()[0];
633 auto last = std::unique(
634 sorted_points.begin(), sorted_points.end(),
636 return meas1.getGlobalPosition()[0] == meas2.getGlobalPosition()[0];
640 return std::distance(sorted_points.begin(), last);
#define DECLARE_PRODUCER(CLASS)
Macro which allows the framework to construct a producer given its name during configuration.
std::string getName() const
Get the processor name.
Implements an event buffer system for storing event data.
bool exists(const std::string &name, const std::string &passName, bool unique=true) const
Check for the existence of an object or collection with the given name and pass name in the event.
Class which represents the process under execution.
Class encapsulating parameters for configuring a processor.
const T & get(const std::string &name) const
Retrieve the parameter of the given name.
Stores reconstructed hit information from the ECAL.
std::array< float, 2 > getLocalPosition() const
Class representing a simulated particle.
Represents a simulated tracker hit in the simulation.
std::vector< float > getPosition() const
Get the XYZ position of the hit [mm].
std::string out_seed_collection_
The name of the output collection of seeds to be stored.
LinearSeedFinder(const std::string &name, framework::Process &process)
Constructor.
void configure(framework::config::Parameters ¶meters) override
Configure the processor using the given user specified parameters.
void produce(framework::Event &event) override
Run the processor and create a collection of results which indicate if a charge particle can be found...
void onProcessEnd() override
Output event statistics.
std::string input_hits_collection_
The name of the input hits collection to use in finding seeds..
void onProcessStart() override
Setup the truth matching.
std::string input_rec_hits_collection_
The name of the tagger Tracks (only for Recoil Seeding)
a helper base class providing some methods to shorten access to common conditions used within the tra...
The measurement calibrator can be a function or a class/struct able to retrieve the sim hits containe...