Skip to content

Optimized running ped length, fixed all waveform contamination from prior crossings.#4178

Merged
pinkenburg merged 2 commits intosPHENIX-Collaboration:masterfrom
mchiu-bnl:mbd
Feb 15, 2026
Merged

Optimized running ped length, fixed all waveform contamination from prior crossings.#4178
pinkenburg merged 2 commits intosPHENIX-Collaboration:masterfrom
mchiu-bnl:mbd

Conversation

@mchiu-bnl
Copy link
Contributor

@mchiu-bnl mchiu-bnl commented Feb 15, 2026

comment: changed running ped to 8 samples, added ped study, handle contamination to waveform due to events from prior crossings better

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, ...)

comment: Did ped study, found that it's optimal to use event-by-event pedestals, and where that is impossible, such as when there is an event from prior crossings contaminating the pedestal, use a short running mean (set to 8 samples, whereas previously it was 100 samples). Also when doing the study I noticed a few cases where the contamination from prior crossings needed to be handled better. Should be all good now.

TODOs (if applicable)

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

Summary: Optimized MBD Pedestal Calculation and Improved Pileup Contamination Handling

Motivation / context

Reduce pedestal bias and mitigate waveform contamination from prior bunch crossings (pileup) to improve MBD charge and time reconstruction fidelity, informed by a dedicated pedestal study.

Key changes

  • Pedestal handling
    • Reduced running pedestal window from 100 → 8 samples (MbdRunningStats(8)) based on a pedestal study.
    • Prefer event-by-event pedestal estimates when available; fall back to the short (8-sample) running mean when event-by-event pedestals are contaminated.
    • Added event-level pedestal study instrumentation: per-channel TGraphErrors gPedvsEvent, WritePedvsEvent(), and associated write/cleanup paths.
  • Pileup / waveform contamination
    • Major overhaul of Remove_Pileup() with multiple contamination-case handling and channel-type-specific logic:
      • Time channels: compute sample-of-max, choose template- or fit-based background paths, subtract per-sample offsets at peak region.
      • Charge channels: pre-pulse/template fitting and saturation-aware subtraction paths.
    • New flag-gated verbose diagnostics and additional fit-function management for pileup removal.
    • Extended pileup parameter support in MbdCalib: new get_pileup()/set_pileup() accommodating up to 5 parameters (ipar 0–4) and channel-type-specific error access.
  • Housekeeping and outputs
    • Added WriteChi2Hist() writes at End() for each signal.
    • Removed several noisy debug prints.
    • ped0stats ownership changed from std::unique_ptr to raw pointer (manual delete in destructor).
    • Bounds-check fix to avoid sample-number underflow when selecting samples for pedestal/running-mean calculations.

Potential risk areas

  • Reconstruction behavior change: shortening pedestal window to 8 samples will alter pedestal estimates (notably for early events) and may affect reconstructed charge/time; results depend on availability of event-by-event pedestals or pre-calibrations.
  • Pileup subtraction correctness: the new multi-path template/fit logic is complex; incorrect branch conditions or fit failures could bias charge/time for contaminated events.
  • I/O and disk usage: enabling the pedestal-vs-event study (_pedstudyflag) and added chi2 histograms increases output volume.
  • Memory/ownership: ped0stats converted to a raw pointer—ensure destructor always deletes it to avoid leaks or double-frees.
  • Thread-safety: added per-signal graphs and fit objects and changed ownership semantics—verify concurrent access patterns in multi-threaded reconstruction are safe.
  • Calibration interface: changes to MbdCalib (new params and setters) alter how pileup corrections are configured; ensure calibration tooling and consumers are updated accordingly.

Possible future improvements

  • Make running-window length configurable (runtime or calibration file) and validate across datasets.
  • Add unit/integration tests and validation plots for each pileup subtraction pathway and for early-run pedestal behavior.
  • Restore RAII ownership (std::unique_ptr) or use safer smart pointers to avoid manual memory-management regressions.
  • Document pileup parameter semantics (p0–p4) and channel-type usage in calibration docs.
  • Provide automated monitoring of output size when pedestal-study mode is enabled.

Note on AI-generated content

This summary was assembled from the provided diffs and AI-generated summaries; AI can make mistakes or miss subtle control-flow details. Please review the code paths (especially Remove_Pileup(), memory ownership changes, and calibration parameter semantics) directly during code review.

…on to waveform due to events from prior crossings better
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 15, 2026

📝 Walkthrough

Walkthrough

Adds parameterized pileup accessors to MbdCalib, introduces per-event pedestal tracking and output in MbdSig, significantly refactors pileup-removal logic to separate time/charge handling, adjusts pedestal-running stats ownership and window, and updates end-of-run outputs and some debug logging.

Changes

Cohort / File(s) Summary
Pileup calibration accessors
offline/packages/mbd/MbdCalib.h, offline/packages/mbd/MbdCalib.cc
Added get_pileup(ifeech, ipar) (supports ipar 0–4) and set_pileup(ifeech, ipar, val). Fixed Reset_Pileup to initialize _pileup_chi2ndf instead of _qfit_chi2ndf.
Pedestal-event study & ownership change
offline/packages/mbd/MbdSig.h, offline/packages/mbd/MbdSig.cc
Added gPedvsEvent (TGraphErrors*), _pedstudyflag, and WritePedvsEvent() to record/write pedestal vs event. Changed ped0stats from std::unique_ptr<MbdRunningStats> to raw MbdRunningStats*. Reduced ped0_stats window from 100→8.
Pileup removal refactor
offline/packages/mbd/MbdSig.cc
Overhauled Remove_Pileup() to branch time vs charge channels, introduce dynamic per-channel template/fit handling, per-sample background subtraction, saturation handling, and more verbose/conditional logging.
Event output & logging
offline/packages/mbd/MbdEvent.cc
Now calls WriteChi2Hist() (and existing ped-vs-event outputs) during End(); removed a couple of debug prints for empty histograms.

Sequence Diagram(s)

sequenceDiagram
    participant EventLoop as EventLoop
    participant MbdSig as MbdSig
    participant MbdCalib as MbdCalib
    participant TemplateFit as TemplateFitter
    participant Writer as OutputWriter

    EventLoop->>MbdSig: Process event samples
    MbdSig->>MbdCalib: query pileup params (get_pileup)
    alt time channel
        MbdSig->>TemplateFit: perform template fit / compute correction
        TemplateFit-->>MbdSig: template/background model
        MbdSig->>MbdSig: subtract per-sample background
    else charge channel
        MbdSig->>TemplateFit: perform charge-specific fit/subtraction
        TemplateFit-->>MbdSig: correction
        MbdSig->>MbdSig: apply saturation checks & subtract
    end
    MbdSig->>MbdSig: update running pedestals (ped0stats)
    alt pedestal study enabled
        MbdSig->>MbdSig: append point to gPedvsEvent
    end
    EventLoop->>Writer: End of run
    Writer->>MbdSig: WritePedvsEvent(), WriteChi2Hist()
    Writer-->>EventLoop: files written
Loading

Possibly related PRs

✨ 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
Contributor

@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: 2

Caution

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

⚠️ Outside diff range comments (1)
offline/packages/mbd/MbdSig.cc (1)

309-312: ⚠️ Potential issue | 🟠 Major

Missing _pileup_p0 validity check in SetXY pileup path.

SetY() at line 235 guards Remove_Pileup() with ispileup==1 && !std::isnan(_pileup_p0), but SetXY() at line 309 only checks ispileup==1. If pileup calibration parameters are not loaded (NaN), Remove_Pileup() will proceed with invalid coefficients, producing garbage corrections on the waveform.

Align with SetY guard
-    if ( ispileup==1 )
+    if ( ispileup==1 && !std::isnan(_pileup_p0) )
     {
       Remove_Pileup();
     }

Comment on lines +390 to +460
if ( x_at_max != 0 )
{
gSubPulse->Fit( fit_pileup, "R" );
gSubPulse->Draw("ap");
PadUpdate();
// Fit a pulse in prev crossing
template_fcn->SetParameters(ymax, x_at_max);
template_fcn->SetRange(0, x_at_max+2.1);

if (_verbose == 0)
{
//std::cout << PHWHERE << std::endl;
gSubPulse->Fit(template_fcn, "RNQ");
}
else
{
std::cout << "pre-pileup " << _ch << "\t" << x_at_max << "\t" << ymax << std::endl;
gSubPulse->Fit(template_fcn, "R");
gSubPulse->Draw("ap");
gSubPulse->GetHistogram()->SetTitle(gSubPulse->GetName());
gPad->SetGridy(1);
PadUpdate();
//gSubPulse->Print("ALL");
}

}
else
{
gSubPulse->Fit( fit_pileup, "RNQ" );
// Fit the tail
if ( fit_pileup == nullptr )
{
TString name = "fit_pileup"; name += _ch;
fit_pileup = new TF1(name,"gaus",-0.1,4.1);
fit_pileup->SetLineColor(2);
}

fit_pileup->SetRange(-0.1,4.1);
fit_pileup->SetParameters( _pileup_p0*gSubPulse->GetPointY(0), _pileup_p1, _pileup_p2 );

// fix par limits
double plow{0.};
double phigh{0.};
fit_pileup->GetParLimits(2,plow,phigh);
if ( phigh < _pileup_p2 )
{
phigh = 2*_pileup_p2;
fit_pileup->SetParLimits(2,plow,phigh);
}

if ( _verbose )
{
gSubPulse->Fit( fit_pileup, "R" );
gSubPulse->Draw("ap");
PadUpdate();
}
else
{
gSubPulse->Fit( fit_pileup, "RNQ" );
}
}

// subtract pre-pulse
for (int isamp = 0; isamp < _nsamples; isamp++)
{

double bkg = fit_pileup->Eval(isamp);
double bkg = 0.;
if ( x_at_max != 0 )
{
bkg = template_fcn->Eval(isamp);
}
else
{
bkg = fit_pileup->Eval(isamp);
}
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 15, 2026

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:

# Check template_fcn declaration and usage patterns in MbdSig
rg -n 'template_fcn' offline/packages/mbd/MbdSig.cc -B2 -A2

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 6869


🏁 Script executed:

# Check SetTemplate implementation to confirm it initializes template_fcn
rg -n 'SetTemplate|template_fcn.*new' offline/packages/mbd/MbdSig.cc -B2 -A5

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 1763


🏁 Script executed:

# Examine SetXY and SetY implementations for guards
rg -n 'void MbdSig::Set(XY|Y)\(' offline/packages/mbd/MbdSig.cc -A20 | head -80

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 1248


🏁 Script executed:

# Check MbdEvent.cc for SetTemplate() call patterns and guards
rg -n 'SetTemplate|do_templatefit' offline/packages/mbd/MbdEvent.cc -B2 -A2

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 1175


🏁 Script executed:

# Find Remove_Pileup() calls and surrounding context
rg -n 'Remove_Pileup' offline/packages/mbd/MbdSig.cc -B5 -A2

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 534


🏁 Script executed:

# Check SetY implementation around the mentioned line 235
sed -n '225,245p' offline/packages/mbd/MbdSig.cc

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 588


🏁 Script executed:

# Check SetXY implementation around the mentioned line 309
sed -n '300,320p' offline/packages/mbd/MbdSig.cc

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 658


🏁 Script executed:

# Search for sampmax-6 or y_min6 in Remove_Pileup
rg -n 'y_min6|sampmax.*-.*6' offline/packages/mbd/MbdSig.cc -B2 -A2

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 396


🏁 Script executed:

# Get full Remove_Pileup function to check all details
sed -n '325,470p' offline/packages/mbd/MbdSig.cc

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 3776


🏁 Script executed:

# Check what determines ispileup and when it's set
rg -n 'ispileup' offline/packages/mbd/MbdSig.cc -B2 -A2 | head -50

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 1132


🏁 Script executed:

# Check MbdEvent for ispileup logic
rg -n 'ispileup' offline/packages/mbd/MbdEvent.cc -B2 -A2

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 60


🏁 Script executed:

# Check what CalcEventPed0_PreSamp returns and when ispileup is set
rg -n 'CalcEventPed0_PreSamp|int.*ispileup' offline/packages/mbd/MbdSig.cc -A3 | head -40

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 1559


🏁 Script executed:

# Verify the exact line numbers of Remove_Pileup charge vs time channel logic
sed -n '343,350p' offline/packages/mbd/MbdSig.cc
sed -n '383,395p' offline/packages/mbd/MbdSig.cc

Repository: sPHENIX-Collaboration/coresoftware

Length of output: 578


Add null guard for template_fcn in charge-channel pileup path.

template_fcn is only initialized in SetTemplate(), which is called from MbdEvent.cc only when do_templatefit && type==1 (line 204). In the online path (do_templatefit=0), the pointer remains null. Unlike SetY() at line 235 which guards Remove_Pileup() with !std::isnan(_pileup_p0), SetXY() at line 309 calls Remove_Pileup() with only ispileup==1. This allows charge channels with pileup to reach the code at lines 393, 399, 404, and 455, where template_fcn is dereferenced without a null check. On online data where templates are not loaded, this will segfault.

Add a null guard before using template_fcn in the x_at_max != 0 branch:

if ( x_at_max != 0 )
{
  if ( template_fcn == nullptr )
  {
    return;  // No template available; skip pileup correction
  }
  // Fit a pulse in prev crossing
  template_fcn->SetParameters(ymax, x_at_max);
  ...
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think the rabbit is right on this one. We use the fit of MBD_SHAPE in all situations.

Copy link
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for all SetTemplate calls to understand when template_fcn is initialized
rg -n 'SetTemplate\(' --type=cc --type=h -C3

Length of output: 26


🏁 Script executed:

#!/bin/bash
# Search for MBD_SHAPE usage to understand template loading
rg -n 'MBD_SHAPE|do_templatefit' offline/packages/mbd/ -C3

Length of output: 5007


🏁 Script executed:

#!/bin/bash
# Check if there are any other places where template_fcn is initialized
rg -n 'template_fcn.*=.*new|template_fcn.*nullptr' offline/packages/mbd/MbdSig.cc -C2

Length of output: 1037


🏁 Script executed:

#!/bin/bash
# Look for the initialization flow in MbdEvent to understand the setup order
rg -n 'Init|SetTemplate|MBD_SHAPE' offline/packages/mbd/MbdEvent.cc -C2

Length of output: 716


@mchiu-bnl Thank you for the clarification. I see that MBD_SHAPES is loaded when do_templatefit==1, and SetTemplate() initializes template_fcn accordingly.

Could you help me understand how template_fcn is guaranteed to be initialized before Remove_Pileup() is called? I'm seeing:

  • In online mode, do_templatefit=0 (line 60 of MbdEvent.cc)
  • SetTemplate() is only called when do_templatefit && type==1 (line 204)
  • The new charge-channel pileup path uses template_fcn at line 393

Is there a different initialization path for online mode, or does online mode not encounter this particular pileup scenario? I want to make sure I understand the complete picture correctly.


🧬 Code Graph Analysis Results

[No direct code snippet blocks identified]
[If you prefer, I can provide exact verbatim blocks, but none of the provided snippets from the other files appear to directly cover MbdSig.cc's TemplateFcn/SetTemplate/FitTemplate without summarization.]

Copy link
Contributor

@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.

Caution

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

⚠️ Outside diff range comments (1)
offline/packages/mbd/MbdSig.cc (1)

140-166: ⚠️ Potential issue | 🟠 Major

Memory leak: fit_pileup is never deleted.

fit_pileup is heap-allocated in Remove_Pileup() (line 352 for time channels, line 430 for charge channels) but is not freed in the destructor. Every channel that encounters pileup will leak a TF1.

Proposed fix
   delete h_chi2ndf;
+  delete fit_pileup;
   if ( _pedstudyflag )
   {
     delete gPedvsEvent;
   }
 }

@sphenix-jenkins-ci
Copy link

Build & test report

Report for commit 70b8bd9b95015b806190b0e29dd06b7e90da6d60:
Jenkins passed


Automatically generated by sPHENIX Jenkins continuous integration
sPHENIX             jenkins.io

@sphenix-jenkins-ci
Copy link

Build & test report

Report for commit a07fca0c6093a63463753a2ebf05e00f9f4cc6de:
Jenkins passed


Automatically generated by sPHENIX Jenkins continuous integration
sPHENIX             jenkins.io

@pinkenburg
Copy link
Contributor

clang-tidy warnings are from recent tracking PR, will be fixed in my next PR

@pinkenburg pinkenburg merged commit 9f22933 into sPHENIX-Collaboration:master Feb 15, 2026
21 of 22 checks passed
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.

2 participants

Comments