diff --git a/src/celeritas/CMakeLists.txt b/src/celeritas/CMakeLists.txt index 1918cf250c..e2624d961f 100644 --- a/src/celeritas/CMakeLists.txt +++ b/src/celeritas/CMakeLists.txt @@ -110,6 +110,7 @@ list(APPEND SOURCES optical/CoreParams.cc optical/CoreState.cc optical/CoreTrackData.cc + optical/detector/ScoringParams.cc optical/gen/CherenkovParams.cc optical/gen/ScintillationParams.cc optical/gen/GeneratorBase.cc @@ -390,6 +391,8 @@ celeritas_polysource(optical/action/DiscreteSelectAction) celeritas_polysource(optical/action/PreStepAction) celeritas_polysource(optical/action/TrackingCutAction) celeritas_polysource(optical/action/detail/TrackInitAlgorithms) +celeritas_polysource(optical/detector/DetectorData) +celeritas_polysource(optical/detector/DetectorAction) celeritas_polysource(optical/gen/GeneratorAction) celeritas_polysource(optical/gen/OffloadAction) celeritas_polysource(optical/gen/OffloadGatherAction) diff --git a/src/celeritas/inp/Problem.hh b/src/celeritas/inp/Problem.hh index 387443d4b1..139def62d4 100644 --- a/src/celeritas/inp/Problem.hh +++ b/src/celeritas/inp/Problem.hh @@ -92,6 +92,8 @@ struct OpticalProblem OpticalTrackingLimits limits; //! Per-process state sizes for optical tracking loop OpticalStateCapacity capacity; + //! User scoring configuration for optical detectors + OpticalScoring scoring; //! Number of streams size_type num_streams{}; //! Random number generator seed diff --git a/src/celeritas/inp/Scoring.hh b/src/celeritas/inp/Scoring.hh index 8eb49676cd..fea4380ab9 100644 --- a/src/celeritas/inp/Scoring.hh +++ b/src/celeritas/inp/Scoring.hh @@ -6,6 +6,7 @@ //---------------------------------------------------------------------------// #pragma once +#include #include #include #include @@ -19,6 +20,11 @@ class G4LogicalVolume; namespace celeritas { +namespace optical +{ +struct DetectorHit; +} + namespace inp { //---------------------------------------------------------------------------// @@ -148,6 +154,22 @@ struct Scoring std::optional simple_calo; }; +//---------------------------------------------------------------------------// +/*! + * Enable detector callback for hits in optical physics simulations. + */ +struct OpticalScoring +{ + //!@{ + //! \name Type aliases + using HitCallbackFunc + = std::function const&)>; + //!@} + + //! Hit callback function for optical detectors + std::optional detector_callback; +}; + //---------------------------------------------------------------------------// } // namespace inp } // namespace celeritas diff --git a/src/celeritas/optical/CoreParams.hh b/src/celeritas/optical/CoreParams.hh index 4fd532175c..9fcf5d66a7 100644 --- a/src/celeritas/optical/CoreParams.hh +++ b/src/celeritas/optical/CoreParams.hh @@ -33,6 +33,7 @@ namespace optical //---------------------------------------------------------------------------// class MaterialParams; class PhysicsParams; +class ScoringParams; class SimParams; class SurfacePhysicsParams; //---------------------------------------------------------------------------// @@ -57,6 +58,7 @@ class CoreParams final : public ParamsDataInterface using SPConstSurface = std::shared_ptr; using SPConstSurfacePhysics = std::shared_ptr; using SPConstDetectors = std::shared_ptr; + using SPConstScoring = std::shared_ptr; using SPConstCherenkov = std::shared_ptr; using SPConstScintillation = std::shared_ptr; @@ -85,6 +87,7 @@ class CoreParams final : public ParamsDataInterface SPConstSurfacePhysics surface_physics; SPConstDetectors detectors; + SPConstScoring scoring; //!< Optional SPConstCherenkov cherenkov; //!< Optional SPConstScintillation scintillation; //!< Optional @@ -134,6 +137,7 @@ class CoreParams final : public ParamsDataInterface SPAuxRegistry const& aux_reg() const { return input_.aux_reg; } SPGeneratorRegistry const& gen_reg() const { return input_.gen_reg; } SPConstDetectors const& detectors() const { return input_.detectors; } + SPConstScoring const& scoring() const { return input_.scoring; } SPConstCherenkov const& cherenkov() const { return input_.cherenkov; } SPConstScintillation const& scintillation() const { diff --git a/src/celeritas/optical/CoreTrackData.cc b/src/celeritas/optical/CoreTrackData.cc index dbe0c359b9..42d06fb22c 100644 --- a/src/celeritas/optical/CoreTrackData.cc +++ b/src/celeritas/optical/CoreTrackData.cc @@ -37,6 +37,7 @@ void resize(CoreStateData* state, resize(&state->particle, size); resize(&state->physics, size); resize(&state->rng, params.rng, stream_id, size); + resize(&state->scoring, size); resize(&state->sim, size); resize(&state->surface_physics, size); resize(&state->init, stream_id, size); diff --git a/src/celeritas/optical/CoreTrackData.hh b/src/celeritas/optical/CoreTrackData.hh index abbac783c7..ab858dd84c 100644 --- a/src/celeritas/optical/CoreTrackData.hh +++ b/src/celeritas/optical/CoreTrackData.hh @@ -20,6 +20,7 @@ #include "PhysicsData.hh" #include "SimData.hh" #include "TrackInitData.hh" +#include "detector/DetectorData.hh" #include "gen/CherenkovData.hh" #include "gen/ScintillationData.hh" #include "surface/SurfacePhysicsData.hh" @@ -103,6 +104,7 @@ struct CoreStateData ParticleStateData particle; PhysicsStateData physics; RngStateData rng; + DetectorStateData scoring; SimStateData sim; SurfacePhysicsStateData surface_physics; TrackInitStateData init; @@ -116,8 +118,8 @@ struct CoreStateData //! Whether the data are assigned explicit CELER_FUNCTION operator bool() const { - return geometry && particle && physics && rng && sim && surface_physics - && init && stream_id; + return geometry && particle && physics && rng && scoring && sim + && surface_physics && init && stream_id; } //! Assign from another set of data @@ -129,6 +131,7 @@ struct CoreStateData particle = other.particle; physics = other.physics; rng = other.rng; + scoring = other.scoring; sim = other.sim; surface_physics = other.surface_physics; init = other.init; diff --git a/src/celeritas/optical/CoreTrackView.hh b/src/celeritas/optical/CoreTrackView.hh index f585c22d44..a781516e1d 100644 --- a/src/celeritas/optical/CoreTrackView.hh +++ b/src/celeritas/optical/CoreTrackView.hh @@ -17,6 +17,7 @@ #include "PhysicsTrackView.hh" #include "SimTrackView.hh" #include "TrackInitializer.hh" +#include "detector/ScoringTrackView.hh" #include "surface/SurfacePhysicsTrackView.hh" #if !CELER_DEVICE_COMPILE @@ -82,6 +83,9 @@ class CoreTrackView // Return a sensitive detector view inline CELER_FUNCTION DetectorView detectors() const; + // Return a scoring view + inline CELER_FUNCTION ScoringTrackView scoring() const; + // Return an RNG engine inline CELER_FUNCTION RngEngine rng() const; @@ -151,6 +155,9 @@ CoreTrackView::operator=(TrackInitializer const& init) // Initialize the surface state this->surface_physics().reset(); + // Initialize scoring + this->scoring().clear_hit(); + return *this; } @@ -259,6 +266,14 @@ CELER_FUNCTION auto CoreTrackView::detectors() const -> DetectorView return DetectorView{params_.detectors}; } +//---------------------------------------------------------------------------// +/*! + * Return a scoring view. + */ +CELER_FUNCTION auto CoreTrackView::scoring() const -> ScoringTrackView +{ + return ScoringTrackView{states_.scoring, this->track_slot_id()}; +} //---------------------------------------------------------------------------// /*! * Return the RNG engine. diff --git a/src/celeritas/optical/detector/DetectorAction.cc b/src/celeritas/optical/detector/DetectorAction.cc new file mode 100644 index 0000000000..4ecf40d387 --- /dev/null +++ b/src/celeritas/optical/detector/DetectorAction.cc @@ -0,0 +1,104 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detector/DetectorAction.cc +//---------------------------------------------------------------------------// +#include "DetectorAction.hh" + +#include "celeritas/optical/action/ActionLauncher.hh" +#include "celeritas/optical/action/TrackSlotExecutor.hh" + +#include "DetectorExecutor.hh" + +namespace celeritas +{ +namespace optical +{ +//---------------------------------------------------------------------------// +/*! + * Construct with action ID. + */ +DetectorAction::DetectorAction(ActionId aid) + : StaticConcreteAction(aid, "scoring-detector", "Score detector hits") +{ +} + +//---------------------------------------------------------------------------// +/*! + * Launch the detector action on host. + */ +void DetectorAction::step(CoreParams const& params, CoreStateHost& state) const +{ + TrackSlotExecutor execute{ + params.ptr(), state.ptr(), DetectorExecutor{}}; + launch_action(state, execute); + + this->process_hits(params, state); +} + +#if !CELER_USE_DEVICE +void DetectorAction::step(CoreParams const&, CoreStateDevice&) const +{ + CELER_NOT_CONFIGURED("CUDA OR HIP"); +} +#endif + +//---------------------------------------------------------------------------// +/*! + * Process hits copied from the kernels and send them to the callback. + */ +void DetectorAction::process_hits(CoreParams const& params, + CoreStateHost& state) const +{ + this->process_hits_impl(params, state); +} + +//---------------------------------------------------------------------------// +/*! + * Process hits copied from the kernels and send them to the callback. + */ +void DetectorAction::process_hits(CoreParams const& params, + CoreStateDevice& state) const +{ + this->process_hits_impl(params, state); +} + +//---------------------------------------------------------------------------// +/*! + * 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 + * valid hits occurs. + */ +template +void DetectorAction::process_hits_impl(CoreParams const& params, + CoreState& state) const +{ + DetectorHitOutput hit_results; + + // Copy hits (possibly from device) into pinned vector + copy_hits(&hit_results, state.ref().scoring, state.stream_id()); + + // Erase all hits with invalid detector ID + hit_results.hits.erase( + std::remove_if(hit_results.hits.begin(), + hit_results.hits.end(), + [](DetectorHit const& hit) { + return !static_cast(hit.detector); + }), + hit_results.hits.end()); + + // Send hits to the callback function, if there are any + if (!hit_results.hits.empty()) + { + auto const& scoring = params.scoring(); + CELER_ASSERT(scoring); + scoring->process_hits(make_span(hit_results.hits)); + } +} + +//---------------------------------------------------------------------------// +} // namespace optical +} // namespace celeritas diff --git a/src/celeritas/optical/detector/DetectorAction.cu b/src/celeritas/optical/detector/DetectorAction.cu new file mode 100644 index 0000000000..c5b6347806 --- /dev/null +++ b/src/celeritas/optical/detector/DetectorAction.cu @@ -0,0 +1,35 @@ +//------------------------------ -*- cuda -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detector/DetectorAction.cu +//---------------------------------------------------------------------------// +#include "DetectorAction.hh" + +#include "celeritas/optical/action/ActionLauncher.device.hh" +#include "celeritas/optical/action/TrackSlotExecutor.hh" + +#include "DetectorExecutor.hh" + +namespace celeritas +{ +namespace optical +{ +//---------------------------------------------------------------------------// +/*! + * Launch the detector action on device. + */ +void DetectorAction::step(CoreParams const& params, CoreStateDevice& state) const +{ + TrackSlotExecutor execute{ + params.ptr(), state.ptr(), DetectorExecutor{}}; + + static ActionLauncher const launch_kernel(*this); + launch_kernel(state, execute); + + this->process_hits(params, state); +} + +//---------------------------------------------------------------------------// +} // namespace optical +} // namespace celeritas diff --git a/src/celeritas/optical/detector/DetectorAction.hh b/src/celeritas/optical/detector/DetectorAction.hh new file mode 100644 index 0000000000..24f6936c11 --- /dev/null +++ b/src/celeritas/optical/detector/DetectorAction.hh @@ -0,0 +1,62 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detector/DetectorAction.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +#include "celeritas/optical/CoreParams.hh" +#include "celeritas/optical/CoreState.hh" +#include "celeritas/optical/action/ActionInterface.hh" + +#include "DetectorData.hh" +#include "ScoringParams.hh" + +namespace celeritas +{ +namespace optical +{ +//---------------------------------------------------------------------------// +/*! + * Record sensitive detector data for optical photons at the end of every step. + * + * The \c DetectorExecutor is responsible for copying hit data for every photon + * into the state buffer at the end of every step on a kernel level. Even if a + * track was not in a detector, it is still copied into the state buffer with + * an invalid detector ID. All hits are copied into pinned memory on the host, + * 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, + public StaticConcreteAction +{ + public: + // Construct with ID + explicit DetectorAction(ActionId); + + // Launch kernel with host data + void step(CoreParams const&, CoreStateHost&) const final; + + // Launch kernel with device data + void step(CoreParams const&, CoreStateDevice&) const final; + + //! Dependency ordering of the action + StepActionOrder order() const final { return StepActionOrder::post; } + + private: + //!@{ + //! Process hits copied from the kernels and send them to the callback + void process_hits(CoreParams const&, CoreStateHost&) const; + void process_hits(CoreParams const&, CoreStateDevice&) const; + + template + void process_hits_impl(CoreParams const&, CoreState&) const; + //!@} +}; + +//---------------------------------------------------------------------------// +} // namespace optical +} // namespace celeritas diff --git a/src/celeritas/optical/detector/DetectorData.cc b/src/celeritas/optical/detector/DetectorData.cc new file mode 100644 index 0000000000..a07650d3a6 --- /dev/null +++ b/src/celeritas/optical/detector/DetectorData.cc @@ -0,0 +1,39 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detector/DetectorData.cc +//---------------------------------------------------------------------------// +#include "DetectorData.hh" + +#include + +namespace celeritas +{ +namespace optical +{ +//---------------------------------------------------------------------------// +/*! + * Copy hits from host state data to pinned memory. + * + * Because both buffer reside host-side, this is just a trivial copy between + * the buffers. + */ +template<> +void copy_hits( + DetectorHitOutput* output, + DetectorStateData const& state, + StreamId /* unused */) +{ + // Trivial copy to pinned memory + output->hits.resize(state.all_track_hits.size()); + + for (auto tid : range(TrackSlotId{state.all_track_hits.size()})) + { + output->hits[tid.unchecked_get()] = state.all_track_hits[tid]; + } +} + +//---------------------------------------------------------------------------// +} // namespace optical +} // namespace celeritas diff --git a/src/celeritas/optical/detector/DetectorData.cu b/src/celeritas/optical/detector/DetectorData.cu new file mode 100644 index 0000000000..f036709526 --- /dev/null +++ b/src/celeritas/optical/detector/DetectorData.cu @@ -0,0 +1,48 @@ +//------------------------------ -*- cuda -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detector/DetectorData.cu +//---------------------------------------------------------------------------// +#include "DetectorData.hh" + +namespace celeritas +{ +namespace optical +{ +//---------------------------------------------------------------------------// +/*! + * Copy hits from device to pinned memory. + * + * All hits from all tracks are copied. These may include invalid hits where a + * track is not in a detector, which is indicated by an invalid detector ID. + * The user of the output is therefore responsible for parsing the pinned + * memory for only valid hits. + */ +template<> +void copy_hits( + DetectorHitOutput* output, + DetectorStateData const& state, + StreamId stream_id) +{ + CELER_EXPECT(output); + CELER_EXPECT(stream_id); + + size_type num_tracks = state.all_track_hits.size(); + + // Copy all track hits from device + output->hits.resize(num_tracks); + Copier copy{{output->hits.data(), num_tracks}, + stream_id}; + copy(MemSpace::device, {state.all_track_hits.data().get(), num_tracks}); + + // Synchronize to ensure all data is transferred before continuing + CELER_DEVICE_API_CALL( + StreamSynchronize(celeritas::device().stream(stream_id).get())); + + CELER_ENSURE(output->hits.size() == num_tracks); +} + +//---------------------------------------------------------------------------// +} // namespace optical +} // namespace celeritas diff --git a/src/celeritas/optical/detector/DetectorData.hh b/src/celeritas/optical/detector/DetectorData.hh new file mode 100644 index 0000000000..572322f9fc --- /dev/null +++ b/src/celeritas/optical/detector/DetectorData.hh @@ -0,0 +1,139 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detector/DetectorData.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +#include "corecel/data/Collection.hh" +#include "corecel/data/PinnedAllocator.hh" +#include "celeritas/Quantities.hh" +#include "celeritas/optical/Types.hh" + +namespace celeritas +{ +namespace optical +{ +//---------------------------------------------------------------------------// +/*! + * A single hit of a photon track on a sensitive detector. + */ +struct DetectorHit +{ + using Energy = units::MevEnergy; + + DetectorId detector{}; + Energy energy; + real_type time; + Real3 position; + VolumeInstanceId volume_instance; +}; + +//---------------------------------------------------------------------------// +/*! + * Pinned memory buffer for transferring detector hits. + */ +struct DetectorHitOutput +{ + //!@{ + //! \name Type aliases + template + using PinnedVec = std::vector>; + //!@} + + PinnedVec hits; +}; + +//---------------------------------------------------------------------------// +/*! + * State buffer for storing detector hits. + * + * Detector hits is large enough to store a hit for every track at the end of a + * step. Stored hits may be invalid if the track is not in a detector region. + */ +template +struct DetectorStateData +{ + //!@{ + //! \name Type aliases + template + using StateItems = StateCollection; + //!@} + + StateItems all_track_hits; + + //! Whether data is assigned and valid + explicit CELER_FUNCTION operator bool() const + { + return !all_track_hits.empty(); + } + + //! State size + CELER_FUNCTION size_type size() const { return all_track_hits.size(); } + + //! Assign from another set of data + template + DetectorStateData& operator=(DetectorStateData& other) + { + CELER_EXPECT(other); + all_track_hits = other.all_track_hits; + return *this; + } +}; + +//---------------------------------------------------------------------------// +// Copy hits from a memory space to pinned memory + +template +void copy_hits(DetectorHitOutput* output, + DetectorStateData const& state, + StreamId stream); + +template<> +void copy_hits( + DetectorHitOutput*, + DetectorStateData const&, + StreamId); + +template<> +void copy_hits( + DetectorHitOutput*, + DetectorStateData const&, + StreamId); + +//---------------------------------------------------------------------------// +#if !CELER_USE_DEVICE +template<> +inline void +copy_hits(DetectorHitOutput*, + DetectorStateData const&, + StreamId) +{ + CELER_NOT_CONFIGURED("CUDA OR HIP"); +} +#endif + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Resize the state in host code. + */ +template +inline void +resize(DetectorStateData* state, size_type size) +{ + CELER_EXPECT(state); + CELER_EXPECT(size > 0); + + resize(&state->all_track_hits, size); + + CELER_ENSURE(*state); +} + +//---------------------------------------------------------------------------// +} // namespace optical +} // namespace celeritas diff --git a/src/celeritas/optical/detector/DetectorExecutor.hh b/src/celeritas/optical/detector/DetectorExecutor.hh new file mode 100644 index 0000000000..ced9542eff --- /dev/null +++ b/src/celeritas/optical/detector/DetectorExecutor.hh @@ -0,0 +1,77 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detector/DetectorExecutor.hh +//---------------------------------------------------------------------------// +#pragma once + +namespace celeritas +{ +namespace optical +{ +//---------------------------------------------------------------------------// +/*! + * Populate detector state buffer at the end of a step. + * + * All tracks have hits copied into the state buffer. If the track is not alive + * or is not in a detector region, an invalid hit is set in the corresponding + * buffer track slot. + * + * When a track generates a valid hit, it is killed (absorbed by the detector). + */ +struct DetectorExecutor +{ + // Copy track hit into the state buffer + inline CELER_FUNCTION void operator()(CoreTrackView const&) const; +}; + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Copy track hit into the state buffer. + */ +CELER_FUNCTION void +DetectorExecutor::operator()(CoreTrackView const& track) const +{ + auto score = track.scoring(); + auto sim = track.sim(); + + if (sim.status() == TrackStatus::alive) + { + auto const detectors = track.detectors(); + + auto geometry = track.geometry(); + + auto const volume_id = geometry.volume_id(); + auto const detector_id = detectors.detector_id(volume_id); + + if (detector_id) + { + // Score a valid hit + score.score_hit(DetectorHit{detector_id, + track.particle().energy(), + sim.time(), + geometry.pos(), + geometry.volume_instance_id()}); + + // Kill the track + sim.status(TrackStatus::killed); + } + else + { + // Mark that the track is not in a detector + score.clear_hit(); + } + } + else + { + // Ensure killed, inactive, and errored tracks don't contribute to hits + score.clear_hit(); + } +} + +//---------------------------------------------------------------------------// +} // namespace optical +} // namespace celeritas diff --git a/src/celeritas/optical/detector/ScoringParams.cc b/src/celeritas/optical/detector/ScoringParams.cc new file mode 100644 index 0000000000..373036378b --- /dev/null +++ b/src/celeritas/optical/detector/ScoringParams.cc @@ -0,0 +1,60 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detector/ScoringParams.cc +//---------------------------------------------------------------------------// +#include "ScoringParams.hh" + +#include "corecel/cont/Span.hh" +#include "corecel/io/Logger.hh" +#include "corecel/sys/ActionRegistry.hh" + +#include "DetectorAction.hh" +#include "DetectorData.hh" + +namespace celeritas +{ +namespace optical +{ +//---------------------------------------------------------------------------// +/*! + * Construct scoring parameters from input. + * + * Registers the \c DetectorAction as a post-step action and stores the + * callback function for the \c DetectorAction to send hits to. + * + * If no callback function is provided, then \c DetectorAction is not + * registered. + */ +ScoringParams::ScoringParams(ActionRegistry* action_reg, + inp::OpticalScoring input) + : detector_callback_(std::move(input.detector_callback)) +{ + CELER_EXPECT(action_reg); + + if (detector_callback_) + { + detector_action_ + = std::make_shared(action_reg->next_id()); + CELER_ASSERT(detector_action_); + action_reg->insert(detector_action_); + } +} + +//---------------------------------------------------------------------------// +/*! + * Send hits to the user provided callback function. + * + * Must be built with a user callback function. + */ +void ScoringParams::process_hits(Span const& hits) const +{ + CELER_EXPECT(detector_callback_); + + (*detector_callback_)(hits); +} + +//---------------------------------------------------------------------------// +} // namespace optical +} // namespace celeritas diff --git a/src/celeritas/optical/detector/ScoringParams.hh b/src/celeritas/optical/detector/ScoringParams.hh new file mode 100644 index 0000000000..d758c461c8 --- /dev/null +++ b/src/celeritas/optical/detector/ScoringParams.hh @@ -0,0 +1,50 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detector/ScoringParams.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +#include "celeritas/inp/Scoring.hh" + +namespace celeritas +{ +class ActionRegistry; + +namespace optical +{ +class DetectorAction; +//---------------------------------------------------------------------------// +/*! + * Manages user callback for optical detectors. + * + * Constructs the \c DetectorAction used by sensitive detectors to send hits + * back to the user provided callback function. If the callback function is not + * provided, then no \c DetectorAction is created. + */ +class ScoringParams final +{ + public: + //!@{ + //! \name Type aliases + using HitCallbackFunc = inp::OpticalScoring::HitCallbackFunc; + //!@} + + public: + // Construct from optical scoring input + ScoringParams(ActionRegistry*, inp::OpticalScoring); + + // Send hits to user callback function + void process_hits(Span const&) const; + + private: + std::optional detector_callback_; + std::shared_ptr detector_action_; +}; + +//---------------------------------------------------------------------------// +} // namespace optical +} // namespace celeritas diff --git a/src/celeritas/optical/detector/ScoringTrackView.hh b/src/celeritas/optical/detector/ScoringTrackView.hh new file mode 100644 index 0000000000..0abf059a42 --- /dev/null +++ b/src/celeritas/optical/detector/ScoringTrackView.hh @@ -0,0 +1,100 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detector/ScoringTrackView.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "celeritas/Types.hh" + +#include "DetectorData.hh" + +namespace celeritas +{ +namespace optical +{ +//---------------------------------------------------------------------------// +/*! + * Track view into the corresponding hit buffer. + * + * For a given track ID, this view accesses the corresponding hit data in the + * detector state buffer. For tracks in detectors, the \c score_hit function + * may be used to populate the track's hit data. Otherwise, the track's hit + * data should be cleared at the end of every step with \c clear_hit. + */ +class ScoringTrackView +{ + public: + //!@{ + //! \name Type aliases + using StateRef = NativeRef; + //!@} + + public: + // Construct from local data + inline CELER_FUNCTION ScoringTrackView(StateRef const&, TrackSlotId); + + // Clear hit data for this track + inline CELER_FUNCTION void clear_hit(); + + // Score hit for this track + inline CELER_FUNCTION void score_hit(DetectorHit); + + // Get hit data associated with this track + inline CELER_FUNCTION DetectorHit& hit(); + + private: + StateRef const& state_; + TrackSlotId track_; +}; + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Construct from detector state buffer and track ID. + */ +CELER_FUNCTION +ScoringTrackView::ScoringTrackView(StateRef const& state, TrackSlotId tid) + : state_(state), track_(tid) +{ + CELER_EXPECT(tid); +} + +//---------------------------------------------------------------------------// +/*! + * Clear hit data for this track. + * + * Marks the hit with an invalid detector ID. + */ +CELER_FUNCTION void ScoringTrackView::clear_hit() +{ + this->hit().detector = {}; +} + +//---------------------------------------------------------------------------// +/*! + * Set the hit data for this track. + * + * Should have a valid detector ID to indicate it is a valid hit. + */ +CELER_FUNCTION void ScoringTrackView::score_hit(DetectorHit hit) +{ + CELER_EXPECT(hit.detector); + this->hit() = std::move(hit); +} + +//---------------------------------------------------------------------------// +/*! + * Access the hit data associated with this track. + */ +CELER_FUNCTION DetectorHit& ScoringTrackView::hit() +{ + CELER_EXPECT(track_ < state_.size()); + return state_.all_track_hits[track_]; +} + +//---------------------------------------------------------------------------// +} // namespace optical +} // namespace celeritas diff --git a/src/celeritas/setup/Problem.cc b/src/celeritas/setup/Problem.cc index d1893d0549..c0b4681dad 100644 --- a/src/celeritas/setup/Problem.cc +++ b/src/celeritas/setup/Problem.cc @@ -73,6 +73,7 @@ #include "celeritas/optical/PhysicsParams.hh" #include "celeritas/optical/SimParams.hh" #include "celeritas/optical/Transporter.hh" +#include "celeritas/optical/detector/ScoringParams.hh" #include "celeritas/optical/gen/CherenkovParams.hh" #include "celeritas/optical/gen/DirectGeneratorAction.hh" #include "celeritas/optical/gen/GeneratorAction.hh" @@ -389,6 +390,8 @@ auto build_optical_params(inp::OpticalProblem const& p, pi.surface_physics = std::make_shared( pi.action_reg.get(), p.physics.surfaces); pi.detectors = std::move(loaded_model.detector); + pi.scoring = std::make_shared(pi.action_reg.get(), + p.scoring); // Streams and capacities pi.max_streams = p.num_streams; diff --git a/test/celeritas/CMakeLists.txt b/test/celeritas/CMakeLists.txt index acacbae8c9..64c63ecf95 100644 --- a/test/celeritas/CMakeLists.txt +++ b/test/celeritas/CMakeLists.txt @@ -450,6 +450,8 @@ endfunction() celeritas_add_standalone_tests(optical/Generator LArSphereGeneratorTest primary) celeritas_add_standalone_tests(optical/Generator LArSphereGeneratorTest direct) celeritas_add_standalone_tests(optical/Generator LArSphereGeneratorTest offload) +celeritas_add_standalone_tests(optical/Detector DetectorTest simple) +celeritas_add_standalone_tests(optical/Detector DetectorTest stress) celeritas_add_test(optical/Absorption.test.cc) celeritas_add_test(optical/Cherenkov.test.cc) diff --git a/test/celeritas/GlobalTestBase.cc b/test/celeritas/GlobalTestBase.cc index 58f9ae37d9..0ef35c7416 100644 --- a/test/celeritas/GlobalTestBase.cc +++ b/test/celeritas/GlobalTestBase.cc @@ -195,6 +195,7 @@ optical::CoreParams::Input GlobalTestBase::optical_params_input() inp.sim = this->optical_sim(); inp.surface_physics = this->optical_surface_physics(); inp.detectors = this->detector(); + inp.scoring = this->optical_scoring(); inp.cherenkov = this->cherenkov(); inp.scintillation = this->scintillation(); inp.capacity = inp::OpticalStateCapacity::from_default( @@ -235,9 +236,9 @@ auto GlobalTestBase::build_core() -> SPConstCore inp.physics = this->physics(); inp.rng = this->rng(); inp.sim = this->sim(); - inp.surface = surface_; - inp.volume = volume_; - inp.detectors = detector_; + inp.surface = this->surface(); + inp.volume = this->volume(); + inp.detectors = this->detector(); inp.wentzel = this->wentzel(); inp.action_reg = this->action_reg(); diff --git a/test/celeritas/GlobalTestBase.hh b/test/celeritas/GlobalTestBase.hh index 99d238c6bf..79111f1a60 100644 --- a/test/celeritas/GlobalTestBase.hh +++ b/test/celeritas/GlobalTestBase.hh @@ -57,6 +57,7 @@ namespace optical class MaterialParams; class PhysicsParams; class SurfacePhysicsParams; +class ScoringParams; } // namespace optical namespace test @@ -104,6 +105,7 @@ class GlobalTestBase : public Test, public LazyGeantGeoManager using SPConstOpticalMaterial = SP; using SPOpticalParams = SP; using SPConstOpticalPhysics = SP; + using SPConstOpticalScoring = SP; using SPConstOpticalSim = SP; using SPConstOpticalSurfacePhysics = SP; @@ -142,6 +144,7 @@ class GlobalTestBase : public Test, public LazyGeantGeoManager inline SPConstOpticalMaterial const& optical_material(); inline SPOpticalParams const& optical_params(); inline SPConstOpticalPhysics const& optical_physics(); + inline SPConstOpticalScoring const& optical_scoring(); inline SPConstOpticalSim const& optical_sim(); inline SPConstOpticalSurfacePhysics const& optical_surface_physics(); inline SPConstScintillation const& scintillation(); @@ -165,6 +168,7 @@ class GlobalTestBase : public Test, public LazyGeantGeoManager inline SPConstOpticalMaterial const& optical_material() const; inline SPOpticalParams const& optical_params() const; inline SPConstOpticalPhysics const& optical_physics() const; + inline SPConstOpticalScoring const& optical_scoring() const; inline SPConstOpticalSim const& optical_sim() const; inline SPConstOpticalSurfacePhysics const& optical_surface_physics() const; inline SPConstScintillation const& scintillation() const; @@ -200,6 +204,7 @@ class GlobalTestBase : public Test, public LazyGeantGeoManager [[nodiscard]] virtual SPConstCherenkov build_cherenkov() = 0; [[nodiscard]] virtual SPConstOpticalMaterial build_optical_material() = 0; [[nodiscard]] virtual SPConstOpticalPhysics build_optical_physics() = 0; + [[nodiscard]] virtual SPConstOpticalScoring build_optical_scoring() = 0; [[nodiscard]] virtual SPConstOpticalSim build_optical_sim() = 0; [[nodiscard]] virtual SPConstOpticalSurfacePhysics build_optical_surface_physics() @@ -213,6 +218,7 @@ class GlobalTestBase : public Test, public LazyGeantGeoManager SPConstSurface const& surface() const { return surface_; } SPConstVolume const& volume() const { return volume_; } SPConstDetectors const& detector() const { return detector_; } + virtual SPConstDetectors detector() { return detector_; } // Implement LazyGeantGeoManager SPConstGeoI build_geo_from_geant(SPConstGeantGeo const&) const final; @@ -255,6 +261,7 @@ class GlobalTestBase : public Test, public LazyGeantGeoManager SPConstOpticalMaterial optical_material_; SPOpticalParams optical_params_; SPConstOpticalPhysics optical_physics_; + SPConstOpticalScoring optical_scoring_; SPConstOpticalSim optical_sim_; SPConstOpticalSurfacePhysics optical_surface_physics_; SPConstScintillation scintillation_; @@ -317,6 +324,7 @@ DEF_GTB_ACCESSORS(SPOpticalParams, optical_params) DEF_GTB_ACCESSORS(SPConstOpticalPhysics, optical_physics) DEF_GTB_ACCESSORS(SPConstOpticalSim, optical_sim) DEF_GTB_ACCESSORS(SPConstOpticalSurfacePhysics, optical_surface_physics) +DEF_OPTIONAL_GTB_ACCESSORS(SPConstOpticalScoring, optical_scoring) DEF_OPTIONAL_GTB_ACCESSORS(SPConstScintillation, scintillation) DEF_OPTIONAL_GTB_ACCESSORS(SPConstWentzelOKVI, wentzel) diff --git a/test/celeritas/ImportedDataTestBase.cc b/test/celeritas/ImportedDataTestBase.cc index c885c1a621..bf513128e7 100644 --- a/test/celeritas/ImportedDataTestBase.cc +++ b/test/celeritas/ImportedDataTestBase.cc @@ -9,12 +9,14 @@ #include "geocel/SurfaceParams.hh" #include "celeritas/em/params/WentzelOKVIParams.hh" #include "celeritas/geo/GeoMaterialParams.hh" +#include "celeritas/inp/Scoring.hh" #include "celeritas/io/ImportData.hh" #include "celeritas/mat/MaterialParams.hh" #include "celeritas/optical/MaterialParams.hh" #include "celeritas/optical/ModelImporter.hh" #include "celeritas/optical/PhysicsParams.hh" #include "celeritas/optical/SimParams.hh" +#include "celeritas/optical/detector/ScoringParams.hh" #include "celeritas/optical/gen/CherenkovParams.hh" #include "celeritas/optical/gen/ScintillationParams.hh" #include "celeritas/optical/surface/SurfacePhysicsParams.hh" @@ -179,6 +181,13 @@ auto ImportedDataTestBase::build_optical_physics() -> SPConstOpticalPhysics return std::make_shared(std::move(input)); } +//---------------------------------------------------------------------------// +auto ImportedDataTestBase::build_optical_scoring() -> SPConstOpticalScoring +{ + return std::make_shared( + this->optical_action_reg().get(), inp::OpticalScoring{}); +} + //---------------------------------------------------------------------------// auto ImportedDataTestBase::build_optical_sim() -> SPConstOpticalSim { diff --git a/test/celeritas/ImportedDataTestBase.hh b/test/celeritas/ImportedDataTestBase.hh index 7807723425..a48d3dacfa 100644 --- a/test/celeritas/ImportedDataTestBase.hh +++ b/test/celeritas/ImportedDataTestBase.hh @@ -51,6 +51,7 @@ class ImportedDataTestBase : virtual public GlobalTestBase SPConstCherenkov build_cherenkov() override; SPConstOpticalMaterial build_optical_material() override; SPConstOpticalPhysics build_optical_physics() override; + SPConstOpticalScoring build_optical_scoring() override; SPConstOpticalSim build_optical_sim() override; SPConstOpticalSurfacePhysics build_optical_surface_physics() override; SPConstScintillation build_scintillation() override; diff --git a/test/celeritas/OnlyCoreTestBase.hh b/test/celeritas/OnlyCoreTestBase.hh index 76913d8250..c0ce2f3384 100644 --- a/test/celeritas/OnlyCoreTestBase.hh +++ b/test/celeritas/OnlyCoreTestBase.hh @@ -31,6 +31,10 @@ class OnlyCoreTestBase : virtual public GlobalTestBase { CELER_ASSERT_UNREACHABLE(); } + SPConstOpticalScoring build_optical_scoring() override + { + CELER_ASSERT_UNREACHABLE(); + } SPConstOpticalSim build_optical_sim() override { CELER_ASSERT_UNREACHABLE(); diff --git a/test/celeritas/optical/Detector.test.cc b/test/celeritas/optical/Detector.test.cc new file mode 100644 index 0000000000..caf85a8da4 --- /dev/null +++ b/test/celeritas/optical/Detector.test.cc @@ -0,0 +1,351 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/Detector.test.cc +//---------------------------------------------------------------------------// +#include +#include + +#include "geocel/UnitUtils.hh" +#include "geocel/VolumeParams.hh" +#include "geocel/inp/Model.hh" +#include "celeritas/GeantTestBase.hh" +#include "celeritas/global/CoreParams.hh" +#include "celeritas/inp/StandaloneInput.hh" +#include "celeritas/optical/CoreParams.hh" +#include "celeritas/optical/CoreState.hh" +#include "celeritas/optical/Transporter.hh" +#include "celeritas/optical/Types.hh" +#include "celeritas/optical/detector/DetectorData.hh" +#include "celeritas/optical/detector/ScoringParams.hh" +#include "celeritas/optical/gen/DirectGeneratorAction.hh" +#include "celeritas/optical/gen/PrimaryGeneratorAction.hh" +#include "celeritas/optical/surface/SurfacePhysicsParams.hh" + +#include "celeritas_test.hh" + +namespace celeritas +{ +namespace optical +{ +namespace test +{ +using namespace ::celeritas::test; + +constexpr bool reference_configuration + = ((CELERITAS_REAL_TYPE == CELERITAS_REAL_TYPE_DOUBLE) + && !CELERITAS_VECGEOM_SURFACE + && CELERITAS_CORE_RNG == CELERITAS_CORE_RNG_XORWOW); + +//---------------------------------------------------------------------------// +/*! + * Test optical detector and scoring. + * + * Because detectors are not directly loaded from GDML files, an override is + * used for loading the detectors into the core parameters. The default optical + * surface is set to be strictly transmitting to ensure hits are always + * recorded. + */ +class DetectorTest : public ::celeritas::test::GeantTestBase +{ + public: + std::string_view gdml_basename() const override { return "optical-box"; } + + GeantPhysicsOptions build_geant_options() const override + { + auto result = GeantTestBase::build_geant_options(); + result.optical = {}; + CELER_ENSURE(result.optical); + return result; + } + + GeantImportDataSelection build_import_data_selection() const override + { + auto result = GeantTestBase::build_import_data_selection(); + result.processes |= GeantImportDataSelection::optical; + return result; + } + + std::vector select_optical_models() const override + { + return {IMC::absorption}; + } + + SPConstOpticalSurfacePhysics build_optical_surface_physics() override + { + PhysSurfaceId phys_surface{0}; + + inp::SurfacePhysics input; + input.materials.push_back({}); + input.roughness.polished.emplace(phys_surface, inp::NoRoughness{}); + input.reflectivity.fresnel.emplace(phys_surface, + inp::FresnelReflection{}); + input.interaction.trivial.emplace(phys_surface, + TrivialInteractionMode::transmit); + + return std::make_shared( + this->optical_action_reg().get(), input); + } + + SPConstDetectors detector() override + { + if (!detector_) + { + inp::Detectors input{{ + {"y-detectors", {VolumeId{1}, VolumeId{2}}}, + {"x-detectors", {VolumeId{3}, VolumeId{4}}}, + {"z-detectors", {VolumeId{5}, VolumeId{6}}}, + }}; + + detector_ = std::make_shared(std::move(input), + *this->volume()); + } + + return detector_; + } + + SPConstOpticalScoring build_optical_scoring() override + { + return std::make_shared( + this->optical_action_reg().get(), scoring_input_); + } + + void initialize_run() + { + Transporter::Input inp; + inp.params = this->optical_params(); + transport_ = std::make_shared(std::move(inp)); + + size_type num_tracks = 128; + state_ = std::make_shared>( + *this->optical_params(), StreamId{0}, num_tracks); + state_->aux() = std::make_shared( + *this->core()->aux_reg(), MemSpace::host, StreamId{0}, num_tracks); + } + + protected: + std::shared_ptr> state_; + std::shared_ptr aux_; + std::shared_ptr transport_; + std::shared_ptr detector_; + + inp::OpticalScoring scoring_input_; +}; + +//---------------------------------------------------------------------------// +// TESTS +//---------------------------------------------------------------------------// +// Run test to check small number of photons and hits to ensure correct hit +// information is populated. + +struct SimpleScores +{ + std::vector detector_ids; + std::vector energies; + std::vector times; + std::vector x_positions; + std::vector y_positions; + std::vector z_positions; + std::vector volume_instance_ids; +}; + +struct SimpleScorer +{ + SimpleScores& scores; + + void operator()(Span const& new_hits) + { + for (auto const& hit : new_hits) + { + scores.detector_ids.push_back(hit.detector.unchecked_get()); + scores.energies.push_back(value_as(hit.energy)); + scores.times.push_back(hit.time); + scores.x_positions.push_back(hit.position[0]); + scores.y_positions.push_back(hit.position[1]); + scores.z_positions.push_back(hit.position[2]); + scores.volume_instance_ids.push_back( + hit.volume_instance.unchecked_get()); + } + } +}; + +TEST_F(DetectorTest, simple) +{ + SimpleScores scores; + scoring_input_.detector_callback = SimpleScorer{scores}; + + // Manually generate arbitrary photons aimed at different detectors + + using E = units::MevEnergy; + using TI = TrackInitializer; + + std::vector const inits{ + TI{E{1e-6}, + Real3{0, 0, 0}, // pos + Real3{1, 0, 0}, // dir + Real3{0, 1, 0}, // pol + 0, // time + {}, + ImplVolumeId{0}}, + TI{E{2e-6}, + Real3{0, 0, 0}, // pos + Real3{-1, 0, 0}, // dir + Real3{0, 1, 0}, // pol + 10, // time + {}, + ImplVolumeId{0}}, + TI{E{3e-6}, + Real3{0, 0, 0}, // pos + Real3{0, 0, 1}, // dir + Real3{0, 1, 0}, // pol + 1, // time + {}, + ImplVolumeId{0}}, + TI{E{4e-6}, + Real3{0, 0, 0}, // pos + Real3{0, 0, -1}, // dir + Real3{0, 1, 0}, // pol + 20, // time + {}, + ImplVolumeId{0}}, + TI{E{5e-6}, + Real3{0, 0, 0}, // pos + Real3{1, 0, 0}, // dir + Real3{0, 1, 0}, // pol + 13, // time + {}, + ImplVolumeId{0}}, + TI{E{6e-6}, + Real3{0, 0, 0}, // pos + Real3{0, -1, 0}, // dir + Real3{1, 0, 0}, // pol + 7, // time + {}, + ImplVolumeId{0}}, + }; + + // Run test + + auto generate + = DirectGeneratorAction::make_and_insert(*this->optical_params()); + this->initialize_run(); + generate->insert(*state_, make_span(inits)); + (*transport_)(*state_); + + // Check results + + real_type const box_size = from_cm(50); + real_type const flight_time = box_size / constants::c_light; + + static size_type const expected_detector_ids[] = {1, 1, 2, 2, 1, 0}; + static real_type const expected_energies[] + = {1e-6, 2e-6, 3e-6, 4e-6, 5e-6, 6e-6}; + static real_type const expected_x_positions[] = { + box_size, + -box_size, + 0, + 0, + box_size, + 0, + }; + static real_type const expected_y_positions[] = { + 0, + 0, + 0, + 0, + 0, + -box_size, + }; + static real_type const expected_z_positions[] = { + 0, + 0, + box_size, + -box_size, + 0, + 0, + }; + static real_type const expected_times[] = { + 0 + flight_time, + 10 + flight_time, + 1 + flight_time, + 20 + flight_time, + 13 + flight_time, + 7 + flight_time, + }; + static size_type const expected_volume_instance_ids[] = {5, 4, 6, 7, 5, 3}; + + if (reference_configuration) + { + EXPECT_VEC_EQ(expected_detector_ids, scores.detector_ids); + EXPECT_VEC_SOFT_EQ(expected_energies, scores.energies); + EXPECT_VEC_SOFT_EQ(expected_x_positions, scores.x_positions); + EXPECT_VEC_SOFT_EQ(expected_y_positions, scores.y_positions); + EXPECT_VEC_SOFT_EQ(expected_z_positions, scores.z_positions); + EXPECT_VEC_SOFT_EQ(expected_times, scores.times); + EXPECT_VEC_EQ(expected_volume_instance_ids, scores.volume_instance_ids); + } +} + +//---------------------------------------------------------------------------// +// Run test over large number of photons to check buffering is done correctly. + +struct StressScorer +{ + std::vector& scores; + size_type& errored; + + void operator()(Span const& hits) + { + for (auto const& hit : hits) + { + if (hit.detector < scores.size()) + { + scores[hit.detector.get()]++; + } + else + { + errored++; + } + } + } +}; + +TEST_F(DetectorTest, stress) +{ + // 3 detectors: x, y, z + std::vector hits(3, 0); + size_type errored = 0; + scoring_input_.detector_callback = StressScorer{hits, errored}; + + // Isotropically generate photons + + inp::OpticalPrimaryGenerator gen; + gen.primaries = 8192; + gen.energy = inp::MonoenergeticDistribution{1e-5}; + gen.angle = inp::IsotropicDistribution{}; + gen.shape = inp::PointDistribution{{0, 0, 0}}; + + // Run test + + auto generate = PrimaryGeneratorAction::make_and_insert( + *this->optical_params(), std::move(gen)); + this->initialize_run(); + generate->insert(*state_); + (*transport_)(*state_); + + // Check results + + if (reference_configuration) + { + static size_type const expected_hits[] = {2673, 2816, 2703}; + + EXPECT_VEC_EQ(expected_hits, hits); + EXPECT_EQ(errored, 0); + } +} + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace optical +} // namespace celeritas diff --git a/test/celeritas/optical/OpticalMockTestBase.hh b/test/celeritas/optical/OpticalMockTestBase.hh index 2533cc3b8c..da9e3b6c6f 100644 --- a/test/celeritas/optical/OpticalMockTestBase.hh +++ b/test/celeritas/optical/OpticalMockTestBase.hh @@ -67,6 +67,10 @@ class OpticalMockTestBase : public GlobalTestBase { CELER_ASSERT_UNREACHABLE(); } + SPConstOpticalScoring build_optical_scoring() override + { + CELER_ASSERT_UNREACHABLE(); + } SPConstOpticalSim build_optical_sim() override { CELER_ASSERT_UNREACHABLE(); diff --git a/test/celeritas/optical/SurfacePhysicsIntegrationTestBase.cc b/test/celeritas/optical/SurfacePhysicsIntegrationTestBase.cc index 58fe75a61b..e0ce21dbaf 100644 --- a/test/celeritas/optical/SurfacePhysicsIntegrationTestBase.cc +++ b/test/celeritas/optical/SurfacePhysicsIntegrationTestBase.cc @@ -51,23 +51,6 @@ auto SurfacePhysicsIntegrationTestBase::build_optical_surface_physics() this->setup_surface_models(input); - // Default surface - - PhysSurfaceId phys_surface = [&] { - size_type num_surfaces = 0; - for (auto const& mats : input.materials) - { - num_surfaces += mats.size() + 1; - } - return PhysSurfaceId(num_surfaces); - }(); - - input.materials.push_back({}); - input.roughness.polished.emplace(phys_surface, inp::NoRoughness{}); - input.reflectivity.fresnel.emplace(phys_surface, inp::FresnelReflection{}); - input.interaction.trivial.emplace(phys_surface, - TrivialInteractionMode::absorb); - return std::make_shared( this->optical_action_reg().get(), input); } diff --git a/test/geocel/data/optical-box.gdml b/test/geocel/data/optical-box.gdml index 3a1889b5b0..d486c4a3fd 100644 --- a/test/geocel/data/optical-box.gdml +++ b/test/geocel/data/optical-box.gdml @@ -35,20 +35,39 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + + @@ -60,12 +79,28 @@ + + + + + + + + + + + + + + + + + + + + - - - -