File indexing completed on 2026-08-31 08:21:42
0001 #include "Tpc_PolyClusterResiduals.h"
0002
0003 #include "tpctrackreco/Tpc_PolyCluster.h"
0004 #include "tpctrackreco/Tpc_PolyClusterContainer.h"
0005 #include "tpctrackreco/Tpc_PolyTrack.h"
0006 #include "tpctrackreco/Tpc_PolyTrackContainer.h"
0007 #include "tpctrackreco/Tpc_PolyTrackVertex.h"
0008 #include "tpctrackreco/Tpc_PolyTrackVertexContainer.h"
0009
0010 #include <fun4all/Fun4AllReturnCodes.h>
0011 #include <phool/PHCompositeNode.h>
0012 #include <phool/getClass.h>
0013 #include <trackbase/TpcDefs.h>
0014 #include <trackbase/TrkrDefs.h>
0015
0016 #include <TFile.h>
0017 #include <TTree.h>
0018
0019 #include <algorithm>
0020 #include <cmath>
0021 #include <iostream>
0022 #include <limits>
0023 #include <map>
0024 #include <vector>
0025
0026 namespace
0027 {
0028 double wrap_phi(double phi)
0029 {
0030 const double pi = std::acos(-1.0);
0031 while (phi > pi)
0032 {
0033 phi -= 2.0 * pi;
0034 }
0035 while (phi <= -pi)
0036 {
0037 phi += 2.0 * pi;
0038 }
0039 return phi;
0040 }
0041
0042 struct HelixCircle
0043 {
0044 bool ok{false};
0045 double xc{0.0};
0046 double yc{0.0};
0047 double radius{0.0};
0048 double sign{0.0};
0049 double phi0{0.0};
0050 double z0{0.0};
0051 double dzds{0.0};
0052 };
0053
0054 HelixCircle make_track_circle(const Tpc_PolyTrack* trk,
0055 const double magnetic_field_tesla)
0056 {
0057 HelixCircle circle;
0058 if (!trk || trk->get_fit_status() == 0)
0059 {
0060 return circle;
0061 }
0062
0063 const double x = trk->get_x();
0064 const double y = trk->get_y();
0065 const double z = trk->get_z();
0066 const double px = trk->get_px();
0067 const double py = trk->get_py();
0068 const double pz = trk->get_pz();
0069 const double charge = trk->get_charge();
0070 if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(z) ||
0071 !std::isfinite(px) || !std::isfinite(py) || !std::isfinite(pz) ||
0072 !std::isfinite(charge))
0073 {
0074 return circle;
0075 }
0076
0077 const double pt = std::hypot(px, py);
0078 if (pt <= 0.0 || std::fabs(charge * magnetic_field_tesla) < 1.0e-12)
0079 {
0080 return circle;
0081 }
0082
0083 const double signed_radius = pt / (0.003 * charge * magnetic_field_tesla);
0084 circle.radius = std::fabs(signed_radius);
0085 if (circle.radius <= 0.0 || !std::isfinite(circle.radius))
0086 {
0087 return circle;
0088 }
0089
0090 const double tx = px / pt;
0091 const double ty = py / pt;
0092 circle.sign = signed_radius > 0.0 ? 1.0 : -1.0;
0093 circle.xc = x + circle.sign * circle.radius * ty;
0094 circle.yc = y - circle.sign * circle.radius * tx;
0095 circle.phi0 = std::atan2(y - circle.yc, x - circle.xc);
0096 circle.z0 = z;
0097 circle.dzds = pz / pt;
0098 circle.ok = std::isfinite(circle.xc) && std::isfinite(circle.yc) &&
0099 std::isfinite(circle.phi0) && std::isfinite(circle.z0) &&
0100 std::isfinite(circle.dzds);
0101 return circle;
0102 }
0103
0104 bool helix_z_at_dca_to_vertex(const HelixCircle& circle,
0105 const double vertex_x,
0106 const double vertex_y,
0107 double& z_at_dca)
0108 {
0109 if (!circle.ok)
0110 {
0111 return false;
0112 }
0113
0114 const double dx = vertex_x - circle.xc;
0115 const double dy = vertex_y - circle.yc;
0116 const double dc = std::hypot(dx, dy);
0117 if (!std::isfinite(dc) || dc <= 1.0e-12)
0118 {
0119 return false;
0120 }
0121
0122 const double pca_x = circle.xc + circle.radius * dx / dc;
0123 const double pca_y = circle.yc + circle.radius * dy / dc;
0124 const double pca_phi_on_circle = std::atan2(pca_y - circle.yc, pca_x - circle.xc);
0125
0126 double best_arc = 0.0;
0127 double best_abs_arc = std::numeric_limits<double>::max();
0128 const double pi = std::acos(-1.0);
0129 for (int k = -4; k <= 4; ++k)
0130 {
0131 const double dphi = pca_phi_on_circle - circle.phi0 + 2.0 * pi * static_cast<double>(k);
0132 const double arc = -circle.sign * circle.radius * dphi;
0133 const double abs_arc = std::fabs(arc);
0134 if (abs_arc < best_abs_arc)
0135 {
0136 best_abs_arc = abs_arc;
0137 best_arc = arc;
0138 }
0139 }
0140
0141 z_at_dca = circle.z0 + circle.dzds * best_arc;
0142 return std::isfinite(z_at_dca);
0143 }
0144
0145 bool helix_z_at_radius(const HelixCircle& circle,
0146 const double target_r,
0147 const double reference_z,
0148 const double arc_direction,
0149 double& z_state)
0150 {
0151 if (!circle.ok)
0152 {
0153 return false;
0154 }
0155
0156 const double center_r = std::hypot(circle.xc, circle.yc);
0157 if (!std::isfinite(target_r) || !std::isfinite(center_r) ||
0158 target_r <= 0.0 || center_r <= 1.0e-12)
0159 {
0160 return false;
0161 }
0162
0163 const double radius_sum = target_r + circle.radius;
0164 const double radius_diff = std::fabs(target_r - circle.radius);
0165 if (center_r > radius_sum || center_r < radius_diff)
0166 {
0167 return false;
0168 }
0169
0170 const double a = (target_r * target_r - circle.radius * circle.radius + center_r * center_r) /
0171 (2.0 * center_r);
0172 const double h2 = target_r * target_r - a * a;
0173 if (h2 < -1.0e-8)
0174 {
0175 return false;
0176 }
0177
0178 const double h = std::sqrt(std::max(0.0, h2));
0179 const double ux = circle.xc / center_r;
0180 const double uy = circle.yc / center_r;
0181
0182 double best_z = 0.0;
0183 double best_abs_dz = std::numeric_limits<double>::max();
0184 const double pi = std::acos(-1.0);
0185 for (int isign = -1; isign <= 1; isign += 2)
0186 {
0187 const double x = a * ux - static_cast<double>(isign) * h * uy;
0188 const double y = a * uy + static_cast<double>(isign) * h * ux;
0189 const double phi_on_circle = std::atan2(y - circle.yc, x - circle.xc);
0190
0191 for (int k = -4; k <= 4; ++k)
0192 {
0193 const double dphi = phi_on_circle - circle.phi0 + 2.0 * pi * static_cast<double>(k);
0194 const double arc = -circle.sign * circle.radius * dphi;
0195 const double z = circle.z0 + arc_direction * circle.dzds * arc;
0196 const double abs_dz = std::fabs(z - reference_z);
0197 if (abs_dz < best_abs_dz)
0198 {
0199 best_abs_dz = abs_dz;
0200 best_z = z;
0201 }
0202 }
0203 }
0204
0205 if (best_abs_dz == std::numeric_limits<double>::max())
0206 {
0207 return false;
0208 }
0209
0210 z_state = best_z;
0211 return std::isfinite(z_state);
0212 }
0213
0214 bool choose_collision_vertex(const Tpc_PolyTrackVertexContainer* vertices,
0215 const HelixCircle& circle,
0216 double& vertex_x,
0217 double& vertex_y,
0218 double& vertex_z)
0219 {
0220 if (!vertices || !vertices->get_collision_vertex_valid() || !circle.ok)
0221 {
0222 return false;
0223 }
0224
0225 double best_dz = std::numeric_limits<double>::max();
0226 const unsigned int nvertices = vertices->get_collision_vertex_count();
0227 for (unsigned int ivtx = 0; ivtx < nvertices; ++ivtx)
0228 {
0229 const double x = vertices->get_collision_x(ivtx);
0230 const double y = vertices->get_collision_y(ivtx);
0231 const double z = vertices->get_collision_z(ivtx);
0232 if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(z))
0233 {
0234 continue;
0235 }
0236
0237 double z_at_dca = 0.0;
0238 if (!helix_z_at_dca_to_vertex(circle, x, y, z_at_dca))
0239 {
0240 continue;
0241 }
0242
0243 const double dz = std::fabs(z - z_at_dca);
0244 if (dz < best_dz)
0245 {
0246 best_dz = dz;
0247 vertex_x = x;
0248 vertex_y = y;
0249 vertex_z = z;
0250 }
0251 }
0252
0253 return best_dz != std::numeric_limits<double>::max();
0254 }
0255
0256 bool line_xy_at_z(const Tpc_PolyTrack* trk,
0257 const double z,
0258 const double arc_direction,
0259 double& x_state,
0260 double& y_state)
0261 {
0262 if (!trk || trk->get_fit_status() == 0 || !std::isfinite(z))
0263 {
0264 return false;
0265 }
0266
0267 const double x0 = trk->get_x();
0268 const double y0 = trk->get_y();
0269 const double z0 = trk->get_z();
0270 const double px = trk->get_px();
0271 const double py = trk->get_py();
0272 const double pz = trk->get_pz();
0273 if (!std::isfinite(x0) || !std::isfinite(y0) || !std::isfinite(z0) ||
0274 !std::isfinite(px) || !std::isfinite(py) || !std::isfinite(pz) ||
0275 std::fabs(pz) < 1.0e-12)
0276 {
0277 return false;
0278 }
0279
0280 const double dz = z - z0;
0281 x_state = x0 + arc_direction * px / pz * dz;
0282 y_state = y0 + arc_direction * py / pz * dz;
0283 return std::isfinite(x_state) && std::isfinite(y_state);
0284 }
0285
0286 bool line_z_at_radius(const Tpc_PolyTrack* trk,
0287 const double target_r,
0288 const double reference_z,
0289 const double arc_direction,
0290 double& z_state)
0291 {
0292 if (!trk || trk->get_fit_status() == 0 || !std::isfinite(target_r) || target_r <= 0.0)
0293 {
0294 return false;
0295 }
0296
0297 const double x0 = trk->get_x();
0298 const double y0 = trk->get_y();
0299 const double z0 = trk->get_z();
0300 const double px = trk->get_px();
0301 const double py = trk->get_py();
0302 const double pz = trk->get_pz();
0303 if (!std::isfinite(x0) || !std::isfinite(y0) || !std::isfinite(z0) ||
0304 !std::isfinite(px) || !std::isfinite(py) || !std::isfinite(pz) ||
0305 std::fabs(pz) < 1.0e-12)
0306 {
0307 return false;
0308 }
0309
0310 const double ax = arc_direction * px / pz;
0311 const double ay = arc_direction * py / pz;
0312 const double a = ax * ax + ay * ay;
0313 const double b = 2.0 * (x0 * ax + y0 * ay);
0314 const double c = x0 * x0 + y0 * y0 - target_r * target_r;
0315 if (a < 1.0e-20)
0316 {
0317 return false;
0318 }
0319
0320 const double disc = b * b - 4.0 * a * c;
0321 if (disc < -1.0e-8)
0322 {
0323 return false;
0324 }
0325 const double root = std::sqrt(std::max(0.0, disc));
0326 const double dz1 = (-b - root) / (2.0 * a);
0327 const double dz2 = (-b + root) / (2.0 * a);
0328 const double z1 = z0 + dz1;
0329 const double z2 = z0 + dz2;
0330 z_state = std::fabs(z1 - reference_z) <= std::fabs(z2 - reference_z) ? z1 : z2;
0331 return std::isfinite(z_state);
0332 }
0333
0334 bool line_z_at_dca_to_vertex(const Tpc_PolyTrack* trk,
0335 const double vertex_x,
0336 const double vertex_y,
0337 const double arc_direction,
0338 double& z_at_dca,
0339 double& dca_xy)
0340 {
0341 if (!trk || trk->get_fit_status() == 0)
0342 {
0343 return false;
0344 }
0345
0346 const double x0 = trk->get_x();
0347 const double y0 = trk->get_y();
0348 const double z0 = trk->get_z();
0349 const double px = trk->get_px();
0350 const double py = trk->get_py();
0351 const double pz = trk->get_pz();
0352 if (!std::isfinite(x0) || !std::isfinite(y0) || !std::isfinite(z0) ||
0353 !std::isfinite(px) || !std::isfinite(py) || !std::isfinite(pz) ||
0354 !std::isfinite(vertex_x) || !std::isfinite(vertex_y) || std::fabs(pz) < 1.0e-12)
0355 {
0356 return false;
0357 }
0358
0359 const double ax = arc_direction * px / pz;
0360 const double ay = arc_direction * py / pz;
0361 const double den = ax * ax + ay * ay;
0362 if (den < 1.0e-20)
0363 {
0364 return false;
0365 }
0366 const double dz = ((vertex_x - x0) * ax + (vertex_y - y0) * ay) / den;
0367 const double x_at_dca = x0 + ax * dz;
0368 const double y_at_dca = y0 + ay * dz;
0369 z_at_dca = z0 + dz;
0370 dca_xy = std::hypot(x_at_dca - vertex_x, y_at_dca - vertex_y);
0371 return std::isfinite(z_at_dca) && std::isfinite(dca_xy);
0372 }
0373
0374 bool choose_collision_vertex_line(const Tpc_PolyTrackVertexContainer* vertices,
0375 const Tpc_PolyTrack* trk,
0376 const double arc_direction,
0377 double& vertex_x,
0378 double& vertex_y,
0379 double& vertex_z,
0380 double& rdca)
0381 {
0382 if (!vertices || !vertices->get_collision_vertex_valid() || !trk)
0383 {
0384 return false;
0385 }
0386
0387 double best_dz = std::numeric_limits<double>::max();
0388 const unsigned int nvertices = vertices->get_collision_vertex_count();
0389 for (unsigned int ivtx = 0; ivtx < nvertices; ++ivtx)
0390 {
0391 const double x = vertices->get_collision_x(ivtx);
0392 const double y = vertices->get_collision_y(ivtx);
0393 const double z = vertices->get_collision_z(ivtx);
0394 if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(z))
0395 {
0396 continue;
0397 }
0398
0399 double z_at_dca = 0.0;
0400 double dca_xy = 0.0;
0401 if (!line_z_at_dca_to_vertex(trk, x, y, arc_direction, z_at_dca, dca_xy))
0402 {
0403 continue;
0404 }
0405
0406 const double dz = std::fabs(z - z_at_dca);
0407 if (dz < best_dz)
0408 {
0409 best_dz = dz;
0410 vertex_x = x;
0411 vertex_y = y;
0412 vertex_z = z;
0413 rdca = dca_xy;
0414 }
0415 }
0416
0417 return best_dz != std::numeric_limits<double>::max();
0418 }
0419
0420 unsigned int cluster_sector(const Tpc_PolyCluster* cluster)
0421 {
0422 if (!cluster || cluster->size_hits() == 0)
0423 {
0424 return 0xffffffffU;
0425 }
0426 const Tpc_PolyCluster::HitIndex hit_index = cluster->get_hit_index(0);
0427 return TpcDefs::getSectorId(hit_index.first);
0428 }
0429
0430 unsigned int cluster_layer(const Tpc_PolyCluster* cluster)
0431 {
0432 if (!cluster || cluster->size_hits() == 0)
0433 {
0434 return 0xffffffffU;
0435 }
0436 const Tpc_PolyCluster::HitIndex hit_index = cluster->get_hit_index(0);
0437 return TrkrDefs::getLayer(hit_index.first);
0438 }
0439
0440 bool project_track_to_z(const Tpc_PolyTrack* trk,
0441 const double z,
0442 const double magnetic_field_tesla,
0443 const double arc_direction,
0444 const bool use_straight_line,
0445 double& x_state,
0446 double& y_state)
0447 {
0448 if (!trk || trk->get_fit_status() == 0)
0449 {
0450 return false;
0451 }
0452
0453 const double x0 = trk->get_x();
0454 const double y0 = trk->get_y();
0455 const double z0 = trk->get_z();
0456 const double px = trk->get_px();
0457 const double py = trk->get_py();
0458 const double pz = trk->get_pz();
0459 const double charge = trk->get_charge();
0460
0461 if (!std::isfinite(x0) || !std::isfinite(y0) || !std::isfinite(z0) ||
0462 !std::isfinite(px) || !std::isfinite(py) || !std::isfinite(pz) ||
0463 !std::isfinite(z))
0464 {
0465 return false;
0466 }
0467
0468 if (use_straight_line || std::fabs(charge * magnetic_field_tesla) < 1.0e-12)
0469 {
0470 return line_xy_at_z(trk, z, arc_direction, x_state, y_state);
0471 }
0472
0473 const double pt = std::hypot(px, py);
0474 if (pt <= 0.0 || std::fabs(pz) < 1.0e-12)
0475 {
0476 return false;
0477 }
0478
0479 const double signed_radius = pt / (0.003 * charge * magnetic_field_tesla);
0480 const double radius = std::fabs(signed_radius);
0481 if (radius <= 0.0 || !std::isfinite(radius))
0482 {
0483 return false;
0484 }
0485
0486 const double tx = px / pt;
0487 const double ty = py / pt;
0488 const double sign = signed_radius > 0.0 ? 1.0 : -1.0;
0489 const double xc = x0 + sign * radius * ty;
0490 const double yc = y0 - sign * radius * tx;
0491 const double phi0 = std::atan2(y0 - yc, x0 - xc);
0492 const double dzds = pz / pt;
0493 if (std::fabs(dzds) < 1.0e-12)
0494 {
0495 return false;
0496 }
0497
0498 const double arc = arc_direction * (z - z0) / dzds;
0499 const double phi = phi0 - sign * arc / radius;
0500 x_state = xc + radius * std::cos(phi);
0501 y_state = yc + radius * std::sin(phi);
0502 return std::isfinite(x_state) && std::isfinite(y_state);
0503 }
0504
0505 double cluster_line_residual2(const Tpc_PolyTrack* poly_track,
0506 const std::vector<const Tpc_PolyCluster*>& clusters,
0507 const double magnetic_field_tesla,
0508 const double arc_direction,
0509 const bool use_straight_line)
0510 {
0511 if (!poly_track || clusters.empty())
0512 {
0513 return std::numeric_limits<double>::max();
0514 }
0515
0516 double sum = 0.0;
0517 unsigned int n = 0;
0518 for (const Tpc_PolyCluster* cluster : clusters)
0519 {
0520 if (!cluster || !cluster->isValid())
0521 {
0522 continue;
0523 }
0524 const double cx = cluster->get_centroid_x();
0525 const double cy = cluster->get_centroid_y();
0526 const double cz = cluster->get_centroid_z();
0527 if (!std::isfinite(cx) || !std::isfinite(cy) || !std::isfinite(cz))
0528 {
0529 continue;
0530 }
0531
0532 double x = 0.0;
0533 double y = 0.0;
0534 if (!project_track_to_z(poly_track, cz, magnetic_field_tesla, arc_direction, use_straight_line, x, y))
0535 {
0536 continue;
0537 }
0538
0539 const double dx = x - cx;
0540 const double dy = y - cy;
0541 sum += dx * dx + dy * dy;
0542 ++n;
0543 }
0544
0545 return n > 0 ? sum / static_cast<double>(n) : std::numeric_limits<double>::max();
0546 }
0547
0548 }
0549
0550 Tpc_PolyClusterResiduals::Tpc_PolyClusterResiduals(const std::string& name,
0551 const std::string& outfilename)
0552 : SubsysReco(name)
0553 , m_outfilename(outfilename)
0554 , m_clusterNodeName("TPC_POLYCLUSTERS")
0555 , m_finalTrackNodeName("TPC_POLYTRACKS")
0556 , m_finalTrackVertexNodeName("TPC_POLYTRACKVERTICES")
0557 {
0558 }
0559
0560 Tpc_PolyClusterResiduals::~Tpc_PolyClusterResiduals()
0561 {
0562 if (m_outfile)
0563 {
0564 delete m_outfile;
0565 m_outfile = nullptr;
0566 }
0567 }
0568
0569 int Tpc_PolyClusterResiduals::Init(PHCompositeNode* )
0570 {
0571
0572 m_outfile = new TFile(m_outfilename.c_str(), "RECREATE");
0573 if (!m_outfile || m_outfile->IsZombie())
0574 {
0575 std::cerr << Name() << "::Init - cannot open output file " << m_outfilename << std::endl;
0576 return Fun4AllReturnCodes::ABORTRUN;
0577 }
0578
0579 m_tree = new TTree("residuals", "TPC poly cluster r-phi residuals");
0580 m_tree->Branch("event", &m_event, "event/i");
0581 m_tree->Branch("poly_track_id", &m_finalTrackId, "poly_track_id/i");
0582 m_tree->Branch("source_cluster_id", &m_sourceClusterId, "source_cluster_id/i");
0583 m_tree->Branch("source_assembled_track_id", &m_sourceAssembledTrackId, "source_assembled_track_id/i");
0584 m_tree->Branch("cluster_index", &m_clusterIndex);
0585 m_tree->Branch("side", &m_side, "side/I");
0586 m_tree->Branch("sector", &m_sector);
0587 m_tree->Branch("layer", &m_layer);
0588 m_tree->Branch("ntpc_clusters", &m_ntpcClusters, "ntpc_clusters/i");
0589 m_tree->Branch("fit_status", &m_fitStatus, "fit_status/I");
0590 m_tree->Branch("pt", &m_pt, "pt/D");
0591 m_tree->Branch("px", &m_px, "px/D");
0592 m_tree->Branch("py", &m_py, "py/D");
0593 m_tree->Branch("pz", &m_pz, "pz/D");
0594 m_tree->Branch("eta", &m_eta, "eta/D");
0595 m_tree->Branch("theta", &m_theta, "theta/D");
0596 m_tree->Branch("charge", &m_charge, "charge/D");
0597 m_tree->Branch("chi2", &m_chi2, "chi2/D");
0598 m_tree->Branch("ndf", &m_ndf, "ndf/D");
0599 m_tree->Branch("quality", &m_quality, "quality/D");
0600 m_tree->Branch("dedx", &m_dedx, "dedx/D");
0601 m_tree->Branch("vertex_x", &m_vertexX, "vertex_x/D");
0602 m_tree->Branch("vertex_y", &m_vertexY, "vertex_y/D");
0603 m_tree->Branch("vertex_z", &m_vertexZ, "vertex_z/D");
0604 m_tree->Branch("vertex_r", &m_vertexR, "vertex_r/D");
0605 m_tree->Branch("pca_x", &m_pcaX, "pca_x/D");
0606 m_tree->Branch("pca_y", &m_pcaY, "pca_y/D");
0607 m_tree->Branch("pca_z", &m_pcaZ, "pca_z/D");
0608 m_tree->Branch("rDCA", &m_rDCA, "rDCA/D");
0609 m_tree->Branch("rDCA_zero", &m_rDCAZero, "rDCA_zero/D");
0610 m_tree->Branch("zDCA", &m_zDCA, "zDCA/D");
0611 m_tree->Branch("R", &m_R, "R/D");
0612 m_tree->Branch("rzslope", &m_rzSlope, "rzslope/D");
0613 m_tree->Branch("cluster_x", &m_clusterX);
0614 m_tree->Branch("cluster_y", &m_clusterY);
0615 m_tree->Branch("cluster_z", &m_clusterZ);
0616 m_tree->Branch("cluster_r", &m_clusterR);
0617 m_tree->Branch("cluster_phi", &m_clusterPhi);
0618 m_tree->Branch("cluster_adc", &m_clusterAdc);
0619 m_tree->Branch("cluster_pad_size", &m_clusterPadSize);
0620 m_tree->Branch("state_x", &m_stateX);
0621 m_tree->Branch("state_y", &m_stateY);
0622 m_tree->Branch("state_z", &m_stateZ);
0623 m_tree->Branch("state_z_dca", &m_stateZDca);
0624 m_tree->Branch("state_r", &m_stateR);
0625 m_tree->Branch("state_phi", &m_statePhi);
0626 m_tree->Branch("delta_phi", &m_deltaPhi);
0627 m_tree->Branch("residual_rphi", &m_residualRPhi);
0628 m_tree->Branch("residual_z", &m_residualZ);
0629
0630 return Fun4AllReturnCodes::EVENT_OK;
0631 }
0632
0633 bool Tpc_PolyClusterResiduals::get_nodes(PHCompositeNode* topNode)
0634 {
0635 m_clusters = findNode::getClass<Tpc_PolyClusterContainer>(topNode, m_clusterNodeName);
0636 if (!m_clusters)
0637 {
0638 std::cerr << Name() << " - missing " << m_clusterNodeName << std::endl;
0639 return false;
0640 }
0641
0642 m_finalTracks = findNode::getClass<Tpc_PolyTrackContainer>(topNode, m_finalTrackNodeName);
0643 if (!m_finalTracks)
0644 {
0645 std::cerr << Name() << " - missing " << m_finalTrackNodeName << std::endl;
0646 return false;
0647 }
0648
0649 m_finalTrackVertices = findNode::getClass<Tpc_PolyTrackVertexContainer>(topNode, m_finalTrackVertexNodeName);
0650 if (!m_finalTrackVertices && Verbosity() > 0)
0651 {
0652 std::cerr << Name() << " - missing " << m_finalTrackVertexNodeName
0653 << ", rdca will be NaN" << std::endl;
0654 }
0655
0656 return true;
0657 }
0658
0659 void Tpc_PolyClusterResiduals::reset_tree_values()
0660 {
0661 m_event = m_evt;
0662 m_finalTrackId = 0;
0663 m_sourceClusterId = 0;
0664 m_sourceAssembledTrackId = 0;
0665 m_side = 0;
0666 m_ntpcClusters = 0;
0667 m_fitStatus = 0;
0668 m_pt = 0.0;
0669 m_px = std::numeric_limits<double>::quiet_NaN();
0670 m_py = std::numeric_limits<double>::quiet_NaN();
0671 m_pz = std::numeric_limits<double>::quiet_NaN();
0672 m_eta = std::numeric_limits<double>::quiet_NaN();
0673 m_theta = std::numeric_limits<double>::quiet_NaN();
0674 m_charge = 0.0;
0675 m_chi2 = 0.0;
0676 m_ndf = 0.0;
0677 m_quality = std::numeric_limits<double>::quiet_NaN();
0678 m_dedx = std::numeric_limits<double>::quiet_NaN();
0679 m_vertexX = std::numeric_limits<double>::quiet_NaN();
0680 m_vertexY = std::numeric_limits<double>::quiet_NaN();
0681 m_vertexZ = std::numeric_limits<double>::quiet_NaN();
0682 m_vertexR = std::numeric_limits<double>::quiet_NaN();
0683 m_pcaX = std::numeric_limits<double>::quiet_NaN();
0684 m_pcaY = std::numeric_limits<double>::quiet_NaN();
0685 m_pcaZ = std::numeric_limits<double>::quiet_NaN();
0686 m_zDCA = std::numeric_limits<double>::quiet_NaN();
0687 m_rDCA = std::numeric_limits<double>::quiet_NaN();
0688 m_rDCAZero = std::numeric_limits<double>::quiet_NaN();
0689 m_R = std::numeric_limits<double>::quiet_NaN();
0690 m_rzSlope = std::numeric_limits<double>::quiet_NaN();
0691 m_clusterIndex.clear();
0692 m_sector.clear();
0693 m_layer.clear();
0694 m_clusterX.clear();
0695 m_clusterY.clear();
0696 m_clusterZ.clear();
0697 m_clusterR.clear();
0698 m_clusterPhi.clear();
0699 m_clusterAdc.clear();
0700 m_clusterPadSize.clear();
0701 m_stateX.clear();
0702 m_stateY.clear();
0703 m_stateZ.clear();
0704 m_stateZDca.clear();
0705 m_stateR.clear();
0706 m_statePhi.clear();
0707 m_deltaPhi.clear();
0708 m_residualRPhi.clear();
0709 m_residualZ.clear();
0710 }
0711
0712 int Tpc_PolyClusterResiduals::process_event(PHCompositeNode* topNode)
0713 {
0714 ++m_evt;
0715 if (!get_nodes(topNode))
0716 {
0717 return Fun4AllReturnCodes::EVENT_OK;
0718 }
0719 if (!m_tree)
0720 {
0721 return Fun4AllReturnCodes::EVENT_OK;
0722 }
0723
0724 std::map<unsigned int, std::vector<const Tpc_PolyCluster*> > clusters_by_source_assembled_track_id;
0725 std::map<unsigned int, const Tpc_PolyTrackVertex*> track_vertices_by_track_id;
0726 std::map<unsigned int, const Tpc_PolyTrackVertex*> track_vertices_by_source_assembled_track_id;
0727 for (unsigned int icluster = 0; icluster < m_clusters->size(); ++icluster)
0728 {
0729 const Tpc_PolyCluster* cluster = m_clusters->get_cluster(icluster);
0730 if (!cluster || !cluster->isValid())
0731 {
0732 continue;
0733 }
0734 clusters_by_source_assembled_track_id[cluster->get_source_assembled_track_id()].push_back(cluster);
0735 }
0736
0737 if (m_finalTrackVertices)
0738 {
0739 for (unsigned int ivtx = 0; ivtx < m_finalTrackVertices->size(); ++ivtx)
0740 {
0741 const Tpc_PolyTrackVertex* vtx = m_finalTrackVertices->get_vertex(ivtx);
0742 if (!vtx)
0743 {
0744 continue;
0745 }
0746 track_vertices_by_track_id[vtx->get_track_id()] = vtx;
0747 track_vertices_by_source_assembled_track_id[vtx->get_source_assembled_track_id()] = vtx;
0748 }
0749 }
0750
0751 unsigned int nfilled = 0;
0752 const unsigned int npoly_tracks = m_finalTracks->size();
0753 for (unsigned int ifinal = 0; ifinal < npoly_tracks; ++ifinal)
0754 {
0755 const Tpc_PolyTrack* poly_track = m_finalTracks->get_track(ifinal);
0756 if (!poly_track || !poly_track->isValid())
0757 {
0758 continue;
0759 }
0760
0761 const double px = poly_track->get_px();
0762 const double py = poly_track->get_py();
0763 const double pz = poly_track->get_pz();
0764 const double charge = poly_track->get_charge();
0765 const bool use_straight_line = m_useStraightLineTracks || std::fabs(charge * m_magneticFieldTesla) < 1.0e-12;
0766 const double pt = std::hypot(px, py);
0767 if (!std::isfinite(pt) || (!use_straight_line && (pt < m_minPt || pt > m_maxPt)))
0768 {
0769 continue;
0770 }
0771
0772 const double eta = (pt > 0.0 && std::isfinite(pz)) ? std::asinh(pz / pt) : std::numeric_limits<double>::quiet_NaN();
0773 const double theta = (pt > 0.0 && std::isfinite(pz)) ? std::atan2(pt, pz) : std::numeric_limits<double>::quiet_NaN();
0774 const double chi2 = poly_track->get_chi2();
0775 const double ndf = poly_track->get_ndf();
0776 const double quality = (std::isfinite(chi2) && std::isfinite(ndf) && ndf > 0.0) ? chi2 / ndf : std::numeric_limits<double>::quiet_NaN();
0777
0778 const auto cluster_iter = clusters_by_source_assembled_track_id.find(poly_track->get_source_assembled_track_id());
0779 if (cluster_iter == clusters_by_source_assembled_track_id.end())
0780 {
0781 continue;
0782 }
0783
0784 const std::vector<const Tpc_PolyCluster*>& track_clusters = cluster_iter->second;
0785 const Tpc_PolyTrackVertex* track_vertex = nullptr;
0786 const auto vertex_source_iter = track_vertices_by_source_assembled_track_id.find(poly_track->get_source_assembled_track_id());
0787 if (vertex_source_iter != track_vertices_by_source_assembled_track_id.end())
0788 {
0789 track_vertex = vertex_source_iter->second;
0790 }
0791 else
0792 {
0793 const auto vertex_track_iter = track_vertices_by_track_id.find(poly_track->get_track_id());
0794 if (vertex_track_iter != track_vertices_by_track_id.end())
0795 {
0796 track_vertex = vertex_track_iter->second;
0797 }
0798 }
0799 const unsigned int ntpc_clusters = track_clusters.size();
0800 if (ntpc_clusters < m_minTpcClusters || ntpc_clusters > m_maxTpcClusters)
0801 {
0802 continue;
0803 }
0804
0805 const double forward_residual2 = cluster_line_residual2(poly_track, track_clusters, m_magneticFieldTesla, 1.0, use_straight_line);
0806 const double reverse_residual2 = cluster_line_residual2(poly_track, track_clusters, m_magneticFieldTesla, -1.0, use_straight_line);
0807 const double arc_direction = forward_residual2 <= reverse_residual2 ? 1.0 : -1.0;
0808
0809 double vertex_x = std::numeric_limits<double>::quiet_NaN();
0810 double vertex_y = std::numeric_limits<double>::quiet_NaN();
0811 double vertex_z = std::numeric_limits<double>::quiet_NaN();
0812 double rdca = std::numeric_limits<double>::quiet_NaN();
0813 double rdca_zero = std::numeric_limits<double>::quiet_NaN();
0814 double pca_x = std::numeric_limits<double>::quiet_NaN();
0815 double pca_y = std::numeric_limits<double>::quiet_NaN();
0816 double pca_z = std::numeric_limits<double>::quiet_NaN();
0817 double zdca = std::numeric_limits<double>::quiet_NaN();
0818 const HelixCircle circle = use_straight_line ? HelixCircle() : make_track_circle(poly_track, m_magneticFieldTesla);
0819 const double dedx = poly_track->get_dedx();
0820
0821 if (use_straight_line)
0822 {
0823 if (choose_collision_vertex_line(m_finalTrackVertices, poly_track, arc_direction, vertex_x, vertex_y, vertex_z, rdca))
0824 {
0825 double z_at_zero = 0.0;
0826 line_z_at_dca_to_vertex(poly_track, 0.0, 0.0, arc_direction, z_at_zero, rdca_zero);
0827 }
0828 }
0829 else if (choose_collision_vertex(m_finalTrackVertices, circle, vertex_x, vertex_y, vertex_z))
0830 {
0831 rdca = std::hypot(circle.xc - vertex_x, circle.yc - vertex_y) - circle.radius;
0832 rdca_zero = std::hypot(circle.xc, circle.yc) - circle.radius;
0833 }
0834
0835 if (track_vertex && track_vertex->get_pca_valid())
0836 {
0837 pca_x = track_vertex->get_pca_x();
0838 pca_y = track_vertex->get_pca_y();
0839 pca_z = track_vertex->get_pca_z();
0840 if (std::isfinite(pca_z) && std::isfinite(vertex_z))
0841 {
0842 zdca = pca_z - vertex_z;
0843 }
0844 }
0845
0846 reset_tree_values();
0847 m_event = m_evt;
0848 m_finalTrackId = poly_track->get_track_id();
0849 m_sourceClusterId = track_clusters.empty() ? 0 : track_clusters.front()->get_cluster_id();
0850 m_sourceAssembledTrackId = poly_track->get_source_assembled_track_id();
0851 m_side = track_clusters.empty() ? 0 : track_clusters.front()->get_side();
0852 m_ntpcClusters = ntpc_clusters;
0853 m_fitStatus = poly_track->get_fit_status();
0854 m_pt = pt;
0855 m_px = px;
0856 m_py = py;
0857 m_pz = pz;
0858 m_eta = eta;
0859 m_theta = theta;
0860 m_charge = charge;
0861 m_chi2 = chi2;
0862 m_ndf = ndf;
0863 m_quality = quality;
0864 m_dedx = dedx;
0865 m_vertexX = vertex_x;
0866 m_vertexY = vertex_y;
0867 m_vertexZ = vertex_z;
0868 m_vertexR = std::hypot(vertex_x, vertex_y);
0869 m_pcaX = pca_x;
0870 m_pcaY = pca_y;
0871 m_pcaZ = pca_z;
0872 m_rDCA = rdca;
0873 m_rDCAZero = rdca_zero;
0874 m_zDCA = zdca;
0875 m_R = circle.ok ? circle.radius : std::numeric_limits<double>::quiet_NaN();
0876 const double straight_line_slope =
0877 (use_straight_line && pt > 0.0) ? pz / pt : std::numeric_limits<double>::quiet_NaN();
0878 m_rzSlope = circle.ok ? circle.dzds : straight_line_slope;
0879 for (const Tpc_PolyCluster* cluster : track_clusters)
0880 {
0881 if (!cluster || !cluster->isValid())
0882 {
0883 continue;
0884 }
0885 const double cluster_x = cluster->get_centroid_x();
0886 const double cluster_y = cluster->get_centroid_y();
0887 const double cluster_z = cluster->get_centroid_z();
0888 if (!std::isfinite(cluster_x) || !std::isfinite(cluster_y) || !std::isfinite(cluster_z))
0889 {
0890 continue;
0891 }
0892
0893 double state_x = 0.0;
0894 double state_y = 0.0;
0895 if (!project_track_to_z(poly_track, cluster_z, m_magneticFieldTesla, arc_direction, use_straight_line, state_x, state_y))
0896 {
0897 continue;
0898 }
0899
0900 const int cluster_side = cluster->get_side();
0901 double state_z = std::numeric_limits<double>::quiet_NaN();
0902 double residual_z = std::numeric_limits<double>::quiet_NaN();
0903 const double cluster_r_for_state = std::hypot(cluster_x, cluster_y);
0904 const bool have_state_z = use_straight_line ? line_z_at_radius(poly_track, cluster_r_for_state, cluster_z, arc_direction, state_z) : helix_z_at_radius(circle, cluster_r_for_state, cluster_z, arc_direction, state_z);
0905 if (have_state_z)
0906 {
0907 residual_z = cluster_z - state_z;
0908 }
0909
0910 m_clusterIndex.push_back(cluster->get_cluster_id());
0911 m_side = cluster_side;
0912 m_sector.push_back(cluster_sector(cluster));
0913 m_layer.push_back(cluster_layer(cluster));
0914 m_clusterX.push_back(cluster_x);
0915 m_clusterY.push_back(cluster_y);
0916 m_clusterZ.push_back(cluster_z);
0917 const double cluster_r = std::hypot(cluster_x, cluster_y);
0918 const double cluster_phi = std::atan2(cluster_y, cluster_x);
0919 m_clusterR.push_back(cluster_r);
0920 m_clusterPhi.push_back(cluster_phi);
0921 m_clusterAdc.push_back(cluster->get_adc());
0922 m_clusterPadSize.push_back(cluster->get_phi_width());
0923 m_stateX.push_back(state_x);
0924 m_stateY.push_back(state_y);
0925 m_stateZ.push_back(state_z);
0926 m_stateZDca.push_back(state_z);
0927 m_stateR.push_back(std::hypot(state_x, state_y));
0928 const double state_phi = std::atan2(state_y, state_x);
0929 m_statePhi.push_back(state_phi);
0930 const double delta_phi = wrap_phi(cluster_phi - state_phi);
0931 m_deltaPhi.push_back(delta_phi);
0932 m_residualRPhi.push_back(cluster_r * delta_phi);
0933 m_residualZ.push_back(residual_z);
0934 ++nfilled;
0935 }
0936
0937 if (!m_clusterIndex.empty())
0938 {
0939 m_tree->Fill();
0940 }
0941 }
0942
0943 if (Verbosity() > 0)
0944 {
0945 std::cout << Name() << "::process_event - event " << m_evt
0946 << " poly_tracks=" << npoly_tracks
0947 << " residuals=" << nfilled << std::endl;
0948 }
0949
0950 return Fun4AllReturnCodes::EVENT_OK;
0951 }
0952
0953 int Tpc_PolyClusterResiduals::End(PHCompositeNode* )
0954 {
0955 if (m_outfile)
0956 {
0957 m_outfile->cd();
0958 if (m_tree)
0959 {
0960 m_tree->Write();
0961 }
0962 m_outfile->Close();
0963 delete m_outfile;
0964 m_outfile = nullptr;
0965 m_tree = nullptr;
0966 }
0967 return Fun4AllReturnCodes::EVENT_OK;
0968 }