From 8b327e05301de1dd825dbd506d2cdb551379a5d9 Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" <johnsonsr@ornl.gov> Date: Wed, 4 Sep 2024 08:47:49 -0400 Subject: [PATCH] Add skeleton optical core params and launch action (#1386) --- src/celeritas/CMakeLists.txt | 3 + src/celeritas/optical/CoreParams.cc | 140 ++++++++++++++ src/celeritas/optical/CoreParams.hh | 145 +++++++++++++++ src/celeritas/optical/CoreState.cc | 84 +++++++++ src/celeritas/optical/CoreState.hh | 174 ++++++++++++++++++ src/celeritas/optical/OpticalCollector.cc | 58 ++++-- src/celeritas/optical/OpticalCollector.hh | 41 +++-- src/celeritas/optical/TrackData.cc | 2 +- src/celeritas/optical/TrackData.hh | 28 +-- src/celeritas/optical/detail/OffloadParams.hh | 2 +- .../optical/detail/OpticalLaunchAction.cc | 152 +++++++++++++++ .../optical/detail/OpticalLaunchAction.hh | 112 +++++++++++ src/corecel/data/CollectionStateStore.hh | 5 + .../optical/OpticalCollector.test.cc | 6 +- 14 files changed, 897 insertions(+), 55 deletions(-) create mode 100644 src/celeritas/optical/CoreParams.cc create mode 100644 src/celeritas/optical/CoreParams.hh create mode 100644 src/celeritas/optical/CoreState.cc create mode 100644 src/celeritas/optical/CoreState.hh create mode 100644 src/celeritas/optical/detail/OpticalLaunchAction.cc create mode 100644 src/celeritas/optical/detail/OpticalLaunchAction.hh diff --git a/src/celeritas/CMakeLists.txt b/src/celeritas/CMakeLists.txt index f81370771c..09cad246f5 100644 --- a/src/celeritas/CMakeLists.txt +++ b/src/celeritas/CMakeLists.txt @@ -72,11 +72,14 @@ list(APPEND SOURCES neutron/process/NeutronElasticProcess.cc neutron/process/NeutronInelasticProcess.cc optical/CerenkovParams.cc + optical/CoreParams.cc + optical/CoreState.cc optical/OpticalCollector.cc optical/MaterialParams.cc optical/TrackData.cc optical/ScintillationParams.cc optical/detail/OffloadParams.cc + optical/detail/OpticalLaunchAction.cc phys/CutoffParams.cc phys/ImportedModelAdapter.cc phys/ImportedProcessAdapter.cc diff --git a/src/celeritas/optical/CoreParams.cc b/src/celeritas/optical/CoreParams.cc new file mode 100644 index 0000000000..146f43f9a8 --- /dev/null +++ b/src/celeritas/optical/CoreParams.cc @@ -0,0 +1,140 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/CoreParams.cc +//---------------------------------------------------------------------------// +#include "CoreParams.hh" + +#include "corecel/io/Logger.hh" +#include "corecel/sys/ActionRegistry.hh" +#include "corecel/sys/ScopedMem.hh" +#include "celeritas/geo/GeoParams.hh" +#include "celeritas/mat/MaterialParams.hh" +#include "celeritas/random/RngParams.hh" +#include "celeritas/track/SimParams.hh" +#include "celeritas/track/TrackInitParams.hh" + +#include "CoreState.hh" +#include "MaterialParams.hh" + +namespace celeritas +{ +namespace optical +{ +namespace +{ +//---------------------------------------------------------------------------// +// HELPER CLASSES AND FUNCTIONS +//---------------------------------------------------------------------------// +//!@{ +template<MemSpace M> +CoreParamsData<Ownership::const_reference, M> +build_params_refs(CoreParams::Input const& p, CoreScalars const& scalars) +{ + CELER_EXPECT(scalars); + + CoreParamsData<Ownership::const_reference, M> ref; + + ref.scalars = scalars; + ref.geometry = get_ref<M>(*p.geometry); + ref.material = get_ref<M>(*p.material); + // TODO: ref.physics = get_ref<M>(*p.physics); + ref.rng = get_ref<M>(*p.rng); + ref.sim = get_ref<M>(*p.sim); + ref.init = get_ref<M>(*p.init); + + CELER_ENSURE(ref); + return ref; +} + +//---------------------------------------------------------------------------// +/*! + * Construct always-required actions and set IDs. + */ +CoreScalars build_actions(ActionRegistry* reg) +{ + using std::make_shared; + + CoreScalars scalars; + + //// START ACTIONS //// + + CELER_DISCARD(reg); +#if 0 + // NOTE: due to ordering by {start, ID}, ExtendFromPrimariesAction *must* + // precede InitializeTracksAction + reg->insert(make_shared<ExtendFromPrimariesAction>(reg->next_id())); + reg->insert(make_shared<InitializeTracksAction>(reg->next_id())); +#endif + + //// PRE-STEP ACTIONS //// + + //// POST-STEP ACTIONS //// + + // Construct geometry boundary action + scalars.boundary_action = reg->next_id(); +#if 0 + reg->insert(make_shared<detail::BoundaryAction>( + scalars.boundary_action)); +#endif + + //// END ACTIONS //// + + // TODO: extend from secondaries action + + return scalars; +} + +//---------------------------------------------------------------------------// +} // namespace + +//---------------------------------------------------------------------------// +/*! + * Construct with all problem data, creating some actions too. + */ +CoreParams::CoreParams(Input&& input) : input_(std::move(input)) +{ +#define CP_VALIDATE_INPUT(MEMBER) \ + CELER_VALIDATE(input_.MEMBER, \ + << "optical core input is missing " << #MEMBER << " data") + CP_VALIDATE_INPUT(geometry); + CP_VALIDATE_INPUT(material); + // TODO: CP_VALIDATE_INPUT(physics); + CP_VALIDATE_INPUT(rng); + CP_VALIDATE_INPUT(sim); + CP_VALIDATE_INPUT(init); + CP_VALIDATE_INPUT(action_reg); + CP_VALIDATE_INPUT(max_streams); +#undef CP_VALIDATE_INPUT + + CELER_EXPECT(input_); + + ScopedMem record_mem("optical::CoreParams.construct"); + + // Construct always-on actions and save their IDs + CoreScalars scalars = build_actions(input_.action_reg.get()); + + // Save maximum number of streams + scalars.max_streams = input_.max_streams; + + // Save host reference + host_ref_ = build_params_refs<MemSpace::host>(input_, scalars); + if (celeritas::device()) + { + device_ref_ = build_params_refs<MemSpace::device>(input_, scalars); + // Copy device ref to device global memory + device_ref_vec_ = DeviceVector<DeviceRef>(1); + device_ref_vec_.copy_to_device({&device_ref_, 1}); + } + + CELER_LOG(status) << "Celeritas optical setup complete"; + + CELER_ENSURE(host_ref_); + CELER_ENSURE(host_ref_.scalars.max_streams == this->max_streams()); +} + +//---------------------------------------------------------------------------// +} // namespace optical +} // namespace celeritas diff --git a/src/celeritas/optical/CoreParams.hh b/src/celeritas/optical/CoreParams.hh new file mode 100644 index 0000000000..b726e1be53 --- /dev/null +++ b/src/celeritas/optical/CoreParams.hh @@ -0,0 +1,145 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/CoreParams.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/Assert.hh" +#include "corecel/data/DeviceVector.hh" +#include "corecel/data/ObserverPtr.hh" +#include "corecel/data/ParamsDataInterface.hh" +#include "celeritas/geo/GeoFwd.hh" +#include "celeritas/global/ActionInterface.hh" +#include "celeritas/random/RngParamsFwd.hh" + +#include "TrackData.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +class ActionRegistry; +class TrackInitParams; +class SimParams; + +namespace optical +{ +//---------------------------------------------------------------------------// +class MaterialParams; +// TODO: class PhysicsParams; + +//---------------------------------------------------------------------------// +/*! + * Shared parameters for the optical photon loop. + */ +class CoreParams final : public ParamsDataInterface<CoreParamsData> +{ + public: + //!@{ + //! \name Type aliases + using SPConstGeo = std::shared_ptr<GeoParams const>; + using SPConstMaterial = std::shared_ptr<MaterialParams const>; + using SPConstRng = std::shared_ptr<RngParams const>; + using SPConstSim = std::shared_ptr<SimParams const>; + using SPConstTrackInit = std::shared_ptr<TrackInitParams const>; + using SPActionRegistry = std::shared_ptr<ActionRegistry>; + + template<MemSpace M> + using ConstRef = CoreParamsData<Ownership::const_reference, M>; + template<MemSpace M> + using ConstPtr = ObserverPtr<ConstRef<M> const, M>; + //!@} + + struct Input + { + SPConstGeo geometry; + SPConstMaterial material; + // TODO: physics + SPConstRng rng; + SPConstSim sim; + SPConstTrackInit init; + + SPActionRegistry action_reg; + + //! Maximum number of simultaneous threads/tasks per process + StreamId::size_type max_streams{1}; + + //! True if all params are assigned and valid + explicit operator bool() const + { + return geometry && material && rng && sim && init && action_reg + && max_streams; + } + }; + + public: + // Construct with all problem data, creating some actions too + CoreParams(Input&& inp); + + //!@{ + //! \name Data interface + //! Access data on the host + HostRef const& host_ref() const final { return host_ref_; } + //! Access data on the device + DeviceRef const& device_ref() const final { return device_ref_; } + //!@} + + //!@{ + //! Access shared problem parameter data. + SPConstGeo const& geometry() const { return input_.geometry; } + SPConstMaterial const& material() const { return input_.material; } + SPConstRng const& rng() const { return input_.rng; } + SPConstTrackInit const& init() const { return input_.init; } + SPActionRegistry const& action_reg() const { return input_.action_reg; } + //!@} + + // Access host pointers to core data + using ParamsDataInterface<CoreParamsData>::ref; + + // Access a native pointer to properties in the native memory space + template<MemSpace M> + inline ConstPtr<M> ptr() const; + + //! Maximum number of streams + size_type max_streams() const { return input_.max_streams; } + + private: + Input input_; + HostRef host_ref_; + DeviceRef device_ref_; + + // Copy of DeviceRef in device memory + DeviceVector<DeviceRef> device_ref_vec_; +}; + +//---------------------------------------------------------------------------// +/*! + * Access a native pointer to a NativeCRef. + * + * This way, CUDA kernels only need to copy a pointer in the kernel arguments, + * rather than the entire (rather large) DeviceRef object. + */ +template<MemSpace M> +auto CoreParams::ptr() const -> ConstPtr<M> +{ + if constexpr (M == MemSpace::host) + { + return make_observer(&host_ref_); + } +#ifndef __NVCC__ + // CUDA 11.4 complains about 'else if constexpr' ("missing return + // statement") and GCC 11.2 complains about leaving off the 'else' + // ("inconsistent deduction for auto return type") + else +#endif + { + CELER_ENSURE(!device_ref_vec_.empty()); + return make_observer(device_ref_vec_); + } +} + +//---------------------------------------------------------------------------// +} // namespace optical +} // namespace celeritas diff --git a/src/celeritas/optical/CoreState.cc b/src/celeritas/optical/CoreState.cc new file mode 100644 index 0000000000..cbb8abd955 --- /dev/null +++ b/src/celeritas/optical/CoreState.cc @@ -0,0 +1,84 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/CoreState.cc +//---------------------------------------------------------------------------// +#include "CoreState.hh" + +#include "corecel/data/CollectionAlgorithms.hh" +#include "corecel/data/Copier.hh" +#include "corecel/io/Logger.hh" +#include "corecel/sys/ScopedProfiling.hh" + +#include "CoreParams.hh" + +namespace celeritas +{ +namespace optical +{ +//---------------------------------------------------------------------------// +//! Support polymorphic deletion +CoreStateInterface::~CoreStateInterface() = default; + +//---------------------------------------------------------------------------// +/*! + * Construct from CoreParams. + */ +template<MemSpace M> +CoreState<M>::CoreState(CoreParams const& params, + StreamId stream_id, + size_type num_track_slots) +{ + CELER_VALIDATE(stream_id < params.max_streams(), + << "stream ID " << stream_id.unchecked_get() + << " is out of range: max streams is " + << params.max_streams()); + CELER_VALIDATE(num_track_slots > 0, << "number of track slots is not set"); + + ScopedProfiling profile_this{"construct-optical-state"}; + + states_ = CollectionStateStore<CoreStateData, M>( + params.host_ref(), stream_id, num_track_slots); + + counters_.num_vacancies = num_track_slots; + + if constexpr (M == MemSpace::device) + { + device_ref_vec_ = DeviceVector<Ref>(1); + device_ref_vec_.copy_to_device({&this->ref(), 1}); + ptr_ = make_observer(device_ref_vec_); + } + else if constexpr (M == MemSpace::host) + { + ptr_ = make_observer(&this->ref()); + } + + CELER_LOG_LOCAL(status) << "Celeritas optical state initialization " + "complete"; + CELER_ENSURE(states_); + CELER_ENSURE(ptr_); +} + +//---------------------------------------------------------------------------// +/*! + * Inject primaries to be turned into TrackInitializers. + * + * These will be converted by the ProcessPrimaries action. + */ +template<MemSpace M> +void CoreState<M>::insert_primaries(Span<Primary const>) +{ + CELER_NOT_IMPLEMENTED("primary insertion"); +} + +//---------------------------------------------------------------------------// +// EXPLICIT INSTANTIATION +//---------------------------------------------------------------------------// +template class CoreState<MemSpace::host>; +template class CoreState<MemSpace::device>; + +//---------------------------------------------------------------------------// +} // namespace optical +} // namespace celeritas diff --git a/src/celeritas/optical/CoreState.hh b/src/celeritas/optical/CoreState.hh new file mode 100644 index 0000000000..4bedd63492 --- /dev/null +++ b/src/celeritas/optical/CoreState.hh @@ -0,0 +1,174 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/CoreState.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/cont/Span.hh" +#include "corecel/data/AuxInterface.hh" +#include "corecel/data/CollectionStateStore.hh" +#include "corecel/data/ObserverPtr.hh" +#include "celeritas/Types.hh" + +#include "Primary.hh" +#include "TrackData.hh" + +namespace celeritas +{ +namespace optical +{ +class CoreParams; + +//---------------------------------------------------------------------------// +/*! + * Counters for track initialization and activity. + * + * These counters are updated *by value on the host at every step* so they + * should not be stored in TrackInitStateData because then the device-memory + * copy will not be synchronized. + */ +struct CoreStateCounters +{ + // Initialization input + size_type num_vacancies{}; //!< Number of unused track slots + size_type num_primaries{}; //!< Number of primaries to be converted + size_type num_initializers{}; //!< Number of track initializers + + // Diagnostic output + size_type num_secondaries{}; //!< Number of secondaries produced in a step + size_type num_active{}; //!< Number of active tracks at start of a step + size_type num_alive{}; //!< Number of alive tracks at end of step +}; + +//---------------------------------------------------------------------------// +/*! + * Interface class for optical state data. + * + * This inherits from the "aux state" interface to allow stream-local storage + * with the optical offload data. + */ +class CoreStateInterface : public AuxStateInterface +{ + public: + //!@{ + //! \name Type aliases + using size_type = TrackSlotId::size_type; + //!@} + + public: + // Support polymorphic deletion + virtual ~CoreStateInterface(); + + //! Thread/stream ID + virtual StreamId stream_id() const = 0; + + //! Access track initialization counters + virtual CoreStateCounters const& counters() const = 0; + + //! Number of track slots + virtual size_type size() const = 0; + + // Inject optical primaries + virtual void insert_primaries(Span<Primary const> host_primaries) = 0; + + protected: + CoreStateInterface() = default; + CELER_DEFAULT_COPY_MOVE(CoreStateInterface); +}; + +//---------------------------------------------------------------------------// +/*! + * Store all state data for a single thread. + * + * When the state lives on the device, we maintain a separate copy of the + * device "ref" in device memory: otherwise we'd have to copy the entire state + * in launch arguments and access it through constant memory. + * + * \todo Encapsulate all the action management accessors in a helper class. + */ +template<MemSpace M> +class CoreState final : public CoreStateInterface +{ + public: + //!@{ + //! \name Type aliases + template<template<Ownership, MemSpace> class S> + using StateRef = S<Ownership::reference, M>; + + using Ref = StateRef<CoreStateData>; + using Ptr = ObserverPtr<Ref, M>; + //!@} + + public: + // Construct from CoreParams + CoreState(CoreParams const& params, + StreamId stream_id, + size_type num_track_slots); + + //! Thread/stream ID + StreamId stream_id() const final { return this->ref().stream_id; } + + //! Number of track slots + size_type size() const final { return states_.size(); } + + // Whether the state is being transported with no active particles + inline bool warming_up() const; + + //// CORE DATA //// + + //! Get a reference to the mutable state data + Ref& ref() { return states_.ref(); } + + //! Get a reference to the mutable state data + Ref const& ref() const { return states_.ref(); } + + //! Get a native-memspace pointer to the mutable state data + Ptr ptr() { return ptr_; } + + //// COUNTERS //// + + //! Track initialization counters + CoreStateCounters& counters() { return counters_; } + + //! Track initialization counters + CoreStateCounters const& counters() const final { return counters_; } + + // Inject primaries to be turned into TrackInitializers + void insert_primaries(Span<Primary const> host_primaries) final; + + private: + // State data + CollectionStateStore<CoreStateData, M> states_; + + // Copy of state ref in device memory, if M == MemSpace::device + DeviceVector<Ref> device_ref_vec_; + + // Native pointer to ref or + Ptr ptr_; + + // Counters for track initialization and activity + CoreStateCounters counters_; +}; + +//---------------------------------------------------------------------------// +/*! + * Whether the state is being transported with no active particles. + * + * The warmup stage is useful for profiling and debugging since the first + * step iteration can do the following: + * - Initialize asynchronous memory pools + * - Interrogate kernel functions for properties to be output later + * - Allocate "lazy" auxiliary data (e.g. action diagnostics) + */ +template<MemSpace M> +bool CoreState<M>::warming_up() const +{ + return counters_.num_active == 0 && counters_.num_primaries == 0; +} + +//---------------------------------------------------------------------------// +} // namespace optical +} // namespace celeritas diff --git a/src/celeritas/optical/OpticalCollector.cc b/src/celeritas/optical/OpticalCollector.cc index c98c514ffe..54ac7312a7 100644 --- a/src/celeritas/optical/OpticalCollector.cc +++ b/src/celeritas/optical/OpticalCollector.cc @@ -10,18 +10,28 @@ #include "corecel/data/AuxParamsRegistry.hh" #include "corecel/sys/ActionRegistry.hh" #include "celeritas/global/CoreParams.hh" -#include "celeritas/optical/CerenkovParams.hh" -#include "celeritas/optical/MaterialParams.hh" -#include "celeritas/optical/OffloadData.hh" -#include "celeritas/optical/ScintillationParams.hh" +#include "celeritas/track/SimParams.hh" +#include "celeritas/track/TrackInitParams.hh" +#include "CerenkovParams.hh" +#include "CoreParams.hh" +#include "MaterialParams.hh" +#include "OffloadData.hh" +#include "ScintillationParams.hh" + +#include "detail/CerenkovOffloadAction.hh" +#include "detail/OffloadGatherAction.hh" #include "detail/OffloadParams.hh" +#include "detail/OpticalLaunchAction.hh" +#include "detail/ScintOffloadAction.hh" namespace celeritas { //---------------------------------------------------------------------------// /*! * Construct with core data and optical data. + * + * This adds several actions and auxiliary data to the registry. */ OpticalCollector::OpticalCollector(CoreParams const& core, Input&& inp) { @@ -32,10 +42,10 @@ OpticalCollector::OpticalCollector(CoreParams const& core, Input&& inp) setup.scintillation = static_cast<bool>(inp.scintillation); setup.capacity = inp.buffer_capacity; - // Create aux params and add to core - gen_params_ = std::make_shared<detail::OffloadParams>( - core.aux_reg()->next_id(), setup); - core.aux_reg()->insert(gen_params_); + // Create offload params + AuxParamsRegistry& aux = *core.aux_reg(); + gen_params_ = std::make_shared<detail::OffloadParams>(aux.next_id(), setup); + aux.insert(gen_params_); // Action to gather pre-step data needed to generate optical distributions ActionRegistry& actions = *core.action_reg(); @@ -46,33 +56,41 @@ OpticalCollector::OpticalCollector(CoreParams const& core, Input&& inp) if (setup.cerenkov) { // Action to generate Cerenkov optical distributions - cerenkov_offload_action_ - = std::make_shared<detail::CerenkovOffloadAction>( - actions.next_id(), - gen_params_->aux_id(), - std::move(inp.material), - std::move(inp.cerenkov)); - actions.insert(cerenkov_offload_action_); + cerenkov_action_ = std::make_shared<detail::CerenkovOffloadAction>( + actions.next_id(), + gen_params_->aux_id(), + inp.material, + std::move(inp.cerenkov)); + actions.insert(cerenkov_action_); } if (setup.scintillation) { // Action to generate scintillation optical distributions - scint_offload_action_ = std::make_shared<detail::ScintOffloadAction>( + scint_action_ = std::make_shared<detail::ScintOffloadAction>( actions.next_id(), gen_params_->aux_id(), std::move(inp.scintillation)); - actions.insert(scint_offload_action_); + actions.insert(scint_action_); } - // TODO: add an action to launch optical tracking loop + // Create launch action with optical params+state and access to gen data + launch_action_ = detail::OpticalLaunchAction::make_and_insert( + core, inp.material, gen_params_); + + // Launch action must be *after* generator actions + CELER_ENSURE(!cerenkov_action_ + || launch_action_->action_id() + > cerenkov_action_->action_id()); + CELER_ENSURE(!scint_action_ + || launch_action_->action_id() > scint_action_->action_id()); } //---------------------------------------------------------------------------// /*! - * Aux ID for optical generator data. + * Aux ID for optical generator data used for offloading. */ -AuxId OpticalCollector::aux_id() const +AuxId OpticalCollector::offload_aux_id() const { return gen_params_->aux_id(); } diff --git a/src/celeritas/optical/OpticalCollector.hh b/src/celeritas/optical/OpticalCollector.hh index 8034369153..a94da58396 100644 --- a/src/celeritas/optical/OpticalCollector.hh +++ b/src/celeritas/optical/OpticalCollector.hh @@ -14,41 +14,46 @@ #include "OffloadData.hh" -#include "detail/CerenkovOffloadAction.hh" -#include "detail/OffloadGatherAction.hh" -#include "detail/ScintOffloadAction.hh" - namespace celeritas { //---------------------------------------------------------------------------// class ActionRegistry; -class CerenkovParams; -class ScintillationParams; class CoreParams; namespace optical { +class CerenkovParams; class MaterialParams; +class ScintillationParams; } namespace detail { +class CerenkovOffloadAction; +class OffloadGatherAction; +class OpticalLaunchAction; class OffloadParams; +class ScintOffloadAction; } // namespace detail //---------------------------------------------------------------------------// /*! - * Generate scintillation and Cerenkov optical distribution data at each step. + * Generate and track optical photons. * * This class is the interface between the main stepping loop and the photon * stepping loop and constructs kernel actions for: * - gathering the pre-step data needed to generate the optical distributions, - * - generating the optical distributions at the end of the step, and + * - generating the scintillation and Cerenkov optical distributions at the + * end of the step, and * - launching the photon stepping loop. * + * The photon stepping loop will then generate optical primaries. + * * The "collector" (TODO: rename?) will "own" the optical state data and * optical params since it's the only thing that launches the optical stepping * loop. + * + * \todo Rename to OpticalOffload */ class OpticalCollector { @@ -74,7 +79,7 @@ class OpticalCollector //! True if all input is assigned and valid explicit operator bool() const { - return (scintillation || (cerenkov && material)) + return material && (scintillation || cerenkov) && buffer_capacity > 0; } }; @@ -83,28 +88,28 @@ class OpticalCollector // Construct with core data and optical params OpticalCollector(CoreParams const&, Input&&); - // Aux ID for optical generator data - AuxId aux_id() const; + // Aux ID for optical offload data + AuxId offload_aux_id() const; private: //// TYPES //// using SPOffloadParams = std::shared_ptr<detail::OffloadParams>; - using SPCerenkovOffloadAction - = std::shared_ptr<detail::CerenkovOffloadAction>; - using SPScintOffloadAction = std::shared_ptr<detail::ScintOffloadAction>; + using SPCerenkovAction = std::shared_ptr<detail::CerenkovOffloadAction>; + using SPScintAction = std::shared_ptr<detail::ScintOffloadAction>; using SPGatherAction = std::shared_ptr<detail::OffloadGatherAction>; + using SPLaunchAction = std::shared_ptr<detail::OpticalLaunchAction>; //// DATA //// SPOffloadParams gen_params_; SPGatherAction gather_action_; - SPCerenkovOffloadAction cerenkov_offload_action_; - SPScintOffloadAction scint_offload_action_; + SPCerenkovAction cerenkov_action_; + SPScintAction scint_action_; + SPLaunchAction launch_action_; - // TODO: tracking loop launcher - // TODO: store optical core params and state? + // TODO: tracking loop launch action }; //---------------------------------------------------------------------------// diff --git a/src/celeritas/optical/TrackData.cc b/src/celeritas/optical/TrackData.cc index 6f99312c78..61fd956699 100644 --- a/src/celeritas/optical/TrackData.cc +++ b/src/celeritas/optical/TrackData.cc @@ -42,7 +42,7 @@ void resize(CoreStateData<Ownership::value, M>* state, resize(&state->init, params.init, stream_id, size); state->stream_id = stream_id; - CELER_ENSURE(state); + CELER_ENSURE(*state); } //---------------------------------------------------------------------------// diff --git a/src/celeritas/optical/TrackData.hh b/src/celeritas/optical/TrackData.hh index 985828f31d..a64b94072f 100644 --- a/src/celeritas/optical/TrackData.hh +++ b/src/celeritas/optical/TrackData.hh @@ -15,6 +15,7 @@ #include "celeritas/track/SimData.hh" #include "celeritas/track/TrackInitData.hh" +#include "MaterialData.hh" #include "Types.hh" namespace celeritas @@ -28,18 +29,26 @@ namespace optical template<Ownership W, MemSpace M> struct PhysicsParamsData { - explicit CELER_FUNCTION operator bool() const { return false; } + explicit CELER_FUNCTION operator bool() const { return true; } }; template<Ownership W, MemSpace M> struct PhysicsStateData { + explicit CELER_FUNCTION operator bool() const { return true; } + + //! Assign from another set of data + template<Ownership W2, MemSpace M2> + PhysicsStateData& operator=(PhysicsStateData<W2, M2>&) + { + return *this; + } }; + template<MemSpace M> inline void resize(PhysicsStateData<Ownership::value, M>*, HostCRef<PhysicsParamsData> const&, size_type) { - CELER_NOT_IMPLEMENTED("optical physics state"); } // XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX @@ -54,12 +63,11 @@ struct CoreScalars ActionId boundary_action; StreamId::size_type max_streams{0}; - OpticalMaterialId::size_type num_materials{0}; //! True if assigned and valid explicit CELER_FUNCTION operator bool() const { - return boundary_action && max_streams > 0 && num_materials > 0; + return boundary_action && max_streams > 0; } }; @@ -70,11 +78,8 @@ struct CoreScalars template<Ownership W, MemSpace M> struct CoreParamsData { - template<class T> - using VolumeItems = celeritas::Collection<T, W, M, VolumeId>; - GeoParamsData<W, M> geometry; - VolumeItems<OpticalMaterialId> materials; + MaterialParamsData<W, M> material; PhysicsParamsData<W, M> physics; RngParamsData<W, M> rng; SimParamsData<W, M> sim; @@ -85,8 +90,7 @@ struct CoreParamsData //! True if all params are assigned explicit CELER_FUNCTION operator bool() const { - return geometry && !materials.empty() && physics && rng && sim && init - && scalars; + return geometry && material && physics && rng && sim && init && scalars; } //! Assign from another set of data @@ -95,7 +99,7 @@ struct CoreParamsData { CELER_EXPECT(other); geometry = other.geometry; - materials = other.materials; + material = other.material; physics = other.physics; rng = other.rng; sim = other.sim; @@ -131,7 +135,7 @@ struct CoreStateData //! Whether the data are assigned explicit CELER_FUNCTION operator bool() const { - return geometry && materials && physics && rng && sim && init + return geometry && !materials.empty() && physics && rng && sim && init && stream_id; } diff --git a/src/celeritas/optical/detail/OffloadParams.hh b/src/celeritas/optical/detail/OffloadParams.hh index 094d9d664f..6f6a1e9970 100644 --- a/src/celeritas/optical/detail/OffloadParams.hh +++ b/src/celeritas/optical/detail/OffloadParams.hh @@ -20,7 +20,7 @@ namespace detail { //---------------------------------------------------------------------------// /*! - * Manage metadata about optical offloading. + * Manage metadata for optical offload generation. */ class OffloadParams final : public AuxParamsInterface, public ParamsDataInterface<OffloadParamsData> diff --git a/src/celeritas/optical/detail/OpticalLaunchAction.cc b/src/celeritas/optical/detail/OpticalLaunchAction.cc new file mode 100644 index 0000000000..d93fdabde1 --- /dev/null +++ b/src/celeritas/optical/detail/OpticalLaunchAction.cc @@ -0,0 +1,152 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detail/OpticalLaunchAction.cc +//---------------------------------------------------------------------------// +#include "OpticalLaunchAction.hh" + +#include "corecel/data/AuxParamsRegistry.hh" +#include "corecel/data/AuxStateVec.hh" +#include "corecel/sys/ActionRegistry.hh" +#include "celeritas/global/CoreParams.hh" +#include "celeritas/global/CoreState.hh" +#include "celeritas/optical/CoreParams.hh" +#include "celeritas/optical/CoreState.hh" +#include "celeritas/track/SimParams.hh" + +#include "OffloadParams.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Construct and add to core params. + */ +std::shared_ptr<OpticalLaunchAction> +OpticalLaunchAction::make_and_insert(CoreParams const& core, + SPConstMaterial material, + SPOffloadParams offload) +{ + CELER_EXPECT(material); + CELER_EXPECT(offload); + ActionRegistry& actions = *core.action_reg(); + AuxParamsRegistry& aux = *core.aux_reg(); + auto result = std::make_shared<OpticalLaunchAction>(actions.next_id(), + aux.next_id(), + core, + std::move(material), + std::move(offload)); + + actions.insert(result); + aux.insert(result); + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Construct with action ID, generator storage. + */ +OpticalLaunchAction::OpticalLaunchAction(ActionId action_id, + AuxId data_id, + CoreParams const& core, + SPConstMaterial material, + SPOffloadParams offload) + : action_id_{action_id} + , aux_id_{data_id} + , offload_params_{std::move(offload)} +{ + CELER_EXPECT(material); + CELER_EXPECT(offload_params_); + + // Create optical core params + optical_params_ = std::make_shared<optical::CoreParams>([&] { + optical::CoreParams::Input inp; + inp.geometry = core.geometry(); + inp.material = std::move(material); + // TODO: unique RNG streams for optical loop + inp.rng = core.rng(); + inp.sim = std::make_shared<SimParams>(); + inp.init = core.init(); + inp.action_reg = std::make_shared<ActionRegistry>(); + inp.max_streams = core.max_streams(); + CELER_ENSURE(inp); + return inp; + }()); +} + +//---------------------------------------------------------------------------// +/*! + * Descriptive name of the action. + */ +std::string_view OpticalLaunchAction::description() const +{ + return "launch the optical stepping loop"; +} + +//---------------------------------------------------------------------------// +/*! + * Build state data for a stream. + */ +auto OpticalLaunchAction::create_state(MemSpace m, + StreamId sid, + size_type size) const -> UPState +{ + if (m == MemSpace::host) + { + return std::make_unique<optical::CoreState<MemSpace::host>>( + *optical_params_, sid, size); + } + else if (m == MemSpace::device) + { + return std::make_unique<optical::CoreState<MemSpace::device>>( + *optical_params_, sid, size); + } + CELER_ASSERT_UNREACHABLE(); +} + +//---------------------------------------------------------------------------// +/*! + * Perform a step action with host data. + */ +void OpticalLaunchAction::step(CoreParams const& params, + CoreStateHost& state) const +{ + return this->execute_impl(params, state); +} + +//---------------------------------------------------------------------------// +/*! + * Perform a step action with device data. + */ +void OpticalLaunchAction::step(CoreParams const& params, + CoreStateDevice& state) const +{ + return this->execute_impl(params, state); +} + +//---------------------------------------------------------------------------// +/*! + * Launch the optical tracking loop. + */ +template<MemSpace M> +void OpticalLaunchAction::execute_impl(CoreParams const& core_params, + CoreState<M>& core_state) const +{ + auto& offload_state = get<OpticalOffloadState<M>>( + core_state.aux(), offload_params_->aux_id()); + auto& optical_state + = get<optical::CoreState<M>>(core_state.aux(), this->aux_id()); + + // Loop! + CELER_ASSERT(offload_state); + CELER_ASSERT(optical_state.size() > 0); + CELER_DISCARD(core_params); +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/optical/detail/OpticalLaunchAction.hh b/src/celeritas/optical/detail/OpticalLaunchAction.hh new file mode 100644 index 0000000000..5c9ae9697b --- /dev/null +++ b/src/celeritas/optical/detail/OpticalLaunchAction.hh @@ -0,0 +1,112 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detail/OpticalLaunchAction.hh +//---------------------------------------------------------------------------// +#pragma once + +#include <memory> +#include <string_view> + +#include "corecel/Macros.hh" +#include "corecel/data/AuxInterface.hh" +#include "celeritas/global/ActionInterface.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +class CoreParams; +namespace optical +{ +class CoreParams; +class MaterialParams; +} // namespace optical + +namespace detail +{ +class OffloadParams; +} + +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Manage optical params and state, launching the optical stepping loop. + * + * This stores the optical tracking loop's core params, initializing them at + * the beginning of the run, and stores the optical core state as "aux" + * data. + */ +class OpticalLaunchAction : public AuxParamsInterface, + public CoreStepActionInterface +{ + public: + //!@{ + //! \name Type aliases + using SPOffloadParams = std::shared_ptr<detail::OffloadParams>; + using SPConstMaterial = std::shared_ptr<optical::MaterialParams const>; + //!@} + + public: + // Construct and add to core params + static std::shared_ptr<OpticalLaunchAction> + make_and_insert(CoreParams const& core, + SPConstMaterial material, + SPOffloadParams offload); + + // Construct with IDs, core for copying params, offload gen data + OpticalLaunchAction(ActionId id, + AuxId data_id, + CoreParams const& core, + SPConstMaterial material, + SPOffloadParams offload); + + //!@{ + //! \name Aux/action metadata interface + //! Short name for the action + std::string_view label() const final { return "optical-offload-launch"; } + // Name of the action (for user output) + std::string_view description() const final; + //!@} + + //!@{ + //! \name Aux interface + //! Index of this class instance in its registry + AuxId aux_id() const final { return aux_id_; } + // Build optical core state data for a stream + UPState create_state(MemSpace, StreamId, size_type) const final; + //!@} + + //!@{ + //! \name Action interface + //! ID of the model + ActionId action_id() const final { return action_id_; } + //! Dependency ordering of the action + StepActionOrder order() const final { return StepActionOrder::user_post; } + // Launch kernel with host data + void step(CoreParams const&, CoreStateHost&) const final; + // Launch kernel with device data + void step(CoreParams const&, CoreStateDevice&) const final; + //!@} + + private: + using SPOpticalParams = std::shared_ptr<optical::CoreParams>; + + //// DATA //// + + ActionId action_id_; + AuxId aux_id_; + SPOffloadParams offload_params_; + SPOpticalParams optical_params_; + + //// HELPERS //// + + template<MemSpace M> + void execute_impl(CoreParams const&, CoreState<M>&) const; +}; + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/corecel/data/CollectionStateStore.hh b/src/corecel/data/CollectionStateStore.hh index 1c6d674ef0..c9ec5189a9 100644 --- a/src/corecel/data/CollectionStateStore.hh +++ b/src/corecel/data/CollectionStateStore.hh @@ -115,6 +115,7 @@ CollectionStateStore<S, M>::CollectionStateStore(HostCRef<P> const& p, CELER_EXPECT(sid); CELER_EXPECT(size > 0); resize(&val_, p, sid, size); + CELER_ASSERT(val_); // Save reference ref_ = val_; @@ -134,6 +135,7 @@ CollectionStateStore<S, M>::CollectionStateStore(HostCRef<P> const& p, { CELER_EXPECT(size > 0); resize(&val_, p, size); + CELER_ASSERT(val_); // Save reference ref_ = val_; @@ -151,6 +153,7 @@ CollectionStateStore<S, M>::CollectionStateStore(size_type size) { CELER_EXPECT(size > 0); resize(&val_, size); + CELER_ASSERT(val_); // Save reference ref_ = val_; @@ -181,6 +184,7 @@ CollectionStateStore<S, M>::CollectionStateStore(S<W2, M2> const& other) // Assign using const-cast because state copy operators have to be mutable // even when they're just copying... val_ = const_cast<S<W2, M2>&>(other); + CELER_ASSERT(val_); // Save reference ref_ = val_; } @@ -197,6 +201,7 @@ auto CollectionStateStore<S, M>::operator=(S<W2, M2> const& other) CELER_EXPECT(other); // Assign val_ = const_cast<S<W2, M2>&>(other); + CELER_ASSERT(val_); // Save reference ref_ = val_; return *this; diff --git a/test/celeritas/optical/OpticalCollector.test.cc b/test/celeritas/optical/OpticalCollector.test.cc index 7aa33eed73..4cf598ef9c 100644 --- a/test/celeritas/optical/OpticalCollector.test.cc +++ b/test/celeritas/optical/OpticalCollector.test.cc @@ -155,9 +155,9 @@ auto LArSphereOffloadTest::build_along_step() -> SPConstAction void LArSphereOffloadTest::build_optical_collector() { OpticalCollector::Input inp; + inp.material = this->optical_material(); if (use_cerenkov_) { - inp.material = this->optical_material(); inp.cerenkov = this->cerenkov(); } if (use_scintillation_) @@ -261,8 +261,8 @@ auto LArSphereOffloadTest::run(size_type num_tracks, }; RunResult result; - auto& optical_state = get<OpticalOffloadState<M>>(step.state().aux(), - collector_->aux_id()); + auto& optical_state = get<OpticalOffloadState<M>>( + step.state().aux(), collector_->offload_aux_id()); auto const& state = optical_state.store.ref(); auto const& sizes = optical_state.buffer_size;