Skip to content

Streaming Lumi Counting and BCO Tagging#4269

Open
dillfitz wants to merge 2 commits into
sPHENIX-Collaboration:masterfrom
dillfitz:master
Open

Streaming Lumi Counting and BCO Tagging#4269
dillfitz wants to merge 2 commits into
sPHENIX-Collaboration:masterfrom
dillfitz:master

Conversation

@dillfitz
Copy link
Copy Markdown

@dillfitz dillfitz commented May 18, 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, ...)

(1) Add Subsys reco module (StreamingBcoLumiReco) for producing DST from BcoLumiReco output that contains the gl1 bco, the corresponding streaming window, a boolean to encode if the gl1 bco is usable for analysis, and the luminosity in the streaming data. (2) Add corresponding PHObject classes for storing the luminosity and gl1 info in the DST. (3) Add Subsys reco module to test new DST output.

TODOs (if applicable)

PENDING DISCUSSION WITH THE BCO LUMI STREAMING GROUP -- we should discuss the current structure of the code and if this is what we want moving forward. For example, there are 2 passes over the gl1 raw data which might not be necessary. Other options to consider are:

  1. If we save the info we need for the lumi calculation to the DST created from BcoLumiReco, then we could just load the info directly from the DST. The relevant part of StreamingBcoLumiReco is here.
  2. We could consider moving all logic for saving the streaming DST upstream so that two passes are not done at all (e.g. handle everything in what is now called BcoLumiReco probably renaming to StreamingBcoLumiReco such that it outputs the equivalent of what is now output from StreamingBcoLumiReco). I avoided this as the use of SyncReco and the way the bco info was being accessed in BcoLumiReco was not as straightforward to me to work with for calculating the necessary quantities for the streaming bco lumi DST. However, now that the logic is in place in StreamingBcoLumiReco, it could be moved upstream if desired.

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

Streaming BCO Luminosity Counting and DST Output

Motivation and Context

This PR adds streaming luminosity reconstruction based on BCO (Bunch Crossing Order) tagging and GL1 trigger scaler data. The new module produces a DST containing BCO-based luminosity information for analysis. This functionality was presented to the HF TG group and corresponds to macros PR sPHENIX-Collaboration/macros#1327.

Key Changes

New Reconstruction Module:

  • StreamingBcoLumiReco: Main SubsysReco module that reads GL1 packet 14001 (trigger scalers), extracts per-bunch and per-event BCO information, determines BCO usability for analysis, calculates streaming windows, and computes raw/live/scaled luminosities. Results are written to DST nodes STREAMINGBCOINFO and STREAMINGLUMIINFO.

New PHObject Data Classes:

  • StreamingBcoInfo / StreamingBcoInfov1: Store GL1 BCO, event number, usable BCO flag, and streaming window (pair of uint64_t)
  • StreamingLumiInfo / StreamingLumiInfov1: Store raw/live/scaled luminosity values

Test/Validation Module:

  • StreamingBcoLumiCheck: Debug module to validate streaming DST output, logs luminosity values and BCO-related fields

Build System Updates:

  • Expanded Makefile.am to include new source files, ROOT dictionary generation, and updated library dependencies (-lfun4all)

Removed:

  • Old BcoLumiCheck class (replaced by StreamingBcoLumiCheck)

Potential Risk Areas

  • IO Format Changes: Introduces new DST node structure (STREAMINGBCOINFO, STREAMINGLUMIINFO) that downstream code must handle
  • Reconstruction Logic: Luminosity calculation involves per-bunch accumulation using normalized GL1 scaler counts divided by cross-section factor (m_xsec_MBDNS). Bunch number wraparound logic (0–119 range) and abort gap handling (skips bunch indices >110) should be validated
  • Double-Pass Inefficiency: As acknowledged in the PR, GL1 raw data is read twice (once in BcoLumiReco, once in StreamingBcoLumiReco). Future optimization is planned
  • Fixed Array Sizing: Uses fixed-size arrays for 120 bunch slots with no dynamic resizing

Notes for Reviewers

AI Disclaimer: AI-generated summaries may contain errors; use best judgment when reviewing code logic, especially the bunch number wraparound and luminosity normalization calculations.

The author notes pending discussion with the BCO lumi streaming group regarding code structure. Two refactoring approaches are proposed: (1) save lumi-calculation intermediates in BcoLumiReco DST for reuse, or (2) move streaming DST creation upstream into BcoLumiReco to avoid double-pass processing. Either approach could be adopted in future iterations.

Review Change Stack

…rom BcoLumiReco output that contains the gl1 bco, the corresponding streaming window, a boolean to encode if the gl1 bco is usable for analysis, and the luminosity in the streaming data. (2) Add corresponding PHObject classes for storing the luminosity and gl1 info in the DST. (3) Add Subsys reco module to test new DST output.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 18, 2026

📝 Walkthrough

Walkthrough

This PR replaces the legacy BcoLumiCheck implementation with a comprehensive streaming pipeline. It introduces typed data containers for BCO and luminosity information, a reconstruction module that processes GL1 scaler data to compute per-bunch luminosities, and a simpler monitoring module. All components are wired together in the build system.

Changes

Streaming BCO/Lumi Pipeline Implementation

Layer / File(s) Summary
Streaming BCO Info Container
offline/packages/bcolumicount/StreamingBcoInfo.h, offline/packages/bcolumicount/StreamingBcoInfo.cc, offline/packages/bcolumicount/StreamingBcoInfov1.h, offline/packages/bcolumicount/StreamingBcoInfov1.cc, offline/packages/bcolumicount/StreamingBcoInfoLinkDef.h, offline/packages/bcolumicount/StreamingBcoInfov1LinkDef.h
Defines StreamingBcoInfo (base) and StreamingBcoInfov1 (concrete) to store BCO-related streaming data: BCO value, event number, usability flag, and streaming window pair. Includes ROOT dictionary support.
Streaming Lumi Info Container
offline/packages/bcolumicount/StreamingLumiInfo.h, offline/packages/bcolumicount/StreamingLumiInfo.cc, offline/packages/bcolumicount/StreamingLumiInfov1.h, offline/packages/bcolumicount/StreamingLumiInfov1.cc, offline/packages/bcolumicount/StreamingLumiInfoLinkDef.h, offline/packages/bcolumicount/StreamingLumiInfov1LinkDef.h
Defines StreamingLumiInfo (base) and StreamingLumiInfov1 (concrete) to store luminosity values: raw, live, and scaled. Includes ROOT dictionary support.
Streaming BCO/Lumi Reconstruction Engine
offline/packages/bcolumicount/StreamingBcoLumiReco.h, offline/packages/bcolumicount/StreamingBcoLumiReco.cc
Core SubsysReco module that retrieves GL1 packet 14001, extracts BCO timing and per-bunch scaler information, computes BCO usability and streaming window, fills histograms of BCO differences and trigger-bit firing, accumulates per-bunch crossing counts, and computes per-bunch raw/live/scaled luminosities normalized by cross section at end of run.
Streaming BCO/Lumi Monitoring Module
offline/packages/bcolumicount/StreamingBcoLumiCheck.h, offline/packages/bcolumicount/StreamingBcoLumiCheck.cc
Simple SubsysReco module for monitoring: validates DST node, retrieves STREAMINGBCOINFO, and logs BCO-related debug output when verbosity is requested.
Build Configuration
offline/packages/bcolumicount/Makefile.am
Adds -lfun4all dependency, expands ROOTDICTS with streaming info dictionary sources, updates public header exports, and replaces library source lists: removes legacy BcoLumiCheck.cc and adds StreamingBcoLumiReco.cc, StreamingBcoLumiCheck.cc, and all Streaming*Info implementations.

Possibly Related PRs


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

❤️ Share

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

@sphenix-jenkins-ci
Copy link
Copy Markdown

Build & test report

Report for commit bdf2ea497eb26d18012f3bccd6285d9223b49133:
Jenkins on fire


Automatically generated by sPHENIX Jenkins continuous integration
sPHENIX             jenkins.io

Copy link
Copy Markdown
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: 7


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d69f6fc4-69a4-44f7-9043-a8a2e301e73e

📥 Commits

Reviewing files that changed from the base of the PR and between 3381348 and ca045d5.

📒 Files selected for processing (19)
  • offline/packages/bcolumicount/BcoLumiCheck.cc
  • offline/packages/bcolumicount/BcoLumiCheck.h
  • offline/packages/bcolumicount/Makefile.am
  • offline/packages/bcolumicount/StreamingBcoInfo.cc
  • offline/packages/bcolumicount/StreamingBcoInfo.h
  • offline/packages/bcolumicount/StreamingBcoInfoLinkDef.h
  • offline/packages/bcolumicount/StreamingBcoInfov1.cc
  • offline/packages/bcolumicount/StreamingBcoInfov1.h
  • offline/packages/bcolumicount/StreamingBcoInfov1LinkDef.h
  • offline/packages/bcolumicount/StreamingBcoLumiCheck.cc
  • offline/packages/bcolumicount/StreamingBcoLumiCheck.h
  • offline/packages/bcolumicount/StreamingBcoLumiReco.cc
  • offline/packages/bcolumicount/StreamingBcoLumiReco.h
  • offline/packages/bcolumicount/StreamingLumiInfo.cc
  • offline/packages/bcolumicount/StreamingLumiInfo.h
  • offline/packages/bcolumicount/StreamingLumiInfoLinkDef.h
  • offline/packages/bcolumicount/StreamingLumiInfov1.cc
  • offline/packages/bcolumicount/StreamingLumiInfov1.h
  • offline/packages/bcolumicount/StreamingLumiInfov1LinkDef.h
💤 Files with no reviewable changes (2)
  • offline/packages/bcolumicount/BcoLumiCheck.h
  • offline/packages/bcolumicount/BcoLumiCheck.cc

Comment on lines +7 to +12
void StreamingBcoInfov1::Reset()
{
set_bco(0);
set_usable_bco_tag(false);
set_bco_streaming_window(std::make_pair(0, 0));
return;
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 19, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reset leaves evtno stale across object reuse.

StreamingBcoInfov1::Reset() does not clear the event number, so previous-event metadata can persist after reset and propagate wrong DST bookkeeping.

Proposed fix
 void StreamingBcoInfov1::Reset()
 {
   set_bco(0);
+  set_evtno(0);
   set_usable_bco_tag(false);
   set_bco_streaming_window(std::make_pair(0, 0));
   return;
 }
📝 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
void StreamingBcoInfov1::Reset()
{
set_bco(0);
set_usable_bco_tag(false);
set_bco_streaming_window(std::make_pair(0, 0));
return;
void StreamingBcoInfov1::Reset()
{
set_bco(0);
set_evtno(0);
set_usable_bco_tag(false);
set_bco_streaming_window(std::make_pair(0, 0));
return;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is a relevant comment from the rabbit, we should reset all member variables of the EDM object. I think. Or perhaps the idea is to only set the event number once and have it be incremented on each process_event call? We may consider seeing if there is a "global" event number owned by fun4all that could be set here each reset call, so that it is guaranteed that this object is always in sync with Fun4all

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how evtno is set in StreamingBcoLumiReco process_event
rg -n "set_evtno\|get_evtno\|evtno\|EventNumber\|getEventNumber" --type=cc -C3

# Check Fun4AllServer for global event number accessor
rg -n "getEventNumber\|EventCounter\|m_eventcount\|m_eventcounter" --type=cc -C2

# Look at other PHObject v1 implementations for how Reset() handles evtno-like fields
rg -n "set_evtno\|m_evtno" -C2

Length of output: 54


🏁 Script executed:

#!/bin/bash
# Check how evtno is set in StreamingBcoLumiReco process_event
rg -n "set_evtno\|get_evtno\|m_evtno" -g "*.cc" -g "*.h" -C3

# Check Fun4AllServer for a global event number accessor
rg -n "getEventNumber\|EventCounter\|m_eventcount" -g "*.cc" -g "*.h" -C2

# Check how SyncObject or other mechanisms track event numbers in this package
rg -n "evtno\|evt_number\|event_number\|EventNumber" -g "*.cc" -g "*.h" --iglob "*bcolumicount*" -C3

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Find all files in the bcolumicount package
fd . offline/packages/bcolumicount/

# Cat the streaming bco lumi reco implementation
cat offline/packages/bcolumicount/StreamingBcoLumiReco.cc

# Cat the v1 header to see all private members
cat offline/packages/bcolumicount/StreamingBcoInfov1.h

Length of output: 12683


@osbornjd — good question. Looking at StreamingBcoLumiReco::process_event(), the answer is actually both problems at once: m_evtno is never written anywhere — there is no streaming_bco_info->set_evtno(...) call in process_event(). So it is both stale on object reuse and never populated in the first place.

On the Fun4All global event number question: you're already fetching the right handle. The SyncObject is retrieved at the top of process_event() and syncobject->EventNumber() is used in the debug printout. That is the canonical per-event counter in Fun4All and is exactly what should be written to StreamingBcoInfov1. No need to reach for Fun4AllServer directly.

The fix then has two parts:

1. StreamingBcoInfov1::Reset() — zero all members:

void StreamingBcoInfov1::Reset()
{
  set_bco(0);
  set_evtno(0);           // add this
  set_usable_bco_tag(false);
  set_bco_streaming_window(std::make_pair(0, 0));
}

2. StreamingBcoLumiReco::process_event() — populate evtno from SyncObject:

if (bcoinfo)
{
  // ... existing BCO logic ...
  streaming_bco_info->set_bco(get_bco());
  streaming_bco_info->set_usable_bco_tag(get_usable_bco_tag());
  streaming_bco_info->set_bco_streaming_window(get_bco_streaming_window());
  if (syncobject)                                              // add null guard
  {
    streaming_bco_info->set_evtno(syncobject->EventNumber());  // add this
  }
}

Note the null guard on syncobject — it is currently dereferenced for debug output without a null check, which is an independent correctness issue worth fixing at the same time.


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

Comment on lines +103 to +149
SyncObject *syncobject = findNode::getClass<SyncObject>(topNode, syncdefs::SYNCNODENAME);
//Gl1Packet *gl1packet = findNode::getClass<Gl1Packet>(topNode, 14001);
Event *evt = findNode::getClass<Event>(topNode, "PRDF");
if (evt)
{
if (Verbosity() > 2)
{
evt->identify();
}
if (evt->getEvtType() != DATAEVENT)
{
return Fun4AllReturnCodes::ABORTEVENT;
}
Packet *packet = evt->getPacket(14001);
if (!packet)
{
if (Verbosity() > 0)
{
std::cout << "no gl1 packet 14001" << std::endl;
evt->identify();
}
return Fun4AllReturnCodes::ABORTEVENT;
}
uint64_t gtm_bco = packet->lValue(0, "BCO");
uint64_t gl1_scaledvec = packet->lValue(0, "ScaledVector");
//uint64_t gl1_livevec = packet->lValue(0, "TriggerVector");

int bunchno = packet->lValue(0,"BunchNumber");

// SYNTAX TAKEN FROM ZHIWANS CODE, why = and not +=? If this is correct it seems like a waste to call it for every event (would just need it for the last event in a particular crossing?)
m_bunchnumber_MBDNS_raw[bunchno] = packet->lValue(0, "GL1PRAW");
m_bunchnumber_MBDNS_live[bunchno] = packet->lValue(0, "GL1PLIVE");
m_bunchnumber_MBDNS_scaled[bunchno] = packet->lValue(0, "GL1PSCALED");


if(packet->lValue(0, 0))
{
m_rawgl1scaler = packet->lValue(0, 0);
}

delete packet;

if (Verbosity() > 2)
{
std::cout << "Event No: " << syncobject->EventNumber() /*<< std::hex*/
<< " gl1 bco: " << gtm_bco <<std::dec << std::endl;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add null guards before dereferencing framework nodes.

syncobject (Line 147) and streaming_bco_info (Line 237 onward) are dereferenced without null checks. Missing nodes here become hard crashes.

Proposed fix
 BcoInfo *bcoinfo = findNode::getClass<BcoInfo>(topNode, "BCOINFO");
 SyncObject *syncobject = findNode::getClass<SyncObject>(topNode, syncdefs::SYNCNODENAME);
@@
     if (Verbosity() > 2)
     {
-      std::cout << "Event No: " << syncobject->EventNumber() /*<< std::hex*/
+      if (!syncobject)
+      {
+        std::cout << PHWHERE << " SyncObject missing" << std::endl;
+        delete packet;
+        return Fun4AllReturnCodes::ABORTEVENT;
+      }
+      std::cout << "Event No: " << syncobject->EventNumber() /*<< std::hex*/
                 << " gl1  bco: " << gtm_bco <<std::dec << std::endl;
     }
@@
       StreamingBcoInfo *streaming_bco_info = findNode::getClass<StreamingBcoInfo>(topNode, "STREAMINGBCOINFO");
+      if (!streaming_bco_info)
+      {
+        std::cout << PHWHERE << " STREAMINGBCOINFO node missing" << std::endl;
+        return Fun4AllReturnCodes::ABORTEVENT;
+      }

As per coding guidelines: **/*.{cc,cpp,cxx,c} must prioritize correctness and error handling.

Also applies to: 162-239

Comment on lines +130 to +135
int bunchno = packet->lValue(0,"BunchNumber");

// SYNTAX TAKEN FROM ZHIWANS CODE, why = and not +=? If this is correct it seems like a waste to call it for every event (would just need it for the last event in a particular crossing?)
m_bunchnumber_MBDNS_raw[bunchno] = packet->lValue(0, "GL1PRAW");
m_bunchnumber_MBDNS_live[bunchno] = packet->lValue(0, "GL1PLIVE");
m_bunchnumber_MBDNS_scaled[bunchno] = packet->lValue(0, "GL1PSCALED");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Validate bunch index before writing per-bunch arrays.

Line 133 through Line 135 index fixed-size arrays with bunchno from packet data without bounds checks. An out-of-range bunch id will write out of bounds and corrupt memory.

Proposed fix
 int bunchno = packet->lValue(0,"BunchNumber");
+if (bunchno < 0 || bunchno >= m_bunches)
+{
+  if (Verbosity() > 0)
+  {
+    std::cout << PHWHERE << " invalid bunch number: " << bunchno << std::endl;
+  }
+  delete packet;
+  return Fun4AllReturnCodes::ABORTEVENT;
+}
 
 m_bunchnumber_MBDNS_raw[bunchno] = packet->lValue(0, "GL1PRAW");
 m_bunchnumber_MBDNS_live[bunchno] = packet->lValue(0, "GL1PLIVE");
 m_bunchnumber_MBDNS_scaled[bunchno] = packet->lValue(0, "GL1PSCALED");

As per coding guidelines: **/*.{cc,cpp,cxx,c} must prioritize correctness and memory safety.

Comment on lines +247 to +253
uint64_t rawgl1scalers_per_bunch = m_rawgl1scaler/120.;
for (int i=0; i<m_bunches; i++)
{
m_bunchnumber_lumi_raw[i] = ( m_bunchnumber_crossings[i] * m_bunchnumber_MBDNS_raw[i] ) / ( rawgl1scalers_per_bunch * m_xsec_MBDNS );
m_bunchnumber_lumi_live[i] = ( m_bunchnumber_crossings[i] * m_bunchnumber_MBDNS_live[i]) / ( rawgl1scalers_per_bunch * m_xsec_MBDNS );
m_bunchnumber_lumi_scaled[i] = ( m_bunchnumber_crossings[i] * m_bunchnumber_MBDNS_scaled[i] ) / ( rawgl1scalers_per_bunch * m_xsec_MBDNS );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard lumi normalization against zero/invalid denominator.

Line 247 truncates m_rawgl1scaler/120. into uint64_t; small values become zero and Line 250–252 then divide by zero. This can silently produce invalid lumi values.

Proposed fix
-uint64_t rawgl1scalers_per_bunch = m_rawgl1scaler/120.;
+const double rawgl1scalers_per_bunch = static_cast<double>(m_rawgl1scaler) / 120.0;
+if (rawgl1scalers_per_bunch <= 0.0 || m_xsec_MBDNS <= 0.0)
+{
+  std::cout << PHWHERE << " invalid normalization inputs: rawgl1scalers_per_bunch="
+            << rawgl1scalers_per_bunch << " xsec=" << m_xsec_MBDNS << std::endl;
+  return Fun4AllReturnCodes::ABORTRUN;
+}

As per coding guidelines: **/*.{cc,cpp,cxx,c} must prioritize correctness and error handling.

📝 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
uint64_t rawgl1scalers_per_bunch = m_rawgl1scaler/120.;
for (int i=0; i<m_bunches; i++)
{
m_bunchnumber_lumi_raw[i] = ( m_bunchnumber_crossings[i] * m_bunchnumber_MBDNS_raw[i] ) / ( rawgl1scalers_per_bunch * m_xsec_MBDNS );
m_bunchnumber_lumi_live[i] = ( m_bunchnumber_crossings[i] * m_bunchnumber_MBDNS_live[i]) / ( rawgl1scalers_per_bunch * m_xsec_MBDNS );
m_bunchnumber_lumi_scaled[i] = ( m_bunchnumber_crossings[i] * m_bunchnumber_MBDNS_scaled[i] ) / ( rawgl1scalers_per_bunch * m_xsec_MBDNS );
const double rawgl1scalers_per_bunch = static_cast<double>(m_rawgl1scaler) / 120.0;
if (rawgl1scalers_per_bunch <= 0.0 || m_xsec_MBDNS <= 0.0)
{
std::cout << PHWHERE << " invalid normalization inputs: rawgl1scalers_per_bunch="
<< rawgl1scalers_per_bunch << " xsec=" << m_xsec_MBDNS << std::endl;
return Fun4AllReturnCodes::ABORTRUN;
}
for (int i=0; i<m_bunches; i++)
{
m_bunchnumber_lumi_raw[i] = ( m_bunchnumber_crossings[i] * m_bunchnumber_MBDNS_raw[i] ) / ( rawgl1scalers_per_bunch * m_xsec_MBDNS );
m_bunchnumber_lumi_live[i] = ( m_bunchnumber_crossings[i] * m_bunchnumber_MBDNS_live[i]) / ( rawgl1scalers_per_bunch * m_xsec_MBDNS );
m_bunchnumber_lumi_scaled[i] = ( m_bunchnumber_crossings[i] * m_bunchnumber_MBDNS_scaled[i] ) / ( rawgl1scalers_per_bunch * m_xsec_MBDNS );

Comment on lines +254 to +279
m_lumi_raw += m_bunchnumber_lumi_raw[i];
m_lumi_live += m_bunchnumber_lumi_live[i];
m_lumi_scaled += m_bunchnumber_lumi_scaled[i];

if (Verbosity() > 1)
{
std::cout << "bunchno : " << i << " lumi_raw : " << m_bunchnumber_lumi_raw[i] << std::endl;
}
}
m_streaming_lumi_info->set_lumi_raw(get_lumi_raw());
m_streaming_lumi_info->set_lumi_live(get_lumi_live());
m_streaming_lumi_info->set_lumi_scaled(get_lumi_scaled());
if (Verbosity() > 1)
{
std::cout << "MBD xsec : " << m_xsec_MBDNS << std::endl;
std::cout << "total lumi (raw) : " << m_lumi_raw << std::endl;
}

m_bunchnumber_lumi_raw.fill(0);
m_bunchnumber_lumi_live.fill(0);
m_bunchnumber_lumi_scaled.fill(0);
m_bunchnumber_crossings.fill(0);
m_bunchnumber_MBDNS_raw.fill(0);
m_bunchnumber_MBDNS_live.fill(0);
m_bunchnumber_MBDNS_scaled.fill(0);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reset run-level accumulators to avoid cross-run contamination.

m_lumi_raw, m_lumi_live, m_lumi_scaled, and m_rawgl1scaler are not reset after EndRun. Next runs accumulate stale totals and can publish incorrect per-run lumi.

Proposed fix
   m_bunchnumber_MBDNS_raw.fill(0);
   m_bunchnumber_MBDNS_live.fill(0);
   m_bunchnumber_MBDNS_scaled.fill(0);
+  m_lumi_raw = 0.;
+  m_lumi_live = 0.;
+  m_lumi_scaled = 0.;
+  m_rawgl1scaler = 0;
 
   return Fun4AllReturnCodes::EVENT_OK;

As per coding guidelines: **/*.{cc,cpp,cxx,c} must prioritize correctness.

📝 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
m_lumi_raw += m_bunchnumber_lumi_raw[i];
m_lumi_live += m_bunchnumber_lumi_live[i];
m_lumi_scaled += m_bunchnumber_lumi_scaled[i];
if (Verbosity() > 1)
{
std::cout << "bunchno : " << i << " lumi_raw : " << m_bunchnumber_lumi_raw[i] << std::endl;
}
}
m_streaming_lumi_info->set_lumi_raw(get_lumi_raw());
m_streaming_lumi_info->set_lumi_live(get_lumi_live());
m_streaming_lumi_info->set_lumi_scaled(get_lumi_scaled());
if (Verbosity() > 1)
{
std::cout << "MBD xsec : " << m_xsec_MBDNS << std::endl;
std::cout << "total lumi (raw) : " << m_lumi_raw << std::endl;
}
m_bunchnumber_lumi_raw.fill(0);
m_bunchnumber_lumi_live.fill(0);
m_bunchnumber_lumi_scaled.fill(0);
m_bunchnumber_crossings.fill(0);
m_bunchnumber_MBDNS_raw.fill(0);
m_bunchnumber_MBDNS_live.fill(0);
m_bunchnumber_MBDNS_scaled.fill(0);
m_lumi_raw += m_bunchnumber_lumi_raw[i];
m_lumi_live += m_bunchnumber_lumi_live[i];
m_lumi_scaled += m_bunchnumber_lumi_scaled[i];
if (Verbosity() > 1)
{
std::cout << "bunchno : " << i << " lumi_raw : " << m_bunchnumber_lumi_raw[i] << std::endl;
}
}
m_streaming_lumi_info->set_lumi_raw(get_lumi_raw());
m_streaming_lumi_info->set_lumi_live(get_lumi_live());
m_streaming_lumi_info->set_lumi_scaled(get_lumi_scaled());
if (Verbosity() > 1)
{
std::cout << "MBD xsec : " << m_xsec_MBDNS << std::endl;
std::cout << "total lumi (raw) : " << m_lumi_raw << std::endl;
}
m_bunchnumber_lumi_raw.fill(0);
m_bunchnumber_lumi_live.fill(0);
m_bunchnumber_lumi_scaled.fill(0);
m_bunchnumber_crossings.fill(0);
m_bunchnumber_MBDNS_raw.fill(0);
m_bunchnumber_MBDNS_live.fill(0);
m_bunchnumber_MBDNS_scaled.fill(0);
m_lumi_raw = 0.;
m_lumi_live = 0.;
m_lumi_scaled = 0.;
m_rawgl1scaler = 0;
return Fun4AllReturnCodes::EVENT_OK;

Comment on lines +263 to +265
m_streaming_lumi_info->set_lumi_raw(get_lumi_raw());
m_streaming_lumi_info->set_lumi_live(get_lumi_live());
m_streaming_lumi_info->set_lumi_scaled(get_lumi_scaled());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Check m_streaming_lumi_info before writing output.

Line 263 through Line 265 dereference m_streaming_lumi_info unconditionally. If InitRun failed or node creation was skipped, this is a null-pointer crash path.

Proposed fix
-  m_streaming_lumi_info->set_lumi_raw(get_lumi_raw());
-  m_streaming_lumi_info->set_lumi_live(get_lumi_live());
-  m_streaming_lumi_info->set_lumi_scaled(get_lumi_scaled());
+  if (!m_streaming_lumi_info)
+  {
+    std::cout << PHWHERE << " STREAMINGLUMIINFO node missing in EndRun" << std::endl;
+    return Fun4AllReturnCodes::ABORTRUN;
+  }
+  m_streaming_lumi_info->set_lumi_raw(get_lumi_raw());
+  m_streaming_lumi_info->set_lumi_live(get_lumi_live());
+  m_streaming_lumi_info->set_lumi_scaled(get_lumi_scaled());

As per coding guidelines: **/*.{cc,cpp,cxx,c} must prioritize correctness and memory safety.

📝 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
m_streaming_lumi_info->set_lumi_raw(get_lumi_raw());
m_streaming_lumi_info->set_lumi_live(get_lumi_live());
m_streaming_lumi_info->set_lumi_scaled(get_lumi_scaled());
if (!m_streaming_lumi_info)
{
std::cout << PHWHERE << " STREAMINGLUMIINFO node missing in EndRun" << std::endl;
return Fun4AllReturnCodes::ABORTRUN;
}
m_streaming_lumi_info->set_lumi_raw(get_lumi_raw());
m_streaming_lumi_info->set_lumi_live(get_lumi_live());
m_streaming_lumi_info->set_lumi_scaled(get_lumi_scaled());

Comment on lines +7 to +15
void StreamingLumiInfov1::Reset()
{
// Double check that this is only called once per run!! Or... it should just be called in the InitRun hook?
set_lumi_raw(0.);
set_lumi_live(0.);
set_lumi_scaled(0.);

return;
}
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 19, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clarify Reset() semantics and intended usage.

The comment on line 9 indicates uncertainty about when Reset() should be called and whether it could be invoked multiple times. If this method clears per-run accumulated luminosity, unintended multiple calls could cause data loss. Please document the intended lifecycle of this object (per-event vs. per-run) and clarify whether Reset() should be called in InitRun(), between events, or elsewhere.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reset is called per event, so I don't think this is what you want (or at least, this won't do anything since you only fill these values within StreamingBcoLumiReco::EndRun which is correct)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@osbornjd, thanks for the clarification — that makes sense. Since the lumi values are only populated in StreamingBcoLumiReco::EndRun(), the per-event Reset() calls throughout the run are hitting already-zeroed fields and have no effect on the accumulated data. My concern about data loss doesn't apply here. The lifecycle is correct as-is.

The in-code comment on line 9 could be replaced with a brief explanation of why it's a no-op during the run (e.g., "values are only set in EndRun; per-event Reset() is harmless"), but that's a minor style point — no functional issue.


✏️ Learnings added
Learnt from: osbornjd
Repo: sPHENIX-Collaboration/coresoftware PR: 4269
File: offline/packages/bcolumicount/StreamingLumiInfov1.cc:7-15
Timestamp: 2026-05-20T19:19:16.091Z
Learning: In `offline/packages/bcolumicount/StreamingLumiInfov1.cc`, `Reset()` is called per event by the Fun4All/PHObject framework. The lumi values (`lumi_raw`, `lumi_live`, `lumi_scaled`) are only populated once per run inside `StreamingBcoLumiReco::EndRun()`, so per-event `Reset()` calls are harmless no-ops during the run. Do not flag per-event Reset() calls on this object as a data-loss risk.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


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

@sphenix-jenkins-ci
Copy link
Copy Markdown

Build & test report

Report for commit ca045d501f0648671f39e68b316838fa7c04347c:
Jenkins on fire


Automatically generated by sPHENIX Jenkins continuous integration
sPHENIX             jenkins.io

@sphenix-jenkins-ci
Copy link
Copy Markdown

Build & test report

Report for commit ca045d501f0648671f39e68b316838fa7c04347c:
Jenkins on fire


Automatically generated by sPHENIX Jenkins continuous integration
sPHENIX             jenkins.io

Comment on lines +7 to +12
void StreamingBcoInfov1::Reset()
{
set_bco(0);
set_usable_bco_tag(false);
set_bco_streaming_window(std::make_pair(0, 0));
return;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is a relevant comment from the rabbit, we should reset all member variables of the EDM object. I think. Or perhaps the idea is to only set the event number once and have it be incremented on each process_event call? We may consider seeing if there is a "global" event number owned by fun4all that could be set here each reset call, so that it is guaranteed that this object is always in sync with Fun4all

// TODO: Set BCO Negative window length in the macro
// window length: 360, negative window length: 20
// Therefore, should check if BCO is within 340
// special case if BCO is within 20 of previous BCO?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This should never happen, I think Cameron and I found there is a minimum allowed bco diff of 17 for triggered events which Chris can confirm - this is a real condition in the DAQ

Comment on lines +7 to +15
void StreamingLumiInfov1::Reset()
{
// Double check that this is only called once per run!! Or... it should just be called in the InitRun hook?
set_lumi_raw(0.);
set_lumi_live(0.);
set_lumi_scaled(0.);

return;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reset is called per event, so I don't think this is what you want (or at least, this won't do anything since you only fill these values within StreamingBcoLumiReco::EndRun which is correct)

uint64_t bco_diff_prev = m_bco - bco_prev;
uint64_t bco_diff_futu = bco_futu - m_bco;

// TODO: Set BCO window length in the macro
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why don't we just do this now? We can set the default value to 340 and then have the necessary setter so that it can be modified if necessary

bool m_usable_bco_tag = false;
std::pair<uint64_t, uint64_t> m_bco_streaming_window;

double m_xsec_MBDNS = 24.07*1e9; //convert to pb from Vernier scan DOUBLE CHECK VALUE!
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We may consider having this be a global value in https://github.com/sPHENIX-Collaboration/coresoftware/blob/master/offline/framework/phool/sphenix_constants.h so that all code can use the same value and we only have to update one place


int bunchno = packet->lValue(0,"BunchNumber");

// SYNTAX TAKEN FROM ZHIWANS CODE, why = and not +=? If this is correct it seems like a waste to call it for every event (would just need it for the last event in a particular crossing?)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@pinkenburg can you comment on this?

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