Skip to content

CaloTowerStatus: Refactor Hot Tower Identification#4267

Merged
pinkenburg merged 5 commits into
sPHENIX-Collaboration:masterfrom
Steepspace:CaloTowerStatus
May 15, 2026
Merged

CaloTowerStatus: Refactor Hot Tower Identification#4267
pinkenburg merged 5 commits into
sPHENIX-Collaboration:masterfrom
Steepspace:CaloTowerStatus

Conversation

@Steepspace
Copy link
Copy Markdown
Contributor

@Steepspace Steepspace commented May 13, 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, ...)

  • Added a logical split to handle the default behavior: if the z-score threshold is untouched, the code now relies strictly on the non-zero hotMap values.
  • Refactored the complex if-statement for custom thresholds into an if/else block using descriptive boolean variables (is_dead,exceeds_zscore_limit, is_low_yield_cold) for better readability.
  • Replaced the C-style std::fabs with the modern C++ standard std::abs.
  • Guarded the entire evaluation block behind m_doHotMap to improve clarity.

TODOs (if applicable)

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

CaloTowerStatus: Refactor Hot Tower Identification

Motivation / Context

Hot-tower identification previously used a dense conditional and kept CDBTTree objects as class members, which made the logic hard to read and increased memory lifetime of calibration trees. This change clarifies default vs. custom z-score handling, reduces memory usage by loading calibration into local caches, and improves readability and maintainability.

Key changes

  • Calibration loading and lifetime

    • Create CDBTTree objects locally in InitRun, pass them into LoadCalib(CDBTTree*, CDBTTree*), then delete them immediately after populating the in-memory m_cdbInfo_vec (reduces memory footprint).
    • Remove long-lived CDBTTree* members and default the destructor inline.
    • LoadCalib only reads chi2/hotMap fields when the corresponding m_doHot* flag is enabled and the provided CDBTTree* is non-null.
  • Hot-map and hot-tower logic

    • Guard entire hot-map evaluation behind if (m_doHotMap).
    • Split behavior into explicit branches:
      • Default threshold path (z_score_threshold == z_score_threshold_default): mark tower hot only when hotMap_val > 0.
      • Custom threshold path: compute and use descriptive booleans:
        • is_dead = (hotMap_val == 1)
        • exceeds_zscore_limit = (std::abs(z_score) > z_score_threshold)
        • is_low_yield_cold = (hotMap_val == 3 && z_score >= -1 * z_score_threshold_default)
      • Mark tower hot if any boolean is true.
    • Replace std::fabs with std::abs.
    • Apply set_isHot(true) only when is_hot_tower is true.
  • Minor control-flow change

    • InitRun now calls LoadCalib(...) and deletes local CDBTTree objects before CreateNodeTree(topNode).
    • CreateNodeTree now calls gSystem->Exit(1) when required tower node is missing (was previously throwing).

Potential risk areas

  • Reconstruction behavior
    • Hot-flagging semantics changed: previously any non-zero hotMap marked hot; now default path explicitly requires hotMap_val > 0 and custom path applies more selective rules. Downstream clustering, energy sums, and physics analyses may be affected — validate with regression plots.
  • Threshold semantics
    • The custom low-yield/cold check references z_score_threshold_default in combination with a custom z_score_threshold. Confirm this mixed usage is intended.
  • Calibration edge cases & IO
    • Missing or sentinel CDB entries are now interpreted differently (missing/misencoded values are less likely to be treated as hot). Ensure calibration authors expect this behavior.
    • No on-disk/format changes; interpretation at runtime changed only.
  • Performance & memory
    • Memory usage during InitRun is reduced (good). No significant runtime perf regressions expected.
  • API & tests
    • LoadCalib signature changed and CDBTTree members removed — update any internal tests or callers using the old signature.

Possible future improvements

  • Extract hotMap/z-score interpretation into a small helper / policy object for easier unit testing and per-detector policies.
  • Add unit tests and validation plots comparing pre- and post-refactor classifications across detectors (CEMC, HCALIN, HCALOUT, ZDC, SEPD).
  • Explicitly document hotMap value semantics and sentinel handling in code and calibration documentation; consider logging when calibration values are missing.
  • Consider per-detector or per-run configurable thresholds.

Note: This summary was prepared with AI assistance — AI can make mistakes. Review the changes and validate classification behavior before deployment.

Review Change Stack

…reshold check

- Added a logical split to handle the default behavior: if the z-score threshold is untouched, the code now relies strictly on the non-zero hotMap values.
- Refactored the complex if-statement for custom thresholds into an if/else block using descriptive boolean variables (`is_dead`,
  `exceeds_zscore_limit`, `is_low_yield_cold`) for better readability.
- Replaced the C-style `std::fabs` with the modern C++ standard `std::abs`.
- Guarded the entire evaluation block behind `m_doHotMap` to improve clarity.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 13, 2026

Warning

Rate limit exceeded

@pinkenburg has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 49 minutes and 7 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 21128943-e390-4ac0-9c71-0e45b827c7cf

📥 Commits

Reviewing files that changed from the base of the PR and between 1c6b4f4 and 7fff325.

📒 Files selected for processing (1)
  • offline/packages/CaloReco/CaloTowerStatus.cc
📝 Walkthrough

Walkthrough

InitRun now creates local CDBTTree instances for chi2 and hot-map, passes them into LoadCalib(CDBTTree*, CDBTTree*), deletes them after loading, removes stored CDBTTree members, and refactors process_event hot-map logic to compute is_hot_tower via default-vs-custom branching and set_isHot(true) only when true.

Changes

Calibration ownership and Hot-Tower Determination

Layer / File(s) Summary
Header: remove persistent CDBTTree members & update declaration
offline/packages/CaloReco/CaloTowerStatus.h
Removed m_cdbttree_chi2/m_cdbttree_hotMap members, adjusted includes, defaulted the destructor, and changed void LoadCalib()void LoadCalib(CDBTTree *cdbttree_chi2, CDBTTree *cdbttree_hotMap) in the class declaration.
InitRun: construct local chi2 CDBTTree
offline/packages/CaloReco/CaloTowerStatus.cc
InitRun creates a local cdbttree_chi2 from a direct URL override or CDBInterface::instance()->getUrl(...), replacing prior member usage.
InitRun: construct local hotMap CDBTTree
offline/packages/CaloReco/CaloTowerStatus.cc
InitRun creates a local cdbttree_hotMap similarly, retaining abort/no-hot-map behavior and disabling m_doHotMap when lookup fails.
Invoke LoadCalib and delete locals
offline/packages/CaloReco/CaloTowerStatus.cc
InitRun calls LoadCalib(cdbttree_chi2, cdbttree_hotMap), deletes the local CDBTTree pointers afterward, and then calls CreateNodeTree(topNode) (order changed from prior try/catch).
LoadCalib implementation updated
offline/packages/CaloReco/CaloTowerStatus.cc
LoadCalib implementation now accepts CDBTTree* args and reads calibration fields (fraction_badChi2, hotMap_val, z_score) only when the corresponding m_doHot* flag and passed pointer are present.
process_event: hot-map decision refactor
offline/packages/CaloReco/CaloTowerStatus.cc
When m_doHotMap is enabled, process_event computes is_hot_tower inside an if (m_doHotMap) block; it chooses default (hotMap_val > 0) vs custom sigma/hotMap_val rules based on z_score_threshold, and calls set_isHot(true) only when is_hot_tower is true.
CreateNodeTree: missing node handling
offline/packages/CaloReco/CaloTowerStatus.cc
When the raw tower node is missing, the code now calls gSystem->Exit(1) instead of throwing a std::runtime_error.

Possibly Related PRs

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@Steepspace
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 706f7810-9dc4-45be-a9eb-ac87ae5e6fca

📥 Commits

Reviewing files that changed from the base of the PR and between 48aea72 and 6dd4fee.

📒 Files selected for processing (1)
  • offline/packages/CaloReco/CaloTowerStatus.cc

Comment thread offline/packages/CaloReco/CaloTowerStatus.cc
@sphenix-jenkins-ci
Copy link
Copy Markdown

Build & test report

Report for commit 6dd4feed1f9ad156d07c07bc91552ea75adf31a9:
Jenkins passed


Automatically generated by sPHENIX Jenkins continuous integration
sPHENIX             jenkins.io

@Steepspace Steepspace marked this pull request as ready for review May 13, 2026 18:05
Copilot AI review requested due to automatic review settings May 13, 2026 18:05
Copy link
Copy Markdown
Contributor

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 PR refactors CaloTowerStatus hot-tower identification to separate default hot-map behavior from custom z-score threshold handling.

Changes:

  • Adds an explicit m_doHotMap guard around hot-map evaluation.
  • Splits default vs custom threshold behavior.
  • Introduces named boolean conditions for custom threshold logic and switches to std::abs.

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

Comment thread offline/packages/CaloReco/CaloTowerStatus.cc Outdated
Using hotMap_val != 0 treats the CDB missing-value as a hot tower: CDBTTree::GetIntValue returns std::numeric_limits<int>::min() when the channel or field is absent, and that value is nonzero. This is a behavior regression from the previous explicit status checks and could mask towers if a calibration entry is missing or malformed; limit this default path to valid positive/known status codes instead of any nonzero value.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@pinkenburg
Copy link
Copy Markdown
Contributor

Why is the calibration read from the cdbttree in a separate method? The current scheme forces you to keep the cdbttree object around until the class is deleted (at the end of processing) wasting quite some memory. The arrays with the calibrations should be filled when the cdb is read and then the cdbttrees should be deleted. That simplifies the code and will make it more efficient.

@pinkenburg pinkenburg marked this pull request as draft May 13, 2026 18:35
Refactored the calibration loading process to ensure heavy tree objects are freed from memory immediately after use.

- Moved m_cdbttree_chi2 and m_cdbttree_hotMap from class members to local variables within InitRun to minimize the memory footprint during the event processing loop.
- Updated the LoadCalib method to accept CDBTTree pointers as arguments.
- Implemented immediate deletion of the tree objects after populating the m_cdbInfo_vec cache.
- Simplified the class destructor as it no longer needs to manage these pointers.
@Steepspace
Copy link
Copy Markdown
Contributor Author

@pinkenburg Thanks for pointing out the memory waste with the CDBTTree objects. I’ve refactored the class to delete those trees immediately after the CDB data is loaded into the local cache.
If you could confirm it looks good on your end that would be great.

@sphenix-jenkins-ci
Copy link
Copy Markdown

Build & test report

Report for commit 6589011221aabb717be317596a87dae67e0156bb:
Jenkins passed


Automatically generated by sPHENIX Jenkins continuous integration
sPHENIX             jenkins.io

@sphenix-jenkins-ci
Copy link
Copy Markdown

Build & test report

Report for commit ea5838429ff86f205058cdf062dd1d9f728fc499:
Jenkins passed


Automatically generated by sPHENIX Jenkins continuous integration
sPHENIX             jenkins.io

@Steepspace Steepspace marked this pull request as ready for review May 14, 2026 00:26
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.

Caution

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

⚠️ Outside diff range comments (1)
offline/packages/CaloReco/CaloTowerStatus.cc (1)

79-184: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use RAII (e.g., std::unique_ptr<CDBTTree>) for the local calibration trees to avoid leaks on the exceptional paths.

The new ownership model is a nice cleanup, but the cdbttree_* raw pointers are not exception-safe:

  • If new CDBTTree(calibdir_hotMap) at lines 128 or 135 throws, cdbttree_chi2 (already allocated at lines 89/96) is leaked.
  • The delete calls at lines 177–178 sit inside the try block after LoadCalib. If CreateNodeTree(topNode) throws (it does on a missing DST node, per line 288) or LoadCalib throws, both trees leak — control jumps to the catch at line 180 and the deletes are skipped.

Wrapping the locals in std::unique_ptr<CDBTTree> (and passing .get() into LoadCalib) makes the lifetime correct on every path and removes the raw new/delete pair entirely.

♻️ Suggested RAII refactor
-  CDBTTree *cdbttree_chi2 = nullptr;
+  std::unique_ptr<CDBTTree> cdbttree_chi2;
@@
-    cdbttree_chi2 = new CDBTTree(calibdir_chi2);
+    cdbttree_chi2 = std::make_unique<CDBTTree>(calibdir_chi2);
@@
-      cdbttree_chi2 = new CDBTTree(calibdir_chi2);
+      cdbttree_chi2 = std::make_unique<CDBTTree>(calibdir_chi2);
@@
-  CDBTTree *cdbttree_hotMap = nullptr;
+  std::unique_ptr<CDBTTree> cdbttree_hotMap;
@@
-    cdbttree_hotMap = new CDBTTree(calibdir_hotMap);
+    cdbttree_hotMap = std::make_unique<CDBTTree>(calibdir_hotMap);
@@
-      cdbttree_hotMap = new CDBTTree(calibdir_hotMap);
+      cdbttree_hotMap = std::make_unique<CDBTTree>(calibdir_hotMap);
@@
-    LoadCalib(cdbttree_chi2, cdbttree_hotMap);
-
-    delete cdbttree_chi2;
-    delete cdbttree_hotMap;
+    LoadCalib(cdbttree_chi2.get(), cdbttree_hotMap.get());
   }

Add #include <memory> if not already pulled in transitively. LoadCalib's CDBTTree* signature stays unchanged, and the trees are released automatically at end of scope, including on the catch path.

As per coding guidelines ("ownership semantics (RAII), and avoiding raw new/delete").

♻️ Duplicate comments (1)
offline/packages/CaloReco/CaloTowerStatus.cc (1)

242-266: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Asymmetry between default and custom z_score_threshold paths still stands.

The default branch (line 249) flags every hotMap_val > 0 as hot, while the custom branch only enumerates codes 1 and 3 explicitly and relies on the sigma cut to catch anything else (e.g. hotMap_val == 2). A tower with code 2 and a modest z-score can therefore be hot in the default path and clean in the custom path — that's a real behavioral fork driven only by whether z_score_threshold was changed. Worth either making the custom branch handle all non-zero codes the default does, or documenting the intended hotMap-code semantics in the header so future readers know this is by design.

Also note line 256 uses z_score_threshold_default rather than the user-supplied z_score_threshold for the cold-tail gate on code-3 towers — please double-check that this mixed-threshold behavior is what you want when a physicist sets a custom sigma.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8e4fa331-f6b3-4a67-8f05-c073af9f77ed

📥 Commits

Reviewing files that changed from the base of the PR and between 6589011 and ea58384.

📒 Files selected for processing (2)
  • offline/packages/CaloReco/CaloTowerStatus.cc
  • offline/packages/CaloReco/CaloTowerStatus.h

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: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 55d10523-4c6e-4dd1-b933-0c93a17829a0

📥 Commits

Reviewing files that changed from the base of the PR and between ea58384 and 1c6b4f4.

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

Comment thread offline/packages/CaloReco/CaloTowerStatus.cc Outdated
@sphenix-jenkins-ci
Copy link
Copy Markdown

Build & test report

Report for commit 1c6b4f480350df179d025c890c8f49aeab264f8a:
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 7fff3252e3eb9012bac12cacd4066f1ba7215e91:
Jenkins passed


Automatically generated by sPHENIX Jenkins continuous integration
sPHENIX             jenkins.io

@pinkenburg pinkenburg merged commit 3381348 into sPHENIX-Collaboration:master May 15, 2026
22 checks passed
@Steepspace Steepspace deleted the CaloTowerStatus branch May 15, 2026 03:10
@coderabbitai coderabbitai Bot mentioned this pull request May 18, 2026
4 tasks
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.

3 participants