File indexing completed on 2026-08-31 08:21:21
0001 #include "Tpc_AssembledTrackReco.h"
0002
0003 #include "IdealPadMap.h"
0004 #include "Tpc_AssembledTrack.h"
0005 #include "Tpc_AssembledTrackContainer.h"
0006 #include "Tpc_AssembledTrackContainerv1.h"
0007 #include "Tpc_AssembledTrackv1.h"
0008 #include "Tpc_FittingTools.h"
0009
0010 #include "Tpc_ModuleTrack.h"
0011 #include "Tpc_ModuleTrackContainer.h"
0012
0013 #include <fun4all/Fun4AllReturnCodes.h>
0014
0015 #include <phool/PHCompositeNode.h>
0016 #include <phool/PHIODataNode.h>
0017 #include <phool/PHNodeIterator.h>
0018 #include <phool/PHObject.h>
0019 #include <phool/getClass.h>
0020
0021 #include <trackbase/TpcDefs.h>
0022 #include <trackbase/TrkrDefs.h>
0023 #include <trackbase/TrkrHit.h>
0024 #include <trackbase/TrkrHitSet.h>
0025 #include <trackbase/TrkrHitSetContainer.h>
0026
0027 #include <TFile.h>
0028 #include <TH1D.h>
0029 #include <TH2D.h>
0030 #include <TTree.h>
0031
0032 #include <algorithm>
0033 #include <cmath>
0034 #include <format>
0035 #include <functional>
0036 #include <iostream>
0037 #include <limits>
0038 #include <thread>
0039 #include <utility>
0040 #include <vector>
0041
0042 namespace
0043 {
0044
0045 struct PieceStartSort
0046 {
0047 const std::vector<Tpc_AssembledTrackReco::Piece>* pieces;
0048 explicit PieceStartSort(const std::vector<Tpc_AssembledTrackReco::Piece>* p)
0049 : pieces(p)
0050 {
0051 }
0052 bool operator()(unsigned int a, unsigned int b) const
0053 {
0054 const Tpc_AssembledTrackReco::Piece& pa = (*pieces)[a];
0055 const Tpc_AssembledTrackReco::Piece& pb = (*pieces)[b];
0056 if (pa.first_layer != pb.first_layer)
0057 {
0058 return pa.first_layer < pb.first_layer;
0059 }
0060 if (pa.last_layer != pb.last_layer)
0061 {
0062 return pa.last_layer < pb.last_layer;
0063 }
0064 if (pa.sector != pb.sector)
0065 {
0066 return pa.sector < pb.sector;
0067 }
0068 return pa.source_track_id < pb.source_track_id;
0069 }
0070 };
0071
0072 struct CandidateStartSort
0073 {
0074 const std::vector<Tpc_AssembledTrackReco::Candidate>* candidates;
0075 explicit CandidateStartSort(const std::vector<Tpc_AssembledTrackReco::Candidate>* c)
0076 : candidates(c)
0077 {
0078 }
0079 bool operator()(unsigned int a, unsigned int b) const
0080 {
0081 const Tpc_AssembledTrackReco::Candidate& ca = (*candidates)[a];
0082 const Tpc_AssembledTrackReco::Candidate& cb = (*candidates)[b];
0083 if (ca.first_layer != cb.first_layer)
0084 {
0085 return ca.first_layer < cb.first_layer;
0086 }
0087 if (ca.last_layer != cb.last_layer)
0088 {
0089 return ca.last_layer < cb.last_layer;
0090 }
0091 if (ca.first_sector != cb.first_sector)
0092 {
0093 return ca.first_sector < cb.first_sector;
0094 }
0095 return ca.nsegments < cb.nsegments;
0096 }
0097 };
0098
0099 struct RadiusSort
0100 {
0101 const std::vector<double>* radius;
0102 explicit RadiusSort(const std::vector<double>* r)
0103 : radius(r)
0104 {
0105 }
0106 bool operator()(unsigned int a, unsigned int b) const { return (*radius)[a] < (*radius)[b]; }
0107 };
0108
0109
0110 double unwrap_phi_to_reference(double phi, const double ref)
0111 {
0112 while (phi - ref > M_PI)
0113 {
0114 phi -= 2.0 * M_PI;
0115 }
0116 while (phi - ref < -M_PI)
0117 {
0118 phi += 2.0 * M_PI;
0119 }
0120 return phi;
0121 }
0122
0123 double wrap_to_pi(double phi)
0124 {
0125 while (phi > M_PI)
0126 {
0127 phi -= 2.0 * M_PI;
0128 }
0129 while (phi <= -M_PI)
0130 {
0131 phi += 2.0 * M_PI;
0132 }
0133 return phi;
0134 }
0135
0136 int wrapped_sector_delta(const unsigned int sector_a, const unsigned int sector_b)
0137 {
0138 int d = static_cast<int>(sector_b) - static_cast<int>(sector_a);
0139 while (d > 6)
0140 {
0141 d -= 12;
0142 }
0143 while (d < -6)
0144 {
0145 d += 12;
0146 }
0147 return d;
0148 }
0149
0150 double sagitta_model_derivative(double xrot, double x0, double invR)
0151 {
0152 const double dx = xrot - x0;
0153 const double dx2 = dx * dx;
0154 const double invR2 = invR * invR;
0155 const double invR3 = invR2 * invR;
0156 const double invR5 = invR3 * invR2;
0157 return -invR * dx - 0.5 * invR3 * dx2 * dx - 0.375 * invR5 * dx2 * dx2 * dx;
0158 }
0159
0160 double predict_sagitta_phi(double radius, double S, double x0, double invR, double theta, double bline)
0161 {
0162 const double c = std::cos(theta);
0163 const double s = std::sin(theta);
0164 double yy = std::tan(theta) * radius;
0165
0166 for (unsigned int iter = 0; iter < 25; ++iter)
0167 {
0168 const double xrot = c * radius + s * yy;
0169 const double yrot = -s * radius + c * yy;
0170 const double f = Tpc_FittingTools::sagittaModel(xrot, S, x0, invR);
0171 const double g = yrot - f;
0172 const double df = sagitta_model_derivative(xrot, x0, invR);
0173 const double dg = c - df * s;
0174 if (std::fabs(dg) < 1.0e-12)
0175 {
0176 break;
0177 }
0178 const double step = g / dg;
0179 yy -= step;
0180 if (std::fabs(step) < 1.0e-10)
0181 {
0182 break;
0183 }
0184 }
0185
0186 return bline + yy;
0187 }
0188
0189 bool fit_points(const std::vector<double>& radius,
0190 const std::vector<double>& phi,
0191 const std::vector<double>& tbin,
0192 const std::vector<double>& weight,
0193 bool use_sagitta,
0194 double& phi_slope,
0195 double& phi_intercept,
0196 double& phi_S,
0197 double& phi_x0,
0198 double& phi_invR,
0199 double& phi_theta,
0200 double& phi_bline,
0201 bool& phi_sagitta_ok,
0202 double& tbin_slope,
0203 double& tbin_intercept,
0204 double& chi2_phi,
0205 double& chi2_tbin,
0206 int& ndof_phi,
0207 int& ndof_tbin)
0208 {
0209 if (radius.size() < 2 || radius.size() != phi.size() || radius.size() != tbin.size() || radius.size() != weight.size())
0210 {
0211 return false;
0212 }
0213
0214 std::vector<Tpc_FittingTools::FitPoint> phi_points;
0215 std::vector<Tpc_FittingTools::FitPoint> tbin_points;
0216 phi_points.reserve(radius.size());
0217 tbin_points.reserve(radius.size());
0218 for (unsigned int i = 0; i < radius.size(); ++i)
0219 {
0220 phi_points.emplace_back(radius[i], phi[i], weight[i]);
0221 tbin_points.emplace_back(radius[i], tbin[i], weight[i]);
0222 }
0223
0224 const Tpc_FittingTools::LineFit phi_line = Tpc_FittingTools::fitLine(phi_points);
0225 if (!phi_line.ok)
0226 {
0227 return false;
0228 }
0229
0230 phi_slope = phi_line.slope;
0231 phi_intercept = phi_line.intercept;
0232 chi2_phi = phi_line.chi2;
0233 ndof_phi = phi_line.ndof;
0234 phi_S = 0.0;
0235 phi_x0 = 0.0;
0236 phi_invR = 0.0;
0237 phi_theta = std::atan(phi_slope);
0238 phi_bline = phi_intercept;
0239 phi_sagitta_ok = false;
0240
0241 if (use_sagitta && radius.size() >= 3)
0242 {
0243 const Tpc_FittingTools::SagittaFit phi_sagitta = Tpc_FittingTools::fitSagitta(phi_points);
0244 if (phi_sagitta.ok)
0245 {
0246 phi_S = phi_sagitta.S;
0247 phi_x0 = phi_sagitta.x0;
0248 phi_invR = phi_sagitta.invR;
0249 phi_theta = phi_sagitta.theta;
0250 phi_bline = phi_sagitta.b;
0251 chi2_phi = phi_sagitta.chi2;
0252 ndof_phi = phi_sagitta.ndof;
0253 phi_sagitta_ok = true;
0254 }
0255 }
0256
0257 const Tpc_FittingTools::LineFit tbin_line = Tpc_FittingTools::fitLine(tbin_points);
0258 if (!tbin_line.ok)
0259 {
0260 return false;
0261 }
0262
0263 tbin_slope = tbin_line.slope;
0264 tbin_intercept = tbin_line.intercept;
0265 chi2_tbin = tbin_line.chi2;
0266 ndof_tbin = tbin_line.ndof;
0267 return true;
0268 }
0269 }
0270
0271 Tpc_AssembledTrackReco::Piece::Piece()
0272 : source_index(0)
0273 , source_track_id(0)
0274 , event(0)
0275 , region(0)
0276 , sector(0)
0277 , side(0)
0278 , first_layer(0)
0279 , last_layer(0)
0280 , nblobs(0)
0281 , nrawhits(0)
0282 , phi_slope(0.0)
0283 , phi_intercept(0.0)
0284 , phi_S(0.0)
0285 , phi_x0(0.0)
0286 , phi_invR(0.0)
0287 , phi_theta(0.0)
0288 , phi_bline(0.0)
0289 , phi_sagitta_ok(false)
0290 , tbin_slope(0.0)
0291 , tbin_intercept(0.0)
0292 {
0293 }
0294
0295 Tpc_AssembledTrackReco::Candidate::Candidate()
0296 : event(0)
0297 , side(0)
0298 , first_layer(0)
0299 , last_layer(0)
0300 , first_sector(0)
0301 , last_sector(0)
0302 , first_region(0)
0303 , last_region(0)
0304 , nsegments(0)
0305 , nblobs(0)
0306 , nrawhits(0)
0307 , phi_slope(0.0)
0308 , phi_intercept(0.0)
0309 , phi_S(0.0)
0310 , phi_x0(0.0)
0311 , phi_invR(0.0)
0312 , phi_theta(0.0)
0313 , phi_bline(0.0)
0314 , phi_sagitta_ok(false)
0315 , tbin_slope_r(0.0)
0316 , tbin_intercept_r(0.0)
0317 , chi2_phi(0.0)
0318 , chi2_tbin(0.0)
0319 , ndof_phi(0)
0320 , ndof_tbin(0)
0321 {
0322 }
0323
0324 Tpc_AssembledTrackReco::Tpc_AssembledTrackReco(const std::string& name, const std::string& filename)
0325 : SubsysReco(name)
0326 , m_outputFileName(filename)
0327 , m_debugOutputFileName("Tpc_AssembledTrackRecoDebug.root")
0328 , m_inputNodeName("TPC_MODULETRACKS")
0329 , m_outputNodeName("TPC_ASSEMBLEDTRACKS")
0330 , m_outputFile(nullptr)
0331 , m_debugOutputFile(nullptr)
0332 , m_tree(nullptr)
0333 , m_tpcModuleTrackContainer(nullptr)
0334 , m_assembledTrackContainer(nullptr)
0335 , m_hits(nullptr)
0336 , m_event(0)
0337 , m_idealPadMap(nullptr)
0338 , m_connectMaxLayerGap(16)
0339 , m_connect_dphi(0.03)
0340 , m_connect_dtbin(8.0)
0341 , m_connect_dphi_slope(0.01)
0342 , m_connect_dtbin_slope(2.0)
0343 , m_useSagittaPhiFit(true)
0344 , m_seedSigmaX(5.0)
0345 , m_seedSigmaY(5.0)
0346 , m_seedSigmaZ(10.0)
0347 , m_seedSigmaPx(1.0)
0348 , m_seedSigmaPy(1.0)
0349 , m_seedSigmaPz(1.0)
0350 , m_h_dphi(nullptr)
0351 , m_h_dtbin(nullptr)
0352 , m_h_dmphi(nullptr)
0353 , m_h_dmtbin(nullptr)
0354 , m_h_score(nullptr)
0355 , m_h_dphi_vs_dtbin(nullptr)
0356 , m_h_dmphi_vs_dmtbin(nullptr)
0357 , m_h_dphi_vs_dmphi(nullptr)
0358 , m_h_tbin_slope_vs_first_tbin(nullptr)
0359 , m_h_tbin_slope_vs_last_tbin(nullptr)
0360 , m_h_track_tbin_slope_vs_tbin_span_3modules(nullptr)
0361 , m_h_track_tbin_slope_vs_first_tbin_3modules(nullptr)
0362 , m_h_track_tbin_slope_vs_last_tbin_3modules(nullptr)
0363 , m_h_layer_gap(nullptr)
0364 , m_h_nsegments(nullptr)
0365 , m_h_matched_sector_delta(nullptr)
0366 {
0367 }
0368
0369 Tpc_AssembledTrackReco::~Tpc_AssembledTrackReco()
0370 {
0371 delete m_idealPadMap;
0372 m_idealPadMap = nullptr;
0373
0374 if (m_debugOutputFile)
0375 {
0376 delete m_debugOutputFile;
0377 m_debugOutputFile = nullptr;
0378 }
0379
0380 if (m_outputFile)
0381 {
0382 delete m_outputFile;
0383 m_outputFile = nullptr;
0384 }
0385 }
0386
0387 int Tpc_AssembledTrackReco::Init(PHCompositeNode* )
0388 {
0389 if (Verbosity() <= 0)
0390 {
0391 return Fun4AllReturnCodes::EVENT_OK;
0392 }
0393
0394 m_outputFile = new TFile(m_outputFileName.c_str(), "RECREATE");
0395 if (!m_outputFile || m_outputFile->IsZombie())
0396 {
0397 std::cerr << Name() << "::Init - cannot create " << m_outputFileName << std::endl;
0398 return Fun4AllReturnCodes::ABORTRUN;
0399 }
0400
0401 m_debugOutputFile = new TFile(m_debugOutputFileName.c_str(), "RECREATE");
0402 if (!m_debugOutputFile || m_debugOutputFile->IsZombie())
0403 {
0404 std::cerr << Name() << "::Init - cannot create debug file " << m_debugOutputFileName << std::endl;
0405 return Fun4AllReturnCodes::ABORTRUN;
0406 }
0407 create_debug_histograms();
0408
0409 m_tree = new TTree("Tpc_AssembledTracks", "Assembled tracks connected from Tpc_ModuleTrackReco");
0410 m_tree->Branch("event", &m_tree_event, "event/I");
0411 m_tree->Branch("track_id", &m_tree_track_id);
0412 m_tree->Branch("side", &m_tree_side);
0413 m_tree->Branch("nsegments", &m_tree_nsegments);
0414 m_tree->Branch("nblobs", &m_tree_nblobs);
0415 m_tree->Branch("nrawhits", &m_tree_nrawhits);
0416 m_tree->Branch("first_layer", &m_tree_first_layer);
0417 m_tree->Branch("last_layer", &m_tree_last_layer);
0418 m_tree->Branch("first_sector", &m_tree_first_sector);
0419 m_tree->Branch("last_sector", &m_tree_last_sector);
0420 m_tree->Branch("first_region", &m_tree_first_region);
0421 m_tree->Branch("last_region", &m_tree_last_region);
0422
0423 m_tree->Branch("source_assembled_track_id", &m_tree_source_assembled_track_id);
0424 m_tree->Branch("source_inmodule_track_id", &m_tree_source_inmodule_track_id);
0425 m_tree->Branch("source_region", &m_tree_source_region);
0426 m_tree->Branch("source_sector", &m_tree_source_sector);
0427 m_tree->Branch("source_side", &m_tree_source_side);
0428
0429 m_tree->Branch("hit_assembled_track_id", &m_tree_hit_assembled_track_id);
0430 m_tree->Branch("hit_hitsetkey", &m_tree_hit_hitsetkey);
0431 m_tree->Branch("hit_hitkey", &m_tree_hit_hitkey);
0432
0433 return Fun4AllReturnCodes::EVENT_OK;
0434 }
0435
0436 int Tpc_AssembledTrackReco::InitRun(PHCompositeNode* topNode)
0437 {
0438 if (getNodes(topNode) != Fun4AllReturnCodes::EVENT_OK)
0439 {
0440 return Fun4AllReturnCodes::ABORTRUN;
0441 }
0442 if (createNodes(topNode) != Fun4AllReturnCodes::EVENT_OK)
0443 {
0444 return Fun4AllReturnCodes::ABORTRUN;
0445 }
0446
0447 delete m_idealPadMap;
0448 m_idealPadMap = new IdealPadMap();
0449 if (m_idealPadMap->load_from_cdb(Verbosity()) != 0 || !m_idealPadMap->is_loaded())
0450 {
0451 std::cerr << Name() << "::InitRun - cannot load IdealPadMap from CDB" << std::endl;
0452 return Fun4AllReturnCodes::ABORTRUN;
0453 }
0454
0455 m_event = 0;
0456 return Fun4AllReturnCodes::EVENT_OK;
0457 }
0458
0459 int Tpc_AssembledTrackReco::End(PHCompositeNode* )
0460 {
0461 if (m_outputFile)
0462 {
0463 m_outputFile->cd();
0464 if (m_tree)
0465 {
0466 m_tree->Write();
0467 }
0468 m_outputFile->Close();
0469 delete m_outputFile;
0470 m_outputFile = nullptr;
0471 }
0472
0473 write_debug_histograms();
0474 if (m_debugOutputFile)
0475 {
0476 m_debugOutputFile->Close();
0477 delete m_debugOutputFile;
0478 m_debugOutputFile = nullptr;
0479 }
0480
0481 return Fun4AllReturnCodes::EVENT_OK;
0482 }
0483
0484 int Tpc_AssembledTrackReco::getNodes(PHCompositeNode* topNode)
0485 {
0486 m_tpcModuleTrackContainer = findNode::getClass<Tpc_ModuleTrackContainer>(topNode, m_inputNodeName);
0487 if (!m_tpcModuleTrackContainer)
0488 {
0489 std::cerr << Name() << "::getNodes - missing " << m_inputNodeName << std::endl;
0490 return Fun4AllReturnCodes::ABORTRUN;
0491 }
0492
0493 m_hits = findNode::getClass<TrkrHitSetContainer>(topNode, "TRKR_HITSET");
0494 if (!m_hits)
0495 {
0496 std::cerr << Name() << "::getNodes - missing TRKR_HITSET" << std::endl;
0497 return Fun4AllReturnCodes::ABORTRUN;
0498 }
0499
0500 return Fun4AllReturnCodes::EVENT_OK;
0501 }
0502
0503 int Tpc_AssembledTrackReco::createNodes(PHCompositeNode* topNode)
0504 {
0505 PHNodeIterator iter(topNode);
0506 PHCompositeNode* dstNode = dynamic_cast<PHCompositeNode*>(iter.findFirst("PHCompositeNode", "DST"));
0507
0508 if (!dstNode)
0509 {
0510 dstNode = new PHCompositeNode("DST");
0511 topNode->addNode(dstNode);
0512 }
0513
0514 m_assembledTrackContainer = findNode::getClass<Tpc_AssembledTrackContainer>(topNode, m_outputNodeName);
0515 if (!m_assembledTrackContainer)
0516 {
0517 m_assembledTrackContainer = new Tpc_AssembledTrackContainerv1();
0518 PHIODataNode<PHObject>* node = new PHIODataNode<PHObject>(m_assembledTrackContainer, m_outputNodeName, "PHObject");
0519 dstNode->addNode(node);
0520 std::cout << Name() << "::createNodes - created " << m_outputNodeName << " node" << std::endl;
0521 }
0522
0523 return Fun4AllReturnCodes::EVENT_OK;
0524 }
0525
0526 void Tpc_AssembledTrackReco::create_debug_histograms()
0527 {
0528 if (!m_debugOutputFile)
0529 {
0530 return;
0531 }
0532 m_debugOutputFile->cd();
0533
0534 m_h_dphi = new TH1D("h_dphi", std::format("#Delta#phi at match point, cut={:.4g};|#Delta#phi| [rad];tested pairs", m_connect_dphi).c_str(), 200, 0.0, std::max(0.2, 5.0 * m_connect_dphi));
0535 m_h_dtbin = new TH1D("h_dtbin", std::format("#Deltatbin at match point, cut={:.4g};|#Deltatbin|;tested pairs", m_connect_dtbin).c_str(), 200, 0.0, std::max(50.0, 5.0 * m_connect_dtbin));
0536 m_h_dmphi = new TH1D("h_dmphi", std::format("#Delta(d#phi/dr), cut={:.4g};|#Delta(d#phi/dr)| [rad/cm];tested pairs", m_connect_dphi_slope).c_str(), 200, 0.0, std::max(0.08, 5.0 * m_connect_dphi_slope));
0537 m_h_dmtbin = new TH1D("h_dmtbin", std::format("#Delta(dtbin/dr), cut={:.4g};|#Delta(dtbin/dr)| [tbin/cm];tested pairs", m_connect_dtbin_slope).c_str(), 200, 0.0, std::max(20.0, 5.0 * m_connect_dtbin_slope));
0538 m_h_score = new TH1D("h_score", "accepted connection score;score;accepted connections", 200, 0.0, 20.0);
0539
0540 m_h_dphi_vs_dtbin = new TH2D("h_dphi_vs_dtbin", std::format("#Delta#phi vs #Deltatbin, cuts #Delta#phi<{:.4g} #Deltatbin<{:.4g};|#Delta#phi| [rad];|#Deltatbin|", m_connect_dphi, m_connect_dtbin).c_str(), 160, 0.0, std::max(0.2, 5.0 * m_connect_dphi), 160, 0.0, std::max(50.0, 5.0 * m_connect_dtbin));
0541 m_h_dmphi_vs_dmtbin = new TH2D("h_dmphi_vs_dmtbin", std::format("slope residuals, cuts #Delta(d#phi/dr)<{:.4g} #Delta(dtbin/dr)<{:.4g};|#Delta(d#phi/dr)| [rad/cm];|#Delta(dtbin/dr)| [tbin/cm]", m_connect_dphi_slope, m_connect_dtbin_slope).c_str(), 160, 0.0, std::max(0.08, 5.0 * m_connect_dphi_slope), 160, 0.0, std::max(20.0, 5.0 * m_connect_dtbin_slope));
0542 m_h_dphi_vs_dmphi = new TH2D("h_dphi_vs_dmphi", std::format("#phi position vs slope residual, cuts #Delta#phi<{:.4g} #Delta(d#phi/dr)<{:.4g};|#Delta#phi| [rad];|#Delta(d#phi/dr)| [rad/cm]", m_connect_dphi, m_connect_dphi_slope).c_str(), 160, 0.0, std::max(0.2, 5.0 * m_connect_dphi), 160, 0.0, std::max(0.08, 5.0 * m_connect_dphi_slope));
0543
0544 m_h_tbin_slope_vs_first_tbin = new TH2D("h_tbin_slope_vs_first_tbin", "tested connection dtbin/dr vs first timebin;first timebin;dtbin/dr [tbin/cm]", 200, 0.0, 600.0, 200, -20.0, 20.0);
0545 m_h_tbin_slope_vs_last_tbin = new TH2D("h_tbin_slope_vs_last_tbin", "tested connection dtbin/dr vs last timebin;last timebin;dtbin/dr [tbin/cm]", 200, 0.0, 600.0, 200, -20.0, 20.0);
0546 m_h_track_tbin_slope_vs_tbin_span_3modules = new TH2D("h_track_tbin_slope_vs_tbin_span_3modules", "3-module tracks dtbin/dr vs last-first timebin;last timebin - first timebin;dtbin/dr [tbin/cm]", 200, -600.0, 600.0, 200, -20.0, 20.0);
0547 m_h_track_tbin_slope_vs_first_tbin_3modules = new TH2D("h_track_tbin_slope_vs_first_tbin_3modules", "3-module tracks dtbin/dr vs first timebin;first timebin;dtbin/dr [tbin/cm]", 200, 0.0, 600.0, 200, -20.0, 20.0);
0548 m_h_track_tbin_slope_vs_last_tbin_3modules = new TH2D("h_track_tbin_slope_vs_last_tbin_3modules", "3-module tracks dtbin/dr vs last timebin;last timebin;dtbin/dr [tbin/cm]", 200, 0.0, 600.0, 200, -20.0, 20.0);
0549
0550 m_h_layer_gap = new TH1D("h_layer_gap", "accepted connection layer gap;b.first_layer - a.last_layer - 1;accepted connections", 16, -0.5, 15.5);
0551 m_h_nsegments = new TH1D("h_nsegments", "pieces per assembled track;nsegments;assembled tracks", 16, -0.5, 15.5);
0552 m_h_matched_sector_delta = new TH1D("h_matched_sector_delta", "accepted matched sector difference;wrapped #Delta sector;accepted connections", 25, -12.5, 12.5);
0553 }
0554
0555 void Tpc_AssembledTrackReco::write_debug_histograms()
0556 {
0557 if (!m_debugOutputFile)
0558 {
0559 return;
0560 }
0561 m_debugOutputFile->cd();
0562
0563 if (m_h_dphi)
0564 {
0565 m_h_dphi->Write();
0566 }
0567 if (m_h_dtbin)
0568 {
0569 m_h_dtbin->Write();
0570 }
0571 if (m_h_dmphi)
0572 {
0573 m_h_dmphi->Write();
0574 }
0575 if (m_h_dmtbin)
0576 {
0577 m_h_dmtbin->Write();
0578 }
0579 if (m_h_score)
0580 {
0581 m_h_score->Write();
0582 }
0583 if (m_h_dphi_vs_dtbin)
0584 {
0585 m_h_dphi_vs_dtbin->Write();
0586 }
0587 if (m_h_dmphi_vs_dmtbin)
0588 {
0589 m_h_dmphi_vs_dmtbin->Write();
0590 }
0591 if (m_h_dphi_vs_dmphi)
0592 {
0593 m_h_dphi_vs_dmphi->Write();
0594 }
0595 if (m_h_tbin_slope_vs_first_tbin)
0596 {
0597 m_h_tbin_slope_vs_first_tbin->Write();
0598 }
0599 if (m_h_tbin_slope_vs_last_tbin)
0600 {
0601 m_h_tbin_slope_vs_last_tbin->Write();
0602 }
0603 if (m_h_track_tbin_slope_vs_tbin_span_3modules)
0604 {
0605 m_h_track_tbin_slope_vs_tbin_span_3modules->Write();
0606 }
0607 if (m_h_track_tbin_slope_vs_first_tbin_3modules)
0608 {
0609 m_h_track_tbin_slope_vs_first_tbin_3modules->Write();
0610 }
0611 if (m_h_track_tbin_slope_vs_last_tbin_3modules)
0612 {
0613 m_h_track_tbin_slope_vs_last_tbin_3modules->Write();
0614 }
0615 if (m_h_layer_gap)
0616 {
0617 m_h_layer_gap->Write();
0618 }
0619 if (m_h_nsegments)
0620 {
0621 m_h_nsegments->Write();
0622 }
0623 if (m_h_matched_sector_delta)
0624 {
0625 m_h_matched_sector_delta->Write();
0626 }
0627 }
0628
0629 void Tpc_AssembledTrackReco::reset_tree_vars()
0630 {
0631 m_tree_event = m_event;
0632 m_tree_track_id.clear();
0633 m_tree_side.clear();
0634 m_tree_nsegments.clear();
0635 m_tree_nblobs.clear();
0636 m_tree_nrawhits.clear();
0637 m_tree_first_layer.clear();
0638 m_tree_last_layer.clear();
0639 m_tree_first_sector.clear();
0640 m_tree_last_sector.clear();
0641 m_tree_first_region.clear();
0642 m_tree_last_region.clear();
0643 m_tree_source_assembled_track_id.clear();
0644 m_tree_source_inmodule_track_id.clear();
0645 m_tree_source_region.clear();
0646 m_tree_source_sector.clear();
0647 m_tree_source_side.clear();
0648 m_tree_hit_assembled_track_id.clear();
0649 m_tree_hit_hitsetkey.clear();
0650 m_tree_hit_hitkey.clear();
0651 }
0652
0653 bool Tpc_AssembledTrackReco::make_piece(unsigned int source_index, Piece& p) const
0654 {
0655 const Tpc_ModuleTrack* trk = m_tpcModuleTrackContainer->get_track(source_index);
0656 if (!trk || !trk->isValid())
0657 {
0658 return false;
0659 }
0660 if (trk->get_last_layer() < trk->get_first_layer())
0661 {
0662 return false;
0663 }
0664 if (!m_idealPadMap || !m_idealPadMap->is_loaded() || !m_hits)
0665 {
0666 return false;
0667 }
0668
0669 p.source_index = source_index;
0670 p.source_track_id = trk->get_track_id();
0671 p.event = trk->get_event();
0672 p.region = trk->get_region();
0673 p.sector = trk->get_sector();
0674 p.side = trk->get_side();
0675 p.first_layer = trk->get_first_layer();
0676 p.last_layer = trk->get_last_layer();
0677 p.nblobs = trk->get_nblobs();
0678 p.nrawhits = trk->get_nrawhits();
0679
0680 p.radius_values.clear();
0681 p.phi_values.clear();
0682 p.tbin_values.clear();
0683 p.weights.clear();
0684 p.hitsetkeys.clear();
0685 p.hitkeys.clear();
0686
0687 double maxadc = 0.0;
0688 for (unsigned int ih = 0; ih < trk->size_hit_indices(); ++ih)
0689 {
0690 const Tpc_ModuleTrack::HitIndex hi = trk->get_hit_index(ih);
0691 TrkrHitSet* hitset = m_hits->findHitSet(hi.first);
0692 if (!hitset)
0693 {
0694 continue;
0695 }
0696 TrkrHit* hit = hitset->getHit(hi.second);
0697 if (!hit)
0698 {
0699 continue;
0700 }
0701 const double adc = static_cast<double>(hit->getAdc());
0702 maxadc = std::max(adc, maxadc);
0703 }
0704
0705 for (unsigned int ih = 0; ih < trk->size_hit_indices(); ++ih)
0706 {
0707 const Tpc_ModuleTrack::HitIndex hi = trk->get_hit_index(ih);
0708 TrkrHitSet* hitset = m_hits->findHitSet(hi.first);
0709 if (!hitset)
0710 {
0711 continue;
0712 }
0713 TrkrHit* hit = hitset->getHit(hi.second);
0714 if (!hit)
0715 {
0716 continue;
0717 }
0718
0719 const unsigned int layer = TrkrDefs::getLayer(hi.first);
0720 const unsigned int pad = TpcDefs::getPad(hi.second);
0721 const unsigned int tbin = TpcDefs::getTBin(hi.second);
0722
0723 const double radius = m_idealPadMap->get_radius(layer);
0724 const double phi = wrap_to_pi(m_idealPadMap->get_phi(static_cast<unsigned int>(p.side), layer, pad));
0725 if (!std::isfinite(radius) || !std::isfinite(phi))
0726 {
0727 continue;
0728 }
0729
0730 p.radius_values.push_back(radius);
0731 p.phi_values.push_back(phi);
0732 p.tbin_values.push_back(static_cast<double>(tbin));
0733 p.weights.push_back(Tpc_FittingTools::adcWeight(static_cast<double>(hit->getAdc()), maxadc, 0.5, 0.15));
0734 p.hitsetkeys.push_back(hi.first);
0735 p.hitkeys.push_back(hi.second);
0736 }
0737
0738 if (p.radius_values.size() < 2)
0739 {
0740 return false;
0741 }
0742
0743 std::vector<unsigned int> order;
0744 order.reserve(p.radius_values.size());
0745 for (unsigned int i = 0; i < p.radius_values.size(); ++i)
0746 {
0747 order.push_back(i);
0748 }
0749 std::sort(order.begin(), order.end(), RadiusSort(&p.radius_values));
0750
0751 std::vector<double> r_sorted;
0752 std::vector<double> phi_sorted;
0753 std::vector<double> tbin_sorted;
0754 std::vector<double> w_sorted;
0755 std::vector<TrkrDefs::hitsetkey> hsk_sorted;
0756 std::vector<TrkrDefs::hitkey> hk_sorted;
0757 r_sorted.reserve(order.size());
0758 phi_sorted.reserve(order.size());
0759 tbin_sorted.reserve(order.size());
0760 w_sorted.reserve(order.size());
0761 hsk_sorted.reserve(order.size());
0762 hk_sorted.reserve(order.size());
0763
0764 for (unsigned int i : order)
0765 {
0766 double phi = p.phi_values[i];
0767 if (!phi_sorted.empty())
0768 {
0769 phi = unwrap_phi_to_reference(phi, phi_sorted.back());
0770 }
0771 r_sorted.push_back(p.radius_values[i]);
0772 phi_sorted.push_back(phi);
0773 tbin_sorted.push_back(p.tbin_values[i]);
0774 w_sorted.push_back(p.weights[i]);
0775 hsk_sorted.push_back(p.hitsetkeys[i]);
0776 hk_sorted.push_back(p.hitkeys[i]);
0777 }
0778
0779 p.radius_values.swap(r_sorted);
0780 p.phi_values.swap(phi_sorted);
0781 p.tbin_values.swap(tbin_sorted);
0782 p.weights.swap(w_sorted);
0783 p.hitsetkeys.swap(hsk_sorted);
0784 p.hitkeys.swap(hk_sorted);
0785
0786 double chi2_phi = 0.0;
0787 double chi2_tbin = 0.0;
0788 int ndof_phi = 0;
0789 int ndof_tbin = 0;
0790 return fit_points(p.radius_values, p.phi_values, p.tbin_values, p.weights, m_useSagittaPhiFit,
0791 p.phi_slope, p.phi_intercept, p.phi_S, p.phi_x0, p.phi_invR,
0792 p.phi_theta, p.phi_bline, p.phi_sagitta_ok,
0793 p.tbin_slope, p.tbin_intercept, chi2_phi, chi2_tbin, ndof_phi, ndof_tbin);
0794 }
0795
0796 double Tpc_AssembledTrackReco::predict_phi(const Piece& p, double radius) const
0797 {
0798 if (m_useSagittaPhiFit && p.phi_sagitta_ok)
0799 {
0800 return predict_sagitta_phi(radius, p.phi_S, p.phi_x0, p.phi_invR, p.phi_theta, p.phi_bline);
0801 }
0802 return p.phi_slope * radius + p.phi_intercept;
0803 }
0804
0805 double Tpc_AssembledTrackReco::predict_phi(const Candidate& c, double radius) const
0806 {
0807 if (m_useSagittaPhiFit && c.phi_sagitta_ok)
0808 {
0809 return predict_sagitta_phi(radius, c.phi_S, c.phi_x0, c.phi_invR, c.phi_theta, c.phi_bline);
0810 }
0811 return c.phi_slope * radius + c.phi_intercept;
0812 }
0813
0814 double Tpc_AssembledTrackReco::predict_phi_slope(const Piece& p, double radius) const
0815 {
0816 if (!(m_useSagittaPhiFit && p.phi_sagitta_ok))
0817 {
0818 return p.phi_slope;
0819 }
0820 const double eps = 1.0e-3;
0821 return (predict_phi(p, radius + eps) - predict_phi(p, radius - eps)) / (2.0 * eps);
0822 }
0823
0824 double Tpc_AssembledTrackReco::predict_phi_slope(const Candidate& c, double radius) const
0825 {
0826 if (!(m_useSagittaPhiFit && c.phi_sagitta_ok))
0827 {
0828 return c.phi_slope;
0829 }
0830 const double eps = 1.0e-3;
0831 return (predict_phi(c, radius + eps) - predict_phi(c, radius - eps)) / (2.0 * eps);
0832 }
0833
0834 bool Tpc_AssembledTrackReco::refit_candidate(const std::vector<Piece>& pieces, const std::vector<unsigned int>& piece_indices, Candidate& c) const
0835 {
0836 if (piece_indices.empty())
0837 {
0838 return false;
0839 }
0840
0841 std::vector<double> radius;
0842 std::vector<double> phi;
0843 std::vector<double> tbin;
0844 std::vector<double> weight;
0845 c = Candidate();
0846 c.piece_indices = piece_indices;
0847
0848 for (unsigned int ii = 0; ii < piece_indices.size(); ++ii)
0849 {
0850 const Piece& p = pieces[piece_indices[ii]];
0851
0852 for (unsigned int ih = 0; ih < p.radius_values.size(); ++ih)
0853 {
0854 double phiv = p.phi_values[ih];
0855 if (!phi.empty())
0856 {
0857 phiv = unwrap_phi_to_reference(phiv, phi.back());
0858 }
0859 radius.push_back(p.radius_values[ih]);
0860 phi.push_back(phiv);
0861 tbin.push_back(p.tbin_values[ih]);
0862 weight.push_back(p.weights[ih]);
0863 }
0864
0865 if (ii == 0)
0866 {
0867 c.event = p.event;
0868 c.side = p.side;
0869 c.first_layer = p.first_layer;
0870 c.last_layer = p.last_layer;
0871 c.first_sector = p.sector;
0872 c.last_sector = p.sector;
0873 c.first_region = p.region;
0874 c.last_region = p.region;
0875 }
0876 else
0877 {
0878 if (p.first_layer < c.first_layer)
0879 {
0880 c.first_layer = p.first_layer;
0881 c.first_sector = p.sector;
0882 c.first_region = p.region;
0883 }
0884 if (p.last_layer > c.last_layer)
0885 {
0886 c.last_layer = p.last_layer;
0887 c.last_sector = p.sector;
0888 c.last_region = p.region;
0889 }
0890 }
0891
0892 c.nblobs += p.nblobs;
0893 c.nrawhits += p.nrawhits;
0894 for (unsigned int ih = 0; ih < p.hitsetkeys.size(); ++ih)
0895 {
0896 c.hitsetkeys.push_back(p.hitsetkeys[ih]);
0897 c.hitkeys.push_back(p.hitkeys[ih]);
0898 }
0899 }
0900
0901 c.nsegments = static_cast<unsigned int>(piece_indices.size());
0902 return fit_points(radius, phi, tbin, weight, m_useSagittaPhiFit,
0903 c.phi_slope, c.phi_intercept, c.phi_S, c.phi_x0, c.phi_invR,
0904 c.phi_theta, c.phi_bline, c.phi_sagitta_ok,
0905 c.tbin_slope_r, c.tbin_intercept_r,
0906 c.chi2_phi, c.chi2_tbin, c.ndof_phi, c.ndof_tbin);
0907 }
0908
0909 bool Tpc_AssembledTrackReco::candidates_can_connect(const Candidate& a, const Piece& b, double& score, double& b_phi_intercept_shifted) const
0910 {
0911 score = std::numeric_limits<double>::max();
0912 b_phi_intercept_shifted = b.phi_intercept;
0913
0914 if (a.side != b.side)
0915 {
0916 return false;
0917 }
0918 if (a.last_layer >= b.first_layer)
0919 {
0920 return false;
0921 }
0922
0923 const unsigned int gap = b.first_layer - a.last_layer - 1;
0924 if (gap > m_connectMaxLayerGap)
0925 {
0926 return false;
0927 }
0928
0929 const double ra = m_idealPadMap->get_radius(a.last_layer);
0930 const double rb = m_idealPadMap->get_radius(b.first_layer);
0931 if (!std::isfinite(ra) || !std::isfinite(rb) || ra <= 0.0 || rb <= 0.0)
0932 {
0933 return false;
0934 }
0935
0936 const double rmatch = 0.5 * (ra + rb);
0937 const double phi_a = predict_phi(a, rmatch);
0938 const double phi_b_raw = predict_phi(b, rmatch);
0939 const double phi_b = unwrap_phi_to_reference(phi_b_raw, phi_a);
0940 b_phi_intercept_shifted = b.phi_intercept + (phi_b - phi_b_raw);
0941
0942 const double tbin_a = a.tbin_slope_r * rmatch + a.tbin_intercept_r;
0943 const double tbin_b = b.tbin_slope * rmatch + b.tbin_intercept;
0944 const double dphi = std::fabs(phi_a - phi_b);
0945 const double dtbin = std::fabs(tbin_a - tbin_b);
0946 const double dmphi = std::fabs(predict_phi_slope(a, rmatch) - predict_phi_slope(b, rmatch));
0947 const double dmtbin = std::fabs(a.tbin_slope_r - b.tbin_slope);
0948
0949 {
0950 std::lock_guard<std::mutex> lock(m_debugMutex);
0951 if (m_h_dphi)
0952 {
0953 m_h_dphi->Fill(dphi);
0954 }
0955 if (m_h_dtbin)
0956 {
0957 m_h_dtbin->Fill(dtbin);
0958 }
0959 if (m_h_dmphi)
0960 {
0961 m_h_dmphi->Fill(dmphi);
0962 }
0963 if (m_h_dmtbin)
0964 {
0965 m_h_dmtbin->Fill(dmtbin);
0966 }
0967 if (m_h_dphi_vs_dtbin)
0968 {
0969 m_h_dphi_vs_dtbin->Fill(dphi, dtbin);
0970 }
0971 if (m_h_dmphi_vs_dmtbin)
0972 {
0973 m_h_dmphi_vs_dmtbin->Fill(dmphi, dmtbin);
0974 }
0975 if (m_h_dphi_vs_dmphi)
0976 {
0977 m_h_dphi_vs_dmphi->Fill(dphi, dmphi);
0978 }
0979 if (m_h_tbin_slope_vs_last_tbin)
0980 {
0981 m_h_tbin_slope_vs_last_tbin->Fill(a.tbin_slope_r * ra + a.tbin_intercept_r, a.tbin_slope_r);
0982 }
0983 if (m_h_tbin_slope_vs_first_tbin && !b.tbin_values.empty())
0984 {
0985 m_h_tbin_slope_vs_first_tbin->Fill(b.tbin_values.front(), b.tbin_slope);
0986 }
0987 }
0988
0989 if (dphi > m_connect_dphi)
0990 {
0991 return false;
0992 }
0993 if (dtbin > m_connect_dtbin)
0994 {
0995 return false;
0996 }
0997 if (dmphi > m_connect_dphi_slope)
0998 {
0999 return false;
1000 }
1001 if (dmtbin > m_connect_dtbin_slope)
1002 {
1003 return false;
1004 }
1005
1006 constexpr double w_phi = 1.0;
1007 constexpr double w_mphi = 1.0;
1008 constexpr double w_tbin = 1.0;
1009 constexpr double w_mtbin = 2.0;
1010
1011 score = w_phi * (dphi / m_connect_dphi) * (dphi / m_connect_dphi) + w_tbin * (dtbin / m_connect_dtbin) * (dtbin / m_connect_dtbin) + w_mphi * (dmphi / m_connect_dphi_slope) * (dmphi / m_connect_dphi_slope) + w_mtbin * (dmtbin / m_connect_dtbin_slope) * (dmtbin / m_connect_dtbin_slope) + 0.05 * static_cast<double>(gap);
1012
1013 return true;
1014 }
1015
1016 void Tpc_AssembledTrackReco::connect_sector_pieces(const std::vector<Piece>& pieces, int side, unsigned int sector, std::vector<Candidate>& output) const
1017 {
1018 std::vector<unsigned int> order;
1019 for (unsigned int i = 0; i < pieces.size(); ++i)
1020 {
1021 if (pieces[i].side == side && pieces[i].sector == sector)
1022 {
1023 order.push_back(i);
1024 }
1025 }
1026 if (order.empty())
1027 {
1028 return;
1029 }
1030
1031 std::sort(order.begin(), order.end(), PieceStartSort(&pieces));
1032 std::vector<int> used(pieces.size(), 0);
1033
1034 for (unsigned int io = 0; io < order.size(); ++io)
1035 {
1036 const unsigned int iseed = order[io];
1037 if (used[iseed])
1038 {
1039 continue;
1040 }
1041
1042 std::vector<unsigned int> current_indices;
1043 current_indices.push_back(iseed);
1044 used[iseed] = 1;
1045
1046 Candidate current;
1047 if (!refit_candidate(pieces, current_indices, current))
1048 {
1049 continue;
1050 }
1051
1052 bool merged_any = true;
1053 while (merged_any)
1054 {
1055 merged_any = false;
1056 int best_j = -1;
1057 double best_score = std::numeric_limits<double>::max();
1058
1059 for (unsigned int j : order)
1060 {
1061 if (used[j])
1062 {
1063 continue;
1064 }
1065
1066 double score = 0.0;
1067 double shifted_intercept = 0.0;
1068 if (!candidates_can_connect(current, pieces[j], score, shifted_intercept))
1069 {
1070 continue;
1071 }
1072
1073 if (score < best_score)
1074 {
1075 best_score = score;
1076 best_j = static_cast<int>(j);
1077 }
1078 }
1079
1080 if (best_j >= 0)
1081 {
1082 std::vector<unsigned int> trial_indices = current_indices;
1083 trial_indices.push_back(static_cast<unsigned int>(best_j));
1084
1085 Candidate refit;
1086 if (refit_candidate(pieces, trial_indices, refit))
1087 {
1088 const Piece& accepted_piece = pieces[static_cast<unsigned int>(best_j)];
1089 const unsigned int accepted_gap = accepted_piece.first_layer - current.last_layer - 1;
1090 {
1091 std::lock_guard<std::mutex> lock(m_debugMutex);
1092 if (m_h_score)
1093 {
1094 m_h_score->Fill(best_score);
1095 }
1096 if (m_h_layer_gap)
1097 {
1098 m_h_layer_gap->Fill(static_cast<double>(accepted_gap));
1099 }
1100 if (m_h_matched_sector_delta)
1101 {
1102 m_h_matched_sector_delta->Fill(static_cast<double>(wrapped_sector_delta(current.last_sector, accepted_piece.sector)));
1103 }
1104 }
1105
1106 current = refit;
1107 current_indices.swap(trial_indices);
1108 used[best_j] = 1;
1109 merged_any = true;
1110 }
1111 }
1112 }
1113
1114 output.push_back(current);
1115 }
1116 }
1117
1118 bool Tpc_AssembledTrackReco::candidates_can_connect(const Candidate& a, const Candidate& b, double& score) const
1119 {
1120 score = std::numeric_limits<double>::max();
1121
1122 if (a.side != b.side)
1123 {
1124 return false;
1125 }
1126 if (a.last_layer >= b.first_layer)
1127 {
1128 return false;
1129 }
1130 if (a.last_sector == b.first_sector)
1131 {
1132 return false;
1133 }
1134
1135 const unsigned int gap = b.first_layer - a.last_layer - 1;
1136 if (gap > m_connectMaxLayerGap)
1137 {
1138 return false;
1139 }
1140
1141 const double ra = m_idealPadMap->get_radius(a.last_layer);
1142 const double rb = m_idealPadMap->get_radius(b.first_layer);
1143 if (!std::isfinite(ra) || !std::isfinite(rb) || ra <= 0.0 || rb <= 0.0)
1144 {
1145 return false;
1146 }
1147
1148 const double rmatch = 0.5 * (ra + rb);
1149 const double phi_a = predict_phi(a, rmatch);
1150 const double phi_b = unwrap_phi_to_reference(predict_phi(b, rmatch), phi_a);
1151 const double tbin_a = a.tbin_slope_r * rmatch + a.tbin_intercept_r;
1152 const double tbin_b = b.tbin_slope_r * rmatch + b.tbin_intercept_r;
1153 const double dphi = std::fabs(phi_a - phi_b);
1154 const double dtbin = std::fabs(tbin_a - tbin_b);
1155 const double dmphi = std::fabs(predict_phi_slope(a, rmatch) - predict_phi_slope(b, rmatch));
1156 const double dmtbin = std::fabs(a.tbin_slope_r - b.tbin_slope_r);
1157
1158 {
1159 std::lock_guard<std::mutex> lock(m_debugMutex);
1160 if (m_h_dphi)
1161 {
1162 m_h_dphi->Fill(dphi);
1163 }
1164 if (m_h_dtbin)
1165 {
1166 m_h_dtbin->Fill(dtbin);
1167 }
1168 if (m_h_dmphi)
1169 {
1170 m_h_dmphi->Fill(dmphi);
1171 }
1172 if (m_h_dmtbin)
1173 {
1174 m_h_dmtbin->Fill(dmtbin);
1175 }
1176 if (m_h_dphi_vs_dtbin)
1177 {
1178 m_h_dphi_vs_dtbin->Fill(dphi, dtbin);
1179 }
1180 if (m_h_dmphi_vs_dmtbin)
1181 {
1182 m_h_dmphi_vs_dmtbin->Fill(dmphi, dmtbin);
1183 }
1184 if (m_h_dphi_vs_dmphi)
1185 {
1186 m_h_dphi_vs_dmphi->Fill(dphi, dmphi);
1187 }
1188 if (m_h_tbin_slope_vs_last_tbin)
1189 {
1190 m_h_tbin_slope_vs_last_tbin->Fill(a.tbin_slope_r * ra + a.tbin_intercept_r, a.tbin_slope_r);
1191 }
1192 if (m_h_tbin_slope_vs_first_tbin)
1193 {
1194 m_h_tbin_slope_vs_first_tbin->Fill(b.tbin_slope_r * rb + b.tbin_intercept_r, b.tbin_slope_r);
1195 }
1196 }
1197
1198 if (dphi > m_connect_dphi)
1199 {
1200 return false;
1201 }
1202 if (dtbin > m_connect_dtbin)
1203 {
1204 return false;
1205 }
1206 if (dmphi > m_connect_dphi_slope)
1207 {
1208 return false;
1209 }
1210 if (dmtbin > m_connect_dtbin_slope)
1211 {
1212 return false;
1213 }
1214
1215 constexpr double w_phi = 1.0;
1216 constexpr double w_mphi = 1.0;
1217 constexpr double w_tbin = 1.0;
1218 constexpr double w_mtbin = 2.0;
1219
1220 score = w_phi * (dphi / m_connect_dphi) * (dphi / m_connect_dphi) + w_tbin * (dtbin / m_connect_dtbin) * (dtbin / m_connect_dtbin) + w_mphi * (dmphi / m_connect_dphi_slope) * (dmphi / m_connect_dphi_slope) + w_mtbin * (dmtbin / m_connect_dtbin_slope) * (dmtbin / m_connect_dtbin_slope) + 0.05 * static_cast<double>(gap);
1221
1222 return true;
1223 }
1224
1225 void Tpc_AssembledTrackReco::connect_side_candidates(const std::vector<Piece>& pieces,
1226 const std::vector<Candidate>& seeds,
1227 int side,
1228 std::vector<Candidate>& output) const
1229 {
1230 std::vector<unsigned int> order;
1231 for (unsigned int i = 0; i < seeds.size(); ++i)
1232 {
1233 if (seeds[i].side == side)
1234 {
1235 order.push_back(i);
1236 }
1237 }
1238 if (order.empty())
1239 {
1240 return;
1241 }
1242
1243 std::sort(order.begin(), order.end(), CandidateStartSort(&seeds));
1244 std::vector<int> used(seeds.size(), 0);
1245
1246 for (unsigned int io = 0; io < order.size(); ++io)
1247 {
1248 const unsigned int iseed = order[io];
1249 if (used[iseed])
1250 {
1251 continue;
1252 }
1253
1254 std::vector<unsigned int> current_indices = seeds[iseed].piece_indices;
1255 used[iseed] = 1;
1256
1257 Candidate current;
1258 if (!refit_candidate(pieces, current_indices, current))
1259 {
1260 continue;
1261 }
1262
1263 bool merged_any = true;
1264 while (merged_any)
1265 {
1266 merged_any = false;
1267 int best_j = -1;
1268 double best_score = std::numeric_limits<double>::max();
1269
1270 for (unsigned int j : order)
1271 {
1272 if (used[j])
1273 {
1274 continue;
1275 }
1276
1277 double score = 0.0;
1278 if (!candidates_can_connect(current, seeds[j], score))
1279 {
1280 continue;
1281 }
1282
1283 if (score < best_score)
1284 {
1285 best_score = score;
1286 best_j = static_cast<int>(j);
1287 }
1288 }
1289
1290 if (best_j >= 0)
1291 {
1292 std::vector<unsigned int> trial_indices = current_indices;
1293 const Candidate& accepted_seed = seeds[static_cast<unsigned int>(best_j)];
1294 trial_indices.insert(trial_indices.end(), accepted_seed.piece_indices.begin(), accepted_seed.piece_indices.end());
1295
1296 Candidate refit;
1297 if (refit_candidate(pieces, trial_indices, refit))
1298 {
1299 const unsigned int accepted_gap = accepted_seed.first_layer - current.last_layer - 1;
1300 {
1301 std::lock_guard<std::mutex> lock(m_debugMutex);
1302 if (m_h_score)
1303 {
1304 m_h_score->Fill(best_score);
1305 }
1306 if (m_h_layer_gap)
1307 {
1308 m_h_layer_gap->Fill(static_cast<double>(accepted_gap));
1309 }
1310 if (m_h_matched_sector_delta)
1311 {
1312 m_h_matched_sector_delta->Fill(static_cast<double>(wrapped_sector_delta(current.last_sector, accepted_seed.first_sector)));
1313 }
1314 }
1315
1316 current = refit;
1317 current_indices.swap(trial_indices);
1318 used[best_j] = 1;
1319 merged_any = true;
1320 }
1321 }
1322 }
1323
1324 output.push_back(current);
1325 }
1326 }
1327
1328 Tpc_AssembledTrackReco::SeedParameters
1329 Tpc_AssembledTrackReco::make_seed_parameters(const Candidate& c) const
1330 {
1331 SeedParameters seed;
1332 if (!m_idealPadMap)
1333 {
1334 return seed;
1335 }
1336
1337 const double r_first = m_idealPadMap->get_radius(c.first_layer);
1338 const double r_last = m_idealPadMap->get_radius(c.last_layer);
1339 if (!std::isfinite(r_first) || !std::isfinite(r_last))
1340 {
1341 return seed;
1342 }
1343
1344 const double radius = 0.5 * (r_first + r_last);
1345 const double phi = wrap_to_pi(predict_phi(c, radius));
1346 const double dphi_dr = predict_phi_slope(c, radius);
1347 const double dtbin_dr = c.tbin_slope_r;
1348 const double tbin = c.tbin_slope_r * radius + c.tbin_intercept_r;
1349 if (!std::isfinite(radius) || !std::isfinite(phi) ||
1350 !std::isfinite(dphi_dr) || !std::isfinite(dtbin_dr) || !std::isfinite(tbin))
1351 {
1352 return seed;
1353 }
1354
1355 seed.x = radius * std::cos(phi);
1356 seed.y = radius * std::sin(phi);
1357 seed.z = tbin;
1358
1359 const double dx_dr = std::cos(phi) - radius * std::sin(phi) * dphi_dr;
1360 const double dy_dr = std::sin(phi) + radius * std::cos(phi) * dphi_dr;
1361 const double dz_dr = dtbin_dr;
1362 const double norm = std::sqrt(dx_dr * dx_dr + dy_dr * dy_dr + dz_dr * dz_dr);
1363 if (!std::isfinite(norm) || norm <= 0.0)
1364 {
1365 return seed;
1366 }
1367
1368 seed.px = dx_dr / norm;
1369 seed.py = dy_dr / norm;
1370 seed.pz = dz_dr / norm;
1371
1372 for (auto& i : seed.cov)
1373 {
1374 for (double& j : i)
1375 {
1376 j = 0.0;
1377 }
1378 }
1379 seed.cov[0][0] = m_seedSigmaX * m_seedSigmaX;
1380 seed.cov[1][1] = m_seedSigmaY * m_seedSigmaY;
1381 seed.cov[2][2] = m_seedSigmaZ * m_seedSigmaZ;
1382 seed.cov[3][3] = m_seedSigmaPx * m_seedSigmaPx;
1383 seed.cov[4][4] = m_seedSigmaPy * m_seedSigmaPy;
1384 seed.cov[5][5] = m_seedSigmaPz * m_seedSigmaPz;
1385 seed.ok = std::isfinite(seed.x) && std::isfinite(seed.y) && std::isfinite(seed.z) &&
1386 std::isfinite(seed.px) && std::isfinite(seed.py) && std::isfinite(seed.pz);
1387 return seed;
1388 }
1389
1390 int Tpc_AssembledTrackReco::process_event(PHCompositeNode* )
1391 {
1392 reset_tree_vars();
1393 if (m_assembledTrackContainer)
1394 {
1395 m_assembledTrackContainer->Reset();
1396 }
1397
1398 std::vector<Piece> pieces;
1399 if (m_tpcModuleTrackContainer)
1400 {
1401 const unsigned int n = m_tpcModuleTrackContainer->size();
1402 pieces.reserve(n);
1403 for (unsigned int i = 0; i < n; ++i)
1404 {
1405 Piece p;
1406 if (make_piece(i, p))
1407 {
1408 pieces.push_back(p);
1409 }
1410 }
1411 }
1412
1413 std::vector<std::vector<Candidate> > sector_outputs(24);
1414 std::vector<std::thread> workers;
1415 workers.reserve(24);
1416 for (int side = 0; side < 2; ++side)
1417 {
1418 for (unsigned int sector = 0; sector < 12; ++sector)
1419 {
1420 const unsigned int index = static_cast<unsigned int>(side) * 12 + sector;
1421 workers.emplace_back(&Tpc_AssembledTrackReco::connect_sector_pieces, this, std::cref(pieces), side, sector, std::ref(sector_outputs[index]));
1422 }
1423 }
1424 for (std::thread& worker : workers)
1425 {
1426 worker.join();
1427 }
1428
1429 std::vector<Candidate> sector_tracks;
1430 for (auto& sector_output : sector_outputs)
1431 {
1432 sector_tracks.insert(sector_tracks.end(), sector_output.begin(), sector_output.end());
1433 }
1434
1435 std::vector<Candidate> assembled_tracks;
1436 connect_side_candidates(pieces, sector_tracks, 0, assembled_tracks);
1437 connect_side_candidates(pieces, sector_tracks, 1, assembled_tracks);
1438
1439 for (unsigned int it = 0; it < assembled_tracks.size(); ++it)
1440 {
1441 const Candidate& c = assembled_tracks[it];
1442 const unsigned int assembled_id = m_assembledTrackContainer ? m_assembledTrackContainer->size() : it;
1443 if (m_h_nsegments)
1444 {
1445 m_h_nsegments->Fill(static_cast<double>(c.nsegments));
1446 }
1447 if (c.nsegments == 3 && m_idealPadMap)
1448 {
1449 const double rfirst = m_idealPadMap->get_radius(c.first_layer);
1450 const double rlast = m_idealPadMap->get_radius(c.last_layer);
1451 if (std::isfinite(rfirst) && std::isfinite(rlast))
1452 {
1453 const double first_tbin = c.tbin_slope_r * rfirst + c.tbin_intercept_r;
1454 const double last_tbin = c.tbin_slope_r * rlast + c.tbin_intercept_r;
1455 const double dtbin_track = last_tbin - first_tbin;
1456 if (m_h_track_tbin_slope_vs_tbin_span_3modules)
1457 {
1458 m_h_track_tbin_slope_vs_tbin_span_3modules->Fill(dtbin_track, c.tbin_slope_r);
1459 }
1460 if (m_h_track_tbin_slope_vs_first_tbin_3modules)
1461 {
1462 m_h_track_tbin_slope_vs_first_tbin_3modules->Fill(first_tbin, c.tbin_slope_r);
1463 }
1464 if (m_h_track_tbin_slope_vs_last_tbin_3modules)
1465 {
1466 m_h_track_tbin_slope_vs_last_tbin_3modules->Fill(last_tbin, c.tbin_slope_r);
1467 }
1468 }
1469 }
1470
1471 Tpc_AssembledTrackv1* out = new Tpc_AssembledTrackv1();
1472 out->set_event(static_cast<unsigned int>(m_event));
1473 out->set_track_id(assembled_id);
1474 out->set_side(c.side);
1475 out->set_nsegments(c.nsegments);
1476 out->set_nblobs(c.nblobs);
1477 out->set_nrawhits(c.nrawhits);
1478 out->set_first_layer(c.first_layer);
1479 out->set_last_layer(c.last_layer);
1480 out->set_first_sector(c.first_sector);
1481 out->set_last_sector(c.last_sector);
1482 out->set_first_region(c.first_region);
1483 out->set_last_region(c.last_region);
1484
1485 const SeedParameters seed = make_seed_parameters(c);
1486 if (seed.ok)
1487 {
1488 out->set_seed_valid(1);
1489 out->set_seed_x(seed.x);
1490 out->set_seed_y(seed.y);
1491 out->set_seed_z(seed.z);
1492 out->set_seed_px(seed.px);
1493 out->set_seed_py(seed.py);
1494 out->set_seed_pz(seed.pz);
1495 for (unsigned int iseed = 0; iseed < 6; ++iseed)
1496 {
1497 for (unsigned int jseed = 0; jseed < 6; ++jseed)
1498 {
1499 out->set_seed_cov(iseed, jseed, seed.cov[iseed][jseed]);
1500 }
1501 }
1502 }
1503
1504 m_tree_track_id.push_back(assembled_id);
1505 m_tree_side.push_back(c.side);
1506 m_tree_nsegments.push_back(c.nsegments);
1507 m_tree_nblobs.push_back(c.nblobs);
1508 m_tree_nrawhits.push_back(c.nrawhits);
1509 m_tree_first_layer.push_back(c.first_layer);
1510 m_tree_last_layer.push_back(c.last_layer);
1511 m_tree_first_sector.push_back(c.first_sector);
1512 m_tree_last_sector.push_back(c.last_sector);
1513 m_tree_first_region.push_back(c.first_region);
1514 m_tree_last_region.push_back(c.last_region);
1515
1516 for (unsigned int piece_indice : c.piece_indices)
1517 {
1518 const Piece& p = pieces[piece_indice];
1519 out->add_source_track(p.source_track_id, p.region, p.sector);
1520 m_tree_source_assembled_track_id.push_back(assembled_id);
1521 m_tree_source_inmodule_track_id.push_back(p.source_track_id);
1522 m_tree_source_region.push_back(p.region);
1523 m_tree_source_sector.push_back(p.sector);
1524 m_tree_source_side.push_back(p.side);
1525 }
1526
1527 for (unsigned int ih = 0; ih < c.hitsetkeys.size(); ++ih)
1528 {
1529 out->add_hit_index(c.hitsetkeys[ih], c.hitkeys[ih]);
1530 m_tree_hit_assembled_track_id.push_back(assembled_id);
1531 m_tree_hit_hitsetkey.push_back(static_cast<unsigned long long>(c.hitsetkeys[ih]));
1532 m_tree_hit_hitkey.push_back(static_cast<unsigned long long>(c.hitkeys[ih]));
1533 }
1534
1535 if (m_assembledTrackContainer)
1536 {
1537 m_assembledTrackContainer->add_track(out);
1538 }
1539 else
1540 {
1541 delete out;
1542 }
1543 }
1544
1545 if (m_tree)
1546 {
1547 m_tree->Fill();
1548 }
1549
1550 if (Verbosity() > 0)
1551 {
1552 std::cout << Name() << "::process_event - event " << m_event
1553 << " input pieces=" << pieces.size()
1554 << " assembled_tracks=" << assembled_tracks.size() << std::endl;
1555 }
1556
1557 ++m_event;
1558 return Fun4AllReturnCodes::EVENT_OK;
1559 }