Skip to content

Conversation

@blackcathj
Copy link

@blackcathj blackcathj commented Jan 19, 2026

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)

Summary by CodeRabbit

Release Notes

  • New Features

    • Added configurable particle-level trigger system with flexible kinematic cuts for event filtering.
    • Introduced tower information combining capability for calorimeter embedding workflows.
    • Added cluster pruning controls for track reconstruction optimization.
  • Improvements

    • Enhanced jet trigger with particle-type filtering to exclude irrelevant particles from jet inputs.
    • Improved robustness of event processing with better handling of missing data and boundary conditions.
    • Extended detector calibration support for event-level corrections.
  • Code Quality

    • Modernized codebase with improved const-correctness and standard library usage.
    • Enhanced pointer and reference handling for improved type safety.

✏️ Tip: You can customize this high-level summary in your review settings.

Daniel J Lis and others added 30 commits December 17, 2025 20:59
Fill ntuple for both matched and unmatched waveforms
…s_pool_evaluation_update

Small update on TPOT event combining evaluation
if multiple hits are found on the same strip only the first one, timewise, is kept.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
add quality flag to total energy calc and correlations
Copilot AI review requested due to automatic review settings January 19, 2026 16:44
@coderabbitai
Copy link

coderabbitai bot commented Jan 19, 2026

📝 Walkthrough

Walkthrough

This pull request introduces a comprehensive update across 60+ files spanning event triggers, QA systems, detector infrastructure, and tracking. Key additions include HepMCParticleTrigger for particle-level event filtering, StateClusterResidualsQA for tracking residuals, and data-merging utilities (CombineTowerInfo, CopyIODataNodes). Numerous const-correctness enhancements, pointer semantics refinements, and algorithmic improvements are applied throughout TPC, MVTX, and micromegas subsystems, along with framework changes for I/O state management and calibration handling.

Changes

Cohort / File(s) Summary
HepMC Triggers & Generation
generators/Herwig/HepMCTrigger/HepMCJetTrigger.cc, HepMCParticleTrigger.cc, HepMCParticleTrigger.h, Makefile.am
generators/PHPythia8/PHPythia8.cc
generators/phhepmc/PHHepMCGenEventv1.cc
New HepMCParticleTrigger class with configurable particle-level kinematic cuts (Pt, P, Eta, Pz). HepMCJetTrigger now filters neutrinos (PDG 12–18) and applies eta cut. Pythia8 now preserves cout formatting state; HepMCGenEventv1 returns NaN instead of 0 for missing flow values.
Calorimeter QA & Tower Processing
offline/QA/Calorimeters/CaloValid.cc, CaloValid.h
offline/packages/CaloEmbedding/CombineTowerInfo.cc, CombineTowerInfo.h, CopyIODataNodes.cc, CopyIODataNodes.h, Makefile.am
offline/packages/CaloReco/PhotonClusterBuilder.cc
CaloValid now guards energy accumulators with isGood and uses trigger bit 12. New CombineTowerInfo module merges two TowerInfo containers; CopyIODataNodes extended to copy tower data between trees. PhotonClusterBuilder adds e22 shower shape parameter.
Tracking QA
offline/QA/Tracking/Makefile.am, StateClusterResidualsQA.cc, StateClusterResidualsQA.h
offline/QA/Tracking/MicromegasClusterQA.cc, MicromegasClusterQA.h
New StateClusterResidualsQA module computes track-state cluster residuals with configurable selection criteria and multi-histogram support. MicromegasClusterQA adds sample-range filtering for signal hits via set_sample_min/max setters.
Framework & I/O State Management
offline/framework/ffamodules/CDBInterface.cc, CDBInterface.h
offline/framework/ffamodules/FlagHandler.h, HeadReco.cc, HeadReco.h, SyncReco.h, Timing.cc, Timing.h
offline/framework/fun4all/Fun4AllServer.cc, Fun4AllServer.h
offline/framework/fun4allraw/SingleTriggeredInput.cc, SingleTriggeredInput.h
Framework classes refactored for const-correctness and defaulted destructors; PHCompositeNode forward declarations removed. Fun4AllServer preserves cout formatting state. CDBInterface skips empty URLs. SingleTriggeredInput adds clock-sync retry logic (skiptrace) and event counter. HeadReco guards flow psi updates.
Input File Processing
offline/framework/fun4all/InputFileHandler.cc, InputFileHandler.h
New RunBeforeOpening method and SetOpeningScript/Args configuration for pre-file-open hooks; enables script execution before opening next file.
Stream Management
offline/framework/fun4allraw/Fun4AllStreamingInputManager.cc
offline/framework/fun4allraw/SingleMicromegasPoolInput_v2.cc, SingleMicromegasPoolInput_v2.h
offline/framework/fun4allraw/SingleMvtxPoolInput.cc
FillIntt/FillMvtx refactored from erase-based to range-based iteration with explicit BCO bounds. SingleMicromegas adds "matched" evaluation field and GTM-BCO matching logging. SingleMvtx adjusts strobe threshold and negative BCO range.
Production File Handling
offline/framework/frog/CreateFileList.pl
Added production type 37 ("hijing O+O (0-15fm)") with filetypes and pileup configuration.
MBD (Minimum Bias Detector)
offline/packages/mbd/MbdCalib.cc
offline/packages/mbd/MbdEvent.cc, MbdReco.cc, MbdReco.h, MbdSig.h
MbdCalib updates error message for missing TRMS. MbdEvent adds zero-length packet guards and sample range checks. MbdReco introduces event number tracking (_evtnum). MbdSig adds GetNSamples accessor.
Micromegas Detector
offline/packages/micromegas/MicromegasClusterizer.cc
offline/packages/micromegas/MicromegasCombinedDataDecoder.cc, MicromegasCombinedDataDecoder.h
offline/packages/micromegas/MicromegasDefs.cc, MicromegasDefs.h
MicromegasClusterizer implements local hit-map clustering with sample ordering. MicromegasCombinedDataDecoder now tracks (sample_index, adc) pairs. MicromegasDefs extends genHitKey to include sample parameter; adds getSample accessor; changes getStrip return type to uint8_t.
MVTX (Pixel Tracker)
offline/packages/mvtx/CylinderGeom_Mvtx.cc, CylinderGeom_Mvtx.h
offline/packages/mvtx/MvtxClusterPruner.cc
offline/packages/mvtx/MvtxClusterizer.cc, MvtxClusterizer.h
offline/packages/mvtx/MvtxHitPruner.cc
offline/packages/mvtx/SegmentationAlpide.cc
CylinderGeom_Mvtx adds const qualifiers to pixel accessors; removes using namespace std. MvtxClusterPruner/Pruner refactored for pointer consistency and safety checks. MvtxClusterizer adds const to are_adjacent methods. SegmentationAlpide replaces Boost.Format with std::format.
TPC (Time Projection Chamber)
offline/packages/tpc/LaserClusterizer.cc
offline/packages/tpc/LaserEventIdentifier.cc
offline/packages/tpc/Tpc3DClusterizer.cc
offline/packages/tpc/TpcClusterMover.cc, TpcClusterMover.h
offline/packages/tpc/TpcClusterizer.cc
offline/packages/tpc/TpcCombinedRawDataUnpacker.cc, TpcCombinedRawDataUnpackerDebug.cc
offline/packages/tpc/TpcDistortionCorrection.cc
offline/packages/tpc/TpcLoadDistortionCorrection.cc
offline/packages/tpc/TpcRawDataTree.cc, TpcRawWriter.cc
offline/packages/tpc/TpcSimpleClusterizer.cc
offline/packages/tpc/TrainingHits.cc
Widespread refactoring: std::format replaces ROOT string construction; pointer conventions (auto*) applied consistently; std::max/min replace manual boundary logic; empty() replaces size() checks. TpcClusterMover adds const to get_circle_circle_intersection. Range-based loops and brace-wrapped blocks standardized. TrainingHits moves data-member initialization to initializer list. Minor unsigned shift fix in LaserEventIdentifier.
Track Reconstruction & Alignment
offline/packages/trackbase/AlignmentTransformation.cc, AlignmentTransformation.h
offline/packages/trackreco/DSTClusterPruning.cc, DSTClusterPruning.h
offline/packages/trackreco/PHActsTrkFitter.cc, PHActsTrkFitter.h
offline/packages/trackreco/PHSiliconTpcTrackMatching.cc, PHSiliconTpcTrackMatching.h
offline/packages/trackreco/PHSimpleVertexFinder.h
offline/packages/trackreco/PHTpcDeltaZCorrection.h
offline/packages/PHGenFitPkg/PHGenFit/Fitter.cc
offline/packages/PHGenFitPkg/PHGenFit/Track.cc
AlignmentTransformation refactors transform construction with use_module_tilt flag for TPC modules; revises rotation/translation order. DSTClusterPruning adds pruneAllSeeds bulk-promotion mode. PHActsTrkFitter/PHSimpleVertexFinder/PHTpcDeltaZCorrection update setTrkrClusterContainerName to const reference. PHSiliconTpcTrackMatching makes cluster-map name configurable. Fitter simplifies string comparisons; Track uses initializer lists and range-based loops.
Trigger & Skimming
offline/packages/Skimmers/Trigger/TriggerDSTSkimmer.cc, TriggerDSTSkimmer.h
TriggerDSTSkimmer adds set_accept_max to limit accepted events; replaces GL1Packet string lookup with class ID 14001.
Jet Background & Calibration
offline/packages/jetbackground/DetermineTowerBackground.cc, DetermineTowerBackground.h
offline/packages/jetbackground/Makefile.am
offline/packages/KFParticle_sPHENIX/KFParticle_sPHENIX.cc
DetermineTowerBackground adds do_flow==4 centrality-based flow handling with LoadCalibrations; stores/loads centrality-v2 table. Makefile adds -lcentrality_io, -lcdbobjects, -lffamodules dependencies. KFParticle_sPHENIX returns EVENT_OK instead of ABORTEVENT for missing tracks/vertices.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant HepMCParticleTrigger
    participant HepMCEvent
    participant ParticleList

    User->>HepMCParticleTrigger: process_event(PHCompositeNode)
    HepMCParticleTrigger->>HepMCParticleTrigger: Validate PHHepMCGenEventMap
    HepMCParticleTrigger->>HepMCEvent: Iterate events
    loop For each event
        HepMCParticleTrigger->>HepMCParticleTrigger: isGoodEvent(HepMC::GenEvent)
        HepMCParticleTrigger->>ParticleList: getParticles(HepMC::GenEvent)
        ParticleList->>ParticleList: Apply kinematic cuts (Pt, P, Eta, Pz)
        ParticleList-->>HepMCParticleTrigger: Return particle counts
        HepMCParticleTrigger->>HepMCParticleTrigger: Check trigger thresholds
        alt Event passes trigger
            HepMCParticleTrigger->>HepMCParticleTrigger: Increment good event counter
        end
    end
    HepMCParticleTrigger-->>User: Return EVENT_OK
Loading
sequenceDiagram
    participant InputFileHandler
    participant Script
    participant FileSystem
    participant FileStream

    InputFileHandler->>InputFileHandler: OpenNextFile()
    alt RunBeforeOpeningScript configured
        InputFileHandler->>Script: Validate script exists & is executable
        Script-->>InputFileHandler: Script state (valid/invalid)
        alt Script valid
            InputFileHandler->>Script: Execute with current file info
            Script->>FileSystem: Perform pre-open operations
            FileSystem-->>Script: Operation results
            Script-->>InputFileHandler: Return exit code
        end
    end
    InputFileHandler->>FileStream: Open next file
    FileStream-->>InputFileHandler: File handle
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 Whiskers twitching with glee at the trigger refine,
Particles clustered so neat, in arrays divine,
State-residuals computed with quantum precision,
Const-guards and pointer-wise, made with wise vision,
From TPC to MVTX, alignment so tight—
This PR hops forward, the physics burns bright!

🚥 Pre-merge checks | ❌ 3
❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description contains only the template checklist with no substantive information about the changes, objectives, or rationale for the PR. Fill in the description template with a clear explanation of what changes are included, why they were made, and any links to related issues or discussion.
Docstring Coverage ⚠️ Warning Docstring coverage is 21.62% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Test' is vague and generic, providing no meaningful information about the actual changeset contents. Replace with a descriptive title summarizing the main change, e.g., 'Merge updates: HepMC triggers, QA modules, and code modernization' or similar.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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 and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request contains extensive code quality improvements and modernizations across the sPHENIX codebase. The changes include formatting improvements, const correctness additions, modernization of C++ patterns, and several bug fixes and new features.

Changes:

  • Code style and formatting improvements (spacing, indentation, const correctness)
  • Modernization to C++20/modern C++ patterns (std::format, std::max/std::min, range-based loops)
  • Bug fixes (return codes, error handling, BCO handling improvements)
  • New features (CombineTowerInfo, StateClusterResidualsQA, calibration loading for jets)

Reviewed changes

Copilot reviewed 87 out of 87 changed files in this pull request and generated no comments.

Show a summary per file
File Description
trackreco/*.h/cc Added const correctness to setters, formatting improvements, code cleanup
trackbase/AlignmentTransformation.h/cc Added use_module_tilt flag logic, reorganized transform calculation
tpc/*.cc Replaced if-else min/max with std::max/std::min, improved const correctness
mvtx/*.h/cc Added const to methods, improved formatting, replaced C-style casts
micromegas/MicromegasDefs.* Added sample field to hit key, improved bit masking
micromegas/MicromegasCombinedDataDecoder.* Store sample with hit, improved max ADC finding
mbd/*.cc Added error handling for empty packets, improved diagnostics
jetbackground/* Added centrality-based v2 loading from calibration database
Skimmers/Trigger/* Added max accept events feature, fixed GL1 packet lookup
fun4allraw/* Major BCO handling improvements, GL1 skip logic fixes
fun4all/* Added script execution before file opening, cout state preservation
ffamodules/* Removed unnecessary includes, modernized destructors
QA/Tracking/* Added new StateClusterResidualsQA module, sample range filtering
CaloEmbedding/* Added new CombineTowerInfo module for combining tower energies

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@coderabbitai
Copy link

coderabbitai bot commented Jan 19, 2026

Note

Docstrings generation - SUCCESS
Generated docstrings for this pull request at #12

coderabbitai bot added a commit that referenced this pull request Jan 19, 2026
Docstrings generation was requested by @blackcathj.

* #11 (comment)

The following files were modified:

* `generators/Herwig/HepMCTrigger/HepMCJetTrigger.cc`
* `generators/Herwig/HepMCTrigger/HepMCParticleTrigger.cc`
* `generators/Herwig/HepMCTrigger/HepMCParticleTrigger.h`
* `generators/PHPythia8/PHPythia8.cc`
* `generators/phhepmc/PHHepMCGenEventv1.cc`
* `offline/QA/Calorimeters/CaloValid.cc`
* `offline/QA/Tracking/MicromegasClusterQA.cc`
* `offline/QA/Tracking/MicromegasClusterQA.h`
* `offline/QA/Tracking/StateClusterResidualsQA.cc`
* `offline/QA/Tracking/StateClusterResidualsQA.h`
* `offline/framework/ffamodules/CDBInterface.cc`
* `offline/framework/ffamodules/FlagHandler.h`
* `offline/framework/ffamodules/HeadReco.cc`
* `offline/framework/ffamodules/HeadReco.h`
* `offline/framework/ffamodules/SyncReco.h`
* `offline/framework/ffamodules/Timing.cc`
* `offline/framework/ffamodules/Timing.h`
* `offline/framework/fun4all/Fun4AllServer.cc`
* `offline/framework/fun4all/Fun4AllServer.h`
* `offline/framework/fun4all/InputFileHandler.cc`
* `offline/framework/fun4all/InputFileHandler.h`
* `offline/framework/fun4allraw/Fun4AllStreamingInputManager.cc`
* `offline/framework/fun4allraw/SingleMicromegasPoolInput_v2.cc`
* `offline/framework/fun4allraw/SingleMicromegasPoolInput_v2.h`
* `offline/framework/fun4allraw/SingleMvtxPoolInput.cc`
* `offline/framework/fun4allraw/SingleTriggeredInput.cc`
* `offline/framework/fun4allraw/SingleTriggeredInput.h`
* `offline/packages/CaloEmbedding/CombineTowerInfo.cc`
* `offline/packages/CaloEmbedding/CombineTowerInfo.h`
* `offline/packages/CaloEmbedding/CopyIODataNodes.cc`
* `offline/packages/CaloEmbedding/CopyIODataNodes.h`
* `offline/packages/CaloReco/PhotonClusterBuilder.cc`
* `offline/packages/KFParticle_sPHENIX/KFParticle_sPHENIX.cc`
* `offline/packages/PHGenFitPkg/PHGenFit/Fitter.cc`
* `offline/packages/PHGenFitPkg/PHGenFit/Track.cc`
* `offline/packages/Skimmers/Trigger/TriggerDSTSkimmer.cc`
* `offline/packages/Skimmers/Trigger/TriggerDSTSkimmer.h`
* `offline/packages/jetbackground/DetermineTowerBackground.cc`
* `offline/packages/jetbackground/DetermineTowerBackground.h`
* `offline/packages/mbd/MbdCalib.cc`
* `offline/packages/mbd/MbdEvent.cc`
* `offline/packages/mbd/MbdReco.cc`
* `offline/packages/mbd/MbdSig.h`
* `offline/packages/micromegas/MicromegasClusterizer.cc`
* `offline/packages/micromegas/MicromegasCombinedDataDecoder.cc`
* `offline/packages/micromegas/MicromegasCombinedDataDecoder.h`
* `offline/packages/micromegas/MicromegasDefs.cc`
* `offline/packages/micromegas/MicromegasDefs.h`
* `offline/packages/mvtx/CylinderGeom_Mvtx.cc`
* `offline/packages/mvtx/CylinderGeom_Mvtx.h`
* `offline/packages/mvtx/MvtxClusterPruner.cc`
* `offline/packages/mvtx/MvtxClusterizer.cc`
* `offline/packages/mvtx/MvtxHitPruner.cc`
* `offline/packages/mvtx/SegmentationAlpide.cc`
* `offline/packages/tpc/LaserClusterizer.cc`
* `offline/packages/tpc/LaserEventIdentifier.cc`
* `offline/packages/tpc/Tpc3DClusterizer.cc`
* `offline/packages/tpc/TpcClusterMover.cc`
* `offline/packages/tpc/TpcClusterMover.h`
* `offline/packages/tpc/TpcClusterizer.cc`
* `offline/packages/tpc/TpcCombinedRawDataUnpacker.cc`
* `offline/packages/tpc/TpcCombinedRawDataUnpackerDebug.cc`
* `offline/packages/tpc/TpcDistortionCorrection.cc`
* `offline/packages/tpc/TpcLoadDistortionCorrection.cc`
* `offline/packages/tpc/TpcRawDataTree.cc`
* `offline/packages/tpc/TpcRawWriter.cc`
* `offline/packages/tpc/TpcSimpleClusterizer.cc`
* `offline/packages/tpc/TrainingHits.cc`
* `offline/packages/trackbase/AlignmentTransformation.cc`
* `offline/packages/trackbase/AlignmentTransformation.h`
* `offline/packages/trackreco/DSTClusterPruning.cc`
* `offline/packages/trackreco/DSTClusterPruning.h`
* `offline/packages/trackreco/PHActsTrkFitter.cc`
* `offline/packages/trackreco/PHActsTrkFitter.h`
* `offline/packages/trackreco/PHSiliconTpcTrackMatching.cc`
* `offline/packages/trackreco/PHSiliconTpcTrackMatching.h`
* `offline/packages/trackreco/PHSimpleVertexFinder.h`
* `offline/packages/trackreco/PHTpcDeltaZCorrection.h`
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
offline/framework/fun4allraw/SingleMicromegasPoolInput_v2.cc (1)

914-963: Reset evaluation fields per waveform to avoid stale QA data.

m_waveform.packet_id isn’t set here and fee_bco_predicted_matched can retain a prior value when no prediction exists. This can corrupt QA output, especially in the non‑match path.

🔧 Proposed fix (reset defaults and set packet_id)
-    if( m_do_evaluation )
-    {
-      m_waveform.is_heartbeat = is_heartbeat;
-      m_waveform.fee_id = fee_id;
-      m_waveform.channel = payload.channel;
-      m_waveform.fee_bco = fee_bco;
-    }
+    if( m_do_evaluation )
+    {
+      m_waveform.packet_id = packet_id;
+      m_waveform.is_heartbeat = is_heartbeat;
+      m_waveform.fee_id = fee_id;
+      m_waveform.channel = payload.channel;
+      m_waveform.fee_bco = fee_bco;
+      // reset per-waveform defaults to avoid stale values
+      m_waveform.matched = false;
+      m_waveform.gtm_bco_matched = 0;
+      m_waveform.fee_bco_predicted_matched = 0;
+    }
...
-      if( m_do_evaluation )
-      {
-        m_waveform.matched = true;
-        m_waveform.gtm_bco_matched = gtm_bco;
-        {
-          const auto predicted = bco_matching_information.get_predicted_fee_bco(gtm_bco);;
-          if( predicted )
-          {
-            m_waveform.fee_bco_predicted_matched = predicted.value();
-          }
-        }
-        m_evaluation_tree->Fill();
-      }
+      if( m_do_evaluation )
+      {
+        m_waveform.matched = true;
+        m_waveform.gtm_bco_matched = gtm_bco;
+        const auto predicted = bco_matching_information.get_predicted_fee_bco(gtm_bco);
+        m_waveform.fee_bco_predicted_matched = predicted ? predicted.value() : 0;
+        m_evaluation_tree->Fill();
+      }
offline/framework/fun4allraw/Fun4AllStreamingInputManager.cc (1)

779-797: Critical: Missing cleanup logic after processing hits.

The refactored loop iterates over m_InttRawHitMap and adds hits to the container, but unlike the previous pattern (visible in FillTpc at lines 1174-1180 and the cleanup loop at lines 696-704), processed entries are never cleaned up. This will cause:

  1. Memory leak: Hits remain in the map and are never deleted
  2. Double processing: Same hits will be added to the container on subsequent FillIntt() calls
  3. State accumulation: CleanupUsedPackets, clearPacketBClkStackMap, and clearFeeGTML1BCOMap are not called

Compare with the pre-BCO cleanup loop (lines 696-710) which correctly calls all cleanup functions and erases entries.

Proposed fix to add cleanup after hit processing
  for (auto& [bco, hitinfo] : m_InttRawHitMap)
  {
    if (bco > select_crossings)
    {
      break;
    }

    for (auto *intthititer : hitinfo.InttRawHitVector)
    {
      if (Verbosity() > 1)
      {
        std::cout << "Adding intt hit with bco 0x" << std::hex
                  << intthititer->get_bco() << std::dec << std::endl;
      }
      inttcont->AddHit(intthititer);
    }
  }
+
+  // Cleanup processed entries
+  auto it = m_InttRawHitMap.begin();
+  while (it != m_InttRawHitMap.end() && it->first <= select_crossings)
+  {
+    for (auto *iter : m_InttInputVector)
+    {
+      iter->CleanupUsedPackets(it->first);
+      iter->clearPacketBClkStackMap(it->first);
+      iter->clearFeeGTML1BCOMap(it->first);
+    }
+    it->second.InttRawHitVector.clear();
+    it = m_InttRawHitMap.erase(it);
+  }
  return 0;
offline/packages/mbd/MbdEvent.cc (1)

453-499: Validate _nsamples upper bound to prevent OOB writes and stale processing.

m_adc/m_samp are sized by MbdDefs::MAX_SAMPLES (see offline/packages/mbd/MbdEvent.h around lines 140–141). If a corrupted packet reports _nsamples > MAX_SAMPLES, the loop will write out of bounds; additionally, _nsamples > 30 currently skips SetXY but still leaves SetNSamples() > 0, so later stages may process stale/uninitialized state. Please reject invalid _nsamples and explicitly skip channels when you skip SetXY.

🛠️ Suggested guard and explicit skip
-      _nsamples = dstp[ipkt]->iValue(0, "SAMPLES");
+      _nsamples = dstp[ipkt]->iValue(0, "SAMPLES");
+      if (_nsamples <= 0 || _nsamples > MbdDefs::MAX_SAMPLES)
+      {
+        std::cout << PHWHERE << " ERROR, evt " << m_evt
+                  << " invalid nsamples (" << _nsamples
+                  << ") in Packet " << pktid << std::endl;
+        return Fun4AllReturnCodes::ABORTEVENT;
+      }
@@
-        _mbdsig[feech].SetNSamples( _nsamples );
-        
-        if ( _nsamples > 0 && _nsamples <= 30 )
-        {
-          _mbdsig[feech].SetXY(m_samp[feech], m_adc[feech]);
-        }
+        _mbdsig[feech].SetNSamples(_nsamples);
+        if (_nsamples <= 30)
+        {
+          _mbdsig[feech].SetXY(m_samp[feech], m_adc[feech]);
+        }
+        else
+        {
+          _mbdsig[feech].SetNSamples(0); // ensure later stages skip this channel
+        }
offline/packages/tpc/Tpc3DClusterizer.cc (1)

554-633: Cluster size uses phi radians instead of pad index.

iphimin/iphimax are integer pad bins, but you now update them with phi (radians). This yields incorrect phisize and silently truncates doubles to ints. Use the pad index (coords[1]) or rename variables to phiMin/phiMax and keep them as doubles.

🛠️ Proposed fix (use iphi bins)
-    iphimin = std::min<double>(phi, iphimin);
-    iphimax = std::max<double>(phi, iphimax);
+    const int iphi = static_cast<int>(coords[1]);
+    iphimin = std::min(iphi, iphimin);
+    iphimax = std::max(iphi, iphimax);
offline/framework/fun4all/InputFileHandler.h (1)

7-40: Harden pre-open script execution against shell injection.

RunBeforeOpening concatenates script arguments and filenames into a shell command string (lines 183–187 of InputFileHandler.cc) and executes it via gSystem->Exec(). Since gSystem->Exec() invokes the shell via ::system(), this pattern is vulnerable to shell injection if filenames or arguments contain shell metacharacters.

To mitigate: (1) pass arguments as a vector to an exec variant that doesn't invoke a shell (e.g., posix_spawn or fork+execve), or (2) if a shell is required, apply strict whitelisting of allowed filenames/arguments and validate/escape all user-controlled inputs.

🤖 Fix all issues with AI agents
In `@generators/PHPythia8/PHPythia8.cc`:
- Around line 36-49: The constructor may leave m_Pythia8 null when PYTHIA8 is
unset, so add null guards at the start of Init, process_event, and End to avoid
dereferencing it: in Init check if (!m_Pythia8) return ABORTRUN; in
process_event check if (!m_Pythia8) return ABORTEVENT; and in End check if
(!m_Pythia8) return EVENT_OK; ensure these checks occur before any use of
m_Pythia8 (referencing the methods Init, process_event, End and the member
m_Pythia8) so the methods exit safely when the Pythia8 instance was not created.

In `@offline/framework/fun4allraw/Fun4AllStreamingInputManager.cc`:
- Around line 977-1011: The loop iterating m_MvtxRawHitMap lacks the cleanup
performed in the FillIntt path; after finishing with each hitinfo you must
mirror that cleanup: call hitinfo.CleanupUsedPackets() and
hitinfo.clearFeeGTML1BCOMap(), delete any heap-allocated MvtxFeeIdInfo objects
if ownership was not transferred to mvtxEvtHeader (or skip deletion if
mvtxEvtHeader took ownership), clear hitinfo.MvtxFeeIdInfoVector and
hitinfo.MvtxRawHitVector, and finally erase the processed entry from
m_MvtxRawHitMap so memory and state are not leaked (perform these steps in the
same scope as the existing mvtxEvtHeader/mvtxcont handling in
Fun4AllStreamingInputManager::... loop).

In `@offline/packages/micromegas/MicromegasDefs.h`:
- Around line 61-71: genHitKey currently packs strip into 8 bits and getStrip
returns uint8_t, which truncates actual strip indices >255; change the hitkey
bit-packing to allocate enough bits for strip (use at least 10–11 bits to cover
~1084 strips or simply 16 bits for safety), update genHitKey to mask/shift strip
with the new width and getStrip to return a matching wider unsigned type (e.g.,
uint16_t), and adjust getSample/its masking/return type as needed so
TrkrDefs::hitkey encodes both fields without overflow or silent truncation;
alternatively, if you want to keep 8-bit encoding, add a validation in genHitKey
to throw/log and refuse strip>=256, but prefer expanding the strip field to
avoid mis-association.

In `@offline/packages/mvtx/SegmentationAlpide.cc`:
- Line 9: The build fails because SegmentationAlpide.cc uses C++20 <format>;
update the build configuration to enable C++20: either add AM_CXXFLAGS =
-std=c++20 (or append -std=c++20 to existing AM_CXXFLAGS) in
offline/packages/mvtx/Makefile.am or set the project-wide standard in
configure.ac (e.g., via AC_PROG_CXX and appropriate checks or AC_ARG_ENABLE for
CXXFLAGS) so files like SegmentationAlpide.cc that include <format> compile with
C++20 enabled.

In `@offline/packages/tpc/Tpc3DClusterizer.cc`:
- Around line 660-681: Guard the NTuple fill so it only runs when output is
enabled and the NTuple exists: wrap the m_clusterNT->Fill(fX) call in a
conditional check (e.g., if (m_output && m_clusterNT) or if (m_clusterNT)
depending on class invariants) in Tpc3DClusterizer so that m_clusterNT->Fill is
not invoked when m_output is false and m_clusterNT was not created; update the
surrounding block accordingly to avoid null dereference.
♻️ Duplicate comments (1)
offline/packages/mbd/MbdEvent.cc (1)

583-601: Apply the same _nsamples range validation here.

Same concern as above: this overload still lacks an upper-bound guard for _nsamples, which risks overrunning m_adc/m_samp (see offline/packages/mbd/MbdEvent.h around lines 140–141) if a corrupted packet reports too many samples.

🟠 Major comments (16)
offline/packages/CaloReco/PhotonClusterBuilder.cc-568-570 (1)

568-570: Guard against out-of-range access when computing e22.

showershape is only checked for empty, but e22 reads indices 8–11; if get_shower_shapes returns fewer than 12 elements this will be undefined behavior. Add a size check or default to 0 when the vector is too short.

🔧 Proposed fix
-  photon->set_shower_shape_parameter("e22", showershape[8] + showershape[9] + showershape[10] + showershape[11]);
+  float e22 = 0.0F;
+  if (showershape.size() > 11)
+  {
+    e22 = showershape[8] + showershape[9] + showershape[10] + showershape[11];
+  }
+  photon->set_shower_shape_parameter("e22", e22);
offline/packages/PHGenFitPkg/PHGenFit/Fitter.cc-54-57 (1)

54-57: Potential dangling pointer after TGeoManager::Import.

In ROOT, creating a new TGeoManager (which Import does internally) deletes the existing gGeoManager. The sequence here:

  1. Line 54: Creates TGeoManager, _tgeo_manager points to it
  2. Line 57: Import creates a new TGeoManager from the file, deleting the one from line 54

This leaves _tgeo_manager as a dangling pointer. Compare with getInstance at line 192 which correctly captures the return value:

TGeoManager* tgeo_manager = TGeoManager::Import(tgeo_file_name.data(), "Default");
Proposed fix
   Fitter::Fitter(
       const std::string& tgeo_file_name,
       const PHField* field,
       const std::string& fitter_choice,
       const std::string& /*track_rep_choice*/,
       const bool doEventDisplay)
     : verbosity(1000)
-    , _tgeo_manager(new TGeoManager("Default", "Geane geometry"))
+    , _tgeo_manager(nullptr)
     , _doEventDisplay(doEventDisplay)
   {
-    TGeoManager::Import(tgeo_file_name.data());
+    _tgeo_manager = TGeoManager::Import(tgeo_file_name.data(), "Default");
offline/framework/fun4allraw/SingleTriggeredInput.cc-492-497 (1)

492-497: Memory leak: evt not deleted when packet is missing in retry loop.

Same issue as above - when pkt is null, the code continues but the newly fetched evt (from lines 481-491) is leaked.

Proposed fix
              pkt = evt->getPacket(representative_pid);
              if (!pkt)
              {
                std::cout << "representative packet invalid inside skiptrace.. continuing.." << std::endl;
+               delete evt;
+               evt = nullptr;
                continue;
              }
offline/framework/fun4allraw/SingleTriggeredInput.cc-460-465 (1)

460-465: Memory leak: evt not deleted when packet is missing.

When pkt is null, the code calls continue but evt (allocated at line 439-448) is never deleted, causing a memory leak.

Proposed fix
          Packet* pkt = evt->getPacket(representative_pid);
          if (!pkt)
          {
            std::cout << "representative packet invalid inside skiptrace.. continuing.." << std::endl;
+           delete evt;
+           evt = nullptr;
            continue;
          }
offline/framework/fun4allraw/SingleTriggeredInput.cc-509-511 (1)

509-511: Memory leak: pkt not deleted at the end of the skiptrace block.

The Packet* pkt allocated at line 460 (and potentially reassigned at line 492) is never deleted when exiting the skiptrace block. This leaks memory in two scenarios:

  1. When seb_diff == gl1_diff initially (the if-block at line 472 is skipped)
  2. When the retry while-loop exits after recovering or reaching max count
Proposed fix
              clockconstcount++;
            }
          }
+         delete pkt;
        }
offline/framework/fun4allraw/Fun4AllStreamingInputManager.cc-1367-1367 (1)

1367-1367: Inconsistent underflow fallback value.

When m_RefBCO < m_mvtx_negative_bco, this line returns m_mvtx_negative_bco, but line 839 in the same class returns 0 for the identical condition:

// Line 839 (FillMvtx):
uint64_t ref_bco_minus_range = m_RefBCO < m_mvtx_negative_bco ? 0 : ...

// Line 1367 (FillMvtxPool):
uint64_t ref_bco_minus_range = m_RefBCO < m_mvtx_negative_bco ? m_mvtx_negative_bco : ...

This is also inconsistent with FillInttPool (lines 1243-1247) which uses 0 as the fallback.

Proposed fix for consistency
- uint64_t ref_bco_minus_range = m_RefBCO < m_mvtx_negative_bco ? m_mvtx_negative_bco : m_RefBCO - m_mvtx_negative_bco;
+ uint64_t ref_bco_minus_range = m_RefBCO < m_mvtx_negative_bco ? 0 : m_RefBCO - m_mvtx_negative_bco;
offline/framework/fun4allraw/Fun4AllStreamingInputManager.cc-974-976 (1)

974-976: Potential unsigned integer underflow in lower_limit calculation.

When m_mvtx_is_triggered is false, lower_limit is computed as:

select_crossings - m_mvtx_bco_range - m_mvtx_negative_bco

Since select_crossings = m_RefBCO + m_mvtx_bco_range, this simplifies to m_RefBCO - m_mvtx_negative_bco. However, unlike line 839 which has underflow protection, this line does not guard against m_RefBCO < m_mvtx_negative_bco, causing wraparound to a very large value.

Proposed fix to add underflow protection
- uint64_t lower_limit = m_mvtx_is_triggered ? select_crossings : select_crossings - m_mvtx_bco_range - m_mvtx_negative_bco;
+ uint64_t lower_limit = m_mvtx_is_triggered ? select_crossings : ref_bco_minus_range;
  uint64_t upper_limit = m_mvtx_is_triggered ? select_crossings + m_mvtx_bco_range : select_crossings;

This reuses the already-computed ref_bco_minus_range from line 839 which has proper underflow protection.

offline/framework/fun4all/InputFileHandler.cc-95-107 (1)

95-107: Handle RunBeforeOpening() failures before proceeding to open the file.

When the pre-opening script fails (returns non-zero), the code currently logs the failure but then immediately attempts to open the file anyway. If the script is intended to stage, validate, or prepare the file for processing, this behavior will cause confusing cascading failures or partial processing. Skip the file and move to the next one when RunBeforeOpening() fails:

Suggested fix
      if (RunBeforeOpening(stringvec))
      {
        std::cout << PHWHERE << " RunBeforeOpening() failed" << std::endl;
+       m_FileList.pop_front();
+       continue;
      }
offline/framework/fun4all/InputFileHandler.cc-165-199 (1)

165-199: Shell injection vulnerability and incomplete exit-status fix.

The command construction via string concatenation is genuinely risky: m_OpeningArgs and elements from stringvec (filenames) are user-controlled and not escaped, so spaces, quotes, and metacharacters can break execution or enable injection. The exit-status handling is also problematic: storing gSystem->Exec() (which returns wait() status, including -1 on error) in unsigned int loses negative error codes; only the exit code should be extracted via right-shift by 8 after validating success.

The suggested diff addresses only the exit-status issue. For a complete fix, adopt either an argv-based execution method or robust shell-escaping of all parameters before concatenation.

🔧 Exit-status correction (partial)
-  unsigned int iret = gSystem->Exec(fullcmd.c_str());
-
-  if (iret)
-  {
-    iret = iret >> 8U;
-  }
-  return static_cast<int> (iret);
+  int iret = gSystem->Exec(fullcmd.c_str());
+  if (iret < 0)
+  {
+    return iret;
+  }
+  if (iret)
+  {
+    iret = iret >> 8;
+  }
+  return iret;
offline/packages/micromegas/MicromegasClusterizer.cc-223-275 (1)

223-275: Confirm that the code actually keeps all samples per strip, not just the earliest.

The comment claims "only the first one (in time) is kept," but the implementation contradicts this. The local_hitmap stores all hits from hit_range as separate entries (since each hitkey is unique). When hit_range is ordered numerically by hitkey (strip in bits 0-7, sample in bits 8-23), hits come in order: all sample=0 hits, then sample=1, etc. During insertion into local_hitmap, all hits are retained—nothing filters them to one per strip. Iteration yields all samples per strip, just ordered by strip due to StripSortFtor.

If the intent is to keep only the earliest sample per strip, explicitly track the minimum sample per strip as shown in the suggested fix. Otherwise, clarify the comment to reflect that all samples are retained.

offline/packages/CaloEmbedding/CopyIODataNodes.cc-307-327 (1)

307-327: Add null/size checks in CopyTowerInfo to avoid crashes.

from_towerInfo and to_towerInfo are dereferenced without validation, and size mismatches are unchecked. Either can produce a segfault or out-of-range access.

🛠️ Safer copy loop
 void CopyIODataNodes::CopyTowerInfo(PHCompositeNode *from_topNode, PHCompositeNode *to_topNode)
 {
   TowerInfoContainer *from_towerInfo = findNode::getClass<TowerInfoContainer>(from_topNode, from_towerInfo_name);
   TowerInfoContainer   *to_towerInfo = findNode::getClass<TowerInfoContainer>(  to_topNode, to_towerInfo_name);
-  unsigned int ntowers = from_towerInfo->size();
+  if (!from_towerInfo || !to_towerInfo)
+  {
+    std::cout << "TowerInfoContainer missing on source or destination" << std::endl;
+    m_CopyTowerInfoFlag = false;
+    return;
+  }
+  const unsigned int ntowers = from_towerInfo->size();
+  if (to_towerInfo->size() != ntowers)
+  {
+    std::cout << "TowerInfoContainer size mismatch: from="
+              << ntowers << " to=" << to_towerInfo->size() << std::endl;
+  }
   for (unsigned int ch = 0; ch < ntowers; ++ch)
   {
     TowerInfo *from_tow = from_towerInfo->get_tower_at_channel(ch);
-    to_towerInfo->get_tower_at_channel(ch)->copy_tower(from_tow);
+    TowerInfo *to_tow = to_towerInfo->get_tower_at_channel(ch);
+    if (!from_tow || !to_tow) { continue; }
+    to_tow->copy_tower(from_tow);
   }
offline/packages/CaloEmbedding/CombineTowerInfo.cc-35-72 (1)

35-72: Guard against missing detector node before dereference.
DetNode can be null (e.g., m_detector unset or node missing), which will crash on DetNode->addNode(node). Please validate or create the node.

🔧 Proposed fix
 void CombineTowerInfo::CreateNodes(PHCompositeNode* topNode)
 {
   PHNodeIterator iter(topNode);
   PHCompositeNode* dstNode =
       dynamic_cast<PHCompositeNode*>(iter.findFirst("PHCompositeNode", "DST"));

   if (!dstNode)
   {
     throw std::runtime_error("CombineTowerInfo: DST node not found");
   }

-  PHCompositeNode *DetNode = dynamic_cast<PHCompositeNode *>(iter.findFirst("PHCompositeNode", m_detector));
+  if (m_detector.empty())
+  {
+    throw std::runtime_error("CombineTowerInfo: detector node name not set");
+  }
+  PHCompositeNode *DetNode =
+      dynamic_cast<PHCompositeNode *>(iter.findFirst("PHCompositeNode", m_detector));
+  if (!DetNode)
+  {
+    DetNode = new PHCompositeNode(m_detector);
+    dstNode->addNode(DetNode);
+  }
generators/Herwig/HepMCTrigger/HepMCJetTrigger.cc-99-103 (1)

99-103: PDG filter range 12–18 includes muons and taus, not just neutrinos.

The range 12–18 spans electron neutrinos (12), muons (13), muon neutrinos (14), taus (15), tau neutrinos (16), and hypothetical heavy leptons (17–18). If the intent is to exclude only neutrinos from jet clustering, use explicit IDs: pd == 12 || pd == 14 || pd == 16 (and optionally 18). Please clarify the physics requirement.

Also, the typo in the comment: "behavioro" should be "behavior".

offline/QA/Tracking/StateClusterResidualsQA.cc-185-198 (1)

185-198: Potential null pointer usage before null check.

cluster could be nullptr if findCluster fails, but geometry->getGlobalPosition() is called with it at line 189 before the null check at line 193. If getGlobalPosition dereferences the cluster pointer, this will cause undefined behavior.

Suggested fix
          auto *cluster = cluster_map->findCluster(state->get_cluskey());
+         if (!cluster)
+         {
+           continue;
+         }
          float state_x = state->get_x();
          float state_y = state->get_y();
          float state_z = state->get_z();
          Acts::Vector3 glob = geometry->getGlobalPosition(state->get_cluskey(), cluster);
          float cluster_x = glob.x();
          float cluster_y = glob.y();
          float cluster_z = glob.z();
-         if (cluster)
-         {
-           m_histograms_x[h]->Fill(state_x - cluster_x);
-           m_histograms_y[h]->Fill(state_y - cluster_y);
-           m_histograms_z[h]->Fill(state_z - cluster_z);
-         }
+         m_histograms_x[h]->Fill(state_x - cluster_x);
+         m_histograms_y[h]->Fill(state_y - cluster_y);
+         m_histograms_z[h]->Fill(state_z - cluster_z);
generators/Herwig/HepMCTrigger/HepMCParticleTrigger.cc-239-267 (1)

239-267: SetAbsEta* and SetEta* share the same threshold variables.

Both SetAbsEtaHigh/SetAbsEtaLow and SetEtaHigh/SetEtaLow modify _theEtaHigh and _theEtaLow. If a user calls both SetEtaHigh(1.5) and SetAbsEtaHigh(1.0), the second call overwrites the first. Consider using separate storage for absolute eta thresholds (e.g., _theAbsEtaHigh, _theAbsEtaLow) to allow both cuts to be active simultaneously with independent values.

offline/QA/Tracking/StateClusterResidualsQA.h-56-101 (1)

56-101: Undefined behavior if setter called before addHistogram().

All setter methods (e.g., setNMvtx, setPhiRange, etc.) call m_pending.back() without checking if m_pending is empty. If a user calls any setter before calling addHistogram(), this results in undefined behavior.

Suggested fix: Add assertion or guard
   StateClusterResidualsQA& setNMvtx(int min, int max)
   {
+    assert(!m_pending.empty() && "Must call addHistogram() before setting properties");
     m_pending.back().min_mvtx_clusters = min;
     m_pending.back().max_mvtx_clusters = max;
     return *this;
   }

Apply similar checks to all setter methods, or document clearly that addHistogram() must be called first.

🟡 Minor comments (4)
offline/packages/mvtx/MvtxClusterPruner.cc-229-238 (1)

229-238: Guard against zero total when computing fraction.
Line 237 can emit inf/NaN when m_cluster_counter_total == 0. A small guard keeps logs sane and avoids floating divide‑by‑zero edge cases.

🔧 Suggested guard
-  std::cout << "MvtxClusterPruner::End -"
-            << " m_cluster_counter_deleted: " << m_cluster_counter_deleted
-            << " fraction: " << double(m_cluster_counter_deleted) / m_cluster_counter_total
-            << std::endl;
+  std::cout << "MvtxClusterPruner::End -"
+            << " m_cluster_counter_deleted: " << m_cluster_counter_deleted;
+  if (m_cluster_counter_total > 0)
+  {
+    std::cout << " fraction: "
+              << double(m_cluster_counter_deleted) / m_cluster_counter_total;
+  }
+  else
+  {
+    std::cout << " fraction: n/a";
+  }
+  std::cout << std::endl;
offline/packages/tpc/Tpc3DClusterizer.cc-597-601 (1)

597-601: Early return leaks clus.

clus is allocated before the nHits == 0 check, so the early return leaks memory.

🛠️ Proposed fix
   if (nHits == 0)
   {
     std::cout << "no hits" << std::endl;
+    delete clus;
     return;
   }
offline/packages/Skimmers/Trigger/TriggerDSTSkimmer.cc-24-27 (1)

24-27: Guard max_accept to prevent all events being rejected if set to zero.

If set_accept_max(0) is explicitly called, the condition accepted_events >= max_accept will be true from the start (0 >= 0), rejecting all events. Add a guard: max_accept > 0 to prevent this edge case.

🔧 Suggested fix
-  if ((accepted_events >= max_accept) && use_max_accept) 
+  if (use_max_accept && max_accept > 0 && accepted_events >= max_accept)
   {
     return Fun4AllReturnCodes::ABORTEVENT;
   }
offline/QA/Tracking/StateClusterResidualsQA.h-127-127 (1)

127-127: Include guard macro name mismatch in closing comment.

The closing comment references TRACKFITTINGQA_H but the actual include guard defined at the top is STATECLUSTERRESIDUALSQA_H.

Suggested fix
-#endif  // TRACKFITTINGQA_H
+#endif  // STATECLUSTERRESIDUALSQA_H
🧹 Nitpick comments (28)
offline/packages/mbd/MbdSig.h (1)

35-35: Make the accessor const (and optionally noexcept).
This getter should not mutate state; marking it const improves API usability for const instances.

♻️ Suggested tweak
-  int  GetNSamples() { return _nsamples; }
+  int GetNSamples() const noexcept { return _nsamples; }
offline/framework/fun4allraw/SingleTriggeredInput.h (1)

115-115: Consider consistent naming convention.

The new member eventcounter doesn't follow the m_ prefix convention used by most other private members in this class (e.g., m_EventNumber, m_AllDone, m_FilesDone). While there are a few other members without the prefix (firstcall, firstclockcheck), aligning with the dominant convention improves consistency.

Suggested naming
-  long long eventcounter{0};
+  long long m_EventCounter{0};
offline/packages/tpc/TpcDistortionCorrection.cc (1)

17-21: LGTM!

Good modernization. Removing redundant inline (already implied by constexpr) and switching from const T& to by-value is appropriate for a function primarily used with small primitive types like double, where passing by value avoids indirection overhead.

Minor optional note: the const on a by-value parameter (const T x) only prevents modification of the local copy inside the function and is typically omitted in this style. Consider T x if you prefer consistency with common idioms, but this is purely a style preference.

offline/packages/trackreco/DSTClusterPruning.h (1)

77-80: Minor: Inconsistent comment indentation and orphaned Doxygen tag.

The Doxygen comment on lines 77-78 has inconsistent indentation compared to other comments in the file. Also, line 80 has an orphaned //@} closing tag without a corresponding //!@{ opening tag.

Suggested fix
-//! set to true if you want to dump out all clusters on all silicon
-//! and all tpc seeds individually
+  //! set to true if you want to dump out all clusters on all silicon
+  //! and all tpc seeds individually
   bool m_pruneAllSeeds = false;
-  //@}
offline/packages/trackreco/DSTClusterPruning.cc (2)

22-27: Remove duplicate includes.

SvtxTrack.h and SvtxTrackMap.h are included twice (lines 22-23 and 26-27).

Suggested fix
 `#include` <trackbase_historic/SvtxTrack.h>
 `#include` <trackbase_historic/SvtxTrackMap.h>
 `#include` <trackbase_historic/TrackSeed.h>
 
-#include <trackbase_historic/SvtxTrack.h>
-#include <trackbase_historic/SvtxTrackMap.h>
-

192-206: Consider extracting duplicate cluster-copying logic to a helper method.

The cluster lookup, null-check, duplicate-check, copy, and insert pattern appears three times in this file (here, and in the original path for TPC and Silicon seeds around lines 241-255 and 266-280). Extracting this to a private helper method would reduce maintenance burden.

Example helper method
// In the private section of the class:
void copyClusterToReducedMap(TrkrDefs::cluskey cluster_key);

// Implementation:
void DSTClusterPruning::copyClusterToReducedMap(TrkrDefs::cluskey cluster_key)
{
  auto cluster = m_cluster_map->findCluster(cluster_key);
  if (!cluster)
  {
    std::cout << "DSTClusterPruning::evaluate_tracks - unable to find cluster for key " << cluster_key << std::endl;
    return;
  }
  if (!m_reduced_cluster_map->findCluster(cluster_key))
  {
    m_cluster = new TrkrClusterv5();
    m_cluster->CopyFrom(cluster);
    m_reduced_cluster_map->addClusterSpecifyKey(cluster_key, m_cluster);
  }
}
offline/packages/mvtx/MvtxHitPruner.cc (1)

158-165: Consider adding null check for findHitSet result.

findHitSet could potentially return nullptr if the hitset was removed or doesn't exist. While the logic should ensure the key exists (it was added to the multimap from existing hitsets), a defensive null check would prevent crashes if the data structure is modified unexpectedly.

🔧 Suggested defensive check
       // copy all hits to the hitset with strobe 0
       auto* hitset = m_hits->findHitSet(hitsetkey);
+      if (!hitset)
+      {
+        continue;
+      }

       if (Verbosity())
       {
offline/packages/tpc/TpcClusterizer.cc (1)

368-380: LGTM!

Using std::max and std::min for boundary clamping is cleaner and more idiomatic than the if-else patterns. Note that lines 370-373 and 377-380 still use if-statements for the upper bounds - consider using std::min for consistency.

♻️ Optional: Apply std::min consistently for upper bounds
     int isophimax = iphi + 1;
-    if (!(isophimax < NPhiBinsMax))
-    {
-      isophimax = NPhiBinsMax - 1;
-    }
+    isophimax = std::min(isophimax, NPhiBinsMax - 1);
     int isotmin = it - 1;
     isotmin = std::max(isotmin, 0);
     int isotmax = it + 1;
-    if (!(isotmax < NTBinsMax))
-    {
-      isotmax = NTBinsMax - 1;
-    }
+    isotmax = std::min(isotmax, NTBinsMax - 1);
offline/packages/trackreco/PHActsTrkFitter.cc (1)

136-187: Simplify redundant evaluator check (minor cleanup).

Inside the if (m_actsEvaluator) block, the nested if (m_actsEvaluator && !m_simActsEvaluator) is redundant and can be simplified to if (!m_simActsEvaluator) for clarity. Optional cleanup.

offline/framework/fun4allraw/SingleTriggeredInput.cc (1)

355-359: Debug output is unconditional and uses function-static variable.

The static bool firstclockarray persists across all instances of SingleTriggeredInput, so this message only prints once globally rather than once per instance. If per-instance behavior is intended, consider using a member variable. Additionally, this output isn't gated by Verbosity(), unlike other debug statements in this file.

Suggested improvement
-    static bool firstclockarray=true;
-    if(firstclockarray){
-      std::cout << "first clock call pid " << pid << " m_bclkarray_map[pid][0] : " << m_bclkarray_map[pid][0] << std::endl;
-      firstclockarray=false;
-    }
+    if (Verbosity() > 0 && !m_firstClockPrinted)
+    {
+      std::cout << "first clock call pid " << pid << " m_bclkarray_map[pid][0] : " << m_bclkarray_map[pid][0] << std::endl;
+      m_firstClockPrinted = true;
+    }

Add bool m_firstClockPrinted{false}; as a member variable in the header if per-instance behavior is desired.

offline/packages/Skimmers/Trigger/TriggerDSTSkimmer.cc (1)

54-54: Avoid hard-coded class ID for Gl1Packet.

Consider using a named constant (or a class-provided ID) to avoid silent drift if the ID changes. Please also confirm 14001 is the correct class ID in the current data model.

💡 Example
+  constexpr int kGl1PacketClassId = 14001;
-  Gl1Packet *_gl1PacketInfo = findNode::getClass<Gl1Packet>(topNode, 14001);
+  Gl1Packet *_gl1PacketInfo = findNode::getClass<Gl1Packet>(topNode, kGl1PacketClassId);
offline/packages/Skimmers/Trigger/TriggerDSTSkimmer.h (1)

25-30: New max-accept feature looks good; minor style note.

The feature correctly implements the enable/configure pattern for limiting accepted events. The return; statement on line 29 is redundant in a void function.

♻️ Optional: Remove redundant return statement
  void set_accept_max(int max_events) 
  {
    use_max_accept = true;
    max_accept = max_events;
-   return;
  }
offline/packages/micromegas/MicromegasClusterizer.cc (1)

159-167: Geometry-node fallback loop looks good; consider removing the stray comment.

//ma appears to be leftover noise—worth dropping for clarity.

offline/packages/micromegas/MicromegasDefs.cc (1)

69-87: Validate strip/sample ranges and call sites after the API change.

genHitKey now truncates strip to 8 bits and returns uint8_t from getStrip. If any upstream path still uses strip values >255 (or relied on the old genHitKey(strip) overload), keys will collide silently. Please confirm range guarantees and update/verify all call sites.

If range safety isn’t guaranteed, consider explicit validation to avoid silent truncation.

🛠️ Possible guard (example)
 TrkrDefs::hitkey genHitKey(uint16_t strip, uint16_t sample)
 {
+  // guard against silent truncation
+  assert(strip <= 0xFFU);
   const TrkrDefs::hitkey key = (strip&0xFFU) << kBitShiftStrip;
   const TrkrDefs::hitkey tmp = (sample&0xFFFFU) << kBitShiftSample;
   return key|tmp;
 }
offline/QA/Tracking/MicromegasClusterQA.h (2)

54-58: Guard against invalid sample ranges in setters.

If m_sample_min > m_sample_max, all clusters will be filtered out. Consider enforcing min <= max at set time.

♻️ Possible invariant handling
-  void set_sample_min(uint16_t value) { m_sample_min = value; }
+  void set_sample_min(uint16_t value)
+  {
+    m_sample_min = value;
+    if (m_sample_min > m_sample_max) { m_sample_max = m_sample_min; }
+  }

-  void set_sample_max(uint16_t value) { m_sample_max = value; }
+  void set_sample_max(uint16_t value)
+  {
+    m_sample_max = value;
+    if (m_sample_max < m_sample_min) { m_sample_min = m_sample_max; }
+  }

108-112: Consider documenting inclusive/exclusive semantics for defaults.

The filter uses [min, max) in the .cc. A brief comment here would prevent ambiguity.

offline/QA/Tracking/MicromegasClusterQA.cc (1)

168-173: Avoid duplicate getSample calls and handle invalid ranges.

The lambda calls getSample twice per hit, and an invalid range (min >= max) will silently drop all clusters. Consider a tiny refactor to compute once and short‑circuit on invalid configuration.

♻️ Possible refactor
-      if( std::none_of( hit_range.first, hit_range.second,
-        [this]( const TrkrClusterHitAssoc::Map::value_type& pair )
-        { return MicromegasDefs::getSample( pair.second ) >= m_sample_min &&  MicromegasDefs::getSample( pair.second ) < m_sample_max; } ) )
+      if (m_sample_min >= m_sample_max)
+      { continue; }
+      const auto in_range = [this](const TrkrClusterHitAssoc::Map::value_type& pair)
+      {
+        const auto sample = MicromegasDefs::getSample(pair.second);
+        return sample >= m_sample_min && sample < m_sample_max;
+      };
+      if (std::none_of(hit_range.first, hit_range.second, in_range))
       { continue; }
offline/packages/CaloEmbedding/CopyIODataNodes.cc (1)

367-391: Gate logging on verbosity and validate names.

Unconditional std::cout in CreateTowerInfo can be noisy; also consider guarding against empty from_towerInfo_name / to_towerInfo_name to avoid confusing node lookups.

♻️ Example tweak
 void CopyIODataNodes::CreateTowerInfo(PHCompositeNode *from_topNode, PHCompositeNode *to_topNode)
 {
-  std::cout << "copying tower info" << std::endl;
+  if (Verbosity() > 0) { std::cout << "copying tower info" << std::endl; }
+  if (from_towerInfo_name.empty() || to_towerInfo_name.empty())
+  {
+    std::cout << "TowerInfo names not configured" << std::endl;
+    m_CopyTowerInfoFlag = false;
+    return;
+  }
offline/packages/CaloEmbedding/CopyIODataNodes.h (1)

38-79: Validate TowerInfo names before enabling copying.

If either name is empty, findNode::getClass will search for an empty node name. Consider guarding and keeping the flag off unless both names are set.

♻️ Example guard
 void set_CopyTowerInfo(const std::string& set_from_towerInfo_name,const std::string& set_to_towerInfo_name)
 {
-  from_towerInfo_name = set_from_towerInfo_name;
-  to_towerInfo_name = set_to_towerInfo_name;
-  m_CopyTowerInfoFlag = true; 
+  from_towerInfo_name = set_from_towerInfo_name;
+  to_towerInfo_name = set_to_towerInfo_name;
+  m_CopyTowerInfoFlag = !(from_towerInfo_name.empty() || to_towerInfo_name.empty());
   return;
 }
offline/packages/tpc/LaserClusterizer.cc (1)

408-410: Confirm std::format is supported in all build targets.
std::format requires C++20 and a libstdc++/libc++ version with full format support; older toolchains will fail to compile. If any target is < C++20, consider fmt::format or ROOT Form.

offline/packages/jetbackground/DetermineTowerBackground.cc (1)

76-108: Consider returning error code instead of calling exit(-1).

At line 90, calling exit(-1) when calibdir is empty is abrupt. Consider returning an error code (e.g., Fun4AllReturnCodes::ABORTRUN) to allow the caller to handle the failure gracefully, consistent with how the caller already handles LoadCalibrations() failures at lines 65-69.

Suggested change
   if (calibdir.empty())
     {
       std::cout << "Could not find filename for calo average v2, exiting" << std::endl;
-      exit(-1);
+      return Fun4AllReturnCodes::ABORTRUN;
     }
offline/packages/jetbackground/DetermineTowerBackground.h (2)

16-16: Unnecessary include.

<array> is included but not used—the code uses std::vector for _CENTRALITY_V2. Consider removing this include.


41-45: Parameter should be const std::string&.

The url parameter is not modified, so it should be a const reference to allow passing temporaries and const strings:

-  void SetOverwriteCaloV2(std::string &url)
+  void SetOverwriteCaloV2(const std::string &url)
offline/QA/Tracking/StateClusterResidualsQA.cc (2)

32-33: Unused template function.

The square template is defined but never used in this file. Consider removing it unless it's intended for future use.


239-245: EndRun retrieves histogram manager but doesn't use it.

The EndRun method gets the histogram manager and asserts it exists, but doesn't actually do anything with it. If no action is needed, consider removing the unused code or adding a TODO comment if functionality is planned.

offline/QA/Tracking/StateClusterResidualsQA.h (1)

8-12: Consider grouping standard library headers consistently.

The standard headers are not in a consistent order. Consider grouping them alphabetically or by category (containers, utilities, etc.) for readability.

Suggested header ordering
 `#include` <string>
-#include <limits>
-#include <cmath>
-#include <cfloat>
+#include <cfloat>
+#include <cmath>
 `#include` <vector>

Note: <limits> appears unused and can be removed since FLT_MAX from <cfloat> is used instead of std::numeric_limits.

generators/Herwig/HepMCTrigger/HepMCParticleTrigger.h (1)

8-8: Unused include: fastjet/PseudoJet.hh.

This header includes fastjet/PseudoJet.hh but the class declaration doesn't use PseudoJet anywhere. Consider removing it to reduce compilation dependencies.

Suggested fix
 `#include` <fun4all/SubsysReco.h>
 
-#include <fastjet/PseudoJet.hh>
-
 `#include` <cmath>
generators/Herwig/HepMCTrigger/HepMCParticleTrigger.cc (1)

12-16: Remove unused FastJet includes.

The implementation includes fastjet/JetDefinition.hh and fastjet/PseudoJet.hh but doesn't use any FastJet functionality. These appear to be copied from the related HepMCJetTrigger implementation.

Suggested fix
 `#include` <phool/getClass.h>
 
-#include <fastjet/JetDefinition.hh>
-
 `#include` <HepMC/GenEvent.h>
 
-#include <fastjet/PseudoJet.hh>
 `#include` <map>

Comment on lines +36 to +49
/**
* @brief Construct a PHPythia8 generator instance and configure HepMC conversion.
*
* Initializes the Pythia8 engine using the path from the environment variable
* `PYTHIA8`, configures a HepMC::Pythia8ToHepMC converter to store process,
* PDF, and cross-section information, and sets the default embedding ID to 1.
* The constructor preserves and restores std::cout formatting around Pythia8
* construction to avoid altering global stream state.
*
* If `PYTHIA8` is not set, an error message is printed and the Pythia8 instance
* remains uninitialized.
*
* @param name Name forwarded to the SubsysReco base class (module instance name).
*/
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the PHPythia8.cc file around the constructor
cat -n generators/PHPythia8/PHPythia8.cc | head -80

Repository: sPHENIX-Test/coresoftware

Length of output: 3345


🏁 Script executed:

# Let's also get the header file to understand the member variable declaration
fd "PHPythia8.h" generators/

Repository: sPHENIX-Test/coresoftware

Length of output: 101


🏁 Script executed:

# Search for the Init, process_event, and End method implementations
rg "int PHPythia8::Init|int PHPythia8::process_event|int PHPythia8::End" -A 15 generators/PHPythia8/PHPythia8.cc

Repository: sPHENIX-Test/coresoftware

Length of output: 1485


🏁 Script executed:

# Search for m_Pythia8 usage to see if it's dereferenced without guards
rg "m_Pythia8->" generators/PHPythia8/PHPythia8.cc | head -20

Repository: sPHENIX-Test/coresoftware

Length of output: 916


Add guards to prevent null-pointer dereference in Init, process_event, and End.

The constructor leaves m_Pythia8 uninitialized when PYTHIA8 is unset and returns early without error handling. However, Init, process_event, and End immediately dereference m_Pythia8 without any null checks, causing a crash in production if the environment variable is missing.

Add guard checks in all three methods to return appropriate error codes (ABORTRUN for Init, ABORTEVENT for process_event, EVENT_OK for End) before dereferencing m_Pythia8:

🛡️ Guard pattern (apply in Init/process_event/End)
int PHPythia8::Init(PHCompositeNode *topNode)
{
+  if (!m_Pythia8)
+  {
+    std::cout << PHWHERE << " PYTHIA8 not configured; aborting run." << std::endl;
+    return Fun4AllReturnCodes::ABORTRUN;
+  }
  ...
}

int PHPythia8::process_event(PHCompositeNode * /*topNode*/)
{
+  if (!m_Pythia8)
+  {
+    std::cout << PHWHERE << " PYTHIA8 not configured; aborting event." << std::endl;
+    return Fun4AllReturnCodes::ABORTEVENT;
+  }
  ...
}

int PHPythia8::End(PHCompositeNode * /*topNode*/)
{
+  if (!m_Pythia8) return Fun4AllReturnCodes::EVENT_OK;
  ...
}
🤖 Prompt for AI Agents
In `@generators/PHPythia8/PHPythia8.cc` around lines 36 - 49, The constructor may
leave m_Pythia8 null when PYTHIA8 is unset, so add null guards at the start of
Init, process_event, and End to avoid dereferencing it: in Init check if
(!m_Pythia8) return ABORTRUN; in process_event check if (!m_Pythia8) return
ABORTEVENT; and in End check if (!m_Pythia8) return EVENT_OK; ensure these
checks occur before any use of m_Pythia8 (referencing the methods Init,
process_event, End and the member m_Pythia8) so the methods exit safely when the
Pythia8 instance was not created.

Comment on lines +977 to 1011
for (auto& [bco, hitinfo] : m_MvtxRawHitMap)
{
while (select_crossings <= m_MvtxRawHitMap.begin()->first && m_MvtxRawHitMap.begin()->first <= select_crossings + m_mvtx_bco_range) // triggered
if (bco < lower_limit)
{
if (Verbosity() > 2)
{
std::cout << "Adding 0x" << std::hex << m_MvtxRawHitMap.begin()->first
<< " ref: 0x" << select_crossings << std::dec << std::endl;
}
for (auto *mvtxFeeIdInfo : m_MvtxRawHitMap.begin()->second.MvtxFeeIdInfoVector)
{
if (Verbosity() > 1)
{
mvtxFeeIdInfo->identify();
}
mvtxEvtHeader->AddFeeIdInfo(mvtxFeeIdInfo);
delete mvtxFeeIdInfo;
}
m_MvtxRawHitMap.begin()->second.MvtxFeeIdInfoVector.clear();
mvtxEvtHeader->AddL1Trg(m_MvtxRawHitMap.begin()->second.MvtxL1TrgBco);
continue;
}
if (bco > upper_limit)
{
break;
}

for (auto *mvtxhititer : m_MvtxRawHitMap.begin()->second.MvtxRawHitVector)
{
if (Verbosity() > 1)
{
mvtxhititer->identify();
}
mvtxcont->AddHit(mvtxhititer);
}
for (auto *iter : m_MvtxInputVector)
{
iter->CleanupUsedPackets(m_MvtxRawHitMap.begin()->first);
}
m_MvtxRawHitMap.begin()->second.MvtxRawHitVector.clear();
m_MvtxRawHitMap.begin()->second.MvtxL1TrgBco.clear();
m_MvtxRawHitMap.erase(m_MvtxRawHitMap.begin());
// m_MvtxRawHitMap.empty() need to be checked here since we do not call FillPoolMvtx()
if (m_MvtxRawHitMap.empty())
{
break;
}
if (Verbosity() > 2)
{
std::cout << "Adding 0x" << std::hex << bco
<< " ref: 0x" << select_crossings << std::dec << std::endl;
}
}
else
{
while (select_crossings - m_mvtx_bco_range - m_mvtx_negative_bco <= m_MvtxRawHitMap.begin()->first && m_MvtxRawHitMap.begin()->first <= select_crossings) // streamed
for (auto *mvtxFeeIdInfo : hitinfo.MvtxFeeIdInfoVector)
{
if (Verbosity() > 2)
{
std::cout << "Adding 0x" << std::hex << m_MvtxRawHitMap.begin()->first
<< " ref: 0x" << select_crossings << std::dec << std::endl;
}
for (auto *mvtxFeeIdInfo : m_MvtxRawHitMap.begin()->second.MvtxFeeIdInfoVector)
if (Verbosity() > 1)
{
if (Verbosity() > 1)
{
mvtxFeeIdInfo->identify();
}
mvtxEvtHeader->AddFeeIdInfo(mvtxFeeIdInfo);
delete mvtxFeeIdInfo;
mvtxFeeIdInfo->identify();
}
m_MvtxRawHitMap.begin()->second.MvtxFeeIdInfoVector.clear();
mvtxEvtHeader->AddL1Trg(m_MvtxRawHitMap.begin()->second.MvtxL1TrgBco);
mvtxEvtHeader->AddFeeIdInfo(mvtxFeeIdInfo);
}
mvtxEvtHeader->AddL1Trg(hitinfo.MvtxL1TrgBco);

for (auto *mvtxhititer : m_MvtxRawHitMap.begin()->second.MvtxRawHitVector)
{
if (Verbosity() > 1)
{
mvtxhititer->identify();
}
mvtxcont->AddHit(mvtxhititer);
}
for (auto *iter : m_MvtxInputVector)
{
iter->CleanupUsedPackets(m_MvtxRawHitMap.begin()->first);
}
m_MvtxRawHitMap.begin()->second.MvtxRawHitVector.clear();
m_MvtxRawHitMap.begin()->second.MvtxL1TrgBco.clear();
m_MvtxRawHitMap.erase(m_MvtxRawHitMap.begin());
// m_MvtxRawHitMap.empty() need to be checked here since we do not call FillPoolMvtx()
if (m_MvtxRawHitMap.empty())
for (auto *mvtxhititer : hitinfo.MvtxRawHitVector)
{
if (Verbosity() > 1)
{
break;
mvtxhititer->identify();
}
mvtxcont->AddHit(mvtxhititer);
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Missing cleanup logic after processing MVTX hits.

Same issue as FillIntt. The loop processes entries but never:

  1. Calls CleanupUsedPackets() on input vectors
  2. Calls clearFeeGTML1BCOMap() on input vectors
  3. Deletes MvtxFeeIdInfo objects (ownership transferred to mvtxEvtHeader?)
  4. Clears MvtxFeeIdInfoVector, MvtxL1TrgBco, MvtxRawHitVector
  5. Erases processed entries from m_MvtxRawHitMap

Compare with the cleanup loop at lines 856-873 which properly handles all cleanup steps.

Proposed fix to add cleanup after hit processing
    for (auto *mvtxhititer : hitinfo.MvtxRawHitVector)
    {
      if (Verbosity() > 1)
      {
        mvtxhititer->identify();
      }
      mvtxcont->AddHit(mvtxhititer);
    }
  }

+  // Cleanup processed entries
+  auto it = m_MvtxRawHitMap.begin();
+  while (it != m_MvtxRawHitMap.end() && it->first <= upper_limit)
+  {
+    if (it->first < lower_limit)
+    {
+      ++it;
+      continue;
+    }
+    for (auto *iter : m_MvtxInputVector)
+    {
+      iter->CleanupUsedPackets(it->first);
+      iter->clearFeeGTML1BCOMap(it->first);
+    }
+    it->second.MvtxFeeIdInfoVector.clear();
+    it->second.MvtxL1TrgBco.clear();
+    it->second.MvtxRawHitVector.clear();
+    it = m_MvtxRawHitMap.erase(it);
+  }

  return 0;
📝 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
for (auto& [bco, hitinfo] : m_MvtxRawHitMap)
{
while (select_crossings <= m_MvtxRawHitMap.begin()->first && m_MvtxRawHitMap.begin()->first <= select_crossings + m_mvtx_bco_range) // triggered
if (bco < lower_limit)
{
if (Verbosity() > 2)
{
std::cout << "Adding 0x" << std::hex << m_MvtxRawHitMap.begin()->first
<< " ref: 0x" << select_crossings << std::dec << std::endl;
}
for (auto *mvtxFeeIdInfo : m_MvtxRawHitMap.begin()->second.MvtxFeeIdInfoVector)
{
if (Verbosity() > 1)
{
mvtxFeeIdInfo->identify();
}
mvtxEvtHeader->AddFeeIdInfo(mvtxFeeIdInfo);
delete mvtxFeeIdInfo;
}
m_MvtxRawHitMap.begin()->second.MvtxFeeIdInfoVector.clear();
mvtxEvtHeader->AddL1Trg(m_MvtxRawHitMap.begin()->second.MvtxL1TrgBco);
continue;
}
if (bco > upper_limit)
{
break;
}
for (auto *mvtxhititer : m_MvtxRawHitMap.begin()->second.MvtxRawHitVector)
{
if (Verbosity() > 1)
{
mvtxhititer->identify();
}
mvtxcont->AddHit(mvtxhititer);
}
for (auto *iter : m_MvtxInputVector)
{
iter->CleanupUsedPackets(m_MvtxRawHitMap.begin()->first);
}
m_MvtxRawHitMap.begin()->second.MvtxRawHitVector.clear();
m_MvtxRawHitMap.begin()->second.MvtxL1TrgBco.clear();
m_MvtxRawHitMap.erase(m_MvtxRawHitMap.begin());
// m_MvtxRawHitMap.empty() need to be checked here since we do not call FillPoolMvtx()
if (m_MvtxRawHitMap.empty())
{
break;
}
if (Verbosity() > 2)
{
std::cout << "Adding 0x" << std::hex << bco
<< " ref: 0x" << select_crossings << std::dec << std::endl;
}
}
else
{
while (select_crossings - m_mvtx_bco_range - m_mvtx_negative_bco <= m_MvtxRawHitMap.begin()->first && m_MvtxRawHitMap.begin()->first <= select_crossings) // streamed
for (auto *mvtxFeeIdInfo : hitinfo.MvtxFeeIdInfoVector)
{
if (Verbosity() > 2)
{
std::cout << "Adding 0x" << std::hex << m_MvtxRawHitMap.begin()->first
<< " ref: 0x" << select_crossings << std::dec << std::endl;
}
for (auto *mvtxFeeIdInfo : m_MvtxRawHitMap.begin()->second.MvtxFeeIdInfoVector)
if (Verbosity() > 1)
{
if (Verbosity() > 1)
{
mvtxFeeIdInfo->identify();
}
mvtxEvtHeader->AddFeeIdInfo(mvtxFeeIdInfo);
delete mvtxFeeIdInfo;
mvtxFeeIdInfo->identify();
}
m_MvtxRawHitMap.begin()->second.MvtxFeeIdInfoVector.clear();
mvtxEvtHeader->AddL1Trg(m_MvtxRawHitMap.begin()->second.MvtxL1TrgBco);
mvtxEvtHeader->AddFeeIdInfo(mvtxFeeIdInfo);
}
mvtxEvtHeader->AddL1Trg(hitinfo.MvtxL1TrgBco);
for (auto *mvtxhititer : m_MvtxRawHitMap.begin()->second.MvtxRawHitVector)
{
if (Verbosity() > 1)
{
mvtxhititer->identify();
}
mvtxcont->AddHit(mvtxhititer);
}
for (auto *iter : m_MvtxInputVector)
{
iter->CleanupUsedPackets(m_MvtxRawHitMap.begin()->first);
}
m_MvtxRawHitMap.begin()->second.MvtxRawHitVector.clear();
m_MvtxRawHitMap.begin()->second.MvtxL1TrgBco.clear();
m_MvtxRawHitMap.erase(m_MvtxRawHitMap.begin());
// m_MvtxRawHitMap.empty() need to be checked here since we do not call FillPoolMvtx()
if (m_MvtxRawHitMap.empty())
for (auto *mvtxhititer : hitinfo.MvtxRawHitVector)
{
if (Verbosity() > 1)
{
break;
mvtxhititer->identify();
}
mvtxcont->AddHit(mvtxhititer);
}
}
for (auto& [bco, hitinfo] : m_MvtxRawHitMap)
{
if (bco < lower_limit)
{
continue;
}
if (bco > upper_limit)
{
break;
}
if (Verbosity() > 2)
{
std::cout << "Adding 0x" << std::hex << bco
<< " ref: 0x" << select_crossings << std::dec << std::endl;
}
for (auto *mvtxFeeIdInfo : hitinfo.MvtxFeeIdInfoVector)
{
if (Verbosity() > 1)
{
mvtxFeeIdInfo->identify();
}
mvtxEvtHeader->AddFeeIdInfo(mvtxFeeIdInfo);
}
mvtxEvtHeader->AddL1Trg(hitinfo.MvtxL1TrgBco);
for (auto *mvtxhititer : hitinfo.MvtxRawHitVector)
{
if (Verbosity() > 1)
{
mvtxhititer->identify();
}
mvtxcont->AddHit(mvtxhititer);
}
}
// Cleanup processed entries
auto it = m_MvtxRawHitMap.begin();
while (it != m_MvtxRawHitMap.end() && it->first <= upper_limit)
{
if (it->first < lower_limit)
{
+it;
continue;
}
for (auto *iter : m_MvtxInputVector)
{
iter->CleanupUsedPackets(it->first);
iter->clearFeeGTML1BCOMap(it->first);
}
it->second.MvtxFeeIdInfoVector.clear();
it->second.MvtxL1TrgBco.clear();
it->second.MvtxRawHitVector.clear();
it = m_MvtxRawHitMap.erase(it);
}
return 0;
🤖 Prompt for AI Agents
In `@offline/framework/fun4allraw/Fun4AllStreamingInputManager.cc` around lines
977 - 1011, The loop iterating m_MvtxRawHitMap lacks the cleanup performed in
the FillIntt path; after finishing with each hitinfo you must mirror that
cleanup: call hitinfo.CleanupUsedPackets() and hitinfo.clearFeeGTML1BCOMap(),
delete any heap-allocated MvtxFeeIdInfo objects if ownership was not transferred
to mvtxEvtHeader (or skip deletion if mvtxEvtHeader took ownership), clear
hitinfo.MvtxFeeIdInfoVector and hitinfo.MvtxRawHitVector, and finally erase the
processed entry from m_MvtxRawHitMap so memory and state are not leaked (perform
these steps in the same scope as the existing mvtxEvtHeader/mvtxcont handling in
Fun4AllStreamingInputManager::... loop).

Comment on lines 61 to +71
* @brief Generate a hitkey from strip index inside tile
* @param[in] strip strip index
* @param[in] sample sample index
*/
TrkrDefs::hitkey genHitKey(uint16_t strip );
TrkrDefs::hitkey genHitKey(uint16_t strip, uint16_t sample = 0 );

//! get strip from hit key
uint16_t getStrip(TrkrDefs::hitkey);
uint8_t getStrip(TrkrDefs::hitkey);

//! get sample from hit key
uint16_t getSample(TrkrDefs::hitkey);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, locate and examine the MicromegasDefs.h file
find . -name "MicromegasDefs.h" -type f

Repository: sPHENIX-Test/coresoftware

Length of output: 115


🏁 Script executed:

# Get the full context of the file around lines 61-71
cat -n offline/packages/micromegas/MicromegasDefs.h | head -100

Repository: sPHENIX-Test/coresoftware

Length of output: 3516


🏁 Script executed:

# Search for genHitKey implementation and strip-related constants
rg -n "genHitKey|MMPADSPERFIBER|MMSTRIPCOUNT|STRIPS_PER" offline/packages/micromegas/

Repository: sPHENIX-Test/coresoftware

Length of output: 445


🏁 Script executed:

# Search for any documentation on strip count/range
rg -n "strip.*count|num.*strip|MAX.*STRIP" offline/packages/micromegas/ -i

Repository: sPHENIX-Test/coresoftware

Length of output: 2148


🏁 Script executed:

# View the implementation of genHitKey and getStrip in MicromegasDefs.cc
sed -n '69,150p' offline/packages/micromegas/MicromegasDefs.cc

Repository: sPHENIX-Test/coresoftware

Length of output: 1177


🏁 Script executed:

# Also check for any constants related to strip range
rg -n "constexpr|static.*const" offline/packages/micromegas/MicromegasDefs.h | head -30

Repository: sPHENIX-Test/coresoftware

Length of output: 604


🏁 Script executed:

# Check the actual strip counts used in geometry
sed -n '235,250p' offline/packages/micromegas/CylinderGeomMicromegas.cc

Repository: sPHENIX-Test/coresoftware

Length of output: 935


🏁 Script executed:

# Find the pitch value
rg -n "m_pitch|pitch" offline/packages/micromegas/CylinderGeomMicromegas.h | head -20

Repository: sPHENIX-Test/coresoftware

Length of output: 235


🏁 Script executed:

# Look for pitch definition
rg -n "m_pitch.*=" offline/packages/micromegas/CylinderGeomMicromegas.cc | head -10

Repository: sPHENIX-Test/coresoftware

Length of output: 51


🏁 Script executed:

# Check for any test data or documentation showing actual strip counts
rg -n "strip.*count|get_strip_count" offline/packages/micromegas/ -A 5 | head -40

Repository: sPHENIX-Test/coresoftware

Length of output: 4446


🏁 Script executed:

# Search for tile geometry setup and dimensions
rg -n "halfLength|RectangleBounds|set_tile|m_tiles" offline/packages/micromegas/CylinderGeomMicromegas.cc | head -30

Repository: sPHENIX-Test/coresoftware

Length of output: 1719


🏁 Script executed:

# Look for any initialization with dimensions or constants
rg -n "10|20|30|40|50.*cm|mm.*pitch|set_pitch" offline/packages/micromegas/ | grep -v ".git" | head -20

Repository: sPHENIX-Test/coresoftware

Length of output: 2949


🏁 Script executed:

# Check for test or initialization files
fd -e cc -e h offline/packages/micromegas/ | xargs rg -l "CylinderGeomMicromegas" | head -10

Repository: sPHENIX-Test/coresoftware

Length of output: 1034


🏁 Script executed:

# Check the Geant4 detector definition for micromegas
sed -n '1,100p' simulation/g4simulation/g4micromegas/PHG4MicromegasDetector.cc

Repository: sPHENIX-Test/coresoftware

Length of output: 2898


🏁 Script executed:

# Search for geometry or detector size specifications
rg -n "halfLength|width|height|size" simulation/g4simulation/g4micromegas/PHG4MicromegasDetector.cc | head -30

Repository: sPHENIX-Test/coresoftware

Length of output: 621


🏁 Script executed:

# Look for GDML files or geometry definitions
fd --full-path '.*micromegas.*' offline/packages/micromegas/ --type f | xargs basename -a | sort | uniq

Repository: sPHENIX-Test/coresoftware

Length of output: 722


🏁 Script executed:

# Get the tile_length definition and surrounding context
sed -n '130,170p' simulation/g4simulation/g4micromegas/PHG4MicromegasDetector.cc

Repository: sPHENIX-Test/coresoftware

Length of output: 1547


🏁 Script executed:

# Also verify the strip calculation with actual values
python3 << 'EOF'
# Tile dimensions from the code
tile_width = 31.6  # cm
pitch = 0.1  # cm (1 mm)

# Calculate strip count for tile width
strip_count_width = int(tile_width / pitch)
print(f"For tile_width = {tile_width} cm:")
print(f"  Strip count = floor((2 * {tile_width/2}) / {pitch}) = {strip_count_width}")
print(f"  Max uint8_t = 255")
print(f"  Truncation risk: {strip_count_width > 255}")
EOF

Repository: sPHENIX-Test/coresoftware

Length of output: 155


Fix bit-packing scheme: 8-bit strip encoding truncates detectors with 600+ strips.

The micromegas detectors have tile_width = 31.6 cm and tile_length = 54.2 cm with pitch = 0.1 cm, yielding strip counts of ~632 and ~1084 depending on segmentation direction. However, genHitKey only encodes 8 bits for strip ((strip & 0xFFU)), and getStrip returns uint8_t (max 255). Any strip index ≥ 256 silently truncates, causing hit mis-association. The current encoding scheme is incompatible with the detector geometry. Either expand the bit allocation for strip in the hitkey format, or validate and enforce a 256-strip maximum.

🤖 Prompt for AI Agents
In `@offline/packages/micromegas/MicromegasDefs.h` around lines 61 - 71, genHitKey
currently packs strip into 8 bits and getStrip returns uint8_t, which truncates
actual strip indices >255; change the hitkey bit-packing to allocate enough bits
for strip (use at least 10–11 bits to cover ~1084 strips or simply 16 bits for
safety), update genHitKey to mask/shift strip with the new width and getStrip to
return a matching wider unsigned type (e.g., uint16_t), and adjust getSample/its
masking/return type as needed so TrkrDefs::hitkey encodes both fields without
overflow or silent truncation; alternatively, if you want to keep 8-bit
encoding, add a validation in genHitKey to throw/log and refuse strip>=256, but
prefer expanding the strip field to avoid mis-association.

#include <boost/format.hpp>

#include <iostream>
#include <format>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find and check build configuration files
echo "=== Finding CMakeLists.txt files ==="
find . -name "CMakeLists.txt" -type f | head -20

echo ""
echo "=== Checking CMakeLists.txt content for C++ standard ==="
find . -name "CMakeLists.txt" -type f -exec grep -n -E 'CMAKE_CXX_STANDARD|CXX_STANDARD|set\(.*_CXX' {} + | head -50

echo ""
echo "=== Looking for Makefiles ==="
find . -name "Makefile*" -o -name "*.mk" | head -20

echo ""
echo "=== Checking Makefiles for C++ standard settings ==="
find . \( -name "Makefile*" -o -name "*.mk" \) -type f -exec grep -n -E 'std=c\+\+|CXXFLAGS|CPPFLAGS' {} + 2>/dev/null | head -50

Repository: sPHENIX-Test/coresoftware

Length of output: 4568


🏁 Script executed:

#!/bin/bash
# Look for configure.ac or configure files
echo "=== Finding configure.ac files ==="
find . -name "configure.ac" -o -name "configure.in" | head -10

echo ""
echo "=== Checking for C++ standard in configure.ac files ==="
find . -name "configure.ac" -o -name "configure.in" | xargs grep -n -E 'std=c\+\+|AC_PROG_CXX|AX_CXX_COMPILE_STDCXX' 2>/dev/null | head -50

echo ""
echo "=== Checking mvtx specific Makefile.am ==="
find . -path "*/mvtx/*" -name "Makefile.am" | xargs cat

echo ""
echo "=== Looking for any -std=c++ in all Makefiles ==="
find . -name "Makefile.am" -o -name "Makefile" | xargs grep -h "std=c++" 2>/dev/null | sort -u

Repository: sPHENIX-Test/coresoftware

Length of output: 7348


🏁 Script executed:

#!/bin/bash
# Look for root configure.ac
echo "=== Checking root and parent directories ==="
ls -la configure.ac 2>/dev/null || echo "No root configure.ac"
find . -maxdepth 2 -name "configure.ac" -o -name "configure.in" | head -5

echo ""
echo "=== Looking for top-level build configuration ==="
ls -la | grep -E "configure|Makefile|CMake"

echo ""
echo "=== Checking for any global C++ standard flags ==="
find . -name "Makefile.am" | xargs grep -h "AM_CXXFLAGS\|CXXFLAGS" | sort -u | grep -i "std"

echo ""
echo "=== Looking at the SegmentationAlpide.cc file location ==="
find . -name "SegmentationAlpide.cc" -exec ls -la {} \;

echo ""
echo "=== Checking if the file includes format ==="
grep -n "include.*format" offline/packages/mvtx/SegmentationAlpide.cc

Repository: sPHENIX-Test/coresoftware

Length of output: 514


Build system must be updated to support C++20 for <format> usage.

The code uses #include <format> (C++20), but the build system does not specify a C++ standard. Update offline/packages/mvtx/Makefile.am to add AM_CXXFLAGS = -std=c++20 (or ensure a higher standard is set project-wide in the configure.ac). Without this, compilation will fail.

🤖 Prompt for AI Agents
In `@offline/packages/mvtx/SegmentationAlpide.cc` at line 9, The build fails
because SegmentationAlpide.cc uses C++20 <format>; update the build
configuration to enable C++20: either add AM_CXXFLAGS = -std=c++20 (or append
-std=c++20 to existing AM_CXXFLAGS) in offline/packages/mvtx/Makefile.am or set
the project-wide standard in configure.ac (e.g., via AC_PROG_CXX and appropriate
checks or AC_ARG_ENABLE for CXXFLAGS) so files like SegmentationAlpide.cc that
include <format> compile with C++20 enabled.

Comment on lines +660 to +681
float fX[20] = {0};
int n = 0;
fX[n++] = m_event;
fX[n++] = m_seed;
fX[n++] = clusX;
fX[n++] = clusY;
fX[n++] = clusZ;
fX[n++] = clusR;
fX[n++] = clusPhi;
fX[n++] = clusiPhi;
fX[n++] = clusT;
fX[n++] = adcSum;
fX[n++] = maxAdc;
fX[n++] = (layerSum / adcSum);
fX[n++] = maxsector;
fX[n++] = maxside;
fX[n++] = nHits;
fX[n++] = phisize;
fX[n++] = tsize;
fX[n++] = lsize;
m_clusterNT->Fill(fX);
// }
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Guard NTuple fill when m_output is false.

m_clusterNT is only created when m_output is true. Unconditionally calling m_clusterNT->Fill will crash when output is disabled.

🛠️ Proposed fix
-  float fX[20] = {0};
-  int n = 0;
-  fX[n++] = m_event;
-  fX[n++] = m_seed;
-  fX[n++] = clusX;
-  fX[n++] = clusY;
-  fX[n++] = clusZ;
-  fX[n++] = clusR;
-  fX[n++] = clusPhi;
-  fX[n++] = clusiPhi;
-  fX[n++] = clusT;
-  fX[n++] = adcSum;
-  fX[n++] = maxAdc;
-  fX[n++] = (layerSum / adcSum);
-  fX[n++] = maxsector;
-  fX[n++] = maxside;
-  fX[n++] = nHits;
-  fX[n++] = phisize;
-  fX[n++] = tsize;
-  fX[n++] = lsize;
-  m_clusterNT->Fill(fX);
+  if (m_output && m_clusterNT)
+  {
+    float fX[20] = {0};
+    int n = 0;
+    fX[n++] = m_event;
+    fX[n++] = m_seed;
+    fX[n++] = clusX;
+    fX[n++] = clusY;
+    fX[n++] = clusZ;
+    fX[n++] = clusR;
+    fX[n++] = clusPhi;
+    fX[n++] = clusiPhi;
+    fX[n++] = clusT;
+    fX[n++] = adcSum;
+    fX[n++] = maxAdc;
+    fX[n++] = (layerSum / adcSum);
+    fX[n++] = maxsector;
+    fX[n++] = maxside;
+    fX[n++] = nHits;
+    fX[n++] = phisize;
+    fX[n++] = tsize;
+    fX[n++] = lsize;
+    m_clusterNT->Fill(fX);
+  }
📝 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
float fX[20] = {0};
int n = 0;
fX[n++] = m_event;
fX[n++] = m_seed;
fX[n++] = clusX;
fX[n++] = clusY;
fX[n++] = clusZ;
fX[n++] = clusR;
fX[n++] = clusPhi;
fX[n++] = clusiPhi;
fX[n++] = clusT;
fX[n++] = adcSum;
fX[n++] = maxAdc;
fX[n++] = (layerSum / adcSum);
fX[n++] = maxsector;
fX[n++] = maxside;
fX[n++] = nHits;
fX[n++] = phisize;
fX[n++] = tsize;
fX[n++] = lsize;
m_clusterNT->Fill(fX);
// }
if (m_output && m_clusterNT)
{
float fX[20] = {0};
int n = 0;
fX[n++] = m_event;
fX[n++] = m_seed;
fX[n++] = clusX;
fX[n++] = clusY;
fX[n++] = clusZ;
fX[n++] = clusR;
fX[n++] = clusPhi;
fX[n++] = clusiPhi;
fX[n++] = clusT;
fX[n++] = adcSum;
fX[n++] = maxAdc;
fX[n++] = (layerSum / adcSum);
fX[n++] = maxsector;
fX[n++] = maxside;
fX[n++] = nHits;
fX[n++] = phisize;
fX[n++] = tsize;
fX[n++] = lsize;
m_clusterNT->Fill(fX);
}
🤖 Prompt for AI Agents
In `@offline/packages/tpc/Tpc3DClusterizer.cc` around lines 660 - 681, Guard the
NTuple fill so it only runs when output is enabled and the NTuple exists: wrap
the m_clusterNT->Fill(fX) call in a conditional check (e.g., if (m_output &&
m_clusterNT) or if (m_clusterNT) depending on class invariants) in
Tpc3DClusterizer so that m_clusterNT->Fill is not invoked when m_output is false
and m_clusterNT was not created; update the surrounding block accordingly to
avoid null dereference.

@blackcathj blackcathj closed this Jan 19, 2026
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.