Back to home page

sPhenix code displayed by LXR

 
 

    


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 }  // namespace
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* /*unused*/)
0570 {
0571   // cppcheck-suppress publicAllocationError
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* /*unused*/)
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 }