1#include "Tracking/Reco/LinearSeedFinder.h"
13 truth_matching_tool_ = std::make_shared<tracking::sim::TruthMatchingTool>();
19 "out_seed_collection",
getName() +
"LinearRecoilSeedTracks");
23 "input_hits_collection",
"DigiRecoilSimHits");
25 "input_rec_hits_collection",
"EcalRecHits");
28 parameters.getParameter<std::string>(
"input_pass_name",
"");
31 recoil_uncertainty_ = parameters.getParameter<std::vector<double>>(
32 "recoil_uncertainty", {0.006, 5.7735});
34 parameters.getParameter<
double>(
"ecal_uncertainty", {3.87});
35 ecal_distance_threshold_ =
36 parameters.getParameter<
double>(
"ecal_distance_threshold");
37 ecal_first_layer_z_threshold_ =
38 parameters.getParameter<
double>(
"ecal_first_layer_z_threshold");
40 layer12_midpoint_ = parameters.getParameter<
double>(
"layer12_midpoint");
41 layer23_midpoint_ = parameters.getParameter<
double>(
"layer23_midpoint");
42 layer34_midpoint_ = parameters.getParameter<
double>(
"layer34_midpoint");
46 auto start = std::chrono::high_resolution_clock::now();
47 std::vector<ldmx::StraightTrack> straight_seed_tracks;
50 const std::vector<ldmx::Measurement> recoil_hits =
53 const std::vector<ldmx::EcalHit> ecal_rec_hit =
57 std::vector<std::array<double, 3>> first_layer_ecal_rec_hits;
60 for (
const auto& x_ecal : ecal_rec_hit) {
61 if (x_ecal.getZPos() < ecal_first_layer_z_threshold_) {
62 first_layer_ecal_rec_hits.push_back(
63 {x_ecal.getZPos(), x_ecal.getXPos(), x_ecal.getYPos()});
68 if ((recoil_hits.size() < 2) || (first_layer_ecal_rec_hits.empty()) ||
69 (uniqueLayersHit(recoil_hits) < 2)) {
71 n_seeds_ += straight_seed_tracks.size();
77 std::map<int, ldmx::SimParticle> particle_map;
78 if (event.
exists(
"SimParticles")) {
80 truth_matching_tool_->setup(particle_map, recoil_hits);
85 auto [first_sensor, second_sensor] = combineMultiGlobalHits(recoil_hits);
87 for (
const auto& first_point : first_sensor) {
88 for (
const auto& second_point : second_sensor) {
89 for (
const auto& rec_hit : first_layer_ecal_rec_hits) {
93 SeedTracker(first_point, second_point, rec_hit);
96 if (seed_track.getChi2() > 0.0) {
97 straight_seed_tracks.push_back(seed_track);
103 n_seeds_ += straight_seed_tracks.size();
106 auto end = std::chrono::high_resolution_clock::now();
108 auto diff = end - start;
109 processing_time_ += std::chrono::duration<double, std::milli>(diff).count();
111 first_layer_ecal_rec_hits.clear();
112 straight_seed_tracks.clear();
118 std::optional<ldmx::Measurement>>
121 std::optional<ldmx::Measurement>>
123 const std::array<double, 3> ecal_one) {
124 auto [sensor1, layer1, layer2] = recoil_one;
125 auto [sensor2, layer3, layer4] = recoil_two;
126 std::vector<ldmx::Measurement> all_points;
135 all_points.push_back(layer1);
139 if (layer2.has_value()) {
140 all_points.push_back(*layer2);
143 all_points.push_back(layer3);
147 if (layer4.has_value()) {
148 all_points.push_back(*layer4);
154 auto [m_x, b_x, m_y, b_y, seed_cov] = fit3DLine(sensor1, sensor2, ecal_one);
155 std::array<double, 3> temp_extrapolated_point = {
156 ecal_one[0], m_x * ecal_one[0] + b_x, m_y * ecal_one[0] + b_y};
157 double temp_distance = calculateDistance(temp_extrapolated_point, ecal_one);
161 if (temp_distance < ecal_distance_threshold_) {
163 trk.setInterceptX(b_x);
165 trk.setInterceptY(b_y);
166 trk.setTheta(std::atan2(m_y, std::sqrt(1 + m_x * m_x)));
167 trk.setPhi(std::atan2(m_x, 1.0));
169 trk.setAllSensorPoints(all_points);
170 trk.setFirstSensorPosition(sensor1);
171 trk.setSecondSensorPosition(sensor2);
172 trk.setFirstLayerEcalRecHit(ecal_one);
173 trk.setDistancetoRecHit(temp_distance);
175 trk.setTargetLocation(0.0, b_x, b_y);
176 trk.setEcalLayer1Location(temp_extrapolated_point);
178 globalChiSquare(sensor1, sensor2, ecal_one, m_x, m_y, b_x, b_y));
182 trk.setCov(seed_cov);
185 if (truth_matching_tool_->configured()) {
186 auto truth_info = truth_matching_tool_->TruthMatch(all_points);
187 trk.setTrackID(truth_info.trackID);
188 trk.setPdgID(truth_info.pdgID);
189 trk.setTruthProb(truth_info.truthProb);
202 ldmx_log(info) <<
"AVG Time/Event: " << std::fixed << std::setprecision(1)
203 << processing_time_ / n_events_ <<
" ms";
204 ldmx_log(info) <<
"Total Seeds/Events: " << n_seeds_ <<
"/" << n_events_;
205 ldmx_log(info) <<
"not enough seed points " << n_missing_;
210 std::optional<ldmx::Measurement>>>,
212 std::optional<ldmx::Measurement>>>>
213LinearSeedFinder::combineMultiGlobalHits(
214 const std::vector<ldmx::Measurement>& hit_collection) {
215 std::vector<ldmx::Measurement> layer1, layer2, layer3, layer4;
219 for (
const auto& point : hit_collection) {
220 if (point.getGlobalPosition()[0] < layer12_midpoint_)
221 layer1.push_back(point);
222 else if (point.getGlobalPosition()[0] < layer23_midpoint_)
223 layer2.push_back(point);
224 else if (point.getGlobalPosition()[0] < layer34_midpoint_)
225 layer3.push_back(point);
227 layer4.push_back(point);
231 std::optional<ldmx::Measurement>>>
232 first_sensor_merged_hits, second_sensor_merged_hits;
234 if (layer1.empty()) {
235 for (
const auto& point : layer2) {
236 first_sensor_merged_hits.push_back(
237 std::make_tuple(std::array<double, 3>{point.getGlobalPosition()[0],
238 point.getGlobalPosition()[1],
239 point.getGlobalPosition()[2]},
240 point, std::nullopt));
243 else if (layer2.empty()) {
244 for (
const auto& point : layer1) {
245 first_sensor_merged_hits.push_back(
246 std::make_tuple(std::array<double, 3>{point.getGlobalPosition()[0],
247 point.getGlobalPosition()[1],
248 point.getGlobalPosition()[2]},
249 point, std::nullopt));
253 first_sensor_merged_hits = midPointCalculation(layer1, layer2);
256 if (layer3.empty()) {
257 for (
const auto& point : layer4) {
258 second_sensor_merged_hits.push_back(
259 std::make_tuple(std::array<double, 3>{point.getGlobalPosition()[0],
260 point.getGlobalPosition()[1],
261 point.getGlobalPosition()[2]},
262 point, std::nullopt));
265 else if (layer4.empty()) {
266 for (
const auto& point : layer3) {
267 second_sensor_merged_hits.push_back(
268 std::make_tuple(std::array<double, 3>{point.getGlobalPosition()[0],
269 point.getGlobalPosition()[1],
270 point.getGlobalPosition()[2]},
271 point, std::nullopt));
276 second_sensor_merged_hits = midPointCalculation(layer3, layer4);
279 return {first_sensor_merged_hits, second_sensor_merged_hits};
283 std::optional<ldmx::Measurement>>>
284LinearSeedFinder::midPointCalculation(
285 const std::vector<ldmx::Measurement>& layer1,
286 const std::vector<ldmx::Measurement>& layer2) {
288 std::optional<ldmx::Measurement>>>
291 for (
const auto& point1 : layer1) {
292 for (
const auto& point2 : layer2) {
294 (point1.getGlobalPosition()[0] + point2.getGlobalPosition()[0]) /
297 (point1.getGlobalPosition()[1] + point2.getGlobalPosition()[1]) /
303 merged_hits.push_back(std::make_tuple(
304 std::array<double, 3>{z_avg, x_avg, y_avg}, point1, point2));
311std::tuple<double, double, double, double, std::vector<double>>
312LinearSeedFinder::fit3DLine(
const std::array<double, 3>& first_recoil,
313 const std::array<double, 3>& second_recoil,
314 const std::array<double, 3>& ecal) {
315 double z_pos1 = first_recoil[0], x_pos1 = first_recoil[1],
316 y_pos1 = first_recoil[2];
317 double z_pos2 = second_recoil[0], x_pos2 = second_recoil[1],
318 y_pos2 = second_recoil[2];
319 double z_pos3 = ecal[0], x_pos3 = ecal[1], y_pos3 = ecal[2];
321 std::array<double, 6> weights = {
322 1 / pow(recoil_uncertainty_[0], 2), 1 / pow(recoil_uncertainty_[1], 2),
323 1 / pow(recoil_uncertainty_[0], 2), 1 / pow(recoil_uncertainty_[1], 2),
324 1 / pow(ecal_uncertainty_, 2), 1 / pow(ecal_uncertainty_, 2)};
326 Eigen::Matrix<double, 6, 4> A_mat;
327 Eigen::Matrix<double, 6, 1> d_vec, w_vec;
330 A_mat << z_pos1, 1, 0, 0, 0, 0, z_pos1, 1, z_pos2, 1, 0, 0, 0, 0, z_pos2, 1,
331 z_pos3, 1, 0, 0, 0, 0, z_pos3, 1;
334 d_vec << x_pos1, y_pos1, x_pos2, y_pos2, x_pos3, y_pos3;
337 w_vec = Eigen::Matrix<double, 6, 1>(weights.data());
340 Eigen::MatrixXd At_W_A = A_mat.transpose() * w_vec.asDiagonal() * A_mat;
341 Eigen::MatrixXd At_W_d = A_mat.transpose() * w_vec.asDiagonal() * d_vec;
342 Eigen::VectorXd param_vec = At_W_A.ldlt().solve(At_W_d);
344 Eigen::Matrix4d covariance_matrix = At_W_A.inverse();
348 std::vector<double> covariance_vector = {
349 covariance_matrix(0, 0), covariance_matrix(0, 1), covariance_matrix(0, 2),
350 covariance_matrix(0, 3), covariance_matrix(1, 1), covariance_matrix(1, 2),
351 covariance_matrix(1, 3), covariance_matrix(2, 2), covariance_matrix(2, 3),
352 covariance_matrix(3, 3)};
355 return {param_vec(0), param_vec(1), param_vec(2), param_vec(3),
359double LinearSeedFinder::calculateDistance(
360 const std::array<double, 3>& point1,
const std::array<double, 3>& point2) {
361 return sqrt(pow(point1[1] - point2[1], 2) + pow(point1[2] - point2[2], 2));
364double LinearSeedFinder::globalChiSquare(
365 const std::array<double, 3>& first_sensor,
366 const std::array<double, 3>& second_sensor,
367 const std::array<double, 3>& ecal_hit,
double m_x,
double m_y,
double b_x,
369 double chi2_x = 0, chi2_y = 0;
371 (m_x * first_sensor[0] + b_x - first_sensor[1]) / recoil_uncertainty_[0],
374 (m_y * first_sensor[0] + b_y - first_sensor[2]) / recoil_uncertainty_[1],
377 chi2_x += pow((m_x * second_sensor[0] + b_x - second_sensor[1]) /
378 recoil_uncertainty_[0],
380 chi2_y += pow((m_y * second_sensor[0] + b_y - second_sensor[2]) /
381 recoil_uncertainty_[1],
384 chi2_x += pow((m_x * ecal_hit[0] + b_x - ecal_hit[1]) / ecal_uncertainty_, 2);
385 chi2_y += pow((m_y * ecal_hit[0] + b_y - ecal_hit[2]) / ecal_uncertainty_, 2);
387 return chi2_x + chi2_y;
390int LinearSeedFinder::uniqueLayersHit(
391 const std::vector<ldmx::Measurement>& digi_points) {
392 std::vector<ldmx::Measurement> sorted_points = digi_points;
395 std::sort(sorted_points.begin(), sorted_points.end(),
397 return meas1.getGlobalPosition()[0] <
398 meas2.getGlobalPosition()[0];
402 auto last = std::unique(
403 sorted_points.begin(), sorted_points.end(),
405 return meas1.getGlobalPosition()[0] == meas2.getGlobalPosition()[0];
409 return std::distance(sorted_points.begin(), last);
#define DECLARE_PRODUCER_NS(NS, 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.
Stores reconstructed hit information from the ECAL.
Class representing a simulated particle.
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...