Skip to content

Add optical sensitive detectors#2215

Open
hhollenb wants to merge 12 commits intoceleritas-project:developfrom
hhollenb:optical-sd
Open

Add optical sensitive detectors#2215
hhollenb wants to merge 12 commits intoceleritas-project:developfrom
hhollenb:optical-sd

Conversation

@hhollenb
Copy link
Contributor

@hhollenb hhollenb commented Jan 28, 2026

Add optical sensitive detectors for scoring photon hits for #1397

As a simple first implementation of scoring hits, the same detectors from DetectorParams is used to define the detector regions. The DetectorExecutor kernel is responsible for copying hits for every track into the state buffer, with tracks not in detector regions marking their hits with an invalid detector ID. All hits are copied into pinned memory on the host, which is then cleaned up and passed to a user callback function.

This isn't an optimized / complete implementation by any means, but I've added some tests to help check future optimizations are correct.

I ran into some collisions with naming ScoringParams and DetectorParams so I just wrote something quickly and I'm seeking suggestions for what to rename things to.

@hhollenb hhollenb requested a review from amandalund January 28, 2026 23:00
@hhollenb hhollenb requested a review from sethrj as a code owner January 28, 2026 23:00
@hhollenb hhollenb added enhancement New feature or request scoring Hits and track diagnostics labels Jan 28, 2026
@codecov
Copy link

codecov bot commented Jan 28, 2026

Codecov Report

❌ Patch coverage is 88.46154% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.30%. Comparing base (de25875) to head (9300f46).

Files with missing lines Patch % Lines
src/celeritas/optical/detector/DetectorAction.cc 78.78% 6 Missing and 1 partial ⚠️
src/celeritas/optical/detector/DetectorData.hh 75.00% 3 Missing ⚠️
src/celeritas/optical/detector/DetectorExecutor.hh 90.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #2215      +/-   ##
===========================================
+ Coverage    86.29%   86.30%   +0.01%     
===========================================
  Files         1316     1323       +7     
  Lines        41605    41711     +106     
  Branches     12835    12853      +18     
===========================================
+ Hits         35902    36000      +98     
- Misses        4506     4515       +9     
+ Partials      1197     1196       -1     
Files with missing lines Coverage Δ
src/celeritas/inp/Problem.hh 80.00% <ø> (ø)
src/celeritas/inp/Scoring.hh 92.30% <100.00%> (+0.64%) ⬆️
src/celeritas/optical/CoreParams.hh 95.00% <100.00%> (+0.26%) ⬆️
src/celeritas/optical/CoreTrackData.cc 100.00% <100.00%> (ø)
src/celeritas/optical/CoreTrackData.hh 93.33% <100.00%> (+0.47%) ⬆️
src/celeritas/optical/CoreTrackView.hh 81.96% <100.00%> (+1.61%) ⬆️
src/celeritas/optical/detector/DetectorAction.hh 100.00% <100.00%> (ø)
src/celeritas/optical/detector/DetectorData.cc 100.00% <100.00%> (ø)
src/celeritas/optical/detector/ScoringParams.cc 100.00% <100.00%> (ø)
src/celeritas/optical/detector/ScoringTrackView.hh 100.00% <100.00%> (ø)
... and 4 more

... and 6 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions
Copy link

github-actions bot commented Jan 28, 2026

Test summary

 5 825 files   9 348 suites   18m 19s ⏱️
 2 124 tests  2 097 ✅  27 💤 0 ❌
31 774 runs  31 633 ✅ 141 💤 0 ❌

Results for commit 9300f46.

♻️ This comment has been updated with latest results.

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 PR adds a simple first implementation of optical sensitive detectors for scoring photon hits, addressing issue #1397. The implementation uses existing detector regions from DetectorParams to define where hits should be recorded. Hits are copied for every track into a state buffer, with tracks not in detector regions marked with invalid detector IDs. All hits are transferred to pinned memory on the host, cleaned up, and passed to a user callback function.

Changes:

  • Added optical detector scoring infrastructure with ScoringParams, DetectorAction, ScoringTrackView, and related data structures
  • Integrated scoring into the optical core parameters and track initialization
  • Added comprehensive tests for hit recording with both small and large numbers of photons

Reviewed changes

Copilot reviewed 28 out of 28 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
test/geocel/data/optical-box.gdml Modified GDML geometry to add 5 surrounding detector volumes instead of optical surfaces
test/celeritas/optical/SurfacePhysicsIntegrationTestBase.cc Removed default surface setup that's no longer needed
test/celeritas/optical/OpticalMockTestBase.hh Added stub for build_optical_scoring
test/celeritas/optical/Detector.test.cc New comprehensive test file with simple and stress tests for detector scoring
test/celeritas/OnlyCoreTestBase.hh Added unreachable stub for build_optical_scoring
test/celeritas/ImportedDataTestBase.hh/cc Added implementation of build_optical_scoring that creates empty scoring params
test/celeritas/GlobalTestBase.hh/cc Added optical_scoring accessors and virtual detector() override support
test/celeritas/CMakeLists.txt Registered new detector tests
src/celeritas/setup/Problem.cc Integrated ScoringParams creation into optical problem setup
src/celeritas/optical/detector/ScoringTrackView.hh New track view for accessing and managing hit data per track
src/celeritas/optical/detector/ScoringParams.hh/cc New params class that manages user callback and registers DetectorAction
src/celeritas/optical/detector/DetectorExecutor.hh New executor that copies hit data for all tracks at end of step
src/celeritas/optical/detector/DetectorData.hh/cc/cu New data structures for hits and state, plus host/device copy functions
src/celeritas/optical/detector/DetectorAction.hh/cc/cu New post-step action that executes detector executor and processes hits
src/celeritas/optical/CoreTrackView.hh Added scoring() accessor and initialization of hit data
src/celeritas/optical/CoreTrackData.hh/cc Added DetectorStateData to core state and resize logic
src/celeritas/optical/CoreParams.hh Added ScoringParams member and accessor
src/celeritas/inp/Scoring.hh Added OpticalScoring struct with hit callback function
src/celeritas/inp/Problem.hh Added scoring field to OpticalProblem
src/celeritas/CMakeLists.txt Added new source files to build

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

Comment on lines +9 to +13
#include <iostream>

namespace celeritas
{
namespace optical
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

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

The iostream header is included but not used in this file. This should be removed to avoid unnecessary includes.

Suggested change
#include <iostream>
namespace celeritas
{
namespace optical
namespace celeritas
{
namespace optical
namespace optical

Copilot uses AI. Check for mistakes.
/*!
* Copy hits from host state data to pinned memory.
*
* Because both buffer reside host-side, this is just a trivial copy between
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

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

Spelling error: "reside" should be "reside". The comment should read "Because both buffers reside host-side" not "Because both buffer reside host-side".

Suggested change
* Because both buffer reside host-side, this is just a trivial copy between
* Because both buffers reside host-side, this is just a trivial copy between

Copilot uses AI. Check for mistakes.
* Process hits copied from the kernels and send them to the callback.
*
* Copied hits might be invalid, and are removed before sending into the
* callback function. The callback is only execute when a non-zero amount of
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

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

Spelling error: "execute" should be "executed". The comment should read "The callback is only executed when a non-zero amount of valid hits occurs."

Suggested change
* callback function. The callback is only execute when a non-zero amount of
* callback function. The callback is only executed when a non-zero amount of

Copilot uses AI. Check for mistakes.
Comment on lines +28 to +36
class ScoringParams final
{
public:
//!@{
//! \name Type aliases
using HitCallbackFunc = inp::OpticalScoring::HitCallbackFunc;
//!@}

public:
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

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

The naming scheme between "Scoring" and "Detector" terminology is potentially confusing. The PR description mentions this as a known issue. Consider:

  • DetectorStateData is accessed via the "scoring" member in CoreStateData
  • ScoringParams manages DetectorAction
  • ScoringTrackView operates on DetectorStateData

For clarity and consistency, consider either:

  1. Renaming to use "Detector" consistently (e.g., DetectorScoringParams, DetectorTrackView) to clarify these are specifically for detector scoring
  2. Renaming to use "Scoring" consistently for the state and views
  3. Using "Hit" terminology more consistently (e.g., HitScoringParams, HitStateData, HitTrackView)

The current mixed terminology could lead to confusion about which components relate to detector geometry vs. hit scoring.

Copilot uses AI. Check for mistakes.
Copy link
Member

@sethrj sethrj left a comment

Choose a reason for hiding this comment

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

Sorry this took me a minute to review. I have just a couple of thoughts, most of them with naming. Perhaps we could do a quick hackathon today/tomorrow to get the names all lined up across the codebase?

ParticleStateData<W, M> particle;
PhysicsStateData<W, M> physics;
RngStateData<W, M> rng;
DetectorStateData<W, M> scoring;
Copy link
Member

Choose a reason for hiding this comment

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

This is confusing...

CELER_FUNCTION void ScoringTrackView::score_hit(DetectorHit hit)
{
CELER_EXPECT(hit.detector);
this->hit() = std::move(hit);
Copy link
Member

Choose a reason for hiding this comment

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

Since move can't be used on device (and the data is all trivial) let's just send the argument by const reference and make a copy here.

using StateItems = StateCollection<T, W, M>;
//!@}

StateItems<DetectorHit> all_track_hits;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
StateItems<DetectorHit> all_track_hits;
StateItems<DetectorHit> detector_hits;

for consistency with other state data objects in the code

* back to the user provided callback function. If the callback function is not
* provided, then no \c DetectorAction is created.
*/
class ScoringParams final
Copy link
Member

Choose a reason for hiding this comment

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

We don't mark as final any of the other classes that don't inherit from an interface

* where invalid hits are erased. A span of only valid hits is then passed into
* the user provided callback function.
*/
class DetectorAction final : public OpticalStepActionInterface,
Copy link
Member

Choose a reason for hiding this comment

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

I think to be consistent these should all be ScoringAction/Data/Executor... we might want to defer renaming to a different PR though, or at least lay out what the

I was also confused initially because DetectorAction is just an implementation detail of ScoringParams, and the creation of those is what does all the work (kind of like the optical offload). The action/executor definitely need to be in a detail namespace.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Looking back on it, I think scoring is a bad name cause that is usually used for the simpler tallying of things like energy deposits. Maybe a DetectorHitParams or UserDetectorParams is better for indicating that it's meant to deal with the actual hits and callbacks?

Copy link
Member

@sethrj sethrj Feb 4, 2026

Choose a reason for hiding this comment

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

Don't think too hard about it; we could easily end up in a hell of combining detector, sensitive, hit, callback, user, score, tally, ...

I think "sensitive detector" is a special case of a "scoring region": the scoring region is specifically a set of logical volumes, and the scoring action is a general "callback" capability that gets the entire step.

An arguably scoring regions are just a specialization of a "user stepping action"...

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

Labels

enhancement New feature or request scoring Hits and track diagnostics

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants