From f0e49f63c49fd66c0f9061053188f98e040ca2f0 Mon Sep 17 00:00:00 2001 From: Julien Esseiva Date: Fri, 6 Dec 2024 16:19:41 -0800 Subject: [PATCH 01/11] add interface for lazy evaluation of senses --- src/orange/univ/detail/LogicEvaluator.hh | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/orange/univ/detail/LogicEvaluator.hh b/src/orange/univ/detail/LogicEvaluator.hh index 1790f9005a..3acda71e46 100644 --- a/src/orange/univ/detail/LogicEvaluator.hh +++ b/src/orange/univ/detail/LogicEvaluator.hh @@ -7,6 +7,7 @@ //---------------------------------------------------------------------------// #pragma once +#include #include "corecel/Assert.hh" #include "corecel/cont/Span.hh" #include "corecel/data/LdgIterator.hh" @@ -38,6 +39,10 @@ class LogicEvaluator // Evaluate a logical expression, substituting bools from the vector inline CELER_FUNCTION bool operator()(SpanConstSense values) const; + // Evaluate a logical expression, with on-the-fly sense evaluation + template, bool> = true> + inline CELER_FUNCTION bool operator()(F&& eval_sense) const; + private: //// DATA //// @@ -59,6 +64,19 @@ CELER_FUNCTION LogicEvaluator::LogicEvaluator(SpanConstLogic logic) * Evaluate a logical expression, substituting bools from the sense view. */ CELER_FUNCTION bool LogicEvaluator::operator()(SpanConstSense values) const +{ + auto calc_sense = [&](FaceId face_id){ + return values[face_id.get()]; + }; + return (*this)(calc_sense); +} + +//---------------------------------------------------------------------------// +/*! + * Evaluate a logical expression, with on-the-fly sense evaluation. + */ +template, bool>> +CELER_FUNCTION bool LogicEvaluator::operator()(F&& eval_sense) const { LogicStack stack; @@ -67,8 +85,7 @@ CELER_FUNCTION bool LogicEvaluator::operator()(SpanConstSense values) const if (!logic::is_operator_token(lgc)) { // Push a boolean from the senses onto the stack - CELER_EXPECT(lgc < values.size()); - stack.push(static_cast(values[lgc])); + stack.push(static_cast(eval_sense(FaceId{lgc}))); continue; } From e2149a036d60a3db4d8fa17670d9af3a84ee806f Mon Sep 17 00:00:00 2001 From: Julien Esseiva Date: Fri, 6 Dec 2024 16:20:11 -0800 Subject: [PATCH 02/11] lazy evaluation of senses --- src/orange/univ/SimpleUnitTracker.hh | 48 ++++---- src/orange/univ/detail/LazySenseCalculator.hh | 111 ++++++++++++++++++ 2 files changed, 136 insertions(+), 23 deletions(-) create mode 100644 src/orange/univ/detail/LazySenseCalculator.hh diff --git a/src/orange/univ/SimpleUnitTracker.hh b/src/orange/univ/SimpleUnitTracker.hh index b1ae943666..1a08597704 100644 --- a/src/orange/univ/SimpleUnitTracker.hh +++ b/src/orange/univ/SimpleUnitTracker.hh @@ -10,10 +10,12 @@ #include "corecel/Assert.hh" #include "corecel/math/Algorithms.hh" #include "orange/OrangeData.hh" +#include "orange/OrangeTypes.hh" #include "orange/detail/BIHEnclosingVolFinder.hh" #include "orange/surf/LocalSurfaceVisitor.hh" #include "detail/InfixEvaluator.hh" +#include "detail/LazySenseCalculator.hh" #include "detail/LogicEvaluator.hh" #include "detail/SenseCalculator.hh" #include "detail/SurfaceFunctors.hh" @@ -165,22 +167,20 @@ SimpleUnitTracker::initialize(LocalState const& state) const -> Initialization CELER_EXPECT(params_); CELER_EXPECT(!state.surface && !state.volume); - detail::SenseCalculator calc_senses( - this->make_surface_visitor(), state.pos, state.temp_sense); + detail::LazySenseCalculator calc_senses(this->make_surface_visitor(), + state.pos); // Use the BIH to locate a position that's inside, and save whether it's on // a surface in the found volume - bool on_surface{false}; - auto is_inside - = [this, &calc_senses, &on_surface](LocalVolumeId id) -> bool { + auto is_inside = [this, &calc_senses](LocalVolumeId id) -> bool { VolumeView vol = this->make_local_volume(id); - auto logic_state = calc_senses(vol); - on_surface = static_cast(logic_state.face); - return detail::LogicEvaluator(vol.logic())(logic_state.senses); + auto bind_calc_sense + = [&](FaceId face_id) { return calc_senses(vol, face_id); }; + return detail::LogicEvaluator(vol.logic())(bind_calc_sense); }; LocalVolumeId id = this->find_volume_where(state.pos, is_inside); - if (on_surface) + if (static_cast(calc_senses.on_face())) { // Prohibit initialization on a surface id = {}; @@ -202,12 +202,12 @@ CELER_FUNCTION auto SimpleUnitTracker::cross_boundary(LocalState const& state) const -> Initialization { CELER_EXPECT(state.surface && state.volume); - detail::SenseCalculator calc_senses( - this->make_surface_visitor(), state.pos, state.temp_sense); detail::OnLocalSurface on_surface; - auto is_inside = [this, &state, &calc_senses, &on_surface]( - LocalVolumeId const& id) -> bool { + auto is_inside + = [this, &state, &on_surface](LocalVolumeId const& id) -> bool { + detail::LazySenseCalculator calc_senses(this->make_surface_visitor(), + state.pos); if (id == state.volume) { // Cannot cross surface into the same volume @@ -215,13 +215,14 @@ SimpleUnitTracker::cross_boundary(LocalState const& state) const -> Initializati } VolumeView vol = this->make_local_volume(id); - auto logic_state - = calc_senses(vol, detail::find_face(vol, state.surface)); - - if (detail::LogicEvaluator(vol.logic())(logic_state.senses)) + auto on_face = detail::find_face(vol, state.surface); + auto bind_calc_sense = [&](FaceId face_id) { + return calc_senses(vol, face_id, on_face); + }; + if (detail::LogicEvaluator(vol.logic())(bind_calc_sense)) { // Inside: find and save the local surface ID, and end the search - on_surface = get_surface(vol, logic_state.face); + on_surface = get_surface(vol, calc_senses.on_face()); return true; } return false; @@ -620,10 +621,12 @@ CELER_FUNCTION auto SimpleUnitTracker::background_intersect( { CELER_ASSERT(vid != state.volume); VolumeView vol = this->make_local_volume(vid); - auto logic_state = detail::SenseCalculator{ - this->make_surface_visitor(), pos, state.temp_sense}(vol); + auto calc_senses = detail::LazySenseCalculator{ + this->make_surface_visitor(), pos}; + auto bind_calc_sense + = [&](FaceId face_id) { return calc_senses(vol, face_id); }; - if (detail::LogicEvaluator{vol.logic()}(logic_state.senses)) + if (detail::LogicEvaluator{vol.logic()}(bind_calc_sense)) { // We are in this new volume by crossing the tested surface. // Get the sense corresponding to this "crossed" surface. @@ -633,8 +636,7 @@ CELER_FUNCTION auto SimpleUnitTracker::background_intersect( Intersection result; result.distance = state.temp_next.distance[isect]; result.surface = detail::OnLocalSurface{ - surface, - flip_sense(logic_state.senses[face.unchecked_get()])}; + surface, flip_sense(bind_calc_sense(face))}; return result; } } diff --git a/src/orange/univ/detail/LazySenseCalculator.hh b/src/orange/univ/detail/LazySenseCalculator.hh new file mode 100644 index 0000000000..6bde62c01d --- /dev/null +++ b/src/orange/univ/detail/LazySenseCalculator.hh @@ -0,0 +1,111 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2021-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 orange/univ/detail/LazySenseCalculator.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/Assert.hh" +#include "corecel/cont/Range.hh" +#include "corecel/cont/Span.hh" +#include "orange/OrangeTypes.hh" +#include "orange/surf/LocalSurfaceVisitor.hh" +#include "orange/univ/detail/Types.hh" + +#include "SurfaceFunctors.hh" +#include "../VolumeView.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Calculate senses with a fixed particle position. + * + * This is an implementation detail used in initialization *and* complex + * intersection. + */ +class LazySenseCalculator +{ + public: + // Construct from persistent, current, and temporary data + inline CELER_FUNCTION + LazySenseCalculator(LocalSurfaceVisitor const& visit, Real3 const& pos); + + // Calculate senses for a single face of the given volume, possibly on a + // face + inline CELER_FUNCTION Sense operator()(VolumeView const& vol, + FaceId face_id, + OnFace face = {}); + + OnFace& on_face() { return face_; } + + private: + //! The first face encountered that we are "on" + OnFace face_; + + //! Apply a function to a local surface + LocalSurfaceVisitor visit_; + + //! Local position + Real3 pos_; +}; + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Construct from persistent, current, and temporary data. + */ +CELER_FUNCTION +LazySenseCalculator::LazySenseCalculator(LocalSurfaceVisitor const& visit, + Real3 const& pos) + : visit_{visit}, pos_(pos) +{ +} + +//---------------------------------------------------------------------------// +/*! + * Calculate senses for the given volume. + * + * If the point is exactly on one of the volume's surfaces, the \c face value + * of the return will be set. + */ +CELER_FUNCTION auto LazySenseCalculator::operator()(VolumeView const& vol, + FaceId face_id, + OnFace face) -> Sense +{ + CELER_EXPECT(!face || face.id() < vol.num_faces()); + + if (!face_ && face) + { + face_ = face; + } + + Sense sense; + if (face_id != face.id()) + { + // Calculate sense + SignedSense ss = visit_(CalcSense{pos_}, vol.get_surface(face_id)); + sense = to_sense(ss); + if (ss == SignedSense::on && !face_) + { + // This is the first face that we're exactly on: save it + face_ = {face_id, sense}; + } + } + else + { + // Sense is known a priori + sense = face.sense(); + } + + return sense; +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas From 4fedccf547aaf50bc831847012bcad0daef8c769 Mon Sep 17 00:00:00 2001 From: Julien Esseiva Date: Fri, 6 Dec 2024 16:20:32 -0800 Subject: [PATCH 03/11] format --- src/orange/univ/detail/LogicEvaluator.hh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/orange/univ/detail/LogicEvaluator.hh b/src/orange/univ/detail/LogicEvaluator.hh index 3acda71e46..d9cf32a54b 100644 --- a/src/orange/univ/detail/LogicEvaluator.hh +++ b/src/orange/univ/detail/LogicEvaluator.hh @@ -8,6 +8,7 @@ #pragma once #include + #include "corecel/Assert.hh" #include "corecel/cont/Span.hh" #include "corecel/data/LdgIterator.hh" @@ -65,9 +66,7 @@ CELER_FUNCTION LogicEvaluator::LogicEvaluator(SpanConstLogic logic) */ CELER_FUNCTION bool LogicEvaluator::operator()(SpanConstSense values) const { - auto calc_sense = [&](FaceId face_id){ - return values[face_id.get()]; - }; + auto calc_sense = [&](FaceId face_id) { return values[face_id.get()]; }; return (*this)(calc_sense); } From 2df2f886cd5dc4894aa12816dd0cd65087a5f939 Mon Sep 17 00:00:00 2001 From: Julien Esseiva Date: Tue, 10 Dec 2024 14:41:30 -0800 Subject: [PATCH 04/11] pass sense mod flags to the sense calculator --- src/orange/OrangeData.hh | 4 +++ src/orange/OrangeTrackView.hh | 16 ++++++++++ src/orange/OrangeTypes.hh | 31 +++++++++++++++++++ src/orange/univ/SimpleUnitTracker.hh | 10 +++--- src/orange/univ/detail/LazySenseCalculator.hh | 21 ++++++++++--- src/orange/univ/detail/Types.hh | 1 + test/orange/univ/SimpleUnitTracker.test.cc | 1 + test/orange/univ/SimpleUnitTracker.test.hh | 3 ++ 8 files changed, 78 insertions(+), 9 deletions(-) diff --git a/src/orange/OrangeData.hh b/src/orange/OrangeData.hh index ffd2444547..f310a79fdd 100644 --- a/src/orange/OrangeData.hh +++ b/src/orange/OrangeData.hh @@ -473,6 +473,7 @@ struct OrangeStateData // Scratch space with dimensions {track}{max_faces} Items temp_sense; + Items temp_sense_mod; // Scratch space with dimensions {track}{max_intersections} Items temp_face; @@ -500,6 +501,7 @@ struct OrangeStateData && vol.size() == max_depth * this->size() && universe.size() == max_depth * this->size() && !temp_sense.empty() + && !temp_sense_mod.empty() && !temp_face.empty() && temp_distance.size() == temp_face.size() && temp_isect.size() == temp_face.size(); @@ -533,6 +535,7 @@ struct OrangeStateData universe = other.universe; temp_sense = other.temp_sense; + temp_sense_mod = other.temp_sense_mod; temp_face = other.temp_face; temp_distance = other.temp_distance; @@ -576,6 +579,7 @@ inline void resize(OrangeStateData* data, size_type face_states = params.scalars.max_faces * num_tracks; resize(&data->temp_sense, face_states); + resize(&data->temp_sense_mod, face_states); size_type isect_states = params.scalars.max_intersections * num_tracks; resize(&data->temp_face, isect_states); diff --git a/src/orange/OrangeTrackView.hh b/src/orange/OrangeTrackView.hh index 74bae70e17..2c714961dd 100644 --- a/src/orange/OrangeTrackView.hh +++ b/src/orange/OrangeTrackView.hh @@ -198,6 +198,7 @@ class OrangeTrackView // Create local sense reference inline CELER_FUNCTION Span make_temp_sense() const; + inline CELER_FUNCTION Span make_temp_sense_mod() const; // Create local distance inline CELER_FUNCTION detail::TempNextFace make_temp_next() const; @@ -276,6 +277,7 @@ OrangeTrackView::operator=(Initializer_t const& init) local.volume = {}; local.surface = {}; local.temp_sense = this->make_temp_sense(); + local.temp_sense_mod = this->make_temp_sense_mod(); // Helpers for applying parent-to-daughter transformations TransformVisitor apply_transform{params_}; @@ -689,6 +691,7 @@ CELER_FUNCTION void OrangeTrackView::cross_boundary() local.volume = lsa.vol(); local.surface = {this->surf(), this->sense()}; local.temp_sense = this->make_temp_sense(); + local.temp_sense_mod = this->make_temp_sense_mod(); } TrackerVisitor visit_tracker{params_}; @@ -1047,6 +1050,18 @@ CELER_FUNCTION Span OrangeTrackView::make_temp_sense() const offset, max_faces); } +//---------------------------------------------------------------------------// +/*! + * Get a reference to the current volume, or to world volume if outside. + */ +CELER_FUNCTION Span OrangeTrackView::make_temp_sense_mod() const +{ + auto const max_faces = params_.scalars.max_faces; + auto offset = track_slot_.get() * max_faces; + return states_.temp_sense_mod[AllItems{}] + .subspan(offset, max_faces); +} + //---------------------------------------------------------------------------// /*! * Set up intersection scratch space. @@ -1089,6 +1104,7 @@ OrangeTrackView::make_local_state(LevelId level) const local.surface = {}; } local.temp_sense = this->make_temp_sense(); + local.temp_sense_mod = this->make_temp_sense_mod(); local.temp_next = this->make_temp_next(); return local; } diff --git a/src/orange/OrangeTypes.hh b/src/orange/OrangeTypes.hh index c6625d5ec1..6a7fb5208a 100644 --- a/src/orange/OrangeTypes.hh +++ b/src/orange/OrangeTypes.hh @@ -101,6 +101,17 @@ enum class Sense : bool outside, //!< Expression is greater than zero }; +//---------------------------------------------------------------------------// +/*! + * Transformations to apply to senses when using lazy sense evaluation. + */ +enum class SenseMod +{ + normal = 0, + flipped = 1 << 0, +}; +using SenseModFlags = std::underlying_type_t; + //---------------------------------------------------------------------------// /*! * Enumeration for mapping surface classes to integers. @@ -378,6 +389,26 @@ CELER_CONSTEXPR_FUNCTION Sense to_sense(bool s) return static_cast(-static_cast(orig)); } +//---------------------------------------------------------------------------// +/*! + * Check if a sense modifier is set. + */ +[[nodiscard]] CELER_CONSTEXPR_FUNCTION bool +is_sense_mod_set(SenseMod mod, SenseModFlags flags) +{ + return (flags & static_cast(mod)) != 0; +} + +//---------------------------------------------------------------------------// +/*! + * Set a sense modifier. + */ +[[nodiscard]] CELER_CONSTEXPR_FUNCTION SenseModFlags +set_sense_mod(SenseMod mod, SenseModFlags flags) +{ + return flags | static_cast(mod); +} + //---------------------------------------------------------------------------// /*! * Change whether a boundary crossing is reentrant or exiting. diff --git a/src/orange/univ/SimpleUnitTracker.hh b/src/orange/univ/SimpleUnitTracker.hh index 1a08597704..bfc47244c9 100644 --- a/src/orange/univ/SimpleUnitTracker.hh +++ b/src/orange/univ/SimpleUnitTracker.hh @@ -167,8 +167,8 @@ SimpleUnitTracker::initialize(LocalState const& state) const -> Initialization CELER_EXPECT(params_); CELER_EXPECT(!state.surface && !state.volume); - detail::LazySenseCalculator calc_senses(this->make_surface_visitor(), - state.pos); + detail::LazySenseCalculator calc_senses( + this->make_surface_visitor(), state.pos, state.temp_sense_mod); // Use the BIH to locate a position that's inside, and save whether it's on // a surface in the found volume @@ -206,8 +206,8 @@ SimpleUnitTracker::cross_boundary(LocalState const& state) const -> Initializati detail::OnLocalSurface on_surface; auto is_inside = [this, &state, &on_surface](LocalVolumeId const& id) -> bool { - detail::LazySenseCalculator calc_senses(this->make_surface_visitor(), - state.pos); + detail::LazySenseCalculator calc_senses( + this->make_surface_visitor(), state.pos, state.temp_sense_mod); if (id == state.volume) { // Cannot cross surface into the same volume @@ -622,7 +622,7 @@ CELER_FUNCTION auto SimpleUnitTracker::background_intersect( CELER_ASSERT(vid != state.volume); VolumeView vol = this->make_local_volume(vid); auto calc_senses = detail::LazySenseCalculator{ - this->make_surface_visitor(), pos}; + this->make_surface_visitor(), pos, state.temp_sense_mod}; auto bind_calc_sense = [&](FaceId face_id) { return calc_senses(vol, face_id); }; diff --git a/src/orange/univ/detail/LazySenseCalculator.hh b/src/orange/univ/detail/LazySenseCalculator.hh index 6bde62c01d..6cfc9d055c 100644 --- a/src/orange/univ/detail/LazySenseCalculator.hh +++ b/src/orange/univ/detail/LazySenseCalculator.hh @@ -32,8 +32,9 @@ class LazySenseCalculator { public: // Construct from persistent, current, and temporary data - inline CELER_FUNCTION - LazySenseCalculator(LocalSurfaceVisitor const& visit, Real3 const& pos); + inline CELER_FUNCTION LazySenseCalculator(LocalSurfaceVisitor const& visit, + Real3 const& pos, + Span sense_mod); // Calculate senses for a single face of the given volume, possibly on a // face @@ -52,6 +53,9 @@ class LazySenseCalculator //! Local position Real3 pos_; + + //! Temporary senses + Span sense_storage_; }; //---------------------------------------------------------------------------// @@ -62,9 +66,14 @@ class LazySenseCalculator */ CELER_FUNCTION LazySenseCalculator::LazySenseCalculator(LocalSurfaceVisitor const& visit, - Real3 const& pos) - : visit_{visit}, pos_(pos) + Real3 const& pos, + Span sense_mod) + : visit_{visit}, pos_(pos), sense_storage_{sense_mod} { + for (auto& sense : sense_storage_) + { + sense = static_cast(SenseMod::normal); + } } //---------------------------------------------------------------------------// @@ -102,6 +111,10 @@ CELER_FUNCTION auto LazySenseCalculator::operator()(VolumeView const& vol, // Sense is known a priori sense = face.sense(); } + if (is_sense_mod_set(SenseMod::flipped, sense_storage_[face_id.get()])) + { + sense = flip_sense(sense); + } return sense; } diff --git a/src/orange/univ/detail/Types.hh b/src/orange/univ/detail/Types.hh index 35a275511b..d28b280cd5 100644 --- a/src/orange/univ/detail/Types.hh +++ b/src/orange/univ/detail/Types.hh @@ -165,6 +165,7 @@ struct LocalState LocalVolumeId volume; OnLocalSurface surface; Span temp_sense; + Span temp_sense_mod; TempNextFace temp_next; }; diff --git a/test/orange/univ/SimpleUnitTracker.test.cc b/test/orange/univ/SimpleUnitTracker.test.cc index 8af6ab12c2..4e90261ed9 100644 --- a/test/orange/univ/SimpleUnitTracker.test.cc +++ b/test/orange/univ/SimpleUnitTracker.test.cc @@ -146,6 +146,7 @@ LocalState SimpleUnitTrackerTest::make_state(Real3 pos, Real3 dir) auto const& hsref = this->host_state(); auto face_storage = hsref.temp_face[AllItems{}]; state.temp_sense = hsref.temp_sense[AllItems{}]; + state.temp_sense_mod = hsref.temp_sense_mod[AllItems{}]; state.temp_next.face = face_storage.data(); state.temp_next.distance = hsref.temp_distance[AllItems{}].data(); diff --git a/test/orange/univ/SimpleUnitTracker.test.hh b/test/orange/univ/SimpleUnitTracker.test.hh index a79e28effc..cac6f3ff33 100644 --- a/test/orange/univ/SimpleUnitTracker.test.hh +++ b/test/orange/univ/SimpleUnitTracker.test.hh @@ -9,6 +9,7 @@ #include "corecel/Macros.hh" #include "corecel/Types.hh" #include "orange/OrangeData.hh" +#include "orange/OrangeTypes.hh" #include "orange/detail/LevelStateAccessor.hh" #include "orange/univ/SimpleUnitTracker.hh" #include "orange/univ/detail/Types.hh" @@ -60,6 +61,8 @@ inline CELER_FUNCTION LocalState build_local_state(ParamsRef params, size_type const max_faces = params.scalars.max_faces; lstate.temp_sense = states.temp_sense[build_range(max_faces, tid)]; + lstate.temp_sense_mod + = states.temp_sense_mod[build_range(max_faces, tid)]; size_type const max_isect = params.scalars.max_intersections; lstate.temp_next.face From 109aa3aba67d80621db4cb55b7aa04eb79aa3859 Mon Sep 17 00:00:00 2001 From: Julien Esseiva Date: Tue, 10 Dec 2024 14:56:00 -0800 Subject: [PATCH 05/11] lazy sense evaluation for complex intersect --- src/orange/OrangeTypes.hh | 10 ++++++++++ src/orange/univ/SimpleUnitTracker.hh | 22 +++++++++++++--------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/orange/OrangeTypes.hh b/src/orange/OrangeTypes.hh index 6a7fb5208a..942ce656bc 100644 --- a/src/orange/OrangeTypes.hh +++ b/src/orange/OrangeTypes.hh @@ -409,6 +409,16 @@ set_sense_mod(SenseMod mod, SenseModFlags flags) return flags | static_cast(mod); } +//---------------------------------------------------------------------------// +/*! + * Flip a sense modifier. + */ +[[nodiscard]] CELER_CONSTEXPR_FUNCTION SenseModFlags +flip_sense_mod(SenseMod mod, SenseModFlags flags) +{ + return flags ^ static_cast(mod); +} + //---------------------------------------------------------------------------// /*! * Change whether a boundary crossing is reentrant or exiting. diff --git a/src/orange/univ/SimpleUnitTracker.hh b/src/orange/univ/SimpleUnitTracker.hh index bfc47244c9..941c6e3205 100644 --- a/src/orange/univ/SimpleUnitTracker.hh +++ b/src/orange/univ/SimpleUnitTracker.hh @@ -529,13 +529,14 @@ SimpleUnitTracker::complex_intersect(LocalState const& state, CELER_ASSERT(num_isect > 0); // Calculate local senses, taking current face into account - auto logic_state = detail::SenseCalculator( - this->make_surface_visitor(), state.pos, state.temp_sense)( - vol, detail::find_face(vol, state.surface)); - + auto calc_senses = detail::LazySenseCalculator( + this->make_surface_visitor(), state.pos, state.temp_sense_mod); + auto bind_calc_sense = [&](FaceId face_id) { + return calc_senses(vol, face_id, detail::find_face(vol, state.surface)); + }; // Current senses should put us inside the volume detail::LogicEvaluator is_inside(vol.logic()); - CELER_ASSERT(is_inside(logic_state.senses)); + CELER_ASSERT(is_inside(bind_calc_sense)); // Loop over distances and surface indices to cross by iterating over // temp_next.isect[:num_isect]. @@ -548,16 +549,19 @@ SimpleUnitTracker::complex_intersect(LocalState const& state, // Face being crossed in this ordered intersection FaceId face = state.temp_next.face[isect]; // Flip the sense of the face being crossed - Sense new_sense = flip_sense(logic_state.senses[face.get()]); - logic_state.senses[face.unchecked_get()] = new_sense; - if (!is_inside(logic_state.senses)) + state.temp_sense_mod[face.get()] = flip_sense_mod( + SenseMod::flipped, state.temp_sense_mod[face.get()]); + + if (!is_inside(bind_calc_sense)) { // Flipping this sense puts us outside the current volume: in // other words, only after crossing all the internal surfaces along // this direction do we hit a surface that actually puts us // outside. Intersection result; - result.surface = {vol.get_surface(face), flip_sense(new_sense)}; + + result.surface + = {vol.get_surface(face), flip_sense(bind_calc_sense(face))}; result.distance = state.temp_next.distance[isect]; CELER_ENSURE(result.distance > 0 && !std::isinf(result.distance)); return result; From dd678e40183c6709595982f2d36c46dbabe0df0f Mon Sep 17 00:00:00 2001 From: Julien Esseiva Date: Tue, 10 Dec 2024 15:11:44 -0800 Subject: [PATCH 06/11] construct state --- test/orange/univ/TrackerVisitor.test.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/orange/univ/TrackerVisitor.test.cc b/test/orange/univ/TrackerVisitor.test.cc index 42ad1f751f..d0a81d1a6b 100644 --- a/test/orange/univ/TrackerVisitor.test.cc +++ b/test/orange/univ/TrackerVisitor.test.cc @@ -9,6 +9,7 @@ #include "orange/univ/TrackerVisitor.hh" #include "orange/OrangeGeoTestBase.hh" +#include "orange/OrangeTypes.hh" #include "orange/univ/detail/Types.hh" #include "celeritas_test.hh" @@ -48,6 +49,7 @@ detail::LocalState TrackerVisitorTest::make_state(Real3 pos, Real3 dir) auto const& hsref = this->host_state(); auto face_storage = hsref.temp_face[AllItems{}]; state.temp_sense = hsref.temp_sense[AllItems{}]; + state.temp_sense_mod = hsref.temp_sense_mod[AllItems{}]; state.temp_next.face = face_storage.data(); state.temp_next.distance = hsref.temp_distance[AllItems{}].data(); From 24d1aef45918a5e0007393f72f66136dde61d64b Mon Sep 17 00:00:00 2001 From: Julien Esseiva Date: Tue, 10 Dec 2024 15:12:19 -0800 Subject: [PATCH 07/11] use one byte to store flags --- src/orange/OrangeTypes.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/orange/OrangeTypes.hh b/src/orange/OrangeTypes.hh index 942ce656bc..5f8c3a48fc 100644 --- a/src/orange/OrangeTypes.hh +++ b/src/orange/OrangeTypes.hh @@ -105,7 +105,7 @@ enum class Sense : bool /*! * Transformations to apply to senses when using lazy sense evaluation. */ -enum class SenseMod +enum class SenseMod : unsigned char { normal = 0, flipped = 1 << 0, From e2f55c01380c6301370e89b9c4e8e79ab8886c10 Mon Sep 17 00:00:00 2001 From: Julien Esseiva Date: Tue, 10 Dec 2024 15:36:40 -0800 Subject: [PATCH 08/11] cache sense evaluation --- src/orange/OrangeTypes.hh | 11 ++++++ src/orange/univ/SimpleUnitTracker.hh | 32 +++++++++++------ src/orange/univ/detail/LazySenseCalculator.hh | 34 +++++++++++++++---- 3 files changed, 61 insertions(+), 16 deletions(-) diff --git a/src/orange/OrangeTypes.hh b/src/orange/OrangeTypes.hh index 5f8c3a48fc..1079174136 100644 --- a/src/orange/OrangeTypes.hh +++ b/src/orange/OrangeTypes.hh @@ -109,6 +109,7 @@ enum class SenseMod : unsigned char { normal = 0, flipped = 1 << 0, + cached = 1 << 1, }; using SenseModFlags = std::underlying_type_t; @@ -409,6 +410,16 @@ set_sense_mod(SenseMod mod, SenseModFlags flags) return flags | static_cast(mod); } +//---------------------------------------------------------------------------// +/*! + * Unset a sense modifier. + */ +[[nodiscard]] CELER_CONSTEXPR_FUNCTION SenseModFlags +unset_sense_mod(SenseMod mod, SenseModFlags flags) +{ + return flags & ~static_cast(mod); +} + //---------------------------------------------------------------------------// /*! * Flip a sense modifier. diff --git a/src/orange/univ/SimpleUnitTracker.hh b/src/orange/univ/SimpleUnitTracker.hh index 941c6e3205..9988c0e02f 100644 --- a/src/orange/univ/SimpleUnitTracker.hh +++ b/src/orange/univ/SimpleUnitTracker.hh @@ -167,8 +167,10 @@ SimpleUnitTracker::initialize(LocalState const& state) const -> Initialization CELER_EXPECT(params_); CELER_EXPECT(!state.surface && !state.volume); - detail::LazySenseCalculator calc_senses( - this->make_surface_visitor(), state.pos, state.temp_sense_mod); + detail::LazySenseCalculator calc_senses(this->make_surface_visitor(), + state.pos, + state.temp_sense, + state.temp_sense_mod); // Use the BIH to locate a position that's inside, and save whether it's on // a surface in the found volume @@ -176,7 +178,9 @@ SimpleUnitTracker::initialize(LocalState const& state) const -> Initialization VolumeView vol = this->make_local_volume(id); auto bind_calc_sense = [&](FaceId face_id) { return calc_senses(vol, face_id); }; - return detail::LogicEvaluator(vol.logic())(bind_calc_sense); + auto inside = detail::LogicEvaluator(vol.logic())(bind_calc_sense); + calc_senses.invalidate_cache(); + return inside; }; LocalVolumeId id = this->find_volume_where(state.pos, is_inside); @@ -206,8 +210,10 @@ SimpleUnitTracker::cross_boundary(LocalState const& state) const -> Initializati detail::OnLocalSurface on_surface; auto is_inside = [this, &state, &on_surface](LocalVolumeId const& id) -> bool { - detail::LazySenseCalculator calc_senses( - this->make_surface_visitor(), state.pos, state.temp_sense_mod); + detail::LazySenseCalculator calc_senses(this->make_surface_visitor(), + state.pos, + state.temp_sense, + state.temp_sense_mod); if (id == state.volume) { // Cannot cross surface into the same volume @@ -529,8 +535,10 @@ SimpleUnitTracker::complex_intersect(LocalState const& state, CELER_ASSERT(num_isect > 0); // Calculate local senses, taking current face into account - auto calc_senses = detail::LazySenseCalculator( - this->make_surface_visitor(), state.pos, state.temp_sense_mod); + auto calc_senses = detail::LazySenseCalculator(this->make_surface_visitor(), + state.pos, + state.temp_sense, + state.temp_sense_mod); auto bind_calc_sense = [&](FaceId face_id) { return calc_senses(vol, face_id, detail::find_face(vol, state.surface)); }; @@ -551,7 +559,8 @@ SimpleUnitTracker::complex_intersect(LocalState const& state, // Flip the sense of the face being crossed state.temp_sense_mod[face.get()] = flip_sense_mod( SenseMod::flipped, state.temp_sense_mod[face.get()]); - + state.temp_sense_mod[face.get()] = unset_sense_mod( + SenseMod::cached, state.temp_sense_mod[face.get()]); if (!is_inside(bind_calc_sense)) { // Flipping this sense puts us outside the current volume: in @@ -625,8 +634,11 @@ CELER_FUNCTION auto SimpleUnitTracker::background_intersect( { CELER_ASSERT(vid != state.volume); VolumeView vol = this->make_local_volume(vid); - auto calc_senses = detail::LazySenseCalculator{ - this->make_surface_visitor(), pos, state.temp_sense_mod}; + auto calc_senses + = detail::LazySenseCalculator{this->make_surface_visitor(), + pos, + state.temp_sense, + state.temp_sense_mod}; auto bind_calc_sense = [&](FaceId face_id) { return calc_senses(vol, face_id); }; diff --git a/src/orange/univ/detail/LazySenseCalculator.hh b/src/orange/univ/detail/LazySenseCalculator.hh index 6cfc9d055c..0637158f09 100644 --- a/src/orange/univ/detail/LazySenseCalculator.hh +++ b/src/orange/univ/detail/LazySenseCalculator.hh @@ -34,7 +34,8 @@ class LazySenseCalculator // Construct from persistent, current, and temporary data inline CELER_FUNCTION LazySenseCalculator(LocalSurfaceVisitor const& visit, Real3 const& pos, - Span sense_mod); + Span sense_cache, + Span sense_flags); // Calculate senses for a single face of the given volume, possibly on a // face @@ -42,6 +43,14 @@ class LazySenseCalculator FaceId face_id, OnFace face = {}); + void invalidate_cache() + { + for (auto& flags : sense_flags_) + { + flags = unset_sense_mod(SenseMod::cached, flags); + } + } + OnFace& on_face() { return face_; } private: @@ -55,7 +64,8 @@ class LazySenseCalculator Real3 pos_; //! Temporary senses - Span sense_storage_; + Span sense_cache_; + Span sense_flags_; }; //---------------------------------------------------------------------------// @@ -67,10 +77,14 @@ class LazySenseCalculator CELER_FUNCTION LazySenseCalculator::LazySenseCalculator(LocalSurfaceVisitor const& visit, Real3 const& pos, - Span sense_mod) - : visit_{visit}, pos_(pos), sense_storage_{sense_mod} + Span sense_cache, + Span sense_flags) + : visit_{visit} + , pos_(pos) + , sense_cache_(sense_cache) + , sense_flags_{sense_flags} { - for (auto& sense : sense_storage_) + for (auto& sense : sense_flags_) { sense = static_cast(SenseMod::normal); } @@ -94,6 +108,11 @@ CELER_FUNCTION auto LazySenseCalculator::operator()(VolumeView const& vol, face_ = face; } + if (is_sense_mod_set(SenseMod::cached, sense_flags_[face_id.get()])) + { + return sense_cache_[face_id.get()]; + } + Sense sense; if (face_id != face.id()) { @@ -111,11 +130,14 @@ CELER_FUNCTION auto LazySenseCalculator::operator()(VolumeView const& vol, // Sense is known a priori sense = face.sense(); } - if (is_sense_mod_set(SenseMod::flipped, sense_storage_[face_id.get()])) + if (is_sense_mod_set(SenseMod::flipped, sense_flags_[face_id.get()])) { sense = flip_sense(sense); } + sense_flags_[face_id.get()] + = set_sense_mod(SenseMod::cached, sense_flags_[face_id.get()]); + sense_cache_[face_id.get()] = sense; return sense; } From 38ced313115c17899821118640e1d2e666fce701 Mon Sep 17 00:00:00 2001 From: Julien Esseiva Date: Tue, 10 Dec 2024 15:37:12 -0800 Subject: [PATCH 09/11] device functions --- src/orange/univ/detail/LazySenseCalculator.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/orange/univ/detail/LazySenseCalculator.hh b/src/orange/univ/detail/LazySenseCalculator.hh index 0637158f09..a2dbc06e4b 100644 --- a/src/orange/univ/detail/LazySenseCalculator.hh +++ b/src/orange/univ/detail/LazySenseCalculator.hh @@ -43,7 +43,7 @@ class LazySenseCalculator FaceId face_id, OnFace face = {}); - void invalidate_cache() + CELER_FUNCTION void invalidate_cache() { for (auto& flags : sense_flags_) { @@ -51,7 +51,7 @@ class LazySenseCalculator } } - OnFace& on_face() { return face_; } + CELER_FUNCTION OnFace& on_face() { return face_; } private: //! The first face encountered that we are "on" From 40c86315f52bed88509f73325a62662ead9c891c Mon Sep 17 00:00:00 2001 From: Julien Esseiva Date: Tue, 10 Dec 2024 16:09:12 -0800 Subject: [PATCH 10/11] move flip sense logic in the calculator --- src/orange/univ/SimpleUnitTracker.hh | 5 +---- src/orange/univ/detail/LazySenseCalculator.hh | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/orange/univ/SimpleUnitTracker.hh b/src/orange/univ/SimpleUnitTracker.hh index 9988c0e02f..4c91157941 100644 --- a/src/orange/univ/SimpleUnitTracker.hh +++ b/src/orange/univ/SimpleUnitTracker.hh @@ -557,10 +557,7 @@ SimpleUnitTracker::complex_intersect(LocalState const& state, // Face being crossed in this ordered intersection FaceId face = state.temp_next.face[isect]; // Flip the sense of the face being crossed - state.temp_sense_mod[face.get()] = flip_sense_mod( - SenseMod::flipped, state.temp_sense_mod[face.get()]); - state.temp_sense_mod[face.get()] = unset_sense_mod( - SenseMod::cached, state.temp_sense_mod[face.get()]); + calc_senses.flip_sense(face); if (!is_inside(bind_calc_sense)) { // Flipping this sense puts us outside the current volume: in diff --git a/src/orange/univ/detail/LazySenseCalculator.hh b/src/orange/univ/detail/LazySenseCalculator.hh index a2dbc06e4b..864a732856 100644 --- a/src/orange/univ/detail/LazySenseCalculator.hh +++ b/src/orange/univ/detail/LazySenseCalculator.hh @@ -43,6 +43,7 @@ class LazySenseCalculator FaceId face_id, OnFace face = {}); + //! Clear the cached sense values CELER_FUNCTION void invalidate_cache() { for (auto& flags : sense_flags_) @@ -51,8 +52,24 @@ class LazySenseCalculator } } + //! The first face encountered that we are "on" CELER_FUNCTION OnFace& on_face() { return face_; } + //! Flip the sense of a face + CELER_FUNCTION void flip_sense(FaceId face_id) + { + sense_flags_[face_id.get()] + = flip_sense_mod(SenseMod::flipped, sense_flags_[face_id.get()]); + + // If the sense is cached, flip it, otherwise it will be flipped when + // we calculate it + if (is_sense_mod_set(SenseMod::cached, sense_flags_[face_id.get()])) + { + sense_cache_[face_id.get()] + = celeritas::flip_sense(sense_cache_[face_id.get()]); + } + } + private: //! The first face encountered that we are "on" OnFace face_; @@ -132,7 +149,7 @@ CELER_FUNCTION auto LazySenseCalculator::operator()(VolumeView const& vol, } if (is_sense_mod_set(SenseMod::flipped, sense_flags_[face_id.get()])) { - sense = flip_sense(sense); + sense = celeritas::flip_sense(sense); } sense_flags_[face_id.get()] From 90ffca8771135c171b12d3ab319187e1800c2afd Mon Sep 17 00:00:00 2001 From: Julien Esseiva Date: Tue, 10 Dec 2024 16:20:50 -0800 Subject: [PATCH 11/11] init style --- src/orange/univ/detail/LazySenseCalculator.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/orange/univ/detail/LazySenseCalculator.hh b/src/orange/univ/detail/LazySenseCalculator.hh index 864a732856..ca3cbd7c79 100644 --- a/src/orange/univ/detail/LazySenseCalculator.hh +++ b/src/orange/univ/detail/LazySenseCalculator.hh @@ -97,8 +97,8 @@ LazySenseCalculator::LazySenseCalculator(LocalSurfaceVisitor const& visit, Span sense_cache, Span sense_flags) : visit_{visit} - , pos_(pos) - , sense_cache_(sense_cache) + , pos_{pos} + , sense_cache_{sense_cache} , sense_flags_{sense_flags} { for (auto& sense : sense_flags_)