1#include "Tracking/Reco/LinearSeedFinder.h"
5#include "Ecal/Event/EcalHit.h"
16 truth_matching_tool_ = std::make_shared<tracking::sim::TruthMatchingTool>();
22 "out_seed_collection",
getName() +
"LinearRecoilSeedTracks");
26 parameters.
get<std::string>(
"input_hits_collection",
"DigiRecoilSimHits");
28 parameters.
get<std::string>(
"input_rec_hits_collection",
"EcalRecHits");
30 input_pass_name_ = parameters.
get<std::string>(
"input_pass_name",
"");
32 sim_particles_passname_ =
33 parameters.
get<std::string>(
"sim_particles_passname");
35 sim_particles_events_passname_ =
36 parameters.
get<std::string>(
"sim_particles_events_passname");
40 parameters.
get<std::vector<double>>(
"recoil_uncertainty", {0.006, 0.085});
41 ecal_uncertainty_ = parameters.
get<
double>(
"ecal_uncertainty", {3.87});
42 ecal_distance_threshold_ = parameters.
get<
double>(
"ecal_distance_threshold");
43 ecal_first_layer_z_threshold_ =
44 parameters.
get<
double>(
"ecal_first_layer_z_threshold");
46 layer12_midpoint_ = parameters.
get<
double>(
"layer12_midpoint");
47 layer23_midpoint_ = parameters.
get<
double>(
"layer23_midpoint");
48 layer34_midpoint_ = parameters.
get<
double>(
"layer34_midpoint");
52 auto start = std::chrono::high_resolution_clock::now();
53 std::vector<ldmx::StraightTrack> straight_seed_tracks;
59 const auto& ecal_rec_hit =
event.getCollection<
ldmx::EcalHit>(
62 std::vector<std::array<double, 3>> first_layer_ecal_rec_hits;
65 for (
const auto& x_ecal : ecal_rec_hit) {
66 if (x_ecal.getZPos() < ecal_first_layer_z_threshold_) {
67 first_layer_ecal_rec_hits.push_back(
68 {x_ecal.getZPos(), x_ecal.getXPos(), x_ecal.getYPos()});
73 if ((recoil_hits.size() < 2) || (first_layer_ecal_rec_hits.empty()) ||
74 (uniqueLayersHit(recoil_hits) < 2)) {
76 n_seeds_ += straight_seed_tracks.size();
82 std::map<int, ldmx::SimParticle> particle_map;
83 if (event.
exists(
"SimParticles", sim_particles_events_passname_)) {
85 "SimParticles", sim_particles_passname_);
86 truth_matching_tool_->setup(particle_map, recoil_hits);
90 std::tuple<ldmx::Measurement, ldmx::SimTrackerHit, ldmx::SimTrackerHit>>
93 std::tuple<ldmx::Measurement, ldmx::SimTrackerHit, ldmx::SimTrackerHit>>
97 "RecoilSimHits", input_pass_name_);
99 "TargetScoringPlaneHits", input_pass_name_);
102 std::unordered_map<int, std::vector<const ldmx::SimTrackerHit*>>
103 sim_hits_by_track_id;
104 for (
const auto& hit : recoil_sim_hits) {
105 sim_hits_by_track_id[hit.getTrackID()].push_back(&hit);
109 std::unordered_map<int, const ldmx::SimTrackerHit*> scoring_hit_map;
110 for (
const auto& sp_hit : scoring_hits) {
111 if (sp_hit.getPosition()[2] > 0)
112 scoring_hit_map[sp_hit.getTrackID()] = &sp_hit;
115 for (
const auto& point : recoil_hits) {
117 float x = point.getGlobalPosition()[0];
120 auto track_ids = point.getTrackIds();
121 int track_id = (track_ids.size() > 0) ? track_ids.at(0) : -1;
124 auto sim_range_it = sim_hits_by_track_id.find(track_id);
125 if (sim_range_it == sim_hits_by_track_id.end())
continue;
128 const auto& sim_hits = sim_range_it->second;
130 for (
const auto* sim_hit : sim_hits) {
131 float z = sim_hit->getPosition()[2];
133 if (x < layer12_midpoint_) {
134 if (z < layer12_midpoint_) {
135 auto sp_it = scoring_hit_map.find(track_id);
136 if (sp_it != scoring_hit_map.end()) {
137 first_two_layers.emplace_back(point, *sim_hit, *sp_it->second);
143 else if (x < layer23_midpoint_) {
144 if (z > layer12_midpoint_ && z < layer23_midpoint_) {
149 else if (x < layer34_midpoint_) {
150 if (z > layer23_midpoint_ && z < layer34_midpoint_) {
151 auto sp_it = scoring_hit_map.find(track_id);
152 if (sp_it != scoring_hit_map.end()) {
153 second_two_layers.emplace_back(point, *sim_hit, *sp_it->second);
160 if (z > layer34_midpoint_) {
161 second_two_layers.emplace_back(point, *sim_hit,
171 auto first_sensor_combos = processMeasurements(first_two_layers, tg);
172 auto second_sensor_combos = processMeasurements(second_two_layers, tg);
174 for (
const auto& [first_combo_3d_point, first_layer_one, first_layer_two] :
175 first_sensor_combos) {
177 std::optional<ldmx::Measurement>>
180 if (first_layer_two.has_value()) {
181 first_sensor_point = {first_combo_3d_point,
182 std::get<ldmx::Measurement>(first_layer_one),
183 std::get<ldmx::Measurement>(*first_layer_two)};
186 first_sensor_point = {first_combo_3d_point,
187 std::get<ldmx::Measurement>(first_layer_one),
191 for (
const auto& [second_combo_3d_point, second_layer_one,
192 second_layer_two] : second_sensor_combos) {
194 std::optional<ldmx::Measurement>>
196 if (second_layer_two.has_value()) {
197 second_sensor_point = {second_combo_3d_point,
198 std::get<ldmx::Measurement>(second_layer_one),
199 std::get<ldmx::Measurement>(*second_layer_two)};
202 second_sensor_point = {second_combo_3d_point,
203 std::get<ldmx::Measurement>(second_layer_one),
207 for (
const auto& rec_hit : first_layer_ecal_rec_hits) {
211 seedTracker(first_sensor_point, second_sensor_point, rec_hit);
214 if (seed_track.getChi2() > 0.0) {
215 straight_seed_tracks.push_back(seed_track);
221 n_seeds_ += straight_seed_tracks.size();
224 auto end = std::chrono::high_resolution_clock::now();
226 auto diff = end - start;
227 processing_time_ += std::chrono::duration<double, std::milli>(diff).count();
229 first_layer_ecal_rec_hits.clear();
230 straight_seed_tracks.clear();
236 std::optional<ldmx::Measurement>>
239 std::optional<ldmx::Measurement>>
241 const std::array<double, 3> ecal_one) {
242 auto [sensor1, layer1, layer2] = recoil_one;
243 auto [sensor2, layer3, layer4] = recoil_two;
244 std::vector<ldmx::Measurement> all_points;
253 all_points.push_back(layer1);
257 if (layer2.has_value()) {
258 all_points.push_back(*layer2);
261 all_points.push_back(layer3);
265 if (layer4.has_value()) {
266 all_points.push_back(*layer4);
272 auto [m_x, b_x, m_y, b_y, seed_cov] = fit3DLine(sensor1, sensor2, ecal_one);
273 std::array<double, 3> temp_extrapolated_point = {
274 ecal_one[0], m_x * ecal_one[0] + b_x, m_y * ecal_one[0] + b_y};
275 double temp_distance = calculateDistance(temp_extrapolated_point, ecal_one);
279 if (temp_distance < ecal_distance_threshold_) {
281 trk.setInterceptX(b_x);
283 trk.setInterceptY(b_y);
284 trk.setTheta(std::atan2(m_y, std::sqrt(1 + m_x * m_x)));
285 trk.setPhi(std::atan2(m_x, 1.0));
287 trk.setAllSensorPoints(all_points);
288 trk.setFirstSensorPosition(sensor1);
289 trk.setSecondSensorPosition(sensor2);
290 trk.setFirstLayerEcalRecHit(ecal_one);
291 trk.setDistancetoRecHit(temp_distance);
293 trk.setTargetLocation(0.0, b_x, b_y);
294 trk.setEcalLayer1Location(temp_extrapolated_point);
296 globalChiSquare(sensor1, sensor2, ecal_one, m_x, m_y, b_x, b_y));
300 trk.setCov(seed_cov);
303 if (truth_matching_tool_->configured()) {
304 auto truth_info = truth_matching_tool_->truthMatch(all_points);
305 trk.setTrackID(truth_info.track_id_);
306 trk.setPdgID(truth_info.pdg_id_);
307 trk.setTruthProb(truth_info.truth_prob_);
320 ldmx_log(info) <<
"AVG Time/Event: " << std::fixed << std::setprecision(1)
321 << processing_time_ / n_events_ <<
" ms";
322 ldmx_log(info) <<
"Total Seeds/Events: " << n_seeds_ <<
"/" << n_events_;
323 ldmx_log(info) <<
"not enough seed points " << n_missing_;
327std::array<double, 3> LinearSeedFinder::getPointAtZ(
328 std::array<double, 3> target, std::array<double, 3> measurement,
330 double slope_x = (measurement[1] - target[0]) / (measurement[0] - target[2]);
331 double slope_y = (measurement[2] - target[1]) / (measurement[0] - target[2]);
333 double intercept_x = target[0] - slope_x * target[2];
334 double intercept_y = target[1] - slope_y * target[2];
336 double x_target = slope_x * z_target + intercept_x;
337 double y_target = slope_y * z_target + intercept_y;
339 return {z_target, x_target, y_target};
342std::vector<std::tuple<
343 std::array<double, 3>,
344 std::tuple<ldmx::Measurement, ldmx::SimTrackerHit, ldmx::SimTrackerHit>,
347LinearSeedFinder::processMeasurements(
350 const geo::TrackersTrackingGeometry& tg) {
352 std::tuple<ldmx::Measurement, ldmx::SimTrackerHit, ldmx::SimTrackerHit>>
355 std::tuple<ldmx::Measurement, ldmx::SimTrackerHit, ldmx::SimTrackerHit>>
357 std::vector<std::tuple<
358 std::array<double, 3>,
359 std::tuple<ldmx::Measurement, ldmx::SimTrackerHit, ldmx::SimTrackerHit>,
362 points_with_measurement;
363 Acts::Vector3 dummy{0., 0., 0.};
366 for (
const auto& [measurement, sim_hit, scoring_hit] : measurements) {
367 if (measurement.getLayerID() % 2 == 0) {
368 axial_measurements.emplace_back(measurement, sim_hit, scoring_hit);
370 stereo_measurements.emplace_back(measurement, sim_hit, scoring_hit);
376 if (!axial_measurements.empty() && !stereo_measurements.empty()) {
377 for (
const auto& axial : axial_measurements) {
378 const auto& [axial_meas, axial_hit, axial_sp] = axial;
380 for (
const auto& stereo : stereo_measurements) {
381 const auto& [stereo_meas, stereo_hit, stereo_sp] = stereo;
383 const Acts::Surface* axial_surface =
384 tg.getSurface(axial_meas.getLayerID());
385 const Acts::Surface* stereo_surface =
386 tg.getSurface(stereo_meas.getLayerID());
388 if (!axial_surface || !stereo_surface)
continue;
390 std::vector<ldmx::SimTrackerHit> sim_hits = {axial_hit, stereo_hit};
391 Acts::Vector3 space_point =
392 simple3DHitV2(axial_meas, *axial_surface, stereo_meas,
393 *stereo_surface, axial_sp, sim_hits);
395 points_with_measurement.push_back(
396 {convertToLdmxStdArray(space_point), axial, stereo});
399 }
else if (!axial_measurements.empty()) {
401 for (
const auto& axial : axial_measurements) {
402 const auto& [axial_meas, axial_hit, axial_sp] = axial;
403 const Acts::Surface* axial_surface =
404 tg.getSurface(axial_meas.getLayerID());
406 Acts::Vector3 axial_meas_hit = axial_surface->localToGlobal(
408 Acts::Vector2(axial_meas.getLocalPosition()[0], 0.0), dummy);
410 points_with_measurement.push_back(
411 {convertToLdmxStdArray(axial_meas_hit), axial, std::nullopt});
413 }
else if (!stereo_measurements.empty()) {
414 for (
const auto& stereo : stereo_measurements) {
415 const auto& [stereo_meas, stereo_hit, stereo_sp] = stereo;
416 const Acts::Surface* stereo_surface =
417 tg.getSurface(stereo_meas.getLayerID());
419 Acts::Vector3 stereo_meas_hit = stereo_surface->localToGlobal(
421 Acts::Vector2(stereo_meas.getLocalPosition()[0], 0.0), dummy);
423 points_with_measurement.push_back(
424 {convertToLdmxStdArray(stereo_meas_hit), stereo, std::nullopt});
427 return points_with_measurement;
431std::array<double, 3> LinearSeedFinder::convertToLdmxStdArray(
432 const Acts::Vector3& vec) {
433 return {vec.x(), vec.y(), vec.z()};
438std::tuple<Acts::Vector3, Acts::Vector3, Acts::Vector3>
439LinearSeedFinder::getSurfaceVectors(
const Acts::Surface& surface) {
440 Acts::Vector3 dummy{0., 0., 0.};
442 surface.localToGlobal(geometryContext(), Acts::Vector2(1, 0), dummy) -
443 surface.center(geometryContext());
445 surface.localToGlobal(geometryContext(), Acts::Vector2(0, 1), dummy) -
446 surface.center(geometryContext());
447 Acts::Vector3 w = u.cross(v).normalized();
448 return {u.normalized(), v.normalized(), w};
460Acts::Vector3 LinearSeedFinder::simple3DHitV2(
464 std::vector<ldmx::SimTrackerHit> pair_sim_hits) {
465 Acts::Vector3 dummy{0., 0., 0.};
466 Acts::Vector3 hit_on_target{target_sp.
getPosition()[0],
470 Acts::Vector3 axial_true_global{pair_sim_hits[0].getPosition()[0],
471 pair_sim_hits[0].getPosition()[1],
472 pair_sim_hits[0].getPosition()[2]};
474 Acts::Vector3 stereo_true_global{pair_sim_hits[1].getPosition()[0],
475 pair_sim_hits[1].getPosition()[1],
476 pair_sim_hits[1].getPosition()[2]};
478 Acts::Vector3 simpart_path = axial_true_global - hit_on_target;
479 Acts::Vector3 simpart_unit = simpart_path.normalized();
483 Acts::Vector3 axial_origin = axial_surface.center(geometryContext());
484 Acts::Vector3 stereo_origin = stereo_surface.center(geometryContext());
488 Acts::Vector3 delta_sensors = stereo_origin - axial_origin;
492 double dx_proj = (simpart_unit[0] / simpart_unit[2]) *
498 auto [axial_u, axial_v, axial_w] = getSurfaceVectors(axial_surface);
499 auto [stereo_u, stereo_v, stereo_w] = getSurfaceVectors(stereo_surface);
500 double salpha = dotProduct(axial_v, stereo_u);
501 double cosalpha = dotProduct(axial_u, stereo_u);
510 stereo_v_value = 0.0;
515 double v_intercept_useproj =
516 (stereo_u_value - (axial_u_value - dx_proj) * cosalpha) / salpha;
517 double u_intercept_useproj = axial_u_value - dx_proj;
520 Acts::Vector3 axst_global_useproj = axial_surface.localToGlobal(
522 Acts::Vector2(u_intercept_useproj, v_intercept_useproj), dummy);
523 Acts::Vector3 dummy_stereo_proj = stereo_surface.localToGlobal(
525 Acts::Vector2(u_intercept_useproj, v_intercept_useproj), dummy);
528 Acts::Vector3 reconstructed_hit{dummy_stereo_proj[0], axst_global_useproj[1],
529 axst_global_useproj[2]};
531 ldmx_log(debug) <<
"The particle projected axst measured position is "
532 "(compare with stereo sim position): "
533 << reconstructed_hit[0] <<
", " << reconstructed_hit[1]
534 <<
", " << reconstructed_hit[2] <<
"\n";
536 return reconstructed_hit;
539double LinearSeedFinder::dotProduct(
const Acts::Vector3& v1,
540 const Acts::Vector3& v2) {
544std::tuple<double, double, double, double, std::vector<double>>
545LinearSeedFinder::fit3DLine(
const std::array<double, 3>& first_recoil,
546 const std::array<double, 3>& second_recoil,
547 const std::array<double, 3>& ecal) {
548 double z_pos1 = first_recoil[0], x_pos1 = first_recoil[1],
549 y_pos1 = first_recoil[2];
550 double z_pos2 = second_recoil[0], x_pos2 = second_recoil[1],
551 y_pos2 = second_recoil[2];
552 double z_pos3 = ecal[0], x_pos3 = ecal[1], y_pos3 = ecal[2];
554 std::array<double, 6> weights = {
555 1 / pow(recoil_uncertainty_[0], 2), 1 / pow(recoil_uncertainty_[1], 2),
556 1 / pow(recoil_uncertainty_[0], 2), 1 / pow(recoil_uncertainty_[1], 2),
557 1 / pow(ecal_uncertainty_, 2), 1 / pow(ecal_uncertainty_, 2)};
559 Eigen::Matrix<double, 6, 4> a_mat;
560 Eigen::Matrix<double, 6, 1> d_vec, w_vec;
563 a_mat << z_pos1, 1, 0, 0, 0, 0, z_pos1, 1, z_pos2, 1, 0, 0, 0, 0, z_pos2, 1,
564 z_pos3, 1, 0, 0, 0, 0, z_pos3, 1;
567 d_vec << x_pos1, y_pos1, x_pos2, y_pos2, x_pos3, y_pos3;
570 w_vec = Eigen::Matrix<double, 6, 1>(weights.data());
573 Eigen::MatrixXd at_w_a = a_mat.transpose() * w_vec.asDiagonal() * a_mat;
574 Eigen::MatrixXd at_w_d = a_mat.transpose() * w_vec.asDiagonal() * d_vec;
575 Eigen::VectorXd param_vec = at_w_a.ldlt().solve(at_w_d);
577 Eigen::Matrix4d covariance_matrix = at_w_a.inverse();
581 std::vector<double> covariance_vector = {
582 covariance_matrix(0, 0), covariance_matrix(0, 1), covariance_matrix(0, 2),
583 covariance_matrix(0, 3), covariance_matrix(1, 1), covariance_matrix(1, 2),
584 covariance_matrix(1, 3), covariance_matrix(2, 2), covariance_matrix(2, 3),
585 covariance_matrix(3, 3)};
588 return {param_vec(0), param_vec(1), param_vec(2), param_vec(3),
592double LinearSeedFinder::calculateDistance(
593 const std::array<double, 3>& point1,
const std::array<double, 3>& point2) {
594 return sqrt(pow(point1[1] - point2[1], 2) + pow(point1[2] - point2[2], 2));
597double LinearSeedFinder::globalChiSquare(
598 const std::array<double, 3>& first_sensor,
599 const std::array<double, 3>& second_sensor,
600 const std::array<double, 3>& ecal_hit,
double m_x,
double m_y,
double b_x,
602 double chi2_x = 0, chi2_y = 0;
604 (m_x * first_sensor[0] + b_x - first_sensor[1]) / recoil_uncertainty_[0],
607 (m_y * first_sensor[0] + b_y - first_sensor[2]) / recoil_uncertainty_[1],
610 chi2_x += pow((m_x * second_sensor[0] + b_x - second_sensor[1]) /
611 recoil_uncertainty_[0],
613 chi2_y += pow((m_y * second_sensor[0] + b_y - second_sensor[2]) /
614 recoil_uncertainty_[1],
617 chi2_x += pow((m_x * ecal_hit[0] + b_x - ecal_hit[1]) / ecal_uncertainty_, 2);
618 chi2_y += pow((m_y * ecal_hit[0] + b_y - ecal_hit[2]) / ecal_uncertainty_, 2);
620 return chi2_x + chi2_y;
623int LinearSeedFinder::uniqueLayersHit(
624 const std::vector<ldmx::Measurement>& digi_points) {
625 std::vector<ldmx::Measurement> sorted_points = digi_points;
628 std::sort(sorted_points.begin(), sorted_points.end(),
630 return meas1.getGlobalPosition()[0] <
631 meas2.getGlobalPosition()[0];
635 auto last = std::unique(
636 sorted_points.begin(), sorted_points.end(),
638 return meas1.getGlobalPosition()[0] == meas2.getGlobalPosition()[0];
642 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...