Skip to content

feat: trkrclusterv6 implementation#4319

Merged
osbornjd merged 14 commits into
sPHENIX-Collaboration:masterfrom
osbornjd:v6impl
Jun 25, 2026
Merged

feat: trkrclusterv6 implementation#4319
osbornjd merged 14 commits into
sPHENIX-Collaboration:masterfrom
osbornjd:v6impl

Conversation

@osbornjd

@osbornjd osbornjd commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

This implements the v6 option now without any performance changes

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work for users)
  • Requiring change in macros repository (Please provide links to the macros pull request in the last section)
  • I am a member of GitHub organization of sPHENIX Collaboration, EIC, or ECCE (contact Chris Pinkenburg to join)

What kind of change does this PR introduce? (Bug fix, feature, ...)

TODOs (if applicable)

Links to other PRs in macros and calibration repositories (if applicable)

AI-assisted summary; AI can make mistakes, so please use best judgment when reviewing.

Motivation / context

  • Add support for trkrclusterv6 on master while keeping the existing v5 clustering path intact.
  • Enrich TPC cluster diagnostics/ntupling to expose additional overlap/edge/mix and geometry-related metadata (including event identifiers) for improved performance studies and validation.

Key changes

  • TPC clustering (TpcClusterizer):
    • Introduce a richer ClusterCounters-based flow to classify overlap plus detailed edge/mixing categories (phi/time edges, dead/hot edges, mix flags) and drive cluster parameter computation.
    • Add a debug-gated selection between TrkrClusterv5 and TrkrClusterv6 when constructing cluster objects.
    • Propagate the global m_debug into threaded clustering context.
    • Add DetailedClusterAnalysis() to enable extended debug behavior.
  • Track/cluster data model (TrkrCluster and cluster implementations):
    • Update TrkrCluster virtual setter/getter signatures to tighten const-correctness and align return types (e.g., getPadMax/getTBinMax and several mix/bin/phase getters) with the v6/diagnostic needs.
    • Update TrkrClusterv5/TrkrClusterv6 method signatures to match the tightened interface expectations.
  • Tracking diagnostics + ntupling:
    • TrackResiduals: capture evt_id from the event header and write it into multiple ROOT trees/branches; expand cluster-tree/residual-tree branch schema with additional cluster metadata (ADC center/max, geometry-derived size/bin/phase fields, detailed edge/mix/overlap counts and bounds).
    • ROOT branch leaf type updates: cluster edge/mix indicators are written with "/B" leaf specifiers (e.g., m_overlap/B, m_slmix/B, etc.), reflecting the intended small-integer/char storage.
    • TrkrNtuplizer: extend NTuple cluster column list and enum indices; fill new cluster fields from additional TrkrCluster getters; adjust which cluster “size” getter is used.

Potential risk areas

  • IO/schema compatibility: ROOT tree branch/leaf lists and ntuple column sets/types are extended; downstream analysis reading these outputs may require updates.
  • Reconstruction/physics behavior: v6 cluster construction and the expanded edge/overlap/mix classification logic could change derived cluster properties used downstream.
  • API changes: TrkrCluster/TrkrClusterv5/TrkrClusterv6 signature and return-type changes may affect any external code building against these classes.
  • Threading/debug behavior: v6 selection and detailed analysis are tied to debug/thread data plumbing; validate behavior in multi-threaded runs.
  • Performance: clustering logic is refactored (even if intended to be performance-neutral); confirm with representative workloads.

Possible future improvements

  • Add explicit configuration/QA checks that confirm v5 vs v6 behavior equivalence where expected (and quantify differences where classification is intentionally richer).
  • Provide documentation/migration notes for downstream consumers of the expanded ROOT trees/ntuples (new fields, types, and semantics).
  • Add regression tests for edge/mix/overlap categorization and for schema stability across releases.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: eb8f4124-799d-4a68-a033-7caebcdda84d

📥 Commits

Reviewing files that changed from the base of the PR and between c0388d0 and a4e04a8.

📒 Files selected for processing (1)
  • offline/packages/TrackingDiagnostics/TrackResiduals.cc
🚧 Files skipped from review as they are similar to previous changes (1)
  • offline/packages/TrackingDiagnostics/TrackResiduals.cc

📝 Walkthrough

Walkthrough

The PR adds evt_id propagation to tracking diagnostics trees, expands cluster metadata with center/max ADC, edge, mix, size, and bin/phase fields, and updates TpcClusterizer and TrkrCluster interfaces to produce and store those values.

Changes

Cluster diagnostics and TPC clustering

Layer / File(s) Summary
Cluster type API updates
offline/packages/trackbase/TrkrCluster.h, offline/packages/trackbase/TrkrClusterv4.cc, offline/packages/trackbase/TrkrClusterv4.h, offline/packages/trackbase/TrkrClusterv5.cc, offline/packages/trackbase/TrkrClusterv5.h, offline/packages/trackbase/TrkrClusterv6.h
TrkrCluster and its v4/v5/v6 implementations change setter qualifiers, bin and mix return types, default member values, and the square helper declarations.
TPC cluster counters
offline/packages/tpc/TpcClusterizer.h, offline/packages/tpc/TpcClusterizer.cc
TpcClusterizer adds a debug toggle and ClusterCounters, and the t/phi extent scans and touching pass record overlap, edge, dead/hot, and mix categories.
TPC cluster assembly
offline/packages/tpc/TpcClusterizer.cc
get_cluster and calc_cluster_parameter consume ClusterCounters, compute size and centroid/bin ADC fields, assign detailed edge and mix properties, and select TrkrClusterv6 when debug mode is enabled. ProcessSectorData reuses the counters across retries and thread setup.
Residual event metadata
offline/packages/TrackingDiagnostics/TrackResiduals.h, offline/packages/TrackingDiagnostics/TrackResiduals.cc
TrackResiduals adds evt_id storage, expands the cached cluster diagnostics state, clears the new vectors, and reads event/run identifiers from EventHeader and recoConsts.
Residual tree branches
offline/packages/TrackingDiagnostics/TrackResiduals.cc
createBranches adds evt_id to the event, failure, vertex, hit, cluster, and residual trees and expands the cluster/residual branch lists; the cluster fill paths populate the new ADC, size, edge, mix, and bin/phase fields.
Cluster NTuple schema
offline/packages/TrackingDiagnostics/TrkrNtuplizer.cc
str_cluster and n_cluster expand to the new cluster columns, and FillCluster() writes the added ADC, edge, mix, overlap, bin, phase, and getRSize() values.

Possibly related PRs


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8f88b0d4-799f-4cda-817b-c936f1f593d1

📥 Commits

Reviewing files that changed from the base of the PR and between ad6119f and c0388d0.

📒 Files selected for processing (11)
  • offline/packages/TrackingDiagnostics/TrackResiduals.cc
  • offline/packages/TrackingDiagnostics/TrackResiduals.h
  • offline/packages/TrackingDiagnostics/TrkrNtuplizer.cc
  • offline/packages/tpc/TpcClusterizer.cc
  • offline/packages/tpc/TpcClusterizer.h
  • offline/packages/trackbase/TrkrCluster.h
  • offline/packages/trackbase/TrkrClusterv4.cc
  • offline/packages/trackbase/TrkrClusterv4.h
  • offline/packages/trackbase/TrkrClusterv5.cc
  • offline/packages/trackbase/TrkrClusterv5.h
  • offline/packages/trackbase/TrkrClusterv6.h

Comment on lines 220 to 234
if (FixedWindow != 0)
{
tup = FixedWindow;
tdown = FixedWindow;
if (tbin + tup >= NTBinsMax)
{
tup = NTBinsMax - tbin - 1;
edge++;
counts.nedge++;
}
if ((tbin - tdown) <= 0)
{
tdown = tbin;
edge++;
counts.nedge++;
}
return;

@coderabbitai coderabbitai Bot Jun 25, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Preserve detailed edge flags in fixed-window mode.

The fixed-window early returns increment counts.nedge but never set tledge/tredge/sledge/sredge; find_phi_range also repeats the generic edge increment once per scanned time bin. Downstream diagnostics read the detailed flags, so fixed-window edge clusters report incomplete or inflated metadata.

Suggested direction
       if (tbin + tup >= NTBinsMax)
       {
         tup = NTBinsMax - tbin - 1;
-        counts.nedge++;
+        if (!ttop_edge)
+        {
+          counts.nedge++;
+          counts.tredge = 1;
+          ttop_edge = true;
+        }
       }
       if ((tbin - tdown) <= 0)
       {
         tdown = tbin;
-        counts.nedge++;
+        if (!tbottom_edge)
+        {
+          counts.nedge++;
+          counts.tledge = 1;
+          tbottom_edge = true;
+        }
       }

Apply the same guarded/category update for phitop_edge and phibottom_edge in find_phi_range.

Also applies to: 327-341

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be fixed in a future PR

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@osbornjd, understood — thanks for the clarification.

This still looks worth tracking since the detailed edge metadata can be incomplete/inflated in fixed-window mode. If you'd like, I can open a follow-up GitHub issue for this and include links back to PR #4319 and the review thread so it doesn’t get lost.


🧠 Learnings used
Learnt from: pinkenburg
Repo: sPHENIX-Collaboration/coresoftware PR: 4268
File: offline/packages/CaloReco/ClusterCDFCalculator.cc:9-9
Timestamp: 2026-05-18T16:21:14.018Z
Learning: In the sPHENIX core software repository, C++20 std::format is supported (via the <format> header). During code review, do not flag uses of std::format / <format> as a potential C++-version or compiler-compatibility concern anywhere in the C++ codebase. (Other issues may still be flagged as appropriate.)


char tsize = tbinhi - tbinlo + 1;
char phisize = phibinhi - phibinlo + 1;
char rsize = size;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Do not narrow cluster hit count to char.

size is an int count of accepted hits, but char rsize = size can wrap or become negative before setRSize, which corrupts the new getRSize() diagnostics/ntuple field for larger clusters. Keep this value in a wider integral type through the cluster API/storage.

Comment on lines 114 to +115
float getRSize() const override { return (float) m_rsize; }
void setRSize(unsigned char rsize) { m_rsize = rsize; }
void setRSize(const char rsize) override { m_rsize = rsize; }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Avoid storing the new cluster size metric in char.

setRSize(const char) stores the hit-count-derived size in m_rsize; the producer narrows an int size into this field. Larger TPC clusters can wrap or become negative on signed-char platforms, corrupting the new ntuple/diagnostic size value. Prefer at least unsigned short/int across the base API, v6 storage, and producer assignment.

Also applies to: 196-196

Comment on lines +342 to +344
auto *rcs = recoConsts::instance();
m_runnumber = rcs->get_IntFlag("RUNNUMBER");
m_segment = rcs->get_IntFlag("RUNSEGMENT");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect PHFlag missing-key behavior and RUNNUMBER/RUNSEGMENT setup sites.
fd -i '^PHFlag\.(cc|cxx|cpp)$' --exec sed -n '/get_IntFlag/,/^}/p' {}
rg -n -C2 '"RUNNUMBER"|"RUNSEGMENT"|get_IntFlag\("RUNNUMBER"|get_IntFlag\("RUNSEGMENT"' .

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 14811


Preserve configured run/segment fallbacks when recoConsts flags are absent

The current code unconditionally reads RUNNUMBER and RUNSEGMENT using the single-argument overload. If these global flags are not set, the API returns 0 and logs an error, overriding any values explicitly set via the runnumber(int) or setSegment(int) member accessors.

Use the two-argument overload to default to the member values, maintaining correct configuration precedence.

Proposed fix
  auto *rcs = recoConsts::instance();
- m_runnumber = rcs->get_IntFlag("RUNNUMBER");
- m_segment = rcs->get_IntFlag("RUNSEGMENT");
+ m_runnumber = rcs->get_IntFlag("RUNNUMBER", m_runnumber);
+ m_segment = rcs->get_IntFlag("RUNSEGMENT", m_segment);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
auto *rcs = recoConsts::instance();
m_runnumber = rcs->get_IntFlag("RUNNUMBER");
m_segment = rcs->get_IntFlag("RUNSEGMENT");
auto *rcs = recoConsts::instance();
m_runnumber = rcs->get_IntFlag("RUNNUMBER", m_runnumber);
m_segment = rcs->get_IntFlag("RUNSEGMENT", m_segment);

Source: Path instructions

Comment thread offline/packages/TrackingDiagnostics/TrackResiduals.cc Outdated
Comment on lines +251 to 253
int m_size = std::numeric_limits<int>::quiet_NaN();
int m_phisize = std::numeric_limits<int>::quiet_NaN();
int m_zsize = std::numeric_limits<int>::quiet_NaN();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Keep cluster size diagnostics as floats end-to-end.

TrkrCluster exposes getRSize(), getPhiSize(), and getZSize() as float; storing them in int scalars/vectors truncates residual and cluster-tree diagnostics. Please make these members float/std::vector<float> and update the ROOT leaflists from /I to /F where these scalars are branched. As per path instructions, header review focuses on API clarity/stability.

Proposed type fix
-  int m_size = std::numeric_limits<int>::quiet_NaN();
-  int m_phisize = std::numeric_limits<int>::quiet_NaN();
-  int m_zsize = std::numeric_limits<int>::quiet_NaN();
+  float m_size = std::numeric_limits<float>::quiet_NaN();
+  float m_phisize = std::numeric_limits<float>::quiet_NaN();
+  float m_zsize = std::numeric_limits<float>::quiet_NaN();
...
-  std::vector<int> m_clussize;
-  std::vector<int> m_clusphisize;
-  std::vector<int> m_cluszsize;
+  std::vector<float> m_clussize;
+  std::vector<float> m_clusphisize;
+  std::vector<float> m_cluszsize;

Also applies to: 323-325

Source: Path instructions

}
*/
fXcluster[n_cluster::nclusize] = cluster->getSize();
fXcluster[n_cluster::nclusize] = cluster->getRSize();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Preserve size for cluster versions without getRSize().

TrkrClusterv4 only overrides getSize(), so this change makes existing v4 clusters write the base getRSize() NaN into the size column. Either implement getRSize() for all cluster versions that reach this ntuple, or keep a fallback to the legacy size value.

Proposed fallback
-  fXcluster[n_cluster::nclusize] = cluster->getRSize();
+  const float rsize = cluster->getRSize();
+  fXcluster[n_cluster::nclusize] = std::isnan(rsize) ? cluster->getSize() : rsize;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fXcluster[n_cluster::nclusize] = cluster->getRSize();
const float rsize = cluster->getRSize();
fXcluster[n_cluster::nclusize] = std::isnan(rsize) ? cluster->getSize() : rsize;

@sphenix-jenkins-ci

Copy link
Copy Markdown

Build & test report

Report for commit c0388d0084e12259c59356d9fd6b216b316e6014:
Jenkins passed


Automatically generated by sPHENIX Jenkins continuous integration
sPHENIX             jenkins.io

@osbornjd osbornjd merged commit d838577 into sPHENIX-Collaboration:master Jun 25, 2026
8 of 9 checks passed
@sphenix-jenkins-ci

Copy link
Copy Markdown

Build & test report

Report for commit a4e04a8c60d442de1c80a25b0e7b545afd835f73:
Jenkins passed


Automatically generated by sPHENIX Jenkins continuous integration
sPHENIX             jenkins.io

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant